Files
OTG_5/Assets/Editor/Lab5SceneBuilder.cs
T

1118 lines
48 KiB
C#
Raw Normal View History

2026-06-04 20:14:47 +03:00
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<string, Material> 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<QuestManager>() != null, "QuestManager is missing");
Require(Object.FindObjectOfType<Inventory>() != null, "Inventory is missing");
Require(Object.FindObjectOfType<QuestLogUI>() != null, "QuestLogUI is missing");
Require(Object.FindObjectOfType<QuestTrackerUI>() != null, "QuestTrackerUI is missing");
Require(Object.FindObjectOfType<InventoryUI>() != null, "InventoryUI is missing");
Require(Object.FindObjectOfType<NotificationUI>() != null, "NotificationUI is missing");
Require(Object.FindObjectOfType<InteractionPromptUI>() != null, "InteractionPromptUI is missing");
Require(Object.FindObjectOfType<DialogueUI>() != null, "DialogueUI is missing");
Require(Object.FindObjectOfType<EventSystem>() != null, "EventSystem is missing");
Require(Object.FindObjectOfType<StandaloneInputModule>() != null, "StandaloneInputModule is missing");
Require(Object.FindObjectOfType<NPCDialogue>() != null, "NPC dialogue is missing");
Require(GameObject.FindGameObjectWithTag("Player") != null, "Player tag object is missing");
Require(Object.FindObjectsOfType<Enemy>().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<ItemData>("Assets/_Data/Items/Wood.asset") != null, "Wood ItemData is missing");
Require(AssetDatabase.LoadAssetAtPath<ItemData>("Assets/_Data/Items/Stone.asset") != null, "Stone ItemData is missing");
Require(AssetDatabase.LoadAssetAtPath<ItemData>("Assets/_Data/Items/GoldOre.asset") != null, "GoldOre ItemData is missing");
Require(AssetDatabase.LoadAssetAtPath<QuestData>("Assets/_Data/Quests/quest_help_lumberjack.asset") != null, "Quest 1 asset is missing");
Require(AssetDatabase.LoadAssetAtPath<QuestData>("Assets/_Data/Quests/quest_clear_camp.asset") != null, "Quest 2 asset is missing");
Font ubuntuFont = AssetDatabase.LoadAssetAtPath<Font>("Assets/_Fonts/Ubuntu-R.ttf");
Require(ubuntuFont != null, "Ubuntu font asset is missing");
ValidateSceneFonts(scene, ubuntuFont);
ValidateQuest(AssetDatabase.LoadAssetAtPath<QuestData>("Assets/_Data/Quests/quest_help_lumberjack.asset"), "Помощь лесорубу");
ValidateQuest(AssetDatabase.LoadAssetAtPath<QuestData>("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<ResourceNode>())
{
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<Text>())
{
if (text.gameObject.scene == scene)
{
Require(text.font == expectedFont, $"Text object does not use Ubuntu Font: {text.name}");
}
}
foreach (TextMesh textMesh in Resources.FindObjectsOfTypeAll<TextMesh>())
{
if (textMesh.gameObject.scene == scene)
{
Require(textMesh.font == expectedFont, $"TextMesh object does not use Ubuntu Font: {textMesh.name}");
}
}
foreach (InventoryUI inventoryUI in Resources.FindObjectsOfTypeAll<InventoryUI>())
{
if (inventoryUI.gameObject.scene == scene)
{
Require(inventoryUI.uiFont == expectedFont, "InventoryUI does not use Ubuntu Font");
}
}
foreach (QuestTrackerUI trackerUI in Resources.FindObjectsOfTypeAll<QuestTrackerUI>())
{
if (trackerUI.gameObject.scene == scene)
{
Require(trackerUI.uiFont == expectedFont, "QuestTrackerUI does not use Ubuntu Font");
}
}
foreach (QuestLogUI questLogUI in Resources.FindObjectsOfTypeAll<QuestLogUI>())
{
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<Font>(destination);
if (ubuntu != null)
{
return ubuntu;
}
}
return Resources.GetBuiltinResource<Font>("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<string, Material> CreateMaterials()
{
return new Dictionary<string, Material>
{
["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<Material>(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<ItemData>(path);
if (item == null)
{
item = ScriptableObject.CreateInstance<ItemData>();
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<QuestData>(path);
if (quest == null)
{
quest = ScriptableObject.CreateInstance<QuestData>();
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<DialogueData>(path);
if (dialogue == null)
{
dialogue = ScriptableObject.CreateInstance<DialogueData>();
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>();
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<string, Material> 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<string, Material> 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<TextMesh>();
mesh.text = text;
mesh.font = font;
mesh.fontSize = 32;
mesh.characterSize = 0.18f;
mesh.anchor = TextAnchor.MiddleCenter;
mesh.color = Gold;
MeshRenderer renderer = label.GetComponent<MeshRenderer>();
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>();
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<string, Material> 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<CapsuleCollider>());
CharacterController controller = player.AddComponent<CharacterController>();
controller.height = 2f;
controller.radius = 0.45f;
controller.center = Vector3.zero;
SimplePlayerController movement = player.AddComponent<SimplePlayerController>();
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<PlayerCombat>();
GameObject prefab = SavePrefab(player, "Assets/_Prefabs/Player.prefab");
Object.DestroyImmediate(player);
return prefab;
}
private static GameObject CreateNpcPrefab(Dictionary<string, Material> 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>();
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<string, Material> 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>();
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<ResourceNode>();
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<string, Material> 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>();
light.type = LightType.Point;
light.color = color;
light.intensity = intensity;
light.range = range;
}
private static void CreateSystems(IEnumerable<QuestData> quests)
{
GameObject systems = new GameObject("Systems");
QuestManager questManager = systems.AddComponent<QuestManager>();
questManager.SetAvailableQuests(quests);
systems.AddComponent<Inventory>();
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>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
CanvasScaler scaler = canvasObject.GetComponent<CanvasScaler>();
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>();
eventSystem.sendNavigationEvents = true;
StandaloneInputModule inputModule = eventSystemObject.GetComponent<StandaloneInputModule>();
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<QuestTrackerUI>();
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>();
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<CanvasGroup>();
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<NotificationUI>();
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<Image>().color = new Color(0.035f, 0.04f, 0.045f, 0.9f);
Outline outline = root.AddComponent<Outline>();
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<InteractionPromptUI>();
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<RectTransform>();
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<Image>().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<Image>().color = new Color(0f, 0f, 0f, 0.08f);
viewport.GetComponent<Mask>().showMaskGraphic = false;
GameObject contentObject = new GameObject("Content", typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter));
contentObject.transform.SetParent(viewport.transform, false);
RectTransform content = contentObject.GetComponent<RectTransform>();
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<VerticalLayoutGroup>();
layout.padding = new RectOffset(18, 18, 18, 18);
layout.spacing = 3f;
layout.childForceExpandWidth = true;
layout.childForceExpandHeight = false;
ContentSizeFitter fitter = contentObject.GetComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
ScrollRect scroll = scrollObject.GetComponent<ScrollRect>();
scroll.viewport = viewportRect;
scroll.content = content;
scroll.horizontal = false;
QuestLogUI questLog = parent.gameObject.AddComponent<QuestLogUI>();
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<Image>().color = new Color(0.04f, 0.04f, 0.04f, 0.93f);
Outline rootOutline = root.AddComponent<Outline>();
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>();
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<RectTransform>();
rect.anchorMin = anchorMin;
rect.anchorMax = anchorMax;
rect.pivot = pivot;
rect.anchoredPosition = anchoredPosition;
rect.sizeDelta = size;
Image image = panel.GetComponent<Image>();
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<RectTransform>();
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.offsetMin = offsetMin;
rect.offsetMax = offsetMax;
Image image = panel.GetComponent<Image>();
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<RectTransform>();
rect.anchorMin = anchor;
rect.anchorMax = anchor;
rect.pivot = pivot;
rect.anchoredPosition = anchoredPosition;
rect.sizeDelta = size;
Image image = panel.GetComponent<Image>();
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<RectTransform>();
rect.anchorMin = pivot;
rect.anchorMax = pivot;
rect.pivot = pivot;
rect.anchoredPosition = anchoredPosition;
rect.sizeDelta = rectSize;
Text text = textObject.GetComponent<Text>();
text.font = font != null ? font : Resources.GetBuiltinResource<Font>("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<RectTransform>();
rect.anchorMin = pivot;
rect.anchorMax = pivot;
rect.pivot = pivot;
rect.anchoredPosition = anchoredPosition;
rect.sizeDelta = size;
VerticalLayoutGroup layout = contentObject.GetComponent<VerticalLayoutGroup>();
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<RectTransform>();
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.offsetMin = Vector2.zero;
rect.offsetMax = Vector2.zero;
VerticalLayoutGroup layout = contentObject.GetComponent<VerticalLayoutGroup>();
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<RectTransform>();
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>();
layoutElement.minHeight = 40f;
layoutElement.preferredHeight = 40f;
layoutElement.flexibleWidth = 1f;
Image image = buttonObject.GetComponent<Image>();
image.color = new Color(0.12f, 0.1f, 0.075f, 0.98f);
Outline outline = buttonObject.GetComponent<Outline>();
outline.effectColor = new Color(1f, 0.73f, 0.24f, 0.45f);
outline.effectDistance = new Vector2(1f, -1f);
Button button = buttonObject.GetComponent<Button>();
ColorBlock colors = button.colors;
colors.normalColor = image.color;
colors.highlightedColor = new Color(0.24f, 0.18f, 0.09f, 1f);
colors.pressedColor = new Color(0.42f, 0.29f, 0.1f, 1f);
colors.selectedColor = colors.highlightedColor;
colors.fadeDuration = 0.08f;
button.colors = colors;
GameObject accent = new GameObject("Accent", typeof(RectTransform), typeof(Image));
accent.transform.SetParent(buttonObject.transform, false);
RectTransform accentRect = accent.GetComponent<RectTransform>();
accentRect.anchorMin = new Vector2(0f, 0f);
accentRect.anchorMax = new Vector2(0f, 1f);
accentRect.pivot = new Vector2(0f, 0.5f);
accentRect.anchoredPosition = Vector2.zero;
accentRect.sizeDelta = new Vector2(4f, 0f);
accent.GetComponent<Image>().color = Gold;
Text label = CreateStretchText("Response", buttonObject.transform, font, 17, LightText, FontStyle.Normal, new Vector2(18f, 0f), new Vector2(-14f, 0f));
label.alignment = TextAnchor.MiddleLeft;
return button;
}
private static Text CreateStretchText(string value, Transform parent, Font font, int size, Color color, FontStyle style, Vector2 offsetMin, Vector2 offsetMax)
{
GameObject textObject = new GameObject("Text", typeof(RectTransform), typeof(Text));
textObject.transform.SetParent(parent, false);
RectTransform rect = textObject.GetComponent<RectTransform>();
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.offsetMin = offsetMin;
rect.offsetMax = offsetMax;
Text text = textObject.GetComponent<Text>();
text.font = font != null ? font : Resources.GetBuiltinResource<Font>("Arial.ttf");
text.text = value;
text.fontSize = size;
text.fontStyle = style;
text.color = color;
text.horizontalOverflow = HorizontalWrapMode.Wrap;
text.verticalOverflow = VerticalWrapMode.Truncate;
return text;
}
private static RectTransform Stretch(GameObject gameObject)
{
RectTransform rect = gameObject.GetComponent<RectTransform>();
rect.anchorMin = Vector2.zero;
rect.anchorMax = Vector2.one;
rect.offsetMin = Vector2.zero;
rect.offsetMax = Vector2.zero;
return rect;
}
private static GameObject SavePrefab(GameObject source, string path)
{
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(source, path);
AssetDatabase.ImportAsset(path);
return prefab != null ? prefab : AssetDatabase.LoadAssetAtPath<GameObject>(path);
}
private static GameObject InstantiatePrefab(GameObject prefab, Vector3 position, Quaternion rotation, string name)
{
GameObject instance = prefab != null
? (GameObject)PrefabUtility.InstantiatePrefab(prefab)
: new GameObject(name);
instance.name = name;
instance.transform.position = position;
instance.transform.rotation = rotation;
return instance;
}
private static void AssignMaterial(GameObject gameObject, Material material)
{
Renderer renderer = gameObject.GetComponent<Renderer>();
if (renderer != null)
{
renderer.sharedMaterial = material;
}
}
private static void AddSceneToBuildSettings(string scenePath)
{
List<EditorBuildSettingsScene> scenes = new List<EditorBuildSettingsScene>(EditorBuildSettings.scenes);
if (!scenes.Exists(scene => scene.path == scenePath))
{
scenes.Add(new EditorBuildSettingsScene(scenePath, true));
EditorBuildSettings.scenes = scenes.ToArray();
}
}
}