using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.SceneManagement; using UnityEngine.UI; public static class Lab5SceneBuilder { private const string ScenePath = "Assets/_Scenes/Lab05_QuestSystem.unity"; private static readonly Color DarkPanel = new Color(0.04f, 0.045f, 0.055f, 0.82f); private static readonly Color Gold = new Color(1f, 0.73f, 0.24f, 1f); private static readonly Color LightText = new Color(0.93f, 0.91f, 0.84f, 1f); [MenuItem("Tools/Technical Game Design/Lab5/Create Quest System Scene")] public static void CreateQuestSystemScene() { EnsureFolders(); Font uiFont = ImportUbuntuFont(); Dictionary materials = CreateMaterials(); ItemData wood = CreateItem("Wood", "Древесина", "Пучок сухих веток и брёвен для лесоруба.", 20); ItemData stone = CreateItem("Stone", "Stone", "Камень из каменоломни.", 20); ItemData goldOre = CreateItem("GoldOre", "GoldOre", "Редкая золотая руда.", 10); QuestData woodQuest = CreateQuest( "quest_help_lumberjack", "Помощь лесорубу", "Соберите 5 древесины.", new QuestGoal { goalType = QuestGoalType.CollectItem, targetItem = wood, requiredAmount = 5 }, goldOre, 3); QuestData goblinQuest = CreateQuest( "quest_clear_camp", "Очистка лагеря", "Уничтожьте 3 гоблинов.", new QuestGoal { goalType = QuestGoalType.KillEnemy, enemyType = "Goblin", requiredAmount = 3 }, stone, 5); DialogueData elderDialogue = CreateElderDialogue(woodQuest, goblinQuest); Scene scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); scene.name = "Lab05_QuestSystem"; CreateLighting(); CreateGround(materials); CreateZones(materials, uiFont); Camera camera = CreateCamera(); GameObject playerPrefab = CreatePlayerPrefab(materials); GameObject elderPrefab = CreateNpcPrefab(materials, elderDialogue); GameObject goblinPrefab = CreateGoblinPrefab(materials); GameObject woodPrefab = CreateResourcePrefab("Wood Resource", PrimitiveType.Cylinder, materials["Wood"], wood); GameObject stonePrefab = CreateResourcePrefab("Stone Resource", PrimitiveType.Sphere, materials["Stone"], stone); GameObject goldPrefab = CreateResourcePrefab("GoldOre Resource", PrimitiveType.Sphere, materials["Gold"], goldOre); InstantiatePrefab(playerPrefab, new Vector3(0f, 1f, -5f), Quaternion.identity, "Player"); InstantiatePrefab(elderPrefab, new Vector3(0f, 1f, 3f), Quaternion.Euler(0f, 180f, 0f), "NPC Староста"); PlaceResources(woodPrefab, stonePrefab, goldPrefab); PlaceGoblins(goblinPrefab); CreateEnvironmentProps(materials); CreateSystems(new[] { woodQuest, goblinQuest }); CreateCanvas(uiFont); EditorSceneManager.MarkSceneDirty(scene); EditorSceneManager.SaveScene(scene, ScenePath); AddSceneToBuildSettings(ScenePath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log($"Lab05 quest system scene created: {ScenePath}"); } [MenuItem("Tools/Technical Game Design/Lab5/Validate Quest System Scene")] public static void ValidateGeneratedScene() { if (!File.Exists(ScenePath)) { throw new System.Exception($"Scene does not exist: {ScenePath}"); } Scene scene = EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single); int missingScripts = CountMissingScripts(scene); if (missingScripts > 0) { throw new System.Exception($"Missing scripts found: {missingScripts}"); } Require(Object.FindObjectOfType() != null, "QuestManager is missing"); Require(Object.FindObjectOfType() != null, "Inventory is missing"); Require(Object.FindObjectOfType() != null, "QuestLogUI is missing"); Require(Object.FindObjectOfType() != null, "QuestTrackerUI is missing"); Require(Object.FindObjectOfType() != null, "InventoryUI is missing"); Require(Object.FindObjectOfType() != null, "NotificationUI is missing"); Require(Object.FindObjectOfType() != null, "InteractionPromptUI is missing"); Require(Object.FindObjectOfType() != null, "DialogueUI is missing"); Require(Object.FindObjectOfType() != null, "EventSystem is missing"); Require(Object.FindObjectOfType() != null, "StandaloneInputModule is missing"); Require(Object.FindObjectOfType() != null, "NPC dialogue is missing"); Require(GameObject.FindGameObjectWithTag("Player") != null, "Player tag object is missing"); Require(Object.FindObjectsOfType().Length >= 5, "At least 5 goblins are required"); Require(CountResources("Wood") >= 5, "Wood resources are missing"); Require(CountResources("Stone") >= 1, "Stone resources are missing"); Require(CountResources("GoldOre") >= 1, "GoldOre resources are missing"); Require(AssetDatabase.LoadAssetAtPath("Assets/_Data/Items/Wood.asset") != null, "Wood ItemData is missing"); Require(AssetDatabase.LoadAssetAtPath("Assets/_Data/Items/Stone.asset") != null, "Stone ItemData is missing"); Require(AssetDatabase.LoadAssetAtPath("Assets/_Data/Items/GoldOre.asset") != null, "GoldOre ItemData is missing"); Require(AssetDatabase.LoadAssetAtPath("Assets/_Data/Quests/quest_help_lumberjack.asset") != null, "Quest 1 asset is missing"); Require(AssetDatabase.LoadAssetAtPath("Assets/_Data/Quests/quest_clear_camp.asset") != null, "Quest 2 asset is missing"); Font ubuntuFont = AssetDatabase.LoadAssetAtPath("Assets/_Fonts/Ubuntu-R.ttf"); Require(ubuntuFont != null, "Ubuntu font asset is missing"); ValidateSceneFonts(scene, ubuntuFont); ValidateQuest(AssetDatabase.LoadAssetAtPath("Assets/_Data/Quests/quest_help_lumberjack.asset"), "Помощь лесорубу"); ValidateQuest(AssetDatabase.LoadAssetAtPath("Assets/_Data/Quests/quest_clear_camp.asset"), "Очистка лагеря"); Debug.Log("Lab05 validation passed: scene, UI, quests, rewards, resources, goblins and Ubuntu font are present."); } private static void Require(bool condition, string message) { if (!condition) { throw new System.Exception(message); } } private static int CountMissingScripts(Scene scene) { int count = 0; foreach (GameObject root in scene.GetRootGameObjects()) { count += CountMissingScriptsRecursive(root); } return count; } private static int CountMissingScriptsRecursive(GameObject gameObject) { int count = GameObjectUtility.GetMonoBehavioursWithMissingScriptCount(gameObject); foreach (Transform child in gameObject.transform) { count += CountMissingScriptsRecursive(child.gameObject); } return count; } private static int CountResources(string itemId) { int count = 0; foreach (ResourceNode node in Object.FindObjectsOfType()) { if (node.itemData != null && node.itemData.itemId == itemId) { count++; } } return count; } private static void ValidateQuest(QuestData quest, string expectedName) { Require(quest != null, $"{expectedName} is missing"); Require(quest.questName == expectedName, $"{expectedName} has wrong name"); Require(quest.goals != null && quest.goals.Count > 0, $"{expectedName} has no goals"); Require(quest.rewardItem != null && quest.rewardAmount > 0, $"{expectedName} reward is not configured"); } private static void ValidateSceneFonts(Scene scene, Font expectedFont) { foreach (Text text in Resources.FindObjectsOfTypeAll()) { if (text.gameObject.scene == scene) { Require(text.font == expectedFont, $"Text object does not use Ubuntu Font: {text.name}"); } } foreach (TextMesh textMesh in Resources.FindObjectsOfTypeAll()) { if (textMesh.gameObject.scene == scene) { Require(textMesh.font == expectedFont, $"TextMesh object does not use Ubuntu Font: {textMesh.name}"); } } foreach (InventoryUI inventoryUI in Resources.FindObjectsOfTypeAll()) { if (inventoryUI.gameObject.scene == scene) { Require(inventoryUI.uiFont == expectedFont, "InventoryUI does not use Ubuntu Font"); } } foreach (QuestTrackerUI trackerUI in Resources.FindObjectsOfTypeAll()) { if (trackerUI.gameObject.scene == scene) { Require(trackerUI.uiFont == expectedFont, "QuestTrackerUI does not use Ubuntu Font"); } } foreach (QuestLogUI questLogUI in Resources.FindObjectsOfTypeAll()) { if (questLogUI.gameObject.scene == scene) { Require(questLogUI.uiFont == expectedFont, "QuestLogUI does not use Ubuntu Font"); } } } private static void EnsureFolders() { string[] folders = { "Assets/_Scenes", "Assets/_Scripts", "Assets/_Scripts/QuestSystem", "Assets/_Scripts/Dialogue", "Assets/_Scripts/Inventory", "Assets/_Scripts/UI", "Assets/_Scripts/Gameplay", "Assets/_Data", "Assets/_Data/Quests", "Assets/_Data/Items", "Assets/_Data/Enemies", "Assets/_Prefabs", "Assets/_Materials", "Assets/_Fonts", "Assets/Editor" }; foreach (string folder in folders) { CreateFolderRecursive(folder); } } private static void CreateFolderRecursive(string folder) { if (AssetDatabase.IsValidFolder(folder)) { return; } string parent = Path.GetDirectoryName(folder)?.Replace("\\", "/"); string name = Path.GetFileName(folder); if (!string.IsNullOrEmpty(parent)) { CreateFolderRecursive(parent); AssetDatabase.CreateFolder(parent, name); } } private static Font ImportUbuntuFont() { string destination = "Assets/_Fonts/Ubuntu-R.ttf"; string source = FindUbuntuFont(); if (!string.IsNullOrEmpty(source)) { if (!File.Exists(destination)) { FileUtil.CopyFileOrDirectory(source, destination); } AssetDatabase.ImportAsset(destination, ImportAssetOptions.ForceUpdate); Font ubuntu = AssetDatabase.LoadAssetAtPath(destination); if (ubuntu != null) { return ubuntu; } } return Resources.GetBuiltinResource("Arial.ttf"); } private static string FindUbuntuFont() { string[] roots = { "/usr/share/fonts/truetype/ubuntu", "/usr/share/fonts/TTF", "/usr/share/fonts" }; foreach (string root in roots) { if (!Directory.Exists(root)) { continue; } string[] regular = Directory.GetFiles(root, "Ubuntu-R.ttf", SearchOption.AllDirectories); if (regular.Length > 0) { return regular[0]; } string[] anyUbuntu = Directory.GetFiles(root, "*Ubuntu*.ttf", SearchOption.AllDirectories); if (anyUbuntu.Length > 0) { return anyUbuntu[0]; } } return string.Empty; } private static Dictionary CreateMaterials() { return new Dictionary { ["Ground"] = CreateMaterial("M_Ground_Grass", new Color(0.22f, 0.45f, 0.22f)), ["Square"] = CreateMaterial("M_Central_Square", new Color(0.42f, 0.38f, 0.32f)), ["Forest"] = CreateMaterial("M_Forest_Dark", new Color(0.08f, 0.3f, 0.12f)), ["Quarry"] = CreateMaterial("M_Quarry_Stone", new Color(0.45f, 0.43f, 0.4f)), ["Camp"] = CreateMaterial("M_Goblin_Camp", new Color(0.32f, 0.2f, 0.14f)), ["Wood"] = CreateMaterial("M_Wood", new Color(0.47f, 0.27f, 0.13f)), ["Leaves"] = CreateMaterial("M_Leaves", new Color(0.09f, 0.42f, 0.16f)), ["Stone"] = CreateMaterial("M_Stone", new Color(0.55f, 0.55f, 0.58f)), ["Gold"] = CreateMaterial("M_GoldOre", new Color(1f, 0.72f, 0.22f)), ["Player"] = CreateMaterial("M_Player", new Color(0.18f, 0.36f, 0.8f)), ["Npc"] = CreateMaterial("M_Elder", new Color(0.72f, 0.64f, 0.52f)), ["Goblin"] = CreateMaterial("M_Goblin", new Color(0.24f, 0.62f, 0.18f)), ["Tent"] = CreateMaterial("M_Tent_Red", new Color(0.5f, 0.12f, 0.08f)), ["Accent"] = CreateMaterial("M_Gold_Accent", Gold) }; } private static Material CreateMaterial(string name, Color color) { string path = $"Assets/_Materials/{name}.mat"; Material material = AssetDatabase.LoadAssetAtPath(path); if (material == null) { material = new Material(Shader.Find("Standard")); AssetDatabase.CreateAsset(material, path); } material.color = color; EditorUtility.SetDirty(material); return material; } private static ItemData CreateItem(string id, string displayName, string description, int maxStack) { string path = $"Assets/_Data/Items/{id}.asset"; ItemData item = AssetDatabase.LoadAssetAtPath(path); if (item == null) { item = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(item, path); } item.itemId = id; item.itemName = displayName; item.description = description; item.maxStack = maxStack; EditorUtility.SetDirty(item); return item; } private static QuestData CreateQuest(string id, string name, string description, QuestGoal goal, ItemData rewardItem, int rewardAmount) { string path = $"Assets/_Data/Quests/{id}.asset"; QuestData quest = AssetDatabase.LoadAssetAtPath(path); if (quest == null) { quest = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(quest, path); } quest.questId = id; quest.questName = name; quest.description = description; quest.goals.Clear(); quest.goals.Add(goal); quest.rewardItem = rewardItem; quest.rewardAmount = rewardAmount; EditorUtility.SetDirty(quest); return quest; } private static DialogueData CreateElderDialogue(QuestData woodQuest, QuestData goblinQuest) { string path = "Assets/_Data/VillageElderDialogue.asset"; DialogueData dialogue = AssetDatabase.LoadAssetAtPath(path); if (dialogue == null) { dialogue = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(dialogue, path); } dialogue.npcName = "Староста"; dialogue.greeting = "Добро пожаловать на площадь. Работы много, выбирайте задание."; dialogue.responses.Clear(); dialogue.responses.Add(new DialogueResponse { text = "Нужна работа.", questToStart = woodQuest }); dialogue.responses.Add(new DialogueResponse { text = "Есть ещё задания?", questToStart = goblinQuest }); dialogue.responses.Add(new DialogueResponse { text = "До свидания.", questToStart = null }); EditorUtility.SetDirty(dialogue); return dialogue; } private static void CreateLighting() { GameObject lightObject = new GameObject("Directional Light"); Light light = lightObject.AddComponent(); light.type = LightType.Directional; light.intensity = 1.1f; lightObject.transform.rotation = Quaternion.Euler(50f, -35f, 0f); RenderSettings.ambientLight = new Color(0.45f, 0.48f, 0.52f); RenderSettings.fog = true; RenderSettings.fogColor = new Color(0.55f, 0.62f, 0.58f); RenderSettings.fogDensity = 0.012f; } private static void CreateGround(Dictionary materials) { GameObject ground = GameObject.CreatePrimitive(PrimitiveType.Plane); ground.name = "RPG Polygon Ground"; ground.transform.localScale = new Vector3(5.4f, 1f, 5.4f); AssignMaterial(ground, materials["Ground"]); CreateFlatZone("Центральная площадь", new Vector3(0f, 0.03f, 0f), new Vector3(12f, 0.08f, 12f), materials["Square"]); CreateFlatZone("Лес", new Vector3(-17f, 0.04f, 2f), new Vector3(15f, 0.08f, 17f), materials["Forest"]); CreateFlatZone("Каменоломня", new Vector3(17f, 0.04f, 1f), new Vector3(15f, 0.08f, 17f), materials["Quarry"]); CreateFlatZone("Лагерь гоблинов", new Vector3(0f, 0.04f, 20f), new Vector3(18f, 0.08f, 14f), materials["Camp"]); } private static void CreateFlatZone(string name, Vector3 position, Vector3 scale, Material material) { GameObject zone = GameObject.CreatePrimitive(PrimitiveType.Cube); zone.name = name; zone.transform.position = position; zone.transform.localScale = scale; AssignMaterial(zone, material); } private static void CreateZones(Dictionary materials, Font font) { CreateWorldLabel("Центральная площадь", new Vector3(-4f, 0.2f, -4f), font); CreateWorldLabel("Лес", new Vector3(-18f, 0.2f, -7f), font); CreateWorldLabel("Каменоломня", new Vector3(14f, 0.2f, -7f), font); CreateWorldLabel("Лагерь гоблинов", new Vector3(-5f, 0.2f, 14f), font); for (int i = 0; i < 18; i++) { float x = -23f + (i % 6) * 3.2f; float z = -6f + (i / 6) * 5.1f; CreateTree(new Vector3(x, 0f, z), materials["Wood"], materials["Leaves"]); } for (int i = 0; i < 14; i++) { float x = 13f + (i % 5) * 2.2f; float z = -7f + (i / 5) * 4.2f; CreateRock(new Vector3(x, 0.45f, z), materials["Stone"], 0.8f + i % 3 * 0.25f); } } private static void CreateWorldLabel(string text, Vector3 position, Font font) { GameObject label = new GameObject($"Label {text}", typeof(TextMesh)); label.transform.position = position; label.transform.rotation = Quaternion.Euler(75f, 0f, 0f); TextMesh mesh = label.GetComponent(); mesh.text = text; mesh.font = font; mesh.fontSize = 32; mesh.characterSize = 0.18f; mesh.anchor = TextAnchor.MiddleCenter; mesh.color = Gold; MeshRenderer renderer = label.GetComponent(); if (font != null) { renderer.sharedMaterial = font.material; } } private static void CreateTree(Vector3 position, Material wood, Material leaves) { GameObject trunk = GameObject.CreatePrimitive(PrimitiveType.Cylinder); trunk.name = "Tree Trunk"; trunk.transform.position = position + Vector3.up * 1f; trunk.transform.localScale = new Vector3(0.35f, 1f, 0.35f); AssignMaterial(trunk, wood); GameObject crown = GameObject.CreatePrimitive(PrimitiveType.Sphere); crown.name = "Tree Crown"; crown.transform.position = position + Vector3.up * 2.35f; crown.transform.localScale = new Vector3(1.7f, 1.35f, 1.7f); AssignMaterial(crown, leaves); } private static void CreateRock(Vector3 position, Material material, float scale) { GameObject rock = GameObject.CreatePrimitive(PrimitiveType.Sphere); rock.name = "Quarry Rock"; rock.transform.position = position; rock.transform.localScale = new Vector3(scale * 1.25f, scale * 0.65f, scale); AssignMaterial(rock, material); } private static Camera CreateCamera() { GameObject cameraObject = new GameObject("Main Camera", typeof(Camera), typeof(AudioListener)); cameraObject.tag = "MainCamera"; Camera camera = cameraObject.GetComponent(); camera.fieldOfView = 62f; camera.nearClipPlane = 0.08f; camera.transform.position = new Vector3(0f, 5f, -10f); camera.transform.rotation = Quaternion.Euler(20f, 0f, 0f); return camera; } private static GameObject CreatePlayerPrefab(Dictionary materials) { GameObject player = GameObject.CreatePrimitive(PrimitiveType.Capsule); player.name = "Player"; player.tag = "Player"; player.transform.position = new Vector3(0f, 1f, -5f); AssignMaterial(player, materials["Player"]); Object.DestroyImmediate(player.GetComponent()); CharacterController controller = player.AddComponent(); controller.height = 2f; controller.radius = 0.45f; controller.center = Vector3.zero; SimplePlayerController movement = player.AddComponent(); GameObject pivot = new GameObject("Camera Pivot"); pivot.transform.SetParent(player.transform); pivot.transform.localPosition = new Vector3(0f, 1.35f, 0f); movement.cameraPivot = pivot.transform; player.AddComponent(); GameObject prefab = SavePrefab(player, "Assets/_Prefabs/Player.prefab"); Object.DestroyImmediate(player); return prefab; } private static GameObject CreateNpcPrefab(Dictionary materials, DialogueData dialogue) { GameObject npc = GameObject.CreatePrimitive(PrimitiveType.Capsule); npc.name = "NPC Староста"; npc.transform.position = new Vector3(0f, 1f, 3f); AssignMaterial(npc, materials["Npc"]); NPCDialogue npcDialogue = npc.AddComponent(); npcDialogue.npcId = "VillageElder"; npcDialogue.dialogueData = dialogue; GameObject marker = GameObject.CreatePrimitive(PrimitiveType.Sphere); marker.name = "Quest Marker"; marker.transform.SetParent(npc.transform); marker.transform.localPosition = new Vector3(0f, 1.45f, 0f); marker.transform.localScale = Vector3.one * 0.28f; AssignMaterial(marker, materials["Accent"]); GameObject prefab = SavePrefab(npc, "Assets/_Prefabs/NPC_Starosta.prefab"); Object.DestroyImmediate(npc); return prefab; } private static GameObject CreateGoblinPrefab(Dictionary materials) { GameObject goblin = GameObject.CreatePrimitive(PrimitiveType.Capsule); goblin.name = "Goblin"; goblin.transform.position = Vector3.up; goblin.transform.localScale = new Vector3(0.85f, 0.85f, 0.85f); AssignMaterial(goblin, materials["Goblin"]); Enemy enemy = goblin.AddComponent(); enemy.enemyType = "Goblin"; enemy.health = 100; GameObject head = GameObject.CreatePrimitive(PrimitiveType.Sphere); head.name = "Goblin Head"; head.transform.SetParent(goblin.transform); head.transform.localPosition = new Vector3(0f, 0.75f, 0f); head.transform.localScale = Vector3.one * 0.55f; AssignMaterial(head, materials["Goblin"]); GameObject prefab = SavePrefab(goblin, "Assets/_Prefabs/Goblin.prefab"); Object.DestroyImmediate(goblin); return prefab; } private static GameObject CreateResourcePrefab(string name, PrimitiveType primitive, Material material, ItemData item) { GameObject resource = GameObject.CreatePrimitive(primitive); resource.name = name; resource.transform.position = Vector3.up * 0.5f; resource.transform.localScale = name.StartsWith("Wood") ? new Vector3(0.45f, 1.1f, 0.45f) : Vector3.one * 0.8f; AssignMaterial(resource, material); ResourceNode node = resource.AddComponent(); node.itemData = item; node.amount = 1; GameObject prefab = SavePrefab(resource, $"Assets/_Prefabs/{name.Replace(" ", "_")}.prefab"); Object.DestroyImmediate(resource); return prefab; } private static void PlaceResources(GameObject woodPrefab, GameObject stonePrefab, GameObject goldPrefab) { Vector3[] woodPositions = { new Vector3(-14f, 0.55f, -1f), new Vector3(-17f, 0.55f, 3f), new Vector3(-20f, 0.55f, 6f), new Vector3(-12f, 0.55f, 6f), new Vector3(-18f, 0.55f, 10f), new Vector3(-22f, 0.55f, 0f) }; Vector3[] stonePositions = { new Vector3(14f, 0.6f, -2f), new Vector3(18f, 0.6f, 1f), new Vector3(21f, 0.6f, 5f), new Vector3(15f, 0.6f, 7f), new Vector3(22f, 0.6f, -5f) }; Vector3[] goldPositions = { new Vector3(18f, 0.6f, 9f), new Vector3(23f, 0.6f, 2f), new Vector3(12f, 0.6f, 4f) }; foreach (Vector3 position in woodPositions) { InstantiatePrefab(woodPrefab, position, Quaternion.identity, "Wood"); } foreach (Vector3 position in stonePositions) { InstantiatePrefab(stonePrefab, position, Quaternion.identity, "Stone"); } foreach (Vector3 position in goldPositions) { InstantiatePrefab(goldPrefab, position, Quaternion.identity, "GoldOre"); } } private static void PlaceGoblins(GameObject goblinPrefab) { Vector3[] positions = { new Vector3(-5f, 0.9f, 18f), new Vector3(0f, 0.9f, 20f), new Vector3(5f, 0.9f, 18f), new Vector3(-3f, 0.9f, 24f), new Vector3(4f, 0.9f, 25f) }; foreach (Vector3 position in positions) { InstantiatePrefab(goblinPrefab, position, Quaternion.identity, "Goblin"); } } private static void CreateEnvironmentProps(Dictionary materials) { for (int i = 0; i < 3; i++) { GameObject tent = GameObject.CreatePrimitive(PrimitiveType.Cylinder); tent.name = "Goblin Tent"; tent.transform.position = new Vector3(-6f + i * 6f, 0.65f, 26f); tent.transform.localScale = new Vector3(1.7f, 0.65f, 1.7f); AssignMaterial(tent, materials["Tent"]); } GameObject fire = GameObject.CreatePrimitive(PrimitiveType.Cylinder); fire.name = "Campfire"; fire.transform.position = new Vector3(0f, 0.35f, 18f); fire.transform.localScale = new Vector3(0.75f, 0.35f, 0.75f); AssignMaterial(fire, materials["Accent"]); PointLight("Campfire Light", new Vector3(0f, 1.7f, 18f), new Color(1f, 0.45f, 0.14f), 2.5f, 8f); PointLight("Square Lantern", new Vector3(2f, 3.2f, 2f), Gold, 1.2f, 10f); } private static void PointLight(string name, Vector3 position, Color color, float intensity, float range) { GameObject lightObject = new GameObject(name); lightObject.transform.position = position; Light light = lightObject.AddComponent(); light.type = LightType.Point; light.color = color; light.intensity = intensity; light.range = range; } private static void CreateSystems(IEnumerable quests) { GameObject systems = new GameObject("Systems"); QuestManager questManager = systems.AddComponent(); questManager.SetAvailableQuests(quests); systems.AddComponent(); EditorUtility.SetDirty(questManager); } private static void CreateCanvas(Font font) { CreateEventSystem(); GameObject canvasObject = new GameObject("Canvas", typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster)); Canvas canvas = canvasObject.GetComponent(); canvas.renderMode = RenderMode.ScreenSpaceOverlay; CanvasScaler scaler = canvasObject.GetComponent(); scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; scaler.referenceResolution = new Vector2(1920f, 1080f); scaler.matchWidthOrHeight = 0.5f; CreateQuestTracker(canvasObject.transform, font); CreateInventoryPanel(canvasObject.transform, font); CreateNotificationPanel(canvasObject.transform, font); CreateInteractionPrompt(canvasObject.transform, font); CreateQuestLog(canvasObject.transform, font); CreateDialoguePanel(canvasObject.transform, font); } private static void CreateEventSystem() { GameObject eventSystemObject = new GameObject("EventSystem", typeof(EventSystem), typeof(StandaloneInputModule)); EventSystem eventSystem = eventSystemObject.GetComponent(); eventSystem.sendNavigationEvents = true; StandaloneInputModule inputModule = eventSystemObject.GetComponent(); inputModule.horizontalAxis = "Horizontal"; inputModule.verticalAxis = "Vertical"; inputModule.submitButton = "Submit"; inputModule.cancelButton = "Cancel"; } private static void CreateQuestTracker(Transform parent, Font font) { GameObject panel = CreatePanel("Active Quests Panel", parent, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(18f, -18f), new Vector2(380f, 220f), new Vector2(0f, 1f)); CreateText("Активные квесты", panel.transform, font, 20, Gold, FontStyle.Bold, new Vector2(14f, -12f), new Vector2(350f, 30f), new Vector2(0f, 1f)); Text output = CreateText("Нет активных квестов", panel.transform, font, 16, LightText, FontStyle.Normal, new Vector2(14f, -54f), new Vector2(350f, 142f), new Vector2(0f, 1f)); output.lineSpacing = 1.12f; output.alignment = TextAnchor.UpperLeft; RectTransform content = CreateVerticalContent("Active Quest Content Legacy", panel.transform, new Vector2(14f, -50f), new Vector2(350f, 150f), new Vector2(0f, 1f)); content.gameObject.SetActive(false); QuestTrackerUI tracker = panel.AddComponent(); tracker.content = content; tracker.outputText = output; tracker.uiFont = font; } private static void CreateInventoryPanel(Transform parent, Font font) { GameObject panel = CreatePanel("Inventory Panel", parent, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-18f, -18f), new Vector2(330f, 220f), new Vector2(1f, 1f)); CreateText("Инвентарь", panel.transform, font, 20, Gold, FontStyle.Bold, new Vector2(-14f, -12f), new Vector2(300f, 30f), new Vector2(1f, 1f)); Text output = CreateText("Пусто", panel.transform, font, 16, LightText, FontStyle.Normal, new Vector2(-14f, -54f), new Vector2(300f, 142f), new Vector2(1f, 1f)); output.lineSpacing = 1.12f; output.alignment = TextAnchor.UpperLeft; RectTransform content = CreateVerticalContent("Inventory Content Legacy", panel.transform, new Vector2(-14f, -50f), new Vector2(300f, 150f), new Vector2(1f, 1f)); content.gameObject.SetActive(false); InventoryUI inventoryUI = panel.AddComponent(); inventoryUI.content = content; inventoryUI.outputText = output; inventoryUI.uiFont = font; } private static void CreateNotificationPanel(Transform parent, Font font) { GameObject panel = CreatePanel("Notification Panel", parent, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 42f), new Vector2(620f, 96f), new Vector2(0.5f, 0f)); CanvasGroup group = panel.AddComponent(); Text message = CreateText(string.Empty, panel.transform, font, 22, LightText, FontStyle.Bold, Vector2.zero, new Vector2(570f, 74f), new Vector2(0.5f, 0.5f)); message.alignment = TextAnchor.MiddleCenter; NotificationUI notification = panel.AddComponent(); notification.messageText = message; notification.canvasGroup = group; } private static void CreateInteractionPrompt(Transform parent, Font font) { GameObject root = CreatePanel("Interaction Prompt", parent, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 172f), new Vector2(360f, 46f), new Vector2(0.5f, 0f)); root.GetComponent().color = new Color(0.035f, 0.04f, 0.045f, 0.9f); Outline outline = root.AddComponent(); outline.effectColor = new Color(1f, 0.73f, 0.24f, 0.55f); outline.effectDistance = new Vector2(1f, -1f); Text text = CreateText("E Поговорить", root.transform, font, 18, LightText, FontStyle.Bold, Vector2.zero, new Vector2(320f, 34f), new Vector2(0.5f, 0.5f)); text.alignment = TextAnchor.MiddleCenter; InteractionPromptUI prompt = parent.gameObject.AddComponent(); prompt.root = root; prompt.promptText = text; root.SetActive(false); } private static void CreateQuestLog(Transform parent, Font font) { GameObject root = CreatePanel("Quest Log Panel", parent, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(760f, 650f), new Vector2(0.5f, 0.5f)); CreateText("Журнал квестов", root.transform, font, 28, Gold, FontStyle.Bold, new Vector2(0f, -24f), new Vector2(700f, 42f), new Vector2(0.5f, 1f)); GameObject scrollObject = new GameObject("Scroll View", typeof(RectTransform), typeof(ScrollRect), typeof(Image)); scrollObject.transform.SetParent(root.transform, false); RectTransform scrollRectTransform = scrollObject.GetComponent(); scrollRectTransform.anchorMin = new Vector2(0.5f, 0.5f); scrollRectTransform.anchorMax = new Vector2(0.5f, 0.5f); scrollRectTransform.pivot = new Vector2(0.5f, 0.5f); scrollRectTransform.anchoredPosition = new Vector2(0f, -34f); scrollRectTransform.sizeDelta = new Vector2(700f, 540f); scrollObject.GetComponent().color = new Color(0f, 0f, 0f, 0.18f); GameObject viewport = new GameObject("Viewport", typeof(RectTransform), typeof(Image), typeof(Mask)); viewport.transform.SetParent(scrollObject.transform, false); RectTransform viewportRect = Stretch(viewport); viewport.GetComponent().color = new Color(0f, 0f, 0f, 0.08f); viewport.GetComponent().showMaskGraphic = false; GameObject contentObject = new GameObject("Content", typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter)); contentObject.transform.SetParent(viewport.transform, false); RectTransform content = contentObject.GetComponent(); content.anchorMin = new Vector2(0f, 1f); content.anchorMax = new Vector2(1f, 1f); content.pivot = new Vector2(0.5f, 1f); content.anchoredPosition = Vector2.zero; content.sizeDelta = new Vector2(0f, 0f); VerticalLayoutGroup layout = contentObject.GetComponent(); layout.padding = new RectOffset(18, 18, 18, 18); layout.spacing = 3f; layout.childForceExpandWidth = true; layout.childForceExpandHeight = false; ContentSizeFitter fitter = contentObject.GetComponent(); fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize; ScrollRect scroll = scrollObject.GetComponent(); scroll.viewport = viewportRect; scroll.content = content; scroll.horizontal = false; QuestLogUI questLog = parent.gameObject.AddComponent(); questLog.root = root; questLog.content = content; questLog.uiFont = font; root.SetActive(false); } private static void CreateDialoguePanel(Transform parent, Font font) { GameObject root = CreatePanel("Dialogue Panel", parent, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 38f), new Vector2(980f, 250f), new Vector2(0.5f, 0f)); root.GetComponent().color = new Color(0.04f, 0.04f, 0.04f, 0.93f); Outline rootOutline = root.AddComponent(); rootOutline.effectColor = new Color(1f, 0.73f, 0.24f, 0.24f); rootOutline.effectDistance = new Vector2(1f, -1f); GameObject npcColumn = CreateAnchoredPanel("NPC Dialogue Copy", root.transform, new Vector2(0f, 1f), new Vector2(24f, -24f), new Vector2(590f, 198f), new Vector2(0f, 1f), new Color(0f, 0f, 0f, 0.16f)); Text nameText = CreateText("Староста", npcColumn.transform, font, 25, Gold, FontStyle.Bold, new Vector2(18f, -16f), new Vector2(540f, 36f), new Vector2(0f, 1f)); Text bodyText = CreateText(string.Empty, npcColumn.transform, font, 18, LightText, FontStyle.Normal, new Vector2(18f, -64f), new Vector2(540f, 100f), new Vector2(0f, 1f)); bodyText.lineSpacing = 1.08f; GameObject responseArea = CreateAnchoredPanel("Dialogue Responses", root.transform, new Vector2(1f, 1f), new Vector2(-24f, -24f), new Vector2(340f, 198f), new Vector2(1f, 1f), new Color(0f, 0f, 0f, 0.2f)); CreateText("Ответы", responseArea.transform, font, 15, Gold, FontStyle.Bold, new Vector2(16f, -10f), new Vector2(300f, 24f), new Vector2(0f, 1f)); GameObject responseViewport = CreateAnchoredPanel("Response Button Stack", responseArea.transform, new Vector2(0f, 1f), new Vector2(14f, -44f), new Vector2(312f, 142f), new Vector2(0f, 1f), new Color(0f, 0f, 0f, 0f)); RectTransform responseContainer = CreateStretchVerticalContent("Responses", responseViewport.transform, new RectOffset(0, 0, 0, 0)); Button template = CreateResponseButton("Response Button Template", responseContainer, font); template.gameObject.SetActive(false); DialogueUI dialogueUI = parent.gameObject.AddComponent(); dialogueUI.root = root; dialogueUI.nameText = nameText; dialogueUI.bodyText = bodyText; dialogueUI.responseContainer = responseContainer; dialogueUI.responseButtonTemplate = template; root.SetActive(false); } private static GameObject CreatePanel(string name, Transform parent, Vector2 anchorMin, Vector2 anchorMax, Vector2 anchoredPosition, Vector2 size, Vector2 pivot) { GameObject panel = new GameObject(name, typeof(RectTransform), typeof(Image)); panel.transform.SetParent(parent, false); RectTransform rect = panel.GetComponent(); rect.anchorMin = anchorMin; rect.anchorMax = anchorMax; rect.pivot = pivot; rect.anchoredPosition = anchoredPosition; rect.sizeDelta = size; Image image = panel.GetComponent(); image.color = DarkPanel; return panel; } private static GameObject CreateInsetPanel(string name, Transform parent, Vector2 offsetMin, Vector2 offsetMax, Color color) { GameObject panel = new GameObject(name, typeof(RectTransform), typeof(Image)); panel.transform.SetParent(parent, false); RectTransform rect = panel.GetComponent(); rect.anchorMin = Vector2.zero; rect.anchorMax = Vector2.one; rect.offsetMin = offsetMin; rect.offsetMax = offsetMax; Image image = panel.GetComponent(); image.color = color; return panel; } private static GameObject CreateAnchoredPanel(string name, Transform parent, Vector2 anchor, Vector2 anchoredPosition, Vector2 size, Vector2 pivot, Color color) { GameObject panel = new GameObject(name, typeof(RectTransform), typeof(Image)); panel.transform.SetParent(parent, false); RectTransform rect = panel.GetComponent(); rect.anchorMin = anchor; rect.anchorMax = anchor; rect.pivot = pivot; rect.anchoredPosition = anchoredPosition; rect.sizeDelta = size; Image image = panel.GetComponent(); image.color = color; return panel; } private static Text CreateText(string value, Transform parent, Font font, int size, Color color, FontStyle style, Vector2 anchoredPosition, Vector2 rectSize, Vector2 pivot) { GameObject textObject = new GameObject("Text", typeof(RectTransform), typeof(Text)); textObject.transform.SetParent(parent, false); RectTransform rect = textObject.GetComponent(); rect.anchorMin = pivot; rect.anchorMax = pivot; rect.pivot = pivot; rect.anchoredPosition = anchoredPosition; rect.sizeDelta = rectSize; Text text = textObject.GetComponent(); text.font = font != null ? font : Resources.GetBuiltinResource("Arial.ttf"); text.text = value; text.fontSize = size; text.fontStyle = style; text.color = color; text.horizontalOverflow = HorizontalWrapMode.Wrap; text.verticalOverflow = VerticalWrapMode.Overflow; return text; } private static RectTransform CreateVerticalContent(string name, Transform parent, Vector2 anchoredPosition, Vector2 size, Vector2 pivot) { GameObject contentObject = new GameObject(name, typeof(RectTransform), typeof(VerticalLayoutGroup)); contentObject.transform.SetParent(parent, false); RectTransform rect = contentObject.GetComponent(); rect.anchorMin = pivot; rect.anchorMax = pivot; rect.pivot = pivot; rect.anchoredPosition = anchoredPosition; rect.sizeDelta = size; VerticalLayoutGroup layout = contentObject.GetComponent(); layout.spacing = 2f; layout.childForceExpandWidth = true; layout.childForceExpandHeight = false; return rect; } private static RectTransform CreateStretchVerticalContent(string name, Transform parent, RectOffset padding) { GameObject contentObject = new GameObject(name, typeof(RectTransform), typeof(VerticalLayoutGroup)); contentObject.transform.SetParent(parent, false); RectTransform rect = contentObject.GetComponent(); rect.anchorMin = Vector2.zero; rect.anchorMax = Vector2.one; rect.offsetMin = Vector2.zero; rect.offsetMax = Vector2.zero; VerticalLayoutGroup layout = contentObject.GetComponent(); layout.padding = padding; layout.spacing = 8f; layout.childAlignment = TextAnchor.UpperLeft; layout.childControlWidth = true; layout.childControlHeight = true; layout.childForceExpandWidth = true; layout.childForceExpandHeight = false; return rect; } private static Button CreateResponseButton(string name, Transform parent, Font font) { GameObject buttonObject = new GameObject(name, typeof(RectTransform), typeof(Image), typeof(Button), typeof(LayoutElement), typeof(Outline)); buttonObject.transform.SetParent(parent, false); RectTransform rect = buttonObject.GetComponent(); rect.anchorMin = new Vector2(0f, 1f); rect.anchorMax = new Vector2(1f, 1f); rect.pivot = new Vector2(0.5f, 1f); rect.sizeDelta = new Vector2(0f, 40f); LayoutElement layoutElement = buttonObject.GetComponent(); layoutElement.minHeight = 40f; layoutElement.preferredHeight = 40f; layoutElement.flexibleWidth = 1f; Image image = buttonObject.GetComponent(); image.color = new Color(0.12f, 0.1f, 0.075f, 0.98f); Outline outline = buttonObject.GetComponent(); outline.effectColor = new Color(1f, 0.73f, 0.24f, 0.45f); outline.effectDistance = new Vector2(1f, -1f); Button button = buttonObject.GetComponent