756 lines
38 KiB
C#
756 lines
38 KiB
C#
using UnityEngine;
|
||
using UnityEditor;
|
||
using UnityEditor.SceneManagement;
|
||
using UnityEngine.UI;
|
||
using Unity.AI.Navigation;
|
||
using UnityEngine.AI;
|
||
|
||
public static class Level2SceneBuilder
|
||
{
|
||
[MenuItem("Tools/Synthesia/Build Level 2 (SubLevel Complex)")]
|
||
public static void BuildLevel2Scene()
|
||
{
|
||
Debug.Log("<color=cyan>[Level 2] Начало сборки 2-го уровня...</color>");
|
||
|
||
// 1. Открываем SampleScene как шаблон
|
||
string samplePath = "Assets/Scenes/SampleScene.unity";
|
||
string level2Path = "Assets/Scenes/Level2.unity";
|
||
|
||
var scene = EditorSceneManager.OpenScene(samplePath, OpenSceneMode.Single);
|
||
|
||
// 2. Сохраняем сцену под новым именем Level2.unity
|
||
bool savedAs = EditorSceneManager.SaveScene(scene, level2Path);
|
||
if (!savedAs)
|
||
{
|
||
Debug.LogError("[Level 2] Не удалось сохранить сцену как Level2.unity!");
|
||
return;
|
||
}
|
||
|
||
scene = EditorSceneManager.OpenScene(level2Path, OpenSceneMode.Single);
|
||
|
||
// 3. Очищаем объекты 1-го уровня
|
||
string[] toRemove = new string[] {
|
||
"Laboratory_Complex",
|
||
"Cosmic_Retro_Laboratory",
|
||
"Elevator_Level2_Zone",
|
||
"StoryNotes_Container",
|
||
"Enemies",
|
||
"StoryManager",
|
||
"SubLevel2_Complex",
|
||
"Enemies_Level2"
|
||
};
|
||
|
||
foreach (var name in toRemove)
|
||
{
|
||
var go = GameObject.Find(name);
|
||
if (go != null) Undo.DestroyObjectImmediate(go);
|
||
}
|
||
|
||
// Удаляем старые тестовые коробки патронов
|
||
var allBoxes = Object.FindObjectsByType<AmmoPickup>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||
foreach (var b in allBoxes)
|
||
{
|
||
Undo.DestroyObjectImmediate(b.gameObject);
|
||
}
|
||
|
||
// 4. Загрузка материалов и префабов
|
||
Material echoMat = AssetDatabase.LoadAssetAtPath<Material>("Assets/EchoCloudMat.mat");
|
||
if (echoMat == null) echoMat = AssetDatabase.LoadAssetAtPath<Material>("Assets/EchoMat.mat");
|
||
|
||
GameObject mutantPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Assets/Prefabs/Mutant.prefab");
|
||
Font defaultFont = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
||
|
||
// 5. Создаем контейнер SubLevel2_Complex
|
||
GameObject level2Root = new GameObject("SubLevel2_Complex");
|
||
level2Root.transform.position = Vector3.zero;
|
||
|
||
// Позиционируем игрока в стартовом лифте прибытия (0, 0.1, -22)
|
||
GameObject playerGo = GameObject.FindWithTag("Player");
|
||
if (playerGo == null) playerGo = GameObject.Find("Player");
|
||
if (playerGo != null)
|
||
{
|
||
playerGo.transform.position = new Vector3(0f, 0.15f, -22f);
|
||
playerGo.transform.rotation = Quaternion.identity;
|
||
}
|
||
|
||
// --- ГЕОМЕТРИЯ 2-ГО УРОВНЯ ---
|
||
// А. Шахта лифта прибытия (Arrival Chamber): (0, 0, -22), размер 6x6
|
||
BuildRoom(level2Root.transform, "Room_ArrivalElevator", new Vector3(0f, 0f, -22f), new Vector3(6f, 0.2f, 6f), 3.8f, echoMat,
|
||
hasNorthDoor: true, hasSouthDoor: false, hasWestDoor: false, hasEastDoor: false);
|
||
|
||
// Рама лифта прибытия
|
||
BuildElevatorShaft(level2Root.transform, new Vector3(0f, 0f, -22.5f), echoMat);
|
||
|
||
// Б. Южный шлюзовой коридор: (0, 0, -12.5), размер 4x13
|
||
BuildCorridor(level2Root.transform, "Corridor_South", new Vector3(0f, 0f, -12.5f), new Vector3(4f, 0.2f, 13f), 3.8f, echoMat, isNS: true);
|
||
|
||
// В. Центральный узел (Central Hub): (0, 0, 0), размер 12x12
|
||
BuildRoom(level2Root.transform, "Room_CentralHub", new Vector3(0f, 0f, 0f), new Vector3(12f, 0.2f, 12f), 3.8f, echoMat,
|
||
hasNorthDoor: true, hasSouthDoor: true, hasWestDoor: true, hasEastDoor: true);
|
||
|
||
// Колонны в Центральном узле
|
||
BuildPillar(level2Root.transform, new Vector3(-3.5f, 1.9f, -3.5f), new Vector3(1.2f, 3.8f, 1.2f), echoMat);
|
||
BuildPillar(level2Root.transform, new Vector3(3.5f, 1.9f, -3.5f), new Vector3(1.2f, 3.8f, 1.2f), echoMat);
|
||
BuildPillar(level2Root.transform, new Vector3(-3.5f, 1.9f, 3.5f), new Vector3(1.2f, 3.8f, 1.2f), echoMat);
|
||
BuildPillar(level2Root.transform, new Vector3(3.5f, 1.9f, 3.5f), new Vector3(1.2f, 3.8f, 1.2f), echoMat);
|
||
|
||
// Г. Западный сектор: Крио-изолятор и виварий (-17, 0, 0), размер 14x14
|
||
BuildCorridor(level2Root.transform, "Corridor_West", new Vector3(-8f, 0f, 0f), new Vector3(4f, 0.2f, 4f), 3.8f, echoMat, isNS: false);
|
||
BuildRoom(level2Root.transform, "Room_CryoIsolation", new Vector3(-17f, 0f, 0f), new Vector3(14f, 0.2f, 14f), 3.8f, echoMat,
|
||
hasNorthDoor: false, hasSouthDoor: false, hasWestDoor: false, hasEastDoor: true);
|
||
|
||
// Д. Восточный сектор: Био-синтез и химические реакторы (17, 0, 0), размер 14x14
|
||
BuildCorridor(level2Root.transform, "Corridor_East", new Vector3(8f, 0f, 0f), new Vector3(4f, 0.2f, 4f), 3.8f, echoMat, isNS: false);
|
||
BuildRoom(level2Root.transform, "Room_BioSynthesis", new Vector3(17f, 0f, 0f), new Vector3(14f, 0.2f, 14f), 3.8f, echoMat,
|
||
hasNorthDoor: false, hasSouthDoor: false, hasWestDoor: true, hasEastDoor: false);
|
||
|
||
// Е. Северный переход к архиву и реактору (0, 0, 9), размер 4x6
|
||
BuildCorridor(level2Root.transform, "Corridor_North", new Vector3(0f, 0f, 9f), new Vector3(4f, 0.2f, 6f), 3.8f, echoMat, isNS: true);
|
||
|
||
// Ж. Сектор архивов и серверной (-11, 0, 15), размер 10x10
|
||
BuildCorridor(level2Root.transform, "Corridor_ArchivesBranch", new Vector3(-4.5f, 0f, 9f), new Vector3(5f, 0.2f, 4f), 3.8f, echoMat, isNS: false);
|
||
BuildRoom(level2Root.transform, "Room_SecurityArchives", new Vector3(-11f, 0f, 15f), new Vector3(10f, 0.2f, 10f), 3.8f, echoMat,
|
||
hasNorthDoor: false, hasSouthDoor: true, hasWestDoor: false, hasEastDoor: false);
|
||
|
||
// З. Главный реакторный отсек и эвакуационный шлюз (0, 0, 26), просторная арена 26x26 м
|
||
BuildRoom(level2Root.transform, "Room_ReactorAirlock", new Vector3(0f, 0f, 26f), new Vector3(26f, 0.2f, 26f), 4.5f, echoMat,
|
||
hasNorthDoor: false, hasSouthDoor: true, hasWestDoor: false, hasEastDoor: false);
|
||
|
||
// Массивные реакторные колонны-укрытия на арене босса
|
||
BuildPillar(level2Root.transform, new Vector3(-6.5f, 2.25f, 20f), new Vector3(1.6f, 4.5f, 1.6f), echoMat);
|
||
BuildPillar(level2Root.transform, new Vector3(6.5f, 2.25f, 20f), new Vector3(1.6f, 4.5f, 1.6f), echoMat);
|
||
BuildPillar(level2Root.transform, new Vector3(-6.5f, 2.25f, 31f), new Vector3(1.6f, 4.5f, 1.6f), echoMat);
|
||
BuildPillar(level2Root.transform, new Vector3(6.5f, 2.25f, 31f), new Vector3(1.6f, 4.5f, 1.6f), echoMat);
|
||
|
||
// Финальный терминал эвакуации и шлюз на севере арены (0, 0, 38.2)
|
||
BuildFinalEscapeAirlock(level2Root.transform, new Vector3(0f, 0f, 38.2f), echoMat);
|
||
|
||
// Атмосферное аварийное освещение (тусклые маяки)
|
||
BuildBeacon(level2Root.transform, new Vector3(0f, 2.5f, -22f), new Color(0.2f, 0.6f, 0.9f), 1.8f, 8f);
|
||
BuildBeacon(level2Root.transform, new Vector3(0f, 2.8f, 0f), new Color(1.0f, 0.75f, 0.2f), 2.2f, 12f);
|
||
BuildBeacon(level2Root.transform, new Vector3(-17f, 2.8f, 0f), new Color(0.1f, 0.8f, 0.9f), 1.8f, 10f);
|
||
BuildBeacon(level2Root.transform, new Vector3(17f, 2.8f, 0f), new Color(1.0f, 0.45f, 0.1f), 1.8f, 10f);
|
||
BuildBeacon(level2Root.transform, new Vector3(-11f, 2.5f, 15f), new Color(0.3f, 0.8f, 0.4f), 1.5f, 8f);
|
||
BuildBeacon(level2Root.transform, new Vector3(-8f, 3.2f, 22f), new Color(1.0f, 0.2f, 0.2f), 2.2f, 14f);
|
||
BuildBeacon(level2Root.transform, new Vector3(8f, 3.2f, 22f), new Color(1.0f, 0.2f, 0.2f), 2.2f, 14f);
|
||
BuildBeacon(level2Root.transform, new Vector3(-8f, 3.2f, 31f), new Color(1.0f, 0.2f, 0.2f), 2.2f, 14f);
|
||
BuildBeacon(level2Root.transform, new Vector3(8f, 3.2f, 31f), new Color(1.0f, 0.2f, 0.2f), 2.2f, 14f);
|
||
|
||
// Патроны на уровне 2
|
||
CreateAmmoBox(new Vector3(-18f, 0.3f, 4f), 14);
|
||
CreateAmmoBox(new Vector3(18f, 0.3f, -4f), 21);
|
||
CreateAmmoBox(new Vector3(-12f, 0.3f, 16f), 14);
|
||
|
||
// Физические предметы для бросков (отвлечение мутантов)
|
||
CreateThrowableProp(new Vector3(-2f, 0.5f, -2f), "Distraction_Flask_1");
|
||
CreateThrowableProp(new Vector3(2f, 0.5f, -1f), "Distraction_Flask_2");
|
||
CreateThrowableProp(new Vector3(-16f, 0.5f, 2f), "Distraction_Flask_3");
|
||
CreateThrowableProp(new Vector3(16f, 0.5f, 2f), "Distraction_Flask_4");
|
||
|
||
// 6. Противники и Босс Уровня 2
|
||
GameObject enemiesContainer = new GameObject("Enemies_Level2");
|
||
if (mutantPrefab != null)
|
||
{
|
||
SpawnMutant(mutantPrefab, enemiesContainer.transform, new Vector3(0f, 0.1f, 3f), "Mutant_Central_Hub");
|
||
SpawnMutant(mutantPrefab, enemiesContainer.transform, new Vector3(-17f, 0.1f, 0f), "Mutant_CryoVault");
|
||
SpawnMutant(mutantPrefab, enemiesContainer.transform, new Vector3(17f, 0.1f, 0f), "Mutant_BioCore");
|
||
SpawnMutant(mutantPrefab, enemiesContainer.transform, new Vector3(-11f, 0.1f, 15f), "Mutant_Archives");
|
||
|
||
// Уникальный Босс 2-го уровня в центре расширенной реакторной арены
|
||
GameObject bossObj = SpawnTitanBoss(mutantPrefab, enemiesContainer.transform, new Vector3(0f, 0.15f, 28f), "Titan_Boss_Alpha");
|
||
BuildBossArenaLockdown(level2Root.transform, new Vector3(0f, 0f, 13f), echoMat, bossObj);
|
||
|
||
// Боеприпасы и тактические колбы на арене босса
|
||
CreateAmmoBox(new Vector3(-9.5f, 0.3f, 18f), 21);
|
||
CreateAmmoBox(new Vector3(9.5f, 0.3f, 18f), 21);
|
||
CreateAmmoBox(new Vector3(-9.5f, 0.3f, 32f), 21);
|
||
CreateAmmoBox(new Vector3(9.5f, 0.3f, 32f), 14);
|
||
CreateThrowableProp(new Vector3(-6.5f, 0.5f, 25f), "Arena_Distraction_Flask_1");
|
||
CreateThrowableProp(new Vector3(6.5f, 0.5f, 25f), "Arena_Distraction_Flask_2");
|
||
CreateThrowableProp(new Vector3(0f, 0.5f, 34f), "Arena_Distraction_Flask_3");
|
||
}
|
||
|
||
// 7. Настройка UI (Финальная победа, полоса здоровья босса, оверлей ослепления)
|
||
SetupFinalVictoryUI();
|
||
|
||
// 8. Настраиваем и запекаем NavMesh для Уровня 2
|
||
BakeLevel2NavMesh();
|
||
|
||
// 9. Регистрация сцены в Build Settings
|
||
RegisterSceneInBuildSettings(level2Path);
|
||
|
||
// 10. Сохранение сцены
|
||
EditorSceneManager.MarkSceneDirty(scene);
|
||
EditorSceneManager.SaveScene(scene);
|
||
|
||
Debug.Log("<color=green>[Level 2] Сборка 2-го уровня полностью завершена!</color>");
|
||
}
|
||
|
||
private static void BuildRoom(Transform parent, string name, Vector3 center, Vector3 floorSize, float height, Material mat,
|
||
bool hasNorthDoor, bool hasSouthDoor, bool hasWestDoor, bool hasEastDoor)
|
||
{
|
||
GameObject room = new GameObject(name);
|
||
room.transform.SetParent(parent);
|
||
room.transform.position = center;
|
||
|
||
// Пол
|
||
GameObject floor = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
floor.name = "Floor";
|
||
floor.transform.SetParent(room.transform);
|
||
floor.transform.localPosition = Vector3.zero;
|
||
floor.transform.localScale = floorSize;
|
||
if (mat != null) floor.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
|
||
// Потолок
|
||
GameObject roof = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
roof.name = "Ceiling";
|
||
roof.transform.SetParent(room.transform);
|
||
roof.transform.localPosition = new Vector3(0f, height, 0f);
|
||
roof.transform.localScale = floorSize;
|
||
if (mat != null) roof.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
|
||
float halfX = floorSize.x * 0.5f;
|
||
float halfZ = floorSize.z * 0.5f;
|
||
float wallThick = 0.35f;
|
||
|
||
// Северная стена
|
||
BuildWallWithDoor(room.transform, new Vector3(0f, height * 0.5f, halfZ), floorSize.x, height, wallThick, hasNorthDoor, mat, false);
|
||
// Южная стена
|
||
BuildWallWithDoor(room.transform, new Vector3(0f, height * 0.5f, -halfZ), floorSize.x, height, wallThick, hasSouthDoor, mat, false);
|
||
// Западная стена
|
||
BuildWallWithDoor(room.transform, new Vector3(-halfX, height * 0.5f, 0f), floorSize.z, height, wallThick, hasWestDoor, mat, true);
|
||
// Восточная стена
|
||
BuildWallWithDoor(room.transform, new Vector3(halfX, height * 0.5f, 0f), floorSize.z, height, wallThick, hasEastDoor, mat, true);
|
||
}
|
||
|
||
private static void BuildWallWithDoor(Transform parent, Vector3 pos, float length, float height, float thick, bool hasDoor, Material mat, bool rotate90)
|
||
{
|
||
Quaternion rot = rotate90 ? Quaternion.Euler(0f, 90f, 0f) : Quaternion.identity;
|
||
|
||
if (!hasDoor)
|
||
{
|
||
GameObject wall = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
wall.name = "SolidWall";
|
||
wall.transform.SetParent(parent);
|
||
wall.transform.localPosition = pos;
|
||
wall.transform.localRotation = rot;
|
||
wall.transform.localScale = new Vector3(length, height, thick);
|
||
if (mat != null) wall.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
}
|
||
else
|
||
{
|
||
float doorWidth = 3.6f;
|
||
float sideWidth = Mathf.Max((length - doorWidth) * 0.5f, 0.5f);
|
||
|
||
// Левый сегмент
|
||
GameObject leftPart = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
leftPart.name = "Wall_PartL";
|
||
leftPart.transform.SetParent(parent);
|
||
Vector3 offsetL = rotate90 ? new Vector3(0f, 0f, -(doorWidth + sideWidth) * 0.5f) : new Vector3(-(doorWidth + sideWidth) * 0.5f, 0f, 0f);
|
||
leftPart.transform.localPosition = pos + offsetL;
|
||
leftPart.transform.localRotation = rot;
|
||
leftPart.transform.localScale = new Vector3(sideWidth, height, thick);
|
||
if (mat != null) leftPart.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
|
||
// Правый сегмент
|
||
GameObject rightPart = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
rightPart.name = "Wall_PartR";
|
||
rightPart.transform.SetParent(parent);
|
||
Vector3 offsetR = rotate90 ? new Vector3(0f, 0f, (doorWidth + sideWidth) * 0.5f) : new Vector3((doorWidth + sideWidth) * 0.5f, 0f, 0f);
|
||
rightPart.transform.localPosition = pos + offsetR;
|
||
rightPart.transform.localRotation = rot;
|
||
rightPart.transform.localScale = new Vector3(sideWidth, height, thick);
|
||
if (mat != null) rightPart.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
|
||
// Верхняя перемычка
|
||
GameObject topLintel = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
topLintel.name = "Wall_TopLintel";
|
||
topLintel.transform.SetParent(parent);
|
||
Vector3 offsetTop = new Vector3(0f, height * 0.38f, 0f);
|
||
topLintel.transform.localPosition = pos + offsetTop;
|
||
topLintel.transform.localRotation = rot;
|
||
topLintel.transform.localScale = new Vector3(doorWidth, height * 0.25f, thick);
|
||
if (mat != null) topLintel.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
}
|
||
}
|
||
|
||
private static void BuildCorridor(Transform parent, string name, Vector3 pos, Vector3 floorSize, float height, Material mat, bool isNS)
|
||
{
|
||
GameObject corr = new GameObject(name);
|
||
corr.transform.SetParent(parent);
|
||
corr.transform.position = pos;
|
||
|
||
GameObject floor = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
floor.name = "Floor";
|
||
floor.transform.SetParent(corr.transform);
|
||
floor.transform.localPosition = Vector3.zero;
|
||
floor.transform.localScale = floorSize;
|
||
if (mat != null) floor.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
|
||
GameObject roof = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
roof.name = "Ceiling";
|
||
roof.transform.SetParent(corr.transform);
|
||
roof.transform.localPosition = new Vector3(0f, height, 0f);
|
||
roof.transform.localScale = floorSize;
|
||
if (mat != null) roof.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
|
||
float thick = 0.35f;
|
||
if (isNS)
|
||
{
|
||
// Стены слева и справа
|
||
float halfW = floorSize.x * 0.5f;
|
||
GameObject wL = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
wL.name = "Wall_L";
|
||
wL.transform.SetParent(corr.transform);
|
||
wL.transform.localPosition = new Vector3(-halfW, height * 0.5f, 0f);
|
||
wL.transform.localScale = new Vector3(thick, height, floorSize.z);
|
||
if (mat != null) wL.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
|
||
GameObject wR = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
wR.name = "Wall_R";
|
||
wR.transform.SetParent(corr.transform);
|
||
wR.transform.localPosition = new Vector3(halfW, height * 0.5f, 0f);
|
||
wR.transform.localScale = new Vector3(thick, height, floorSize.z);
|
||
if (mat != null) wR.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
}
|
||
else
|
||
{
|
||
// Стены спереди и сзади
|
||
float halfL = floorSize.z * 0.5f;
|
||
GameObject wN = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
wN.name = "Wall_N";
|
||
wN.transform.SetParent(corr.transform);
|
||
wN.transform.localPosition = new Vector3(0f, height * 0.5f, halfL);
|
||
wN.transform.localScale = new Vector3(floorSize.x, height, thick);
|
||
if (mat != null) wN.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
|
||
GameObject wS = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
wS.name = "Wall_S";
|
||
wS.transform.SetParent(corr.transform);
|
||
wS.transform.localPosition = new Vector3(0f, height * 0.5f, -halfL);
|
||
wS.transform.localScale = new Vector3(floorSize.x, height, thick);
|
||
if (mat != null) wS.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
}
|
||
}
|
||
|
||
private static void BuildPillar(Transform parent, Vector3 pos, Vector3 size, Material mat)
|
||
{
|
||
GameObject p = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
p.name = "Structural_Pillar";
|
||
p.transform.SetParent(parent);
|
||
p.transform.localPosition = pos;
|
||
p.transform.localScale = size;
|
||
if (mat != null) p.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
}
|
||
|
||
private static void BuildElevatorShaft(Transform parent, Vector3 pos, Material mat)
|
||
{
|
||
GameObject shaft = new GameObject("Arrival_Elevator_Shaft");
|
||
shaft.transform.SetParent(parent);
|
||
shaft.transform.position = pos;
|
||
|
||
Vector3[] corners = new Vector3[] {
|
||
new Vector3(-2.2f, 1.9f, -2.2f),
|
||
new Vector3(2.2f, 1.9f, -2.2f),
|
||
new Vector3(-2.2f, 1.9f, 2.2f),
|
||
new Vector3(2.2f, 1.9f, 2.2f)
|
||
};
|
||
|
||
for (int i = 0; i < corners.Length; i++)
|
||
{
|
||
GameObject col = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
col.name = $"Shaft_Pillar_{i}";
|
||
col.transform.SetParent(shaft.transform);
|
||
col.transform.localPosition = corners[i];
|
||
col.transform.localScale = new Vector3(0.25f, 3.8f, 0.25f);
|
||
if (mat != null) col.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
}
|
||
}
|
||
|
||
private static void BuildFinalEscapeAirlock(Transform parent, Vector3 pos, Material mat)
|
||
{
|
||
GameObject airlock = new GameObject("Final_Escape_Airlock_Zone");
|
||
airlock.transform.SetParent(parent);
|
||
airlock.transform.position = pos;
|
||
|
||
// Массивная стальная рама шлюза
|
||
GameObject hatch = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
hatch.name = "Airlock_BlastDoor";
|
||
hatch.transform.SetParent(airlock.transform);
|
||
hatch.transform.localPosition = new Vector3(0f, 2.1f, 0f);
|
||
hatch.transform.localScale = new Vector3(5f, 4.2f, 0.8f);
|
||
if (mat != null) hatch.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
|
||
// Консоль управления шлюзом (терминал)
|
||
GameObject console = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
console.name = "FinalExit_Terminal_Console";
|
||
console.transform.SetParent(airlock.transform);
|
||
console.transform.localPosition = new Vector3(0f, 0.6f, -2.5f);
|
||
console.transform.localScale = new Vector3(0.9f, 1.2f, 0.5f);
|
||
if (mat != null) console.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
|
||
// Экран терминала
|
||
GameObject screen = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
screen.name = "Terminal_Screen";
|
||
screen.transform.SetParent(console.transform);
|
||
screen.transform.localPosition = new Vector3(0f, 0.35f, 0.05f);
|
||
screen.transform.localRotation = Quaternion.Euler(25f, 0f, 0f);
|
||
screen.transform.localScale = new Vector3(0.7f, 0.4f, 0.15f);
|
||
if (mat != null) screen.GetComponent<MeshRenderer>().sharedMaterial = mat;
|
||
|
||
// Световой маяк
|
||
GameObject lightObj = new GameObject("Airlock_StatusLight");
|
||
lightObj.transform.SetParent(console.transform);
|
||
lightObj.transform.localPosition = new Vector3(0f, 0.75f, 0f);
|
||
Light sl = lightObj.AddComponent<Light>();
|
||
sl.type = LightType.Point;
|
||
sl.color = new Color(1f, 0.35f, 0.1f);
|
||
sl.intensity = 2.5f;
|
||
sl.range = 6f;
|
||
|
||
// Скрипт финала
|
||
var terminalComp = console.AddComponent<FinalExitTerminal>();
|
||
terminalComp.statusLight = sl;
|
||
terminalComp.screenRenderer = screen.GetComponent<MeshRenderer>();
|
||
terminalComp.interactionDistance = 3.5f;
|
||
}
|
||
|
||
private static void BuildBeacon(Transform parent, Vector3 pos, Color color, float intensity, float range)
|
||
{
|
||
GameObject beacon = new GameObject("Emergency_Beacon");
|
||
beacon.transform.SetParent(parent);
|
||
beacon.transform.position = pos;
|
||
|
||
Light l = beacon.AddComponent<Light>();
|
||
l.type = LightType.Point;
|
||
l.color = color;
|
||
l.intensity = intensity;
|
||
l.range = range;
|
||
}
|
||
|
||
private static void CreateAmmoBox(Vector3 pos, int count)
|
||
{
|
||
GameObject box = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
box.name = $"AmmoBox_({count})";
|
||
box.transform.position = pos;
|
||
box.transform.localScale = new Vector3(0.6f, 0.35f, 0.45f);
|
||
|
||
var pickup = box.AddComponent<AmmoPickup>();
|
||
pickup.ammoAmount = count;
|
||
pickup.pickupOnTouch = true;
|
||
}
|
||
|
||
private static void CreateThrowableProp(Vector3 pos, string name)
|
||
{
|
||
GameObject prop = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
|
||
prop.name = name;
|
||
prop.transform.position = pos;
|
||
prop.transform.localScale = new Vector3(0.3f, 0.35f, 0.3f);
|
||
|
||
var rb = prop.AddComponent<Rigidbody>();
|
||
rb.mass = 2.5f;
|
||
|
||
prop.layer = LayerMask.NameToLayer("Default");
|
||
}
|
||
|
||
private static void SpawnMutant(GameObject prefab, Transform parent, Vector3 pos, string name)
|
||
{
|
||
GameObject mutant = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
|
||
if (mutant == null) mutant = Object.Instantiate(prefab);
|
||
mutant.name = name;
|
||
mutant.transform.SetParent(parent);
|
||
mutant.transform.position = pos;
|
||
}
|
||
|
||
private static GameObject SpawnTitanBoss(GameObject prefab, Transform parent, Vector3 pos, string name)
|
||
{
|
||
GameObject boss = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
|
||
if (boss == null) boss = Object.Instantiate(prefab);
|
||
boss.name = name;
|
||
boss.transform.SetParent(parent);
|
||
boss.transform.position = pos;
|
||
boss.transform.localScale = new Vector3(1.75f, 1.75f, 1.75f); // Массивный титан
|
||
|
||
// Удаляем обычный BlindMutantAI, чтобы боссом управлял специализированный TitanBossAI
|
||
var standardAI = boss.GetComponent<BlindMutantAI>();
|
||
if (standardAI != null) Object.DestroyImmediate(standardAI);
|
||
|
||
// Настраиваем здоровье босса на 200 HP
|
||
var health = boss.GetComponent<MutantHealth>();
|
||
if (health == null) health = boss.AddComponent<MutantHealth>();
|
||
health.maxHealth = 200f;
|
||
|
||
// Добавляем специализированный интеллект босса
|
||
var titanAI = boss.GetComponent<TitanBossAI>();
|
||
if (titanAI == null) titanAI = boss.AddComponent<TitanBossAI>();
|
||
titanAI.walkSpeed = 2.8f;
|
||
titanAI.runSpeed = 4.6f;
|
||
titanAI.meleeDamage = 18f;
|
||
titanAI.meleeCooldown = 2.2f;
|
||
titanAI.spitDamage = 14f;
|
||
titanAI.spitCooldown = 8.5f;
|
||
|
||
// Настраиваем NavMeshAgent под габариты босса
|
||
var agent = boss.GetComponent<UnityEngine.AI.NavMeshAgent>();
|
||
if (agent != null)
|
||
{
|
||
agent.speed = 2.8f;
|
||
agent.radius = 0.85f;
|
||
agent.height = 3.0f;
|
||
agent.stoppingDistance = 2.4f;
|
||
agent.acceleration = 10f;
|
||
}
|
||
|
||
// Подсветка биолюминесценции босса (багровое свечение)
|
||
GameObject bossGlow = new GameObject("Titan_Bioluminescence");
|
||
bossGlow.transform.SetParent(boss.transform);
|
||
bossGlow.transform.localPosition = new Vector3(0f, 1.8f, 0f);
|
||
Light glowLight = bossGlow.AddComponent<Light>();
|
||
glowLight.type = LightType.Point;
|
||
glowLight.color = new Color(0.95f, 0.15f, 0.2f);
|
||
glowLight.range = 7.5f;
|
||
glowLight.intensity = 2.4f;
|
||
|
||
return boss;
|
||
}
|
||
|
||
private static void BuildBossArenaLockdown(Transform parent, Vector3 entrancePos, Material echoMat, GameObject bossObj)
|
||
{
|
||
GameObject arenaRoot = new GameObject("BossArena_LockdownManager");
|
||
arenaRoot.transform.SetParent(parent);
|
||
arenaRoot.transform.position = entrancePos;
|
||
|
||
// 1. Гермоворота (Blast Door) на входе в реакторную комнату (pos = 0, 0, 13)
|
||
// Вход шириной 3.6м, высотой 4.2м
|
||
GameObject doorObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||
doorObj.name = "BlastDoor_Quarantine";
|
||
doorObj.transform.SetParent(arenaRoot.transform);
|
||
doorObj.transform.position = entrancePos + new Vector3(0f, 2.1f, 0f);
|
||
doorObj.transform.localScale = new Vector3(3.8f, 4.2f, 0.45f);
|
||
if (echoMat != null) doorObj.GetComponent<MeshRenderer>().sharedMaterial = echoMat;
|
||
|
||
// Добавляем NavMeshObstacle для предотвращения прохода через закрытую дверь
|
||
var obstacle = doorObj.AddComponent<UnityEngine.AI.NavMeshObstacle>();
|
||
obstacle.carving = true;
|
||
obstacle.size = new Vector3(3.8f, 4.2f, 0.45f);
|
||
|
||
// 2. Сигнальная лампа карантина над дверью
|
||
GameObject lampObj = new GameObject("Quarantine_EmergencyLight");
|
||
lampObj.transform.SetParent(arenaRoot.transform);
|
||
lampObj.transform.position = entrancePos + new Vector3(0f, 3.8f, 0.8f);
|
||
|
||
Light alertLight = lampObj.AddComponent<Light>();
|
||
alertLight.type = LightType.Point;
|
||
alertLight.color = new Color(0.2f, 0.85f, 0.35f); // До боя дежурный зеленоватый свет
|
||
alertLight.range = 10f;
|
||
alertLight.intensity = 1.8f;
|
||
|
||
// 3. Зона триггера блокировки (сразу за дверью внутри комнаты)
|
||
GameObject triggerObj = new GameObject("Trigger_LockdownArea");
|
||
triggerObj.transform.SetParent(arenaRoot.transform);
|
||
triggerObj.transform.position = entrancePos + new Vector3(0f, 1.5f, 2.8f);
|
||
|
||
BoxCollider col = triggerObj.AddComponent<BoxCollider>();
|
||
col.isTrigger = true;
|
||
col.size = new Vector3(5.5f, 3.5f, 3.5f);
|
||
|
||
var rb = triggerObj.AddComponent<Rigidbody>();
|
||
rb.isKinematic = true;
|
||
|
||
// Навешиваем BossArenaController прямо на триггер
|
||
var arenaCtrl = triggerObj.AddComponent<BossArenaController>();
|
||
arenaCtrl.blastDoorRoot = doorObj;
|
||
arenaCtrl.blastDoorCollider = doorObj.GetComponent<Collider>();
|
||
arenaCtrl.emergencyLight = alertLight;
|
||
|
||
if (bossObj != null)
|
||
{
|
||
var titanAI = bossObj.GetComponent<TitanBossAI>();
|
||
arenaCtrl.bossAI = titanAI;
|
||
if (titanAI != null)
|
||
{
|
||
titanAI.arenaController = arenaCtrl;
|
||
}
|
||
}
|
||
}
|
||
|
||
private static void SetupFinalVictoryUI()
|
||
{
|
||
GameObject hudCanvas = GameObject.Find("HUD_Canvas");
|
||
if (hudCanvas == null) return;
|
||
|
||
// Создаем полосу здоровья босса (скрыта по умолчанию)
|
||
BossHealthBarUI.Create(hudCanvas.transform);
|
||
|
||
// Создаем оверлей акустического ослепления босса (скрыт по умолчанию)
|
||
AcousticJammerOverlay.Create(hudCanvas.transform);
|
||
|
||
// Проверяем, существует ли уже панель победы
|
||
var old = hudCanvas.transform.Find("FinalVictoryPanel");
|
||
if (old != null) Undo.DestroyObjectImmediate(old.gameObject);
|
||
|
||
// Корневая панель
|
||
GameObject root = new GameObject("FinalVictoryPanel", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
||
root.transform.SetParent(hudCanvas.transform, false);
|
||
RectTransform rt = root.GetComponent<RectTransform>();
|
||
rt.anchorMin = Vector2.zero;
|
||
rt.anchorMax = Vector2.one;
|
||
rt.sizeDelta = Vector2.zero;
|
||
|
||
Image bg = root.GetComponent<Image>();
|
||
bg.color = new Color(0.02f, 0.02f, 0.03f, 0.95f);
|
||
|
||
// Карточка
|
||
GameObject card = new GameObject("VictoryCard", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Outline));
|
||
card.transform.SetParent(root.transform, false);
|
||
RectTransform cardRt = card.GetComponent<RectTransform>();
|
||
cardRt.anchorMin = new Vector2(0.5f, 0.5f);
|
||
cardRt.anchorMax = new Vector2(0.5f, 0.5f);
|
||
cardRt.pivot = new Vector2(0.5f, 0.5f);
|
||
cardRt.sizeDelta = new Vector2(680f, 440f);
|
||
|
||
Image cardImg = card.GetComponent<Image>();
|
||
cardImg.color = new Color(0.06f, 0.06f, 0.07f, 1f);
|
||
|
||
Outline cardOutline = card.GetComponent<Outline>();
|
||
cardOutline.effectColor = new Color(0.2f, 0.9f, 0.5f, 0.8f);
|
||
cardOutline.effectDistance = new Vector2(2f, -2f);
|
||
|
||
// Заголовок
|
||
GameObject titleObj = new GameObject("Title", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
|
||
titleObj.transform.SetParent(card.transform, false);
|
||
RectTransform titleRt = titleObj.GetComponent<RectTransform>();
|
||
titleRt.anchorMin = new Vector2(0f, 1f);
|
||
titleRt.anchorMax = new Vector2(1f, 1f);
|
||
titleRt.pivot = new Vector2(0.5f, 1f);
|
||
titleRt.anchoredPosition = new Vector2(0f, -25f);
|
||
titleRt.sizeDelta = new Vector2(620f, 45f);
|
||
|
||
Text titleTxt = titleObj.GetComponent<Text>();
|
||
titleTxt.text = "МИССИЯ ВЫПОЛНЕНА // ЭВАКУАЦИЯ УСПЕШНА!";
|
||
titleTxt.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
||
titleTxt.fontSize = 22;
|
||
titleTxt.fontStyle = FontStyle.Bold;
|
||
titleTxt.alignment = TextAnchor.MiddleCenter;
|
||
titleTxt.color = new Color(0.2f, 1.0f, 0.5f, 1.0f);
|
||
|
||
// Описание
|
||
GameObject descObj = new GameObject("Description", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
|
||
descObj.transform.SetParent(card.transform, false);
|
||
RectTransform descRt = descObj.GetComponent<RectTransform>();
|
||
descRt.anchorMin = new Vector2(0.5f, 0.5f);
|
||
descRt.anchorMax = new Vector2(0.5f, 0.5f);
|
||
descRt.pivot = new Vector2(0.5f, 0.5f);
|
||
descRt.anchoredPosition = new Vector2(0f, 15f);
|
||
descRt.sizeDelta = new Vector2(580f, 140f);
|
||
|
||
Text descTxt = descObj.GetComponent<Text>();
|
||
descTxt.text = "Вы успешно преодолели Сектор глубоких исследований, обошли слепых мутантов и активировали аварийный гермошлюз.\n\nКомплекс 'Synthesia' запечатан. Вы выбрались на поверхность живым!";
|
||
descTxt.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
||
descTxt.fontSize = 16;
|
||
descTxt.lineSpacing = 1.3f;
|
||
descTxt.alignment = TextAnchor.MiddleCenter;
|
||
descTxt.color = new Color(0.9f, 0.9f, 0.9f, 0.95f);
|
||
|
||
// Кнопка В главное меню
|
||
GameObject btnMenuObj = new GameObject("MainMenuButton", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button));
|
||
btnMenuObj.transform.SetParent(card.transform, false);
|
||
RectTransform btnMenuRt = btnMenuObj.GetComponent<RectTransform>();
|
||
btnMenuRt.anchorMin = new Vector2(0.5f, 0f);
|
||
btnMenuRt.anchorMax = new Vector2(0.5f, 0f);
|
||
btnMenuRt.pivot = new Vector2(0.5f, 0f);
|
||
btnMenuRt.anchoredPosition = new Vector2(-150f, 30f);
|
||
btnMenuRt.sizeDelta = new Vector2(260f, 48f);
|
||
|
||
Image btnMenuImg = btnMenuObj.GetComponent<Image>();
|
||
btnMenuImg.color = new Color(0.18f, 0.18f, 0.20f, 1f);
|
||
Button btnMenu = btnMenuObj.GetComponent<Button>();
|
||
|
||
GameObject menuTxtObj = new GameObject("Text", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
|
||
menuTxtObj.transform.SetParent(btnMenuObj.transform, false);
|
||
RectTransform menuTxtRt = menuTxtObj.GetComponent<RectTransform>();
|
||
menuTxtRt.anchorMin = Vector2.zero;
|
||
menuTxtRt.anchorMax = Vector2.one;
|
||
menuTxtRt.sizeDelta = Vector2.zero;
|
||
Text mTxt = menuTxtObj.GetComponent<Text>();
|
||
mTxt.text = "В ГЛАВНОЕ МЕНЮ";
|
||
mTxt.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
||
mTxt.fontSize = 15;
|
||
mTxt.fontStyle = FontStyle.Bold;
|
||
mTxt.alignment = TextAnchor.MiddleCenter;
|
||
mTxt.color = Color.white;
|
||
|
||
// Кнопка Начать заново
|
||
GameObject btnRestartObj = new GameObject("RestartButton", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button));
|
||
btnRestartObj.transform.SetParent(card.transform, false);
|
||
RectTransform btnRestartRt = btnRestartObj.GetComponent<RectTransform>();
|
||
btnRestartRt.anchorMin = new Vector2(0.5f, 0f);
|
||
btnRestartRt.anchorMax = new Vector2(0.5f, 0f);
|
||
btnRestartRt.pivot = new Vector2(0.5f, 0f);
|
||
btnRestartRt.anchoredPosition = new Vector2(150f, 30f);
|
||
btnRestartRt.sizeDelta = new Vector2(260f, 48f);
|
||
|
||
Image btnRestartImg = btnRestartObj.GetComponent<Image>();
|
||
btnRestartImg.color = new Color(0.2f, 0.85f, 0.45f, 1f);
|
||
Button btnRestart = btnRestartObj.GetComponent<Button>();
|
||
|
||
GameObject resTxtObj = new GameObject("Text", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text));
|
||
resTxtObj.transform.SetParent(btnRestartObj.transform, false);
|
||
RectTransform resTxtRt = resTxtObj.GetComponent<RectTransform>();
|
||
resTxtRt.anchorMin = Vector2.zero;
|
||
resTxtRt.anchorMax = Vector2.one;
|
||
resTxtRt.sizeDelta = Vector2.zero;
|
||
Text rTxt = resTxtObj.GetComponent<Text>();
|
||
rTxt.text = "ИГРАТЬ ЗАНОВО";
|
||
rTxt.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
||
rTxt.fontSize = 15;
|
||
rTxt.fontStyle = FontStyle.Bold;
|
||
rTxt.alignment = TextAnchor.MiddleCenter;
|
||
rTxt.color = new Color(0.05f, 0.05f, 0.05f, 1f);
|
||
|
||
// Компонент FinalVictoryUI
|
||
var victoryComp = hudCanvas.GetComponent<FinalVictoryUI>();
|
||
if (victoryComp == null) victoryComp = hudCanvas.AddComponent<FinalVictoryUI>();
|
||
|
||
var flags = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;
|
||
victoryComp.GetType().GetField("victoryPanelRoot", flags)?.SetValue(victoryComp, root);
|
||
victoryComp.GetType().GetField("mainMenuButton", flags)?.SetValue(victoryComp, btnMenu);
|
||
victoryComp.GetType().GetField("restartButton", flags)?.SetValue(victoryComp, btnRestart);
|
||
|
||
root.SetActive(false);
|
||
}
|
||
|
||
private static void BakeLevel2NavMesh()
|
||
{
|
||
var surface = Object.FindAnyObjectByType<NavMeshSurface>();
|
||
if (surface == null)
|
||
{
|
||
GameObject sGo = new GameObject("NavMesh_Surface_Level2");
|
||
surface = sGo.AddComponent<NavMeshSurface>();
|
||
}
|
||
|
||
surface.collectObjects = CollectObjects.All;
|
||
surface.useGeometry = NavMeshCollectGeometry.PhysicsColliders;
|
||
surface.BuildNavMesh();
|
||
Debug.Log("<color=green>[Level 2] NavMesh успешно запечен для Уровня 2!</color>");
|
||
}
|
||
|
||
private static void RegisterSceneInBuildSettings(string scenePath)
|
||
{
|
||
var currentScenes = EditorBuildSettings.scenes;
|
||
foreach (var s in currentScenes)
|
||
{
|
||
if (s.path == scenePath) return; // уже добавлена
|
||
}
|
||
|
||
var newScenes = new EditorBuildSettingsScene[currentScenes.Length + 1];
|
||
for (int i = 0; i < currentScenes.Length; i++)
|
||
{
|
||
newScenes[i] = currentScenes[i];
|
||
}
|
||
newScenes[newScenes.Length - 1] = new EditorBuildSettingsScene(scenePath, true);
|
||
EditorBuildSettings.scenes = newScenes;
|
||
Debug.Log($"<color=cyan>[BuildSettings] Сцена {scenePath} зарегистрирована в Build Settings!</color>");
|
||
}
|
||
}
|