Files
PIM_Laba5/Assets/Editor/Lab5SceneBuilder.cs
T
2026-05-26 01:16:57 +03:00

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;
}
}