Files
PIM_6/Assets/Editor/Lab6SceneBuilder.cs
T
2026-06-04 14:27:16 +03:00

313 lines
16 KiB
C#

using System.IO;
using UnityEditor;
using UnityEditor.Events;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public static class Lab6SceneBuilder
{
private const string ScriptsPath = "Assets/_Scripts";
private const string ScenesPath = "Assets/_Scenes";
private const string MaterialsPath = "Assets/_Materials";
private const string PrefabsPath = "Assets/_Prefabs";
private const string EditorPath = "Assets/Editor";
private const string ScenePath = "Assets/_Scenes/PuzzleScene.unity";
[MenuItem("Tools/Lab6/Create Full Puzzle Scene")]
public static void CreateFullPuzzleScene()
{
CreateProjectFolders();
// Builder создаёт сцену с нуля, чтобы после пункта меню не требовалась настройка инспектора.
Scene scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
scene.name = "PuzzleScene";
Material floorMaterial = CreateMaterial("M_Floor", new Color(0.45f, 0.50f, 0.46f));
Material wallMaterial = CreateMaterial("M_Wall", new Color(0.64f, 0.66f, 0.67f));
Material doorMaterial = CreateMaterial("M_Door", new Color(0.36f, 0.21f, 0.12f));
Material exitDoorMaterial = CreateMaterial("M_ExitDoor", new Color(0.12f, 0.28f, 0.42f));
Material keyMaterial = CreateMaterial("M_Key", new Color(1.00f, 0.78f, 0.16f));
Material gemMaterial = CreateMaterial("M_Gem", new Color(0.12f, 0.76f, 0.90f));
Material cogMaterial = CreateMaterial("M_Cog", new Color(0.62f, 0.62f, 0.62f));
Material highlightMaterial = CreateMaterial("M_Item_Highlight", new Color(1.00f, 1.00f, 0.35f));
Material dragMaterial = CreateMaterial("M_Item_Drag", new Color(1.00f, 0.50f, 0.18f));
Material inactiveMaterial = CreateMaterial("M_Item_Inactive", new Color(0.26f, 0.30f, 0.32f));
Material slotNormalMaterial = CreateMaterial("M_Slot_Normal", new Color(0.18f, 0.18f, 0.20f));
Material slotValidMaterial = CreateMaterial("M_Slot_Valid", new Color(0.12f, 0.75f, 0.25f));
Material slotInvalidMaterial = CreateMaterial("M_Slot_Invalid", new Color(0.85f, 0.12f, 0.10f));
Material slotSuccessMaterial = CreateMaterial("M_Slot_Success", new Color(0.10f, 0.45f, 1.00f));
CreateRoom(floorMaterial, wallMaterial);
Camera camera = CreateCameraAndLight();
CreateEventSystem();
Door keyDoor = CreateDoor("Door_Key", new Vector3(0f, 1.25f, -0.45f), new Vector3(2.25f, 2.5f, 0.28f), doorMaterial, Vector3.up * 3.2f);
Door exitDoor = CreateDoor("Door_Exit", new Vector3(0f, 1.25f, 5.85f), new Vector3(2.25f, 2.5f, 0.28f), exitDoorMaterial, Vector3.right * 2.8f);
PuzzleUI puzzleUI = CreateCanvasAndUI();
DraggableObject key = CreateItem("Key", PrimitiveType.Cube, ItemType.Key, new Vector3(-4.6f, 0.35f, -4.25f), new Vector3(0.9f, 0.45f, 0.45f), keyMaterial, highlightMaterial, dragMaterial);
DraggableObject gem = CreateItem("Gem", PrimitiveType.Sphere, ItemType.Gem, new Vector3(-3.7f, 0.42f, 1.9f), new Vector3(0.75f, 0.75f, 0.75f), gemMaterial, highlightMaterial, dragMaterial);
DraggableObject cog = CreateItem("Cog_False", PrimitiveType.Cylinder, ItemType.Cog, new Vector3(4.4f, 0.35f, -4.15f), new Vector3(0.72f, 0.24f, 0.72f), cogMaterial, highlightMaterial, dragMaterial);
gem.inactiveMaterial = inactiveMaterial;
Slot keySlot = CreateSlot("KeySlot", ItemType.Key, new Vector3(-1.4f, 0.08f, -4.25f), slotNormalMaterial, slotValidMaterial, slotInvalidMaterial, slotSuccessMaterial);
Slot gemSlot = CreateSlot("GemSlot", ItemType.Gem, new Vector3(3.15f, 0.08f, 2.15f), slotNormalMaterial, slotValidMaterial, slotInvalidMaterial, slotSuccessMaterial);
PuzzleManager manager = new GameObject("PuzzleManager").AddComponent<PuzzleManager>();
manager.puzzleUI = puzzleUI;
manager.gemItem = gem;
// Связываем слоты с менеджером и дверями через persistent UnityEvent.
AddPersistentListener(keySlot.onItemPlaced, manager.OnKeyPlaced);
AddPersistentListener(keySlot.onItemPlaced, keyDoor.Open);
AddPersistentListener(gemSlot.onItemPlaced, manager.OnGemPlaced);
AddPersistentListener(gemSlot.onItemPlaced, exitDoor.Open);
CreateWorldLabel("KEY", key.transform.position + new Vector3(0f, 0.75f, 0f), Color.black);
CreateWorldLabel("KEY SLOT", keySlot.transform.position + new Vector3(0f, 0.35f, 0f), Color.white);
CreateWorldLabel("GEM", gem.transform.position + new Vector3(0f, 0.85f, 0f), Color.black);
CreateWorldLabel("GEM SLOT", gemSlot.transform.position + new Vector3(0f, 0.35f, 0f), Color.white);
CreateWorldLabel("FALSE ITEM", cog.transform.position + new Vector3(0f, 0.75f, 0f), Color.black);
CreateWorldLabel("Door_Key", keyDoor.transform.position + new Vector3(0f, 1.7f, -0.25f), Color.white);
CreateWorldLabel("Door_Exit", exitDoor.transform.position + new Vector3(0f, 1.7f, -0.25f), Color.white);
Selection.activeObject = camera.gameObject;
EditorSceneManager.SaveScene(scene, ScenePath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("Lab 6 puzzle scene created and saved: " + ScenePath);
}
private static void CreateProjectFolders()
{
Directory.CreateDirectory(ScriptsPath);
Directory.CreateDirectory(ScenesPath);
Directory.CreateDirectory(MaterialsPath);
Directory.CreateDirectory(PrefabsPath);
Directory.CreateDirectory(EditorPath);
AssetDatabase.Refresh();
}
private static Material CreateMaterial(string materialName, Color color)
{
string path = MaterialsPath + "/" + materialName + ".mat";
Material material = AssetDatabase.LoadAssetAtPath<Material>(path);
if (material == null)
{
material = new Material(Shader.Find("Standard"));
AssetDatabase.CreateAsset(material, path);
}
material.color = color;
material.SetColor("_Color", color);
EditorUtility.SetDirty(material);
return material;
}
private static void CreateRoom(Material floorMaterial, Material wallMaterial)
{
GameObject floor = GameObject.CreatePrimitive(PrimitiveType.Plane);
floor.name = "Floor";
floor.transform.localScale = new Vector3(0.8f, 1f, 0.6f);
floor.GetComponent<Renderer>().sharedMaterial = floorMaterial;
CreateCube("Wall_Back", new Vector3(0f, 1.25f, 6.1f), new Vector3(16f, 2.5f, 0.25f), wallMaterial);
CreateCube("Wall_Front", new Vector3(0f, 1.25f, -6.1f), new Vector3(16f, 2.5f, 0.25f), wallMaterial);
CreateCube("Wall_Left", new Vector3(-8.1f, 1.25f, 0f), new Vector3(0.25f, 2.5f, 12.2f), wallMaterial);
CreateCube("Wall_Right", new Vector3(8.1f, 1.25f, 0f), new Vector3(0.25f, 2.5f, 12.2f), wallMaterial);
CreateCube("Partition_Left", new Vector3(-5.05f, 1.25f, -0.45f), new Vector3(5.9f, 2.5f, 0.25f), wallMaterial);
CreateCube("Partition_Right", new Vector3(5.05f, 1.25f, -0.45f), new Vector3(5.9f, 2.5f, 0.25f), wallMaterial);
CreateCube("ExitWall_Left", new Vector3(-5.05f, 1.25f, 5.85f), new Vector3(5.9f, 2.5f, 0.25f), wallMaterial);
CreateCube("ExitWall_Right", new Vector3(5.05f, 1.25f, 5.85f), new Vector3(5.9f, 2.5f, 0.25f), wallMaterial);
CreateCube("WorkZone_Start", new Vector3(-3.2f, 0.025f, -4.2f), new Vector3(6.5f, 0.05f, 2.2f), floorMaterial);
CreateCube("WorkZone_Gem", new Vector3(0f, 0.025f, 2.1f), new Vector3(7.2f, 0.05f, 2.3f), floorMaterial);
}
private static Camera CreateCameraAndLight()
{
GameObject cameraObject = new GameObject("Main Camera");
Camera camera = cameraObject.AddComponent<Camera>();
cameraObject.tag = "MainCamera";
cameraObject.transform.position = new Vector3(0f, 9.5f, -7.5f);
cameraObject.transform.rotation = Quaternion.Euler(56f, 0f, 0f);
camera.orthographic = true;
camera.orthographicSize = 7.2f;
camera.clearFlags = CameraClearFlags.Skybox;
GameObject lightObject = new GameObject("Directional Light");
Light light = lightObject.AddComponent<Light>();
light.type = LightType.Directional;
light.intensity = 1.1f;
lightObject.transform.rotation = Quaternion.Euler(50f, -30f, 0f);
return camera;
}
private static void CreateEventSystem()
{
GameObject eventSystem = new GameObject("EventSystem");
eventSystem.AddComponent<UnityEngine.EventSystems.EventSystem>();
eventSystem.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>();
}
private static PuzzleUI CreateCanvasAndUI()
{
GameObject canvasObject = new GameObject("Canvas");
Canvas canvas = canvasObject.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
CanvasScaler scaler = canvasObject.AddComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1280f, 720f);
canvasObject.AddComponent<GraphicRaycaster>();
PuzzleUI puzzleUI = canvasObject.AddComponent<PuzzleUI>();
Font font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
puzzleUI.objectiveText = CreateUIText(canvasObject.transform, "ObjectiveText", "Перетащите ключ в слот", font, 24, TextAnchor.UpperLeft, new Vector2(18f, -72f), new Vector2(620f, 44f));
puzzleUI.solvedText = CreateUIText(canvasObject.transform, "SolvedText", "Пазл решён!", font, 34, TextAnchor.MiddleCenter, new Vector2(0f, -12f), new Vector2(420f, 70f));
puzzleUI.debugText = CreateUIText(canvasObject.transform, "DebugText", "Key placed: false\nGem placed: false\nPuzzle solved: false", font, 18, TextAnchor.LowerLeft, new Vector2(18f, 18f), new Vector2(360f, 96f));
puzzleUI.messageText = CreateUIText(canvasObject.transform, "MessageText", "", font, 22, TextAnchor.UpperLeft, new Vector2(18f, -232f), new Vector2(620f, 40f));
Text title = CreateUIText(canvasObject.transform, "TitleText", "Лабораторная работа №6: Пазлы и логические игры", font, 26, TextAnchor.UpperLeft, new Vector2(18f, -18f), new Vector2(860f, 42f));
title.fontStyle = FontStyle.Bold;
CreateUIText(canvasObject.transform, "HintText", "ЛКМ — взять и перетащить предмет\nОтпустите ЛКМ над подходящим слотом", font, 20, TextAnchor.UpperLeft, new Vector2(18f, -126f), new Vector2(620f, 72f));
puzzleUI.solvedText.color = new Color(0.1f, 0.85f, 0.2f);
puzzleUI.solvedText.fontStyle = FontStyle.Bold;
puzzleUI.solvedText.gameObject.SetActive(false);
return puzzleUI;
}
private static Text CreateUIText(Transform parent, string name, string value, Font font, int size, TextAnchor anchor, Vector2 anchoredPosition, Vector2 sizeDelta)
{
GameObject textObject = new GameObject(name);
textObject.transform.SetParent(parent, false);
Text text = textObject.AddComponent<Text>();
text.text = value;
text.font = font;
text.fontSize = size;
text.alignment = anchor;
text.color = Color.white;
text.raycastTarget = false;
RectTransform rect = textObject.GetComponent<RectTransform>();
rect.sizeDelta = sizeDelta;
if (anchor == TextAnchor.LowerLeft)
{
rect.anchorMin = new Vector2(0f, 0f);
rect.anchorMax = new Vector2(0f, 0f);
rect.pivot = new Vector2(0f, 0f);
}
else if (anchor == TextAnchor.MiddleCenter)
{
rect.anchorMin = new Vector2(0.5f, 0.5f);
rect.anchorMax = new Vector2(0.5f, 0.5f);
rect.pivot = new Vector2(0.5f, 0.5f);
}
else
{
rect.anchorMin = new Vector2(0f, 1f);
rect.anchorMax = new Vector2(0f, 1f);
rect.pivot = new Vector2(0f, 1f);
}
rect.anchoredPosition = anchoredPosition;
return text;
}
private static Door CreateDoor(string name, Vector3 position, Vector3 scale, Material material, Vector3 openOffset)
{
GameObject doorObject = CreateCube(name, position, scale, material);
Door door = doorObject.AddComponent<Door>();
door.openOffset = openOffset;
door.openSpeed = 2.35f;
return door;
}
private static DraggableObject CreateItem(string name, PrimitiveType primitiveType, ItemType itemType, Vector3 position, Vector3 scale, Material normalMaterial, Material highlightMaterial, Material dragMaterial)
{
GameObject itemObject = GameObject.CreatePrimitive(primitiveType);
itemObject.name = name;
itemObject.transform.position = position;
itemObject.transform.localScale = scale;
itemObject.GetComponent<Renderer>().sharedMaterial = normalMaterial;
DraggableObject draggable = itemObject.AddComponent<DraggableObject>();
draggable.itemType = itemType;
draggable.normalMaterial = normalMaterial;
draggable.highlightMaterial = highlightMaterial;
draggable.dragMaterial = dragMaterial;
draggable.startPosition = position;
draggable.snapDistance = 1.25f;
draggable.dragLift = 0.35f;
return draggable;
}
private static Slot CreateSlot(string name, ItemType itemType, Vector3 position, Material normalMaterial, Material validMaterial, Material invalidMaterial, Material successMaterial)
{
GameObject slotObject = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
slotObject.name = name;
slotObject.transform.position = position;
slotObject.transform.localScale = new Vector3(1.15f, 0.08f, 1.15f);
slotObject.GetComponent<Renderer>().sharedMaterial = normalMaterial;
GameObject snapPoint = new GameObject("SnapPoint");
snapPoint.transform.SetParent(slotObject.transform, false);
snapPoint.transform.localPosition = new Vector3(0f, 4.55f, 0f);
Slot slot = slotObject.AddComponent<Slot>();
slot.requiredItemType = itemType;
slot.normalMaterial = normalMaterial;
slot.validPreviewMaterial = validMaterial;
slot.invalidPreviewMaterial = invalidMaterial;
slot.successMaterial = successMaterial;
slot.snapPoint = snapPoint.transform;
return slot;
}
private static GameObject CreateCube(string name, Vector3 position, Vector3 scale, Material material)
{
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.name = name;
cube.transform.position = position;
cube.transform.localScale = scale;
cube.GetComponent<Renderer>().sharedMaterial = material;
return cube;
}
private static void CreateWorldLabel(string text, Vector3 position, Color color)
{
GameObject labelObject = new GameObject("Label_" + text.Replace(" ", "_"));
labelObject.transform.position = position;
labelObject.transform.rotation = Quaternion.Euler(65f, 0f, 0f);
TextMesh label = labelObject.AddComponent<TextMesh>();
label.text = text;
label.fontSize = 48;
label.characterSize = 0.08f;
label.anchor = TextAnchor.MiddleCenter;
label.alignment = TextAlignment.Center;
label.color = color;
}
private static void AddPersistentListener(UnityEvent unityEvent, UnityAction action)
{
UnityEventTools.AddPersistentListener(unityEvent, action);
}
}