first commit

This commit is contained in:
2026-06-04 19:34:38 +03:00
commit 6318b20158
110 changed files with 9536 additions and 0 deletions
+776
View File
@@ -0,0 +1,776 @@
using System.IO;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public static class Lab4SceneBuilder
{
private const string ScriptsPath = "Assets/_Scripts";
private const string DataPath = "Assets/_Data";
private const string DialoguePath = "Assets/_Data/Dialogue";
private const string ItemsPath = "Assets/_Data/Items";
private const string PrefabsPath = "Assets/_Prefabs";
private const string ScenesPath = "Assets/_Scenes";
private const string MaterialsPath = "Assets/_Materials";
private const string FontsPath = "Assets/_Fonts";
private const string ScenePath = "Assets/_Scenes/Lab04_DialogueSystem.unity";
private const string ResponseButtonPrefabPath = "Assets/_Prefabs/DialogueResponseButton.prefab";
[MenuItem("Tools/Technical Game Design/Lab4/Create Dialogue System Scene")]
public static void CreateDialogueSystemScene()
{
CreateFolders();
Font regularFont = InstallUbuntuFont();
MaterialLibrary materials = CreateMaterials();
ItemLibrary items = CreateItems();
DialogueLibrary dialogue = CreateDialogue(items);
CreateGraphReadme();
GameObject responseButtonPrefab = CreateResponseButtonPrefab(regularFont);
Scene scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
scene.name = "Lab04_DialogueSystem";
BuildScene(materials, items, dialogue, responseButtonPrefab, regularFont);
EditorSceneManager.SaveScene(scene, ScenePath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("Lab04 dialogue system scene generated: " + ScenePath);
}
private static void CreateFolders()
{
string[] paths =
{
ScriptsPath, DataPath, DialoguePath, ItemsPath, PrefabsPath,
ScenesPath, MaterialsPath, FontsPath, "Assets/Editor"
};
foreach (string path in paths)
Directory.CreateDirectory(path);
AssetDatabase.Refresh();
}
private static Font InstallUbuntuFont()
{
string regularDestination = FontsPath + "/Ubuntu-Regular.ttf";
string boldDestination = FontsPath + "/Ubuntu-Bold.ttf";
string regularSource = FindFirstExistingFile(
"/usr/share/fonts/truetype/ubuntu/Ubuntu-Regular.ttf",
"/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf",
"/usr/share/fonts/TTF/Ubuntu-Regular.ttf",
"/usr/share/fonts/TTF/Ubuntu-R.ttf",
"/usr/share/fonts/Ubuntu-Regular.ttf",
"/usr/share/fonts/Ubuntu-R.ttf");
string boldSource = FindFirstExistingFile(
"/usr/share/fonts/truetype/ubuntu/Ubuntu-Bold.ttf",
"/usr/share/fonts/truetype/ubuntu/Ubuntu-B.ttf",
"/usr/share/fonts/TTF/Ubuntu-Bold.ttf",
"/usr/share/fonts/TTF/Ubuntu-B.ttf",
"/usr/share/fonts/Ubuntu-Bold.ttf",
"/usr/share/fonts/Ubuntu-B.ttf");
if (!string.IsNullOrEmpty(regularSource))
File.Copy(regularSource, regularDestination, true);
else
Debug.LogWarning("Ubuntu font was not found in common Linux font folders. Falling back to built-in Arial.");
if (!string.IsNullOrEmpty(boldSource))
File.Copy(boldSource, boldDestination, true);
if (File.Exists(regularDestination))
AssetDatabase.ImportAsset(regularDestination);
if (File.Exists(boldDestination))
AssetDatabase.ImportAsset(boldDestination);
AssetDatabase.Refresh();
Font font = AssetDatabase.LoadAssetAtPath<Font>(regularDestination);
return font != null ? font : Resources.GetBuiltinResource<Font>("Arial.ttf");
}
private static string FindFirstExistingFile(params string[] candidates)
{
foreach (string candidate in candidates)
{
if (File.Exists(candidate))
return candidate;
}
return null;
}
private static MaterialLibrary CreateMaterials()
{
MaterialLibrary library = new MaterialLibrary
{
ground = CreateMaterial("Ground_Mat", new Color(0.34f, 0.43f, 0.34f)),
player = CreateMaterial("Player_Mat", new Color(0.17f, 0.43f, 0.74f)),
npc = CreateMaterial("NPC_Mat", new Color(0.68f, 0.44f, 0.17f)),
pass = CreateMaterial("VillagePass_Mat", new Color(0.95f, 0.78f, 0.25f)),
prop = CreateMaterial("Props_Mat", new Color(0.42f, 0.34f, 0.25f)),
accent = CreateMaterial("Accent_Mat", new Color(0.14f, 0.62f, 0.75f)),
interactionZone = CreateMaterial("InteractionZone_Mat", new Color(0.25f, 0.75f, 1f, 0.22f), true)
};
return library;
}
private static Material CreateMaterial(string name, Color color, bool transparent = false)
{
string path = MaterialsPath + "/" + name + ".mat";
Material material = AssetDatabase.LoadAssetAtPath<Material>(path);
if (material == null)
{
material = new Material(Shader.Find("Standard"));
AssetDatabase.CreateAsset(material, path);
}
material.name = name;
material.color = color;
if (transparent)
{
material.SetFloat("_Mode", 3f);
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
material.SetInt("_ZWrite", 0);
material.DisableKeyword("_ALPHATEST_ON");
material.EnableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
material.renderQueue = 3000;
}
else
{
material.SetFloat("_Mode", 0f);
material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
material.SetInt("_ZWrite", 1);
material.DisableKeyword("_ALPHATEST_ON");
material.DisableKeyword("_ALPHABLEND_ON");
material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
material.renderQueue = -1;
}
EditorUtility.SetDirty(material);
return material;
}
private static ItemLibrary CreateItems()
{
ItemLibrary library = new ItemLibrary
{
villagePass = LoadOrCreateAsset<ItemData>(ItemsPath + "/VillagePass.asset"),
oldAmulet = LoadOrCreateAsset<ItemData>(ItemsPath + "/OldAmulet.asset")
};
library.villagePass.itemId = "VillagePass";
library.villagePass.itemName = "Пропуск";
library.villagePass.description = "Временный документ для прохода к старосте.";
library.villagePass.itemColor = new Color(0.95f, 0.78f, 0.25f);
library.villagePass.maxStack = 1;
library.oldAmulet.itemId = "OldAmulet";
library.oldAmulet.itemName = "Старый амулет";
library.oldAmulet.description = "Потёртый амулет, который ценит смотритель ворот.";
library.oldAmulet.itemColor = new Color(0.35f, 0.82f, 0.8f);
library.oldAmulet.maxStack = 1;
EditorUtility.SetDirty(library.villagePass);
EditorUtility.SetDirty(library.oldAmulet);
AssetDatabase.SaveAssets();
return library;
}
private static DialogueLibrary CreateDialogue(ItemLibrary items)
{
DialogueLibrary library = new DialogueLibrary
{
start = LoadOrCreateAsset<DialogueNode>(DialoguePath + "/StartNode.asset"),
newcomer = LoadOrCreateAsset<DialogueNode>(DialoguePath + "/Node_Newcomer.asset"),
passInfo = LoadOrCreateAsset<DialogueNode>(DialoguePath + "/Node_PassInfo.asset"),
noPassWarning = LoadOrCreateAsset<DialogueNode>(DialoguePath + "/Node_NoPassWarning.asset"),
hasPass = LoadOrCreateAsset<DialogueNode>(DialoguePath + "/Node_HasPass.asset"),
work = LoadOrCreateAsset<DialogueNode>(DialoguePath + "/Node_Work.asset"),
reward = LoadOrCreateAsset<DialogueNode>(DialoguePath + "/Node_Reward.asset"),
amuletComplete = LoadOrCreateAsset<DialogueNode>(DialoguePath + "/Node_AmuletQuestComplete.asset"),
goodbye = LoadOrCreateAsset<DialogueNode>(DialoguePath + "/Node_Goodbye.asset")
};
SetupNode(library.start, "StartNode", "Смотритель ворот", "Приветствую, путник. Ты впервые в нашем поселении?", false,
Response("Да, я только пришёл.", library.newcomer),
Response("Я ищу работу.", library.work),
Response("Мне пора.", library.goodbye));
SetupNode(library.newcomer, "Node_Newcomer", "Смотритель ворот", "Тогда тебе понадобится пропуск. Без него стража не пустит тебя к старосте.", false,
Response("Где его получить?", library.passInfo),
Response("У меня уже есть пропуск.", library.hasPass, requiredItem: items.villagePass, unavailableText: "Нужен предмет: Пропуск"),
Response("Понятно.", library.goodbye));
SetupNode(library.passInfo, "Node_PassInfo", "Смотритель ворот", "Осмотри площадку рядом с воротами. Иногда там оставляют временные пропуска.", false,
Response("Спасибо, я поищу.", library.goodbye),
Response("Я лучше сразу пойду к старосте.", library.noPassWarning));
SetupNode(library.noPassWarning, "Node_NoPassWarning", "Смотритель ворот", "Без пропуска тебя не примут. Сначала найди документ.", false,
Response("Хорошо, найду пропуск.", library.goodbye));
SetupNode(library.hasPass, "Node_HasPass", "Смотритель ворот", "Отлично. Раз у тебя есть пропуск, можешь пройти дальше. Передай старосте, что я тебя направил.", false,
Response("Спасибо за помощь.", library.reward, rewardItem: items.oldAmulet, rewardAmount: 1, eventId: "Gatekeeper_Rewards_Amulet"),
Response("До встречи.", library.goodbye));
SetupNode(library.work, "Node_Work", "Смотритель ворот", "Работа есть. Принеси старый амулет, и я расскажу больше.", false,
Response("У меня есть амулет.", library.amuletComplete, requiredItem: items.oldAmulet, consumeRequiredItem: true, unavailableText: "Нужен предмет: Старый амулет", eventId: "Gatekeeper_Amulet_TurnedIn"),
Response("Я вернусь позже.", library.goodbye));
SetupNode(library.reward, "Node_Reward", "Смотритель ворот", "Возьми этот старый амулет. Он может пригодиться в разговоре о работе.", false,
Response("Благодарю.", library.goodbye));
SetupNode(library.amuletComplete, "Node_AmuletQuestComplete", "Смотритель ворот", "Ты справился. Теперь я доверяю тебе. Для следующего задания приходи позже.", false,
Response("Понял.", library.goodbye));
SetupNode(library.goodbye, "Node_Goodbye", "Смотритель ворот", "Удачи тебе.", true);
AssetDatabase.SaveAssets();
return library;
}
private static DialogueResponse Response(
string text,
DialogueNode nextNode,
bool endsDialogue = false,
ItemData requiredItem = null,
int requiredItemAmount = 1,
string unavailableText = "",
bool hideIfRequirementsNotMet = false,
bool consumeRequiredItem = false,
ItemData rewardItem = null,
int rewardAmount = 0,
string eventId = "")
{
return new DialogueResponse
{
responseText = text,
nextNode = nextNode,
endsDialogue = endsDialogue,
requiredItem = requiredItem,
requiredItemAmount = Mathf.Max(1, requiredItemAmount),
unavailableText = unavailableText,
hideIfRequirementsNotMet = hideIfRequirementsNotMet,
consumeRequiredItem = consumeRequiredItem,
rewardItem = rewardItem,
rewardAmount = rewardAmount,
eventId = eventId
};
}
private static void SetupNode(DialogueNode node, string id, string speaker, string line, bool isEnd, params DialogueResponse[] responses)
{
node.nodeId = id;
node.speakerName = speaker;
node.npcLine = line;
node.isEndNode = isEnd;
node.responses = responses;
node.editorNote = "Generated by Lab4SceneBuilder.";
EditorUtility.SetDirty(node);
}
private static T LoadOrCreateAsset<T>(string path) where T : ScriptableObject
{
T asset = AssetDatabase.LoadAssetAtPath<T>(path);
if (asset != null)
return asset;
asset = ScriptableObject.CreateInstance<T>();
AssetDatabase.CreateAsset(asset, path);
return asset;
}
private static void CreateGraphReadme()
{
const string text =
"StartNode\n" +
"├─ Да, я только пришёл → Node_Newcomer\n" +
"│ ├─ Где его получить? → Node_PassInfo → Node_Goodbye\n" +
"│ ├─ У меня уже есть пропуск [requires VillagePass] → Node_HasPass → Node_Reward [gives OldAmulet] → Node_Goodbye\n" +
"│ └─ Понятно → Node_Goodbye\n" +
"├─ Я ищу работу → Node_Work\n" +
"│ ├─ У меня есть амулет [requires OldAmulet, consumes] → Node_AmuletQuestComplete → Node_Goodbye\n" +
"│ └─ Я вернусь позже → Node_Goodbye\n" +
"└─ Мне пора → Node_Goodbye\n";
File.WriteAllText(DialoguePath + "/DialogueGraph_Readme.txt", text);
}
private static GameObject CreateResponseButtonPrefab(Font font)
{
GameObject root = new GameObject("DialogueResponseButton", typeof(RectTransform), typeof(Image), typeof(Button), typeof(LayoutElement), typeof(Outline));
RectTransform rect = root.GetComponent<RectTransform>();
rect.sizeDelta = new Vector2(0f, 38f);
Image image = root.GetComponent<Image>();
image.color = new Color(0.095f, 0.12f, 0.15f, 0.98f);
Outline outline = root.GetComponent<Outline>();
outline.effectColor = new Color(0.36f, 0.48f, 0.54f, 0.28f);
outline.effectDistance = new Vector2(1f, -1f);
Button button = root.GetComponent<Button>();
ColorBlock colors = button.colors;
colors.normalColor = new Color(0.095f, 0.12f, 0.15f, 0.98f);
colors.highlightedColor = new Color(0.16f, 0.26f, 0.31f, 1f);
colors.pressedColor = new Color(0.08f, 0.42f, 0.52f, 1f);
colors.disabledColor = new Color(0.12f, 0.13f, 0.15f, 0.88f);
button.colors = colors;
LayoutElement layout = root.GetComponent<LayoutElement>();
layout.minHeight = 36f;
layout.preferredHeight = 38f;
Text label = CreateText("Text", root.transform, font, "Ответ", 17, FontStyle.Normal, Color.white, TextAnchor.MiddleLeft);
label.resizeTextForBestFit = true;
label.resizeTextMinSize = 12;
label.resizeTextMaxSize = 17;
label.horizontalOverflow = HorizontalWrapMode.Wrap;
label.verticalOverflow = VerticalWrapMode.Truncate;
RectTransform labelRect = label.GetComponent<RectTransform>();
labelRect.anchorMin = Vector2.zero;
labelRect.anchorMax = Vector2.one;
labelRect.offsetMin = new Vector2(18f, 4f);
labelRect.offsetMax = new Vector2(-18f, -4f);
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(root, ResponseButtonPrefabPath);
Object.DestroyImmediate(root);
return prefab;
}
private static void BuildScene(MaterialLibrary materials, ItemLibrary items, DialogueLibrary dialogue, GameObject responseButtonPrefab, Font font)
{
CreateLighting();
CreateGroundAndProps(materials);
SimplePlayerController playerController = CreatePlayer(materials.player);
Inventory inventory = playerController.GetComponent<Inventory>();
Canvas canvas = CreateCanvas();
DialogueManager dialogueManager = CreateDialogueUi(canvas.transform, font, responseButtonPrefab, inventory, playerController);
DialogueHUD hud = CreateHud(canvas.transform, font, inventory, dialogueManager);
dialogueManager.hintText = hud.hintText;
dialogueManager.statusText = hud.statusText;
CreateNpc(dialogue.start, dialogueManager, materials, font);
CreatePickup(items.villagePass, inventory, materials.pass);
CreateEventSystem();
ApplyFontToAllText(canvas.transform, font);
}
private static void CreateLighting()
{
GameObject lightObject = new GameObject("Directional Light");
Light light = lightObject.AddComponent<Light>();
light.type = LightType.Directional;
light.intensity = 1.15f;
light.shadows = LightShadows.Soft;
lightObject.transform.rotation = Quaternion.Euler(48f, -35f, 0f);
RenderSettings.ambientMode = UnityEngine.Rendering.AmbientMode.Flat;
RenderSettings.ambientLight = new Color(0.42f, 0.45f, 0.48f);
}
private static void CreateGroundAndProps(MaterialLibrary materials)
{
GameObject ground = GameObject.CreatePrimitive(PrimitiveType.Plane);
ground.name = "Training Ground";
ground.transform.localScale = new Vector3(4f, 1f, 4f);
ground.GetComponent<Renderer>().sharedMaterial = materials.ground;
CreateCube("Gate Left Post", new Vector3(-2.2f, 1.25f, 5.5f), new Vector3(0.35f, 2.5f, 0.35f), materials.prop);
CreateCube("Gate Right Post", new Vector3(2.2f, 1.25f, 5.5f), new Vector3(0.35f, 2.5f, 0.35f), materials.prop);
CreateCube("Gate Header", new Vector3(0f, 2.55f, 5.5f), new Vector3(4.8f, 0.25f, 0.35f), materials.prop);
CreateCube("Crate A", new Vector3(-4.1f, 0.35f, 1.4f), new Vector3(0.7f, 0.7f, 0.7f), materials.prop);
CreateCube("Crate B", new Vector3(-3.45f, 0.25f, 1.95f), new Vector3(0.5f, 0.5f, 0.5f), materials.prop);
CreateCylinder("Marker Pillar", new Vector3(3.6f, 0.65f, 0.8f), new Vector3(0.55f, 0.65f, 0.55f), materials.accent);
CreateCylinder("Small Barrel", new Vector3(4.2f, 0.5f, 2.1f), new Vector3(0.55f, 0.5f, 0.55f), materials.prop);
}
private static void 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;
}
private static void CreateCylinder(string name, Vector3 position, Vector3 scale, Material material)
{
GameObject cylinder = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
cylinder.name = name;
cylinder.transform.position = position;
cylinder.transform.localScale = scale;
cylinder.GetComponent<Renderer>().sharedMaterial = material;
}
private static SimplePlayerController CreatePlayer(Material playerMaterial)
{
GameObject player = new GameObject("Player", typeof(CharacterController), typeof(Inventory), typeof(SimplePlayerController));
player.transform.position = new Vector3(0f, 0.05f, -5.8f);
CharacterController characterController = player.GetComponent<CharacterController>();
characterController.height = 1.85f;
characterController.radius = 0.42f;
characterController.center = new Vector3(0f, 0.93f, 0f);
GameObject body = GameObject.CreatePrimitive(PrimitiveType.Capsule);
body.name = "Player Capsule Hidden Visual";
body.transform.SetParent(player.transform, false);
body.transform.localPosition = new Vector3(0f, 0.93f, 0f);
body.transform.localScale = new Vector3(0.82f, 0.9f, 0.82f);
body.GetComponent<Renderer>().sharedMaterial = playerMaterial;
body.GetComponent<Renderer>().enabled = false;
Object.DestroyImmediate(body.GetComponent<Collider>());
GameObject cameraObject = new GameObject("Player Camera", typeof(Camera), typeof(AudioListener));
cameraObject.transform.SetParent(player.transform, false);
cameraObject.transform.localPosition = new Vector3(0f, 1.68f, 0.04f);
cameraObject.transform.localRotation = Quaternion.identity;
Camera camera = cameraObject.GetComponent<Camera>();
camera.fieldOfView = 68f;
camera.nearClipPlane = 0.03f;
SimplePlayerController controller = player.GetComponent<SimplePlayerController>();
controller.playerCamera = camera;
controller.moveSpeed = 4.5f;
controller.mouseSensitivity = 2.0f;
return controller;
}
private static Canvas CreateCanvas()
{
GameObject canvasObject = new GameObject("Dialogue 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 = 0f;
return canvas;
}
private static DialogueManager CreateDialogueUi(Transform canvas, Font font, GameObject responseButtonPrefab, Inventory inventory, SimplePlayerController playerController)
{
GameObject managerObject = new GameObject("DialogueManager");
DialogueManager manager = managerObject.AddComponent<DialogueManager>();
manager.playerInventory = inventory;
manager.playerController = playerController;
manager.responseButtonPrefab = responseButtonPrefab;
GameObject panel = CreateUiPanel("Dialogue Panel", canvas, new Color(0.035f, 0.047f, 0.058f, 0.97f));
RectTransform panelRect = panel.GetComponent<RectTransform>();
panelRect.anchorMin = new Vector2(0.5f, 0f);
panelRect.anchorMax = new Vector2(0.5f, 0f);
panelRect.pivot = new Vector2(0.5f, 0f);
panelRect.anchoredPosition = new Vector2(0f, 24f);
panelRect.sizeDelta = new Vector2(1560f, 332f);
panel.AddComponent<RectMask2D>();
Outline panelOutline = panel.AddComponent<Outline>();
panelOutline.effectColor = new Color(0f, 0f, 0f, 0.55f);
panelOutline.effectDistance = new Vector2(2f, -2f);
VerticalLayoutGroup layout = panel.AddComponent<VerticalLayoutGroup>();
layout.padding = new RectOffset(28, 28, 18, 18);
layout.spacing = 6f;
layout.childControlWidth = true;
layout.childControlHeight = true;
layout.childForceExpandWidth = true;
layout.childForceExpandHeight = false;
Text speaker = CreateText("Speaker Text", panel.transform, font, "Смотритель ворот", 22, FontStyle.Bold, new Color(0.34f, 0.84f, 0.93f), TextAnchor.MiddleLeft);
AddLayout(speaker.gameObject, 26f);
GameObject accentLine = new GameObject("Speaker Accent Line", typeof(RectTransform), typeof(Image));
accentLine.transform.SetParent(panel.transform, false);
accentLine.GetComponent<Image>().color = new Color(0.22f, 0.72f, 0.86f, 0.9f);
AddLayout(accentLine, 2f);
Text npcLine = CreateText("NPC Line", panel.transform, font, string.Empty, 19, FontStyle.Normal, new Color(0.94f, 0.96f, 0.98f), TextAnchor.UpperLeft);
npcLine.horizontalOverflow = HorizontalWrapMode.Wrap;
npcLine.verticalOverflow = VerticalWrapMode.Truncate;
npcLine.resizeTextForBestFit = true;
npcLine.resizeTextMinSize = 14;
npcLine.resizeTextMaxSize = 19;
AddLayout(npcLine.gameObject, 54f);
GameObject responseViewport = new GameObject("Response Viewport", typeof(RectTransform), typeof(Image), typeof(RectMask2D), typeof(ScrollRect), typeof(LayoutElement));
responseViewport.transform.SetParent(panel.transform, false);
Image viewportImage = responseViewport.GetComponent<Image>();
viewportImage.color = new Color(0f, 0f, 0f, 0f);
LayoutElement viewportLayout = responseViewport.GetComponent<LayoutElement>();
viewportLayout.minHeight = 132f;
viewportLayout.preferredHeight = 156f;
viewportLayout.flexibleHeight = 1f;
GameObject responseContainer = new GameObject("Response Content", typeof(RectTransform), typeof(VerticalLayoutGroup), typeof(ContentSizeFitter));
responseContainer.transform.SetParent(responseViewport.transform, false);
RectTransform responseContentRect = responseContainer.GetComponent<RectTransform>();
responseContentRect.anchorMin = new Vector2(0f, 1f);
responseContentRect.anchorMax = new Vector2(1f, 1f);
responseContentRect.pivot = new Vector2(0.5f, 1f);
responseContentRect.anchoredPosition = Vector2.zero;
responseContentRect.sizeDelta = Vector2.zero;
VerticalLayoutGroup responseLayout = responseContainer.GetComponent<VerticalLayoutGroup>();
responseLayout.padding = new RectOffset(0, 0, 0, 0);
responseLayout.spacing = 6f;
responseLayout.childControlWidth = true;
responseLayout.childControlHeight = true;
responseLayout.childForceExpandWidth = true;
responseLayout.childForceExpandHeight = false;
ContentSizeFitter fitter = responseContainer.GetComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
ScrollRect scrollRect = responseViewport.GetComponent<ScrollRect>();
scrollRect.content = responseContentRect;
scrollRect.viewport = responseViewport.GetComponent<RectTransform>();
scrollRect.horizontal = false;
scrollRect.vertical = true;
scrollRect.movementType = ScrollRect.MovementType.Clamped;
scrollRect.scrollSensitivity = 24f;
manager.dialoguePanel = panel;
manager.speakerText = speaker;
manager.npcText = npcLine;
manager.responseContainer = responseContainer.transform;
manager.responseScrollRect = scrollRect;
panel.SetActive(false);
return manager;
}
private static DialogueHUD CreateHud(Transform canvas, Font font, Inventory inventory, DialogueManager manager)
{
GameObject hudObject = new GameObject("DialogueHUD");
DialogueHUD hud = hudObject.AddComponent<DialogueHUD>();
hud.inventory = inventory;
Text hint = CreateTextPanel("Hint Text", canvas, font, string.Empty, 20, TextAnchor.MiddleCenter, new Color(0.035f, 0.047f, 0.058f, 0.92f));
RectTransform hintRect = hint.transform.parent.GetComponent<RectTransform>();
hintRect.anchorMin = new Vector2(0.36f, 0.91f);
hintRect.anchorMax = new Vector2(0.64f, 0.975f);
hintRect.offsetMin = Vector2.zero;
hintRect.offsetMax = Vector2.zero;
hint.transform.parent.gameObject.SetActive(false);
Text inventoryText = CreateTextPanel("Inventory Text", canvas, font, "Инвентарь: пусто", 18, TextAnchor.MiddleLeft, new Color(0.035f, 0.047f, 0.058f, 0.9f));
RectTransform inventoryRect = inventoryText.transform.parent.GetComponent<RectTransform>();
inventoryRect.anchorMin = new Vector2(0.68f, 0.91f);
inventoryRect.anchorMax = new Vector2(0.98f, 0.975f);
inventoryRect.offsetMin = Vector2.zero;
inventoryRect.offsetMax = Vector2.zero;
Text status = CreateTextPanel("Status Text", canvas, font, string.Empty, 18, TextAnchor.MiddleCenter, new Color(0.035f, 0.047f, 0.058f, 0.92f));
RectTransform statusRect = status.transform.parent.GetComponent<RectTransform>();
statusRect.anchorMin = new Vector2(0.34f, 0.455f);
statusRect.anchorMax = new Vector2(0.66f, 0.515f);
statusRect.offsetMin = Vector2.zero;
statusRect.offsetMax = Vector2.zero;
status.transform.parent.gameObject.SetActive(false);
hud.hintText = hint;
hud.inventoryText = inventoryText;
hud.statusText = status;
manager.hintText = hint;
manager.statusText = status;
return hud;
}
private static Text CreateTextPanel(string name, Transform parent, Font font, string value, int size, TextAnchor anchor, Color background)
{
GameObject panel = CreateUiPanel(name, parent, background);
GameObject textObject = new GameObject("Text", typeof(RectTransform), typeof(Text), typeof(Outline));
textObject.transform.SetParent(panel.transform, false);
RectTransform textRect = textObject.GetComponent<RectTransform>();
textRect.anchorMin = Vector2.zero;
textRect.anchorMax = Vector2.one;
textRect.offsetMin = new Vector2(18f, 8f);
textRect.offsetMax = new Vector2(-18f, -8f);
Text text = textObject.GetComponent<Text>();
text.font = font;
text.text = value;
text.fontSize = size;
text.color = Color.white;
text.alignment = anchor;
text.horizontalOverflow = HorizontalWrapMode.Wrap;
text.verticalOverflow = VerticalWrapMode.Truncate;
text.raycastTarget = false;
Outline outline = textObject.GetComponent<Outline>();
outline.effectColor = new Color(0f, 0f, 0f, 0.35f);
outline.effectDistance = new Vector2(1f, -1f);
return text;
}
private static GameObject CreateUiPanel(string name, Transform parent, Color color)
{
GameObject panel = new GameObject(name, typeof(RectTransform), typeof(Image));
panel.transform.SetParent(parent, false);
Image image = panel.GetComponent<Image>();
image.color = color;
return panel;
}
private static Text CreateText(string name, Transform parent, Font font, string value, int size, FontStyle style, Color color, TextAnchor anchor)
{
GameObject textObject = new GameObject(name, typeof(RectTransform), typeof(Text));
textObject.transform.SetParent(parent, false);
Text text = textObject.GetComponent<Text>();
text.font = font;
text.text = value;
text.fontSize = size;
text.fontStyle = style;
text.color = color;
text.alignment = anchor;
text.raycastTarget = false;
return text;
}
private static void AddLayout(GameObject target, float preferredHeight)
{
LayoutElement element = target.GetComponent<LayoutElement>();
if (element == null)
element = target.AddComponent<LayoutElement>();
element.minHeight = preferredHeight;
element.preferredHeight = preferredHeight;
}
private static void CreateNpc(DialogueNode startNode, DialogueManager manager, MaterialLibrary materials, Font font)
{
GameObject npc = GameObject.CreatePrimitive(PrimitiveType.Capsule);
npc.name = "NPC_Gatekeeper";
npc.transform.position = new Vector3(0f, 1f, 2.7f);
npc.GetComponent<Renderer>().sharedMaterial = materials.npc;
Rigidbody rigidbody = npc.AddComponent<Rigidbody>();
rigidbody.isKinematic = true;
rigidbody.useGravity = false;
NPCInteract interact = npc.AddComponent<NPCInteract>();
interact.startNode = startNode;
interact.npcDisplayName = "Смотритель ворот";
interact.interactionRadius = 3f;
interact.dialogueManager = manager;
interact.highlightRenderer = npc.GetComponent<Renderer>();
GameObject zone = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
zone.name = "NPC Interaction Zone Visual";
zone.transform.position = new Vector3(0f, 0.02f, 2.7f);
zone.transform.localScale = new Vector3(3f, 0.02f, 3f);
zone.GetComponent<Renderer>().sharedMaterial = materials.interactionZone;
Object.DestroyImmediate(zone.GetComponent<Collider>());
GameObject nameplate = new GameObject("NPC Nameplate", typeof(TextMesh), typeof(BillboardNameplate));
nameplate.transform.SetParent(npc.transform, false);
nameplate.transform.localPosition = new Vector3(0f, 1.55f, 0f);
TextMesh textMesh = nameplate.GetComponent<TextMesh>();
textMesh.text = "Смотритель ворот";
textMesh.font = font;
textMesh.fontSize = 48;
textMesh.characterSize = 0.045f;
textMesh.anchor = TextAnchor.MiddleCenter;
textMesh.alignment = TextAlignment.Center;
textMesh.color = Color.white;
MeshRenderer renderer = nameplate.GetComponent<MeshRenderer>();
if (font != null)
renderer.sharedMaterial = font.material;
}
private static void CreatePickup(ItemData villagePass, Inventory inventory, Material material)
{
GameObject pickup = GameObject.CreatePrimitive(PrimitiveType.Cube);
pickup.name = "Pickup_VillagePass";
pickup.transform.position = new Vector3(-2.9f, 0.35f, -0.2f);
pickup.transform.rotation = Quaternion.Euler(0f, 25f, 0f);
pickup.transform.localScale = new Vector3(0.55f, 0.12f, 0.78f);
pickup.GetComponent<Renderer>().sharedMaterial = material;
Rigidbody rigidbody = pickup.AddComponent<Rigidbody>();
rigidbody.isKinematic = true;
rigidbody.useGravity = false;
PickupItem pickupItem = pickup.AddComponent<PickupItem>();
pickupItem.item = villagePass;
pickupItem.amount = 1;
pickupItem.targetInventory = inventory;
pickupItem.interactionRadius = 2.25f;
}
private static void CreateEventSystem()
{
if (Object.FindObjectOfType<EventSystem>() != null)
return;
GameObject eventSystem = new GameObject("EventSystem", typeof(EventSystem), typeof(StandaloneInputModule));
eventSystem.transform.position = Vector3.zero;
}
private static void ApplyFontToAllText(Transform root, Font font)
{
if (font == null)
return;
foreach (Text text in root.GetComponentsInChildren<Text>(true))
{
text.font = font;
EditorUtility.SetDirty(text);
}
}
private class MaterialLibrary
{
public Material ground;
public Material player;
public Material npc;
public Material pass;
public Material prop;
public Material accent;
public Material interactionZone;
}
private class ItemLibrary
{
public ItemData villagePass;
public ItemData oldAmulet;
}
private class DialogueLibrary
{
public DialogueNode start;
public DialogueNode newcomer;
public DialogueNode passInfo;
public DialogueNode noPassWarning;
public DialogueNode hasPass;
public DialogueNode work;
public DialogueNode reward;
public DialogueNode amuletComplete;
public DialogueNode goodbye;
}
}