first commit
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user