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(); 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(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().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(); 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.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(); eventSystem.AddComponent(); } private static PuzzleUI CreateCanvasAndUI() { GameObject canvasObject = new GameObject("Canvas"); Canvas canvas = canvasObject.AddComponent(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; CanvasScaler scaler = canvasObject.AddComponent(); scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; scaler.referenceResolution = new Vector2(1280f, 720f); canvasObject.AddComponent(); PuzzleUI puzzleUI = canvasObject.AddComponent(); Font font = Resources.GetBuiltinResource("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 = value; text.font = font; text.fontSize = size; text.alignment = anchor; text.color = Color.white; text.raycastTarget = false; RectTransform rect = textObject.GetComponent(); 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.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().sharedMaterial = normalMaterial; DraggableObject draggable = itemObject.AddComponent(); 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().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.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().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(); 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); } }