665 lines
29 KiB
C#
665 lines
29 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using UnityEditor;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEngine;
|
|
using UnityEngine.AI;
|
|
using UnityEngine.SceneManagement;
|
|
using UnityEngine.UI;
|
|
|
|
public static class Lab5SceneBuilder
|
|
{
|
|
private const string SceneDirectory = "Assets/Scenes";
|
|
private const string ScenePath = SceneDirectory + "/Lab5_StealthMechanics.unity";
|
|
private const string MaterialDirectory = "Assets/Materials/Lab5";
|
|
|
|
private sealed class BuildContext
|
|
{
|
|
public Font font;
|
|
public Material floorMaterial;
|
|
public Material wallMaterial;
|
|
public Material coverMaterial;
|
|
public Material enemyMaterial;
|
|
public Material playerMaterial;
|
|
public Material zoneMaterial;
|
|
public Material visionMaterial;
|
|
public int playerLayer;
|
|
public int coverLayer;
|
|
public int obstacleLayer;
|
|
public int visionLayer;
|
|
public LayerMask obstacleMask;
|
|
public LayerMask targetMask;
|
|
}
|
|
|
|
[MenuItem("Tools/Lab5/Create Full Test Scene")]
|
|
public static void CreateFullTestScene()
|
|
{
|
|
// Builder пересобирает лабораторную с нуля, чтобы не требовать ручной настройки в инспекторе.
|
|
EnsureFolders();
|
|
EnsureTag("Player");
|
|
|
|
BuildContext context = new BuildContext
|
|
{
|
|
playerLayer = EnsureLayer("Player", 8),
|
|
coverLayer = EnsureLayer("Cover", 9),
|
|
obstacleLayer = EnsureLayer("Obstacle", 10),
|
|
visionLayer = EnsureLayer("Vision", 11)
|
|
};
|
|
|
|
context.obstacleMask = (1 << context.coverLayer) | (1 << context.obstacleLayer);
|
|
context.targetMask = 1 << context.playerLayer;
|
|
context.font = LoadBuiltinFont();
|
|
context.floorMaterial = GetOrCreateOpaqueMaterial(MaterialDirectory + "/Lab5_Floor.mat", new Color(0.21f, 0.24f, 0.28f));
|
|
context.wallMaterial = GetOrCreateOpaqueMaterial(MaterialDirectory + "/Lab5_Wall.mat", new Color(0.63f, 0.65f, 0.69f));
|
|
context.coverMaterial = GetOrCreateOpaqueMaterial(MaterialDirectory + "/Lab5_Cover.mat", new Color(0.48f, 0.31f, 0.2f));
|
|
context.enemyMaterial = GetOrCreateOpaqueMaterial(MaterialDirectory + "/Lab5_Enemy.mat", new Color(0.78f, 0.2f, 0.18f));
|
|
context.playerMaterial = GetOrCreateOpaqueMaterial(MaterialDirectory + "/Lab5_Player.mat", new Color(0.15f, 0.46f, 0.9f));
|
|
context.zoneMaterial = GetOrCreateTransparentMaterial(MaterialDirectory + "/Lab5_Zone.mat", new Color(0.15f, 0.95f, 0.95f, 0.3f));
|
|
context.visionMaterial = GetOrCreateTransparentMaterial(MaterialDirectory + "/Lab5_Vision.mat", new Color(0.2f, 0.95f, 0.35f, 0.22f));
|
|
|
|
AssetDatabase.SaveAssets();
|
|
AssetDatabase.Refresh();
|
|
|
|
Scene scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
|
|
|
GameObject sceneRoot = new GameObject("Lab5_Root");
|
|
GameObject environmentRoot = new GameObject("Environment");
|
|
environmentRoot.transform.SetParent(sceneRoot.transform);
|
|
GameObject systemsRoot = new GameObject("Systems");
|
|
systemsRoot.transform.SetParent(sceneRoot.transform);
|
|
GameObject patrolRoot = new GameObject("PatrolRoutes");
|
|
patrolRoot.transform.SetParent(sceneRoot.transform);
|
|
GameObject enemiesRoot = new GameObject("Enemies");
|
|
enemiesRoot.transform.SetParent(sceneRoot.transform);
|
|
|
|
CreateLighting();
|
|
CreateEnvironment(context, environmentRoot.transform);
|
|
|
|
GameObject noiseManager = new GameObject("NoiseManager");
|
|
noiseManager.transform.SetParent(systemsRoot.transform);
|
|
noiseManager.AddComponent<NoiseManager>();
|
|
|
|
GameObject player = CreatePlayer(context, sceneRoot.transform);
|
|
CreateUI(context, sceneRoot.transform, player);
|
|
CreateEnemies(context, enemiesRoot.transform, patrolRoot.transform, player);
|
|
|
|
bool navMeshBaked = TryBakeNavMesh();
|
|
EditorSceneManager.MarkSceneDirty(scene);
|
|
EditorSceneManager.SaveScene(scene, ScenePath);
|
|
AssetDatabase.SaveAssets();
|
|
AssetDatabase.Refresh();
|
|
|
|
Selection.activeObject = AssetDatabase.LoadAssetAtPath<SceneAsset>(ScenePath);
|
|
EditorUtility.DisplayDialog(
|
|
"Lab 5",
|
|
navMeshBaked
|
|
? "Сцена создана и сохранена. NavMesh был перестроен автоматически."
|
|
: "Сцена создана и сохранена. Если враги не встают на NavMesh, откройте Window -> AI -> Navigation и нажмите Bake.",
|
|
"OK");
|
|
}
|
|
|
|
private static void CreateLighting()
|
|
{
|
|
GameObject lightObject = new GameObject("Directional Light");
|
|
Light directionalLight = lightObject.AddComponent<Light>();
|
|
directionalLight.type = LightType.Directional;
|
|
directionalLight.intensity = 1.15f;
|
|
lightObject.transform.rotation = Quaternion.Euler(50f, -30f, 0f);
|
|
|
|
RenderSettings.ambientMode = UnityEngine.Rendering.AmbientMode.Flat;
|
|
RenderSettings.ambientLight = new Color(0.45f, 0.47f, 0.5f);
|
|
}
|
|
|
|
private static void CreateEnvironment(BuildContext context, Transform parent)
|
|
{
|
|
GameObject floor = GameObject.CreatePrimitive(PrimitiveType.Plane);
|
|
floor.name = "Floor";
|
|
floor.transform.SetParent(parent);
|
|
floor.transform.position = Vector3.zero;
|
|
floor.transform.localScale = new Vector3(4f, 1f, 4f);
|
|
floor.GetComponent<Renderer>().sharedMaterial = context.floorMaterial;
|
|
floor.layer = LayerMask.NameToLayer("Default");
|
|
SetNavigationStatic(floor);
|
|
|
|
CreateWall(context, parent, "NorthWall", new Vector3(0f, 1.5f, 20f), new Vector3(40f, 3f, 1f));
|
|
CreateWall(context, parent, "SouthWall", new Vector3(0f, 1.5f, -20f), new Vector3(40f, 3f, 1f));
|
|
CreateWall(context, parent, "EastWall", new Vector3(20f, 1.5f, 0f), new Vector3(1f, 3f, 40f));
|
|
CreateWall(context, parent, "WestWall", new Vector3(-20f, 1.5f, 0f), new Vector3(1f, 3f, 40f));
|
|
|
|
CreateWall(context, parent, "LeftDividerA", new Vector3(-5f, 1.5f, -12f), new Vector3(1f, 3f, 12f));
|
|
CreateWall(context, parent, "LeftDividerB", new Vector3(-5f, 1.5f, 3f), new Vector3(1f, 3f, 6f));
|
|
CreateWall(context, parent, "LeftDividerC", new Vector3(-5f, 1.5f, 14f), new Vector3(1f, 3f, 8f));
|
|
CreateWall(context, parent, "RightDividerA", new Vector3(5f, 1.5f, -14f), new Vector3(1f, 3f, 8f));
|
|
CreateWall(context, parent, "RightDividerB", new Vector3(5f, 1.5f, -3f), new Vector3(1f, 3f, 6f));
|
|
CreateWall(context, parent, "RightDividerC1", new Vector3(5f, 1.5f, 6f), new Vector3(1f, 3f, 8f));
|
|
CreateWall(context, parent, "RightDividerC2", new Vector3(5f, 1.5f, 16f), new Vector3(1f, 3f, 8f));
|
|
|
|
CreateWall(context, parent, "LeftRoomTopA", new Vector3(-15f, 1.5f, 7f), new Vector3(8f, 3f, 1f));
|
|
CreateWall(context, parent, "LeftRoomTopB", new Vector3(-7f, 1.5f, 7f), new Vector3(2f, 3f, 1f));
|
|
CreateWall(context, parent, "LeftRoomBottomA", new Vector3(-15f, 1.5f, -7f), new Vector3(8f, 3f, 1f));
|
|
CreateWall(context, parent, "LeftRoomBottomB", new Vector3(-7f, 1.5f, -7f), new Vector3(2f, 3f, 1f));
|
|
CreateWall(context, parent, "RightRoomTopA", new Vector3(8f, 1.5f, 7f), new Vector3(4f, 3f, 1f));
|
|
CreateWall(context, parent, "RightRoomTopB", new Vector3(16f, 1.5f, 7f), new Vector3(6f, 3f, 1f));
|
|
CreateWall(context, parent, "RightRoomBottomA", new Vector3(8f, 1.5f, -7f), new Vector3(4f, 3f, 1f));
|
|
CreateWall(context, parent, "RightRoomBottomB", new Vector3(16f, 1.5f, -7f), new Vector3(6f, 3f, 1f));
|
|
CreateCover(context, parent, "CenterBarrier", new Vector3(0f, 1f, 0f), new Vector3(2.5f, 2f, 2f));
|
|
|
|
CreateCover(context, parent, "CoverBox_A", new Vector3(-10f, 1f, -3f), new Vector3(2f, 2f, 2f));
|
|
CreateCover(context, parent, "CoverBox_B", new Vector3(-1.5f, 1f, -10f), new Vector3(2f, 2f, 2f));
|
|
CreateCover(context, parent, "CoverBox_C", new Vector3(2f, 1f, 5f), new Vector3(2.5f, 2f, 2f));
|
|
CreateCover(context, parent, "CoverWall_A", new Vector3(10f, 1.25f, 2f), new Vector3(3.5f, 2.5f, 1f));
|
|
CreateCover(context, parent, "CoverWall_B", new Vector3(13f, 1f, 13f), new Vector3(2f, 2f, 2f));
|
|
CreateCover(context, parent, "CoverBox_D", new Vector3(-15f, 1f, 14f), new Vector3(2f, 2f, 2f));
|
|
|
|
GameObject exitZone = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
|
|
exitZone.name = "ExitZone";
|
|
exitZone.transform.SetParent(parent);
|
|
exitZone.transform.position = new Vector3(15f, 0.2f, 15f);
|
|
exitZone.transform.localScale = new Vector3(2f, 0.2f, 2f);
|
|
exitZone.GetComponent<Renderer>().sharedMaterial = context.zoneMaterial;
|
|
exitZone.layer = context.visionLayer;
|
|
exitZone.GetComponent<Collider>().isTrigger = true;
|
|
exitZone.AddComponent<LevelExit>();
|
|
}
|
|
|
|
private static GameObject CreatePlayer(BuildContext context, Transform parent)
|
|
{
|
|
GameObject player = new GameObject("Player");
|
|
player.transform.SetParent(parent);
|
|
player.transform.position = new Vector3(-15f, 0f, 14f);
|
|
player.tag = "Player";
|
|
player.layer = context.playerLayer;
|
|
|
|
CharacterController controller = player.AddComponent<CharacterController>();
|
|
controller.height = 2f;
|
|
controller.radius = 0.35f;
|
|
controller.center = new Vector3(0f, 1f, 0f);
|
|
controller.stepOffset = 0.35f;
|
|
controller.slopeLimit = 45f;
|
|
|
|
GameObject body = GameObject.CreatePrimitive(PrimitiveType.Capsule);
|
|
body.name = "PlayerBody";
|
|
body.transform.SetParent(player.transform);
|
|
body.transform.localPosition = new Vector3(0f, 1f, 0f);
|
|
body.transform.localRotation = Quaternion.identity;
|
|
body.GetComponent<Renderer>().sharedMaterial = context.playerMaterial;
|
|
body.layer = context.playerLayer;
|
|
UnityEngine.Object.DestroyImmediate(body.GetComponent<Collider>());
|
|
|
|
GameObject cameraObject = new GameObject("PlayerCamera");
|
|
cameraObject.transform.SetParent(player.transform);
|
|
cameraObject.transform.localPosition = new Vector3(0f, 0.8f, 0f);
|
|
cameraObject.transform.localRotation = Quaternion.identity;
|
|
Camera cameraComponent = cameraObject.AddComponent<Camera>();
|
|
cameraComponent.nearClipPlane = 0.03f;
|
|
cameraObject.AddComponent<AudioListener>();
|
|
|
|
SimpleFPSController fpsController = player.AddComponent<SimpleFPSController>();
|
|
fpsController.characterController = controller;
|
|
fpsController.playerCamera = cameraComponent;
|
|
fpsController.walkSpeed = 4.1f;
|
|
fpsController.runSpeed = 7.1f;
|
|
fpsController.mouseSensitivity = 2.2f;
|
|
|
|
PlayerStealth stealth = player.AddComponent<PlayerStealth>();
|
|
stealth.characterController = controller;
|
|
stealth.fpsController = fpsController;
|
|
stealth.playerCamera = cameraComponent;
|
|
|
|
player.AddComponent<PlayerHealth>();
|
|
return player;
|
|
}
|
|
|
|
private static StealthIndicator CreateUI(BuildContext context, Transform parent, GameObject player)
|
|
{
|
|
GameObject canvasObject = new GameObject("Canvas");
|
|
canvasObject.transform.SetParent(parent);
|
|
Canvas canvas = canvasObject.AddComponent<Canvas>();
|
|
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
|
CanvasScaler scaler = canvasObject.AddComponent<CanvasScaler>();
|
|
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
|
|
scaler.referenceResolution = new Vector2(1920f, 1080f);
|
|
scaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
|
|
scaler.matchWidthOrHeight = 0.7f;
|
|
canvasObject.AddComponent<GraphicRaycaster>();
|
|
|
|
GameObject eventSystem = new GameObject("EventSystem");
|
|
eventSystem.transform.SetParent(parent);
|
|
eventSystem.AddComponent<UnityEngine.EventSystems.EventSystem>();
|
|
eventSystem.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>();
|
|
|
|
GameObject panel = CreateUIObject("StealthPanel", canvasObject.transform, new Vector2(20f, -20f), new Vector2(390f, 240f), new Vector2(0f, 1f), new Vector2(0f, 1f));
|
|
Image panelImage = panel.AddComponent<Image>();
|
|
panelImage.color = new Color(0.06f, 0.08f, 0.1f, 0.62f);
|
|
|
|
GameObject barObject = CreateUIObject("StateBar", panel.transform, new Vector2(14f, -14f), new Vector2(362f, 18f), new Vector2(0f, 1f), new Vector2(0f, 1f));
|
|
Image barImage = barObject.AddComponent<Image>();
|
|
barImage.color = new Color(0.18f, 0.85f, 0.34f, 0.95f);
|
|
|
|
Text statusText = CreateText(context, panel.transform, "StatusText", "Скрытность: безопасно", 18, FontStyle.Bold, new Vector2(14f, -40f), new Vector2(362f, 28f));
|
|
Text controlsText = CreateText(context, panel.transform, "ControlsText", string.Empty, 14, FontStyle.Normal, new Vector2(14f, -74f), new Vector2(362f, 94f));
|
|
Text debugText = CreateText(context, panel.transform, "DebugText", string.Empty, 14, FontStyle.Normal, new Vector2(14f, -172f), new Vector2(362f, 56f));
|
|
|
|
GameObject healthPanel = CreateUIObject("HealthPanel", canvasObject.transform, new Vector2(-20f, -20f), new Vector2(190f, 42f), new Vector2(1f, 1f), new Vector2(1f, 1f));
|
|
Image healthPanelImage = healthPanel.AddComponent<Image>();
|
|
healthPanelImage.color = new Color(0.06f, 0.08f, 0.1f, 0.62f);
|
|
Text healthText = CreateText(context, healthPanel.transform, "HealthText", "Здоровье: 100", 18, FontStyle.Bold, new Vector2(10f, -6f), new Vector2(170f, 28f));
|
|
|
|
Text gameOverText = CreateCenteredText(context, canvasObject.transform, "GameOverText", "Игрок устранен", 28, FontStyle.Bold, new Vector2(520f, 70f));
|
|
gameOverText.color = new Color(1f, 0.2f, 0.2f, 1f);
|
|
gameOverText.gameObject.SetActive(false);
|
|
|
|
StealthIndicator indicator = panel.AddComponent<StealthIndicator>();
|
|
indicator.stateBar = barImage;
|
|
indicator.statusText = statusText;
|
|
indicator.controlsText = controlsText;
|
|
indicator.debugText = debugText;
|
|
indicator.healthText = healthText;
|
|
indicator.gameOverText = gameOverText;
|
|
indicator.playerStealth = player.GetComponent<PlayerStealth>();
|
|
indicator.playerHealth = player.GetComponent<PlayerHealth>();
|
|
return indicator;
|
|
}
|
|
|
|
private static void CreateEnemies(BuildContext context, Transform enemiesParent, Transform patrolParent, GameObject player)
|
|
{
|
|
CreateEnemy(
|
|
context,
|
|
enemiesParent,
|
|
patrolParent,
|
|
"Enemy_LeftWing",
|
|
new Vector3(-12f, 0f, -10f),
|
|
new[]
|
|
{
|
|
new Vector3(-15f, 0f, -15f),
|
|
new Vector3(-10f, 0f, -15f),
|
|
new Vector3(-10f, 0f, -9f),
|
|
new Vector3(-15f, 0f, -9f)
|
|
},
|
|
player);
|
|
|
|
CreateEnemy(
|
|
context,
|
|
enemiesParent,
|
|
patrolParent,
|
|
"Enemy_CenterCorridor",
|
|
new Vector3(0f, 0f, -4f),
|
|
new[]
|
|
{
|
|
new Vector3(-1f, 0f, -14f),
|
|
new Vector3(-1f, 0f, -2f),
|
|
new Vector3(1f, 0f, 6f),
|
|
new Vector3(1f, 0f, 14f)
|
|
},
|
|
player);
|
|
|
|
CreateEnemy(
|
|
context,
|
|
enemiesParent,
|
|
patrolParent,
|
|
"Enemy_RightWing",
|
|
new Vector3(12f, 0f, 12f),
|
|
new[]
|
|
{
|
|
new Vector3(10f, 0f, 10f),
|
|
new Vector3(15f, 0f, 10f),
|
|
new Vector3(15f, 0f, 16f),
|
|
new Vector3(10f, 0f, 16f)
|
|
},
|
|
player);
|
|
}
|
|
|
|
private static EnemyStateMachine CreateEnemy(
|
|
BuildContext context,
|
|
Transform enemiesParent,
|
|
Transform patrolParent,
|
|
string enemyName,
|
|
Vector3 position,
|
|
Vector3[] waypointPositions,
|
|
GameObject player)
|
|
{
|
|
GameObject enemyRoot = new GameObject(enemyName);
|
|
enemyRoot.transform.SetParent(enemiesParent);
|
|
enemyRoot.transform.position = position;
|
|
enemyRoot.layer = LayerMask.NameToLayer("Default");
|
|
|
|
CapsuleCollider capsuleCollider = enemyRoot.AddComponent<CapsuleCollider>();
|
|
capsuleCollider.center = new Vector3(0f, 1f, 0f);
|
|
capsuleCollider.height = 2f;
|
|
capsuleCollider.radius = 0.4f;
|
|
|
|
GameObject body = GameObject.CreatePrimitive(PrimitiveType.Capsule);
|
|
body.name = "Body";
|
|
body.transform.SetParent(enemyRoot.transform);
|
|
body.transform.localPosition = new Vector3(0f, 1f, 0f);
|
|
body.transform.localRotation = Quaternion.identity;
|
|
body.GetComponent<Renderer>().sharedMaterial = context.enemyMaterial;
|
|
UnityEngine.Object.DestroyImmediate(body.GetComponent<Collider>());
|
|
|
|
NavMeshAgent agent = enemyRoot.AddComponent<NavMeshAgent>();
|
|
agent.radius = 0.35f;
|
|
agent.height = 2f;
|
|
agent.baseOffset = 0f;
|
|
agent.speed = 2.2f;
|
|
agent.angularSpeed = 360f;
|
|
agent.acceleration = 12f;
|
|
agent.stoppingDistance = 0.1f;
|
|
agent.autoBraking = true;
|
|
|
|
EnemyStateMachine stateMachine = enemyRoot.AddComponent<EnemyStateMachine>();
|
|
stateMachine.agent = agent;
|
|
stateMachine.patrolSpeed = 2.2f;
|
|
stateMachine.suspicionSpeed = 2.9f;
|
|
stateMachine.alertSpeed = 4.1f;
|
|
stateMachine.hearingRange = 14f;
|
|
stateMachine.suspicionWaitDuration = 2.5f;
|
|
stateMachine.captureDistance = 1.7f;
|
|
|
|
EnemyVision vision = enemyRoot.AddComponent<EnemyVision>();
|
|
vision.stateMachine = stateMachine;
|
|
vision.player = player.transform;
|
|
vision.viewRadius = 13.5f;
|
|
vision.viewAngle = 70f;
|
|
vision.eyeHeight = 1.4f;
|
|
vision.obstacleMask = context.obstacleMask;
|
|
vision.targetMask = context.targetMask;
|
|
|
|
EnemyVisionVisualizer visualizer = enemyRoot.AddComponent<EnemyVisionVisualizer>();
|
|
visualizer.vision = vision;
|
|
visualizer.stateMachine = stateMachine;
|
|
visualizer.visionMaterialTemplate = context.visionMaterial;
|
|
|
|
stateMachine.vision = vision;
|
|
stateMachine.patrolPoints = CreateWaypoints(enemyName + "_Route", patrolParent, waypointPositions);
|
|
|
|
return stateMachine;
|
|
}
|
|
|
|
private static Transform[] CreateWaypoints(string routeName, Transform patrolParent, Vector3[] positions)
|
|
{
|
|
GameObject routeRoot = new GameObject(routeName);
|
|
routeRoot.transform.SetParent(patrolParent);
|
|
|
|
Transform[] waypoints = new Transform[positions.Length];
|
|
for (int i = 0; i < positions.Length; i++)
|
|
{
|
|
GameObject waypoint = new GameObject("Waypoint_" + i);
|
|
waypoint.transform.SetParent(routeRoot.transform);
|
|
waypoint.transform.position = positions[i];
|
|
waypoints[i] = waypoint.transform;
|
|
}
|
|
|
|
return waypoints;
|
|
}
|
|
|
|
private static GameObject CreateWall(BuildContext context, Transform parent, string name, Vector3 position, Vector3 scale)
|
|
{
|
|
GameObject wall = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
wall.name = name;
|
|
wall.transform.SetParent(parent);
|
|
wall.transform.position = position;
|
|
wall.transform.localScale = scale;
|
|
wall.GetComponent<Renderer>().sharedMaterial = context.wallMaterial;
|
|
wall.layer = context.obstacleLayer;
|
|
SetNavigationStatic(wall);
|
|
return wall;
|
|
}
|
|
|
|
private static GameObject CreateCover(BuildContext context, Transform parent, string name, Vector3 position, Vector3 scale)
|
|
{
|
|
GameObject cover = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
cover.name = name;
|
|
cover.transform.SetParent(parent);
|
|
cover.transform.position = position;
|
|
cover.transform.localScale = scale;
|
|
cover.GetComponent<Renderer>().sharedMaterial = context.coverMaterial;
|
|
cover.layer = context.coverLayer;
|
|
cover.AddComponent<CoverChecker>();
|
|
SetNavigationStatic(cover);
|
|
return cover;
|
|
}
|
|
|
|
private static GameObject CreateUIObject(string name, Transform parent, Vector2 anchoredPosition, Vector2 sizeDelta, Vector2 anchorMin, Vector2 anchorMax)
|
|
{
|
|
GameObject gameObject = new GameObject(name, typeof(RectTransform));
|
|
gameObject.transform.SetParent(parent, false);
|
|
RectTransform rectTransform = gameObject.GetComponent<RectTransform>();
|
|
rectTransform.anchorMin = anchorMin;
|
|
rectTransform.anchorMax = anchorMax;
|
|
rectTransform.pivot = new Vector2(anchorMin.x, anchorMax.y);
|
|
rectTransform.anchoredPosition = anchoredPosition;
|
|
rectTransform.sizeDelta = sizeDelta;
|
|
return gameObject;
|
|
}
|
|
|
|
private static Text CreateText(
|
|
BuildContext context,
|
|
Transform parent,
|
|
string name,
|
|
string text,
|
|
int fontSize,
|
|
FontStyle fontStyle,
|
|
Vector2 anchoredPosition,
|
|
Vector2 sizeDelta)
|
|
{
|
|
GameObject textObject = CreateUIObject(name, parent, anchoredPosition, sizeDelta, new Vector2(0f, 1f), new Vector2(0f, 1f));
|
|
Text uiText = textObject.AddComponent<Text>();
|
|
uiText.font = context.font;
|
|
uiText.text = text;
|
|
uiText.fontSize = fontSize;
|
|
uiText.fontStyle = fontStyle;
|
|
uiText.alignment = TextAnchor.UpperLeft;
|
|
uiText.horizontalOverflow = HorizontalWrapMode.Wrap;
|
|
uiText.verticalOverflow = VerticalWrapMode.Overflow;
|
|
uiText.color = Color.white;
|
|
return uiText;
|
|
}
|
|
|
|
private static Text CreateCenteredText(
|
|
BuildContext context,
|
|
Transform parent,
|
|
string name,
|
|
string text,
|
|
int fontSize,
|
|
FontStyle fontStyle,
|
|
Vector2 sizeDelta)
|
|
{
|
|
GameObject textObject = new GameObject(name, typeof(RectTransform));
|
|
textObject.transform.SetParent(parent, false);
|
|
RectTransform rectTransform = textObject.GetComponent<RectTransform>();
|
|
rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
|
|
rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
|
|
rectTransform.pivot = new Vector2(0.5f, 0.5f);
|
|
rectTransform.anchoredPosition = Vector2.zero;
|
|
rectTransform.sizeDelta = sizeDelta;
|
|
|
|
Text uiText = textObject.AddComponent<Text>();
|
|
uiText.font = context.font;
|
|
uiText.text = text;
|
|
uiText.fontSize = fontSize;
|
|
uiText.fontStyle = fontStyle;
|
|
uiText.alignment = TextAnchor.MiddleCenter;
|
|
uiText.horizontalOverflow = HorizontalWrapMode.Wrap;
|
|
uiText.verticalOverflow = VerticalWrapMode.Overflow;
|
|
uiText.color = Color.white;
|
|
return uiText;
|
|
}
|
|
|
|
private static Material GetOrCreateOpaqueMaterial(string path, Color color)
|
|
{
|
|
Material material = AssetDatabase.LoadAssetAtPath<Material>(path);
|
|
if (material == null)
|
|
{
|
|
material = new Material(Shader.Find("Standard"));
|
|
AssetDatabase.CreateAsset(material, path);
|
|
}
|
|
|
|
material.shader = Shader.Find("Standard");
|
|
material.color = color;
|
|
EditorUtility.SetDirty(material);
|
|
return material;
|
|
}
|
|
|
|
private static Font LoadBuiltinFont()
|
|
{
|
|
Font font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
|
if (font != null)
|
|
{
|
|
return font;
|
|
}
|
|
|
|
font = Resources.GetBuiltinResource<Font>("Arial.ttf");
|
|
if (font != null)
|
|
{
|
|
return font;
|
|
}
|
|
|
|
throw new InvalidOperationException("Не удалось загрузить встроенный шрифт Unity.");
|
|
}
|
|
|
|
private static Material GetOrCreateTransparentMaterial(string path, Color color)
|
|
{
|
|
Material material = AssetDatabase.LoadAssetAtPath<Material>(path);
|
|
if (material == null)
|
|
{
|
|
material = new Material(Shader.Find("Standard"));
|
|
AssetDatabase.CreateAsset(material, path);
|
|
}
|
|
|
|
material.shader = Shader.Find("Standard");
|
|
material.SetFloat("_Mode", 3f);
|
|
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
|
|
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
|
|
material.SetInt("_ZWrite", 0);
|
|
material.DisableKeyword("_ALPHATEST_ON");
|
|
material.EnableKeyword("_ALPHABLEND_ON");
|
|
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
|
|
material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
|
|
material.color = color;
|
|
EditorUtility.SetDirty(material);
|
|
return material;
|
|
}
|
|
|
|
private static void EnsureFolders()
|
|
{
|
|
Directory.CreateDirectory(SceneDirectory);
|
|
Directory.CreateDirectory("Assets/Scripts");
|
|
Directory.CreateDirectory("Assets/Editor");
|
|
Directory.CreateDirectory("Assets/Materials");
|
|
Directory.CreateDirectory(MaterialDirectory);
|
|
}
|
|
|
|
private static void EnsureTag(string tagName)
|
|
{
|
|
SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]);
|
|
SerializedProperty tagsProperty = tagManager.FindProperty("tags");
|
|
|
|
for (int i = 0; i < tagsProperty.arraySize; i++)
|
|
{
|
|
SerializedProperty existingTag = tagsProperty.GetArrayElementAtIndex(i);
|
|
if (existingTag.stringValue == tagName)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
tagsProperty.InsertArrayElementAtIndex(tagsProperty.arraySize);
|
|
tagsProperty.GetArrayElementAtIndex(tagsProperty.arraySize - 1).stringValue = tagName;
|
|
tagManager.ApplyModifiedProperties();
|
|
}
|
|
|
|
private static int EnsureLayer(string layerName, int preferredIndex)
|
|
{
|
|
int existingLayer = LayerMask.NameToLayer(layerName);
|
|
if (existingLayer >= 0)
|
|
{
|
|
return existingLayer;
|
|
}
|
|
|
|
SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]);
|
|
SerializedProperty layersProperty = tagManager.FindProperty("layers");
|
|
|
|
if (preferredIndex >= 8 && preferredIndex < layersProperty.arraySize)
|
|
{
|
|
SerializedProperty preferredLayer = layersProperty.GetArrayElementAtIndex(preferredIndex);
|
|
if (string.IsNullOrEmpty(preferredLayer.stringValue) || preferredLayer.stringValue == layerName)
|
|
{
|
|
preferredLayer.stringValue = layerName;
|
|
tagManager.ApplyModifiedProperties();
|
|
return preferredIndex;
|
|
}
|
|
}
|
|
|
|
for (int i = 8; i < layersProperty.arraySize; i++)
|
|
{
|
|
SerializedProperty layerProperty = layersProperty.GetArrayElementAtIndex(i);
|
|
if (string.IsNullOrEmpty(layerProperty.stringValue))
|
|
{
|
|
layerProperty.stringValue = layerName;
|
|
tagManager.ApplyModifiedProperties();
|
|
return i;
|
|
}
|
|
}
|
|
|
|
Debug.LogWarning("Не удалось добавить слой " + layerName + ". Будет использован Default.");
|
|
return 0;
|
|
}
|
|
|
|
private static void SetNavigationStatic(GameObject gameObject)
|
|
{
|
|
GameObjectUtility.SetStaticEditorFlags(
|
|
gameObject,
|
|
StaticEditorFlags.BatchingStatic |
|
|
StaticEditorFlags.ContributeGI |
|
|
StaticEditorFlags.NavigationStatic |
|
|
StaticEditorFlags.OccludeeStatic |
|
|
StaticEditorFlags.OccluderStatic);
|
|
}
|
|
|
|
private static bool TryBakeNavMesh()
|
|
{
|
|
try
|
|
{
|
|
Type navMeshBuilderType = FindType("UnityEditor.AI.NavMeshBuilder");
|
|
if (navMeshBuilderType == null)
|
|
{
|
|
Debug.LogWarning("UnityEditor.AI.NavMeshBuilder не найден. Враги смогут использовать fallback-движение без NavMesh.");
|
|
return false;
|
|
}
|
|
|
|
MethodInfo clearMethod = navMeshBuilderType.GetMethod("ClearAllNavMeshes", BindingFlags.Public | BindingFlags.Static);
|
|
MethodInfo buildMethod = navMeshBuilderType.GetMethod("BuildNavMesh", BindingFlags.Public | BindingFlags.Static);
|
|
if (buildMethod == null)
|
|
{
|
|
Debug.LogWarning("Метод BuildNavMesh не найден. Используйте Window -> AI -> Navigation -> Bake.");
|
|
return false;
|
|
}
|
|
|
|
clearMethod?.Invoke(null, null);
|
|
buildMethod.Invoke(null, null);
|
|
return true;
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
Debug.LogWarning("Автоматическая запечка NavMesh не удалась: " + exception.Message);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static Type FindType(string fullName)
|
|
{
|
|
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
|
for (int i = 0; i < assemblies.Length; i++)
|
|
{
|
|
Type type = assemblies[i].GetType(fullName);
|
|
if (type != null)
|
|
{
|
|
return type;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|