commit deb988bddf7d56cfc0c5eaa4754ee3f74a621cb9 Author: Dmitry Evdokimov Date: Thu Jun 4 18:31:31 2026 +0300 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f248c6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,64 @@ +# Unity generated folders +/Library/ +/Temp/ +/Obj/ +/Build/ +/Builds/ +/Logs/ +/UserSettings/ +/MemoryCaptures/ +/Recordings/ + +# Unity generated files +*.csproj +*.unityproj +*.sln +*.suo +*.tmp +*.user +*.userprefs +*.pidb +*.booproj +*.svd +*.pdb +*.mdb +*.opendb +*.VC.db + +# Unity crash and profiler files +sysinfo.txt +*.stackdump +*.mem + +# Unity asset/cache metadata that should stay local +/Assets/AssetStoreTools* + +# IDE/editor local state +.idea/ +.vs/ +.vscode/ +*.swp +*.swo +*~ + +# OS files +.DS_Store +.DS_Store? +._* +Thumbs.db +Desktop.ini + +# Local Python tooling used for report generation +.venv-docx/ +__pycache__/ +*.py[cod] + +# Local Codex/agent state +.agents/ +.codex/ + +# Generated lab reports and office documents +*.docx +*.doc +*.odt +*.pdf diff --git a/.xdp-.~3_Система_крафта_ОТГ.docx-EcaXuf b/.xdp-.~3_Система_крафта_ОТГ.docx-EcaXuf new file mode 100644 index 0000000..b0f443f Binary files /dev/null and b/.xdp-.~3_Система_крафта_ОТГ.docx-EcaXuf differ diff --git a/Assets/Editor.meta b/Assets/Editor.meta new file mode 100644 index 0000000..6c3ccce --- /dev/null +++ b/Assets/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: afe3c11973862c90b9a613b7b6144442 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Editor/Lab3SceneBuilder.cs b/Assets/Editor/Lab3SceneBuilder.cs new file mode 100644 index 0000000..84aa7f8 --- /dev/null +++ b/Assets/Editor/Lab3SceneBuilder.cs @@ -0,0 +1,820 @@ +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 Lab3SceneBuilder +{ + private const string ScriptsPath = "Assets/_Scripts"; + private const string DataPath = "Assets/_Data"; + private const string ItemsPath = "Assets/_Data/Items"; + private const string RecipesPath = "Assets/_Data/Recipes"; + 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 EditorPath = "Assets/Editor"; + private const string ScenePath = "Assets/_Scenes/Lab03_CraftingSystem.unity"; + private const string RegularFontPath = "Assets/_Fonts/Ubuntu-Regular.ttf"; + private const string BoldFontPath = "Assets/_Fonts/Ubuntu-Bold.ttf"; + + [MenuItem("Tools/Technical Game Design/Lab3/Create Crafting System Scene")] + public static void CreateCraftingSystemScene() + { + CreateFolders(); + Font uiFont = InstallUbuntuFont(); + Dictionary materials = CreateMaterials(); + Dictionary items = CreateItems(); + List recipes = CreateRecipes(items); + + GameObject recipeButtonPrefab = CreateRecipeButtonPrefab(uiFont); + GameObject slotPrefab = CreateInventorySlotPrefab(uiFont); + GameObject hotbarSlotPrefab = CreateHotbarSlotPrefab(uiFont); + + Scene scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single); + scene.name = "Lab03_CraftingSystem"; + + CreateWorld(materials, items, uiFont); + CreateCameraAndLight(); + CreateSystems(items, recipes, materials, uiFont, recipeButtonPrefab, slotPrefab, hotbarSlotPrefab); + + EditorSceneManager.SaveScene(scene, ScenePath); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + + Debug.Log("Lab03 Crafting System scene created: " + ScenePath); + } + + private static void CreateFolders() + { + string[] folders = + { + ScriptsPath, + DataPath, + ItemsPath, + RecipesPath, + PrefabsPath, + ScenesPath, + MaterialsPath, + FontsPath, + EditorPath + }; + + for (int i = 0; i < folders.Length; i++) + { + if (!AssetDatabase.IsValidFolder(folders[i])) + { + Directory.CreateDirectory(folders[i]); + } + } + + AssetDatabase.Refresh(); + } + + private static Font InstallUbuntuFont() + { + string regularSource = FindFontFile(new[] + { + "/usr/share/fonts/truetype/ubuntu/Ubuntu-R.ttf", + "/usr/share/fonts/truetype/ubuntu/Ubuntu-Regular.ttf", + "/usr/share/fonts/TTF/Ubuntu-R.ttf", + "/usr/share/fonts/TTF/Ubuntu-Regular.ttf" + }, "Ubuntu-R*.ttf", "Ubuntu-Regular*.ttf"); + + string boldSource = FindFontFile(new[] + { + "/usr/share/fonts/truetype/ubuntu/Ubuntu-B.ttf", + "/usr/share/fonts/truetype/ubuntu/Ubuntu-Bold.ttf", + "/usr/share/fonts/TTF/Ubuntu-B.ttf", + "/usr/share/fonts/TTF/Ubuntu-Bold.ttf" + }, "Ubuntu-B*.ttf", "Ubuntu-Bold*.ttf"); + + if (!string.IsNullOrEmpty(regularSource)) + { + File.Copy(regularSource, RegularFontPath, true); + AssetDatabase.ImportAsset(RegularFontPath); + Debug.Log("Ubuntu regular font copied to " + RegularFontPath); + } + else + { + Debug.LogWarning("Ubuntu regular font was not found in system font folders. UI will use built-in Arial fallback."); + } + + if (!string.IsNullOrEmpty(boldSource)) + { + File.Copy(boldSource, BoldFontPath, true); + AssetDatabase.ImportAsset(BoldFontPath); + Debug.Log("Ubuntu bold font copied to " + BoldFontPath); + } + + AssetDatabase.Refresh(); + + Font font = AssetDatabase.LoadAssetAtPath(RegularFontPath); + return font != null ? font : Resources.GetBuiltinResource("LegacyRuntime.ttf"); + } + + private static string FindFontFile(string[] preferredPaths, params string[] patterns) + { + for (int i = 0; i < preferredPaths.Length; i++) + { + if (File.Exists(preferredPaths[i])) + { + return preferredPaths[i]; + } + } + + string[] roots = + { + "/usr/share/fonts/truetype/ubuntu", + "/usr/share/fonts/TTF", + "/usr/share/fonts" + }; + + for (int i = 0; i < roots.Length; i++) + { + if (!Directory.Exists(roots[i])) + { + continue; + } + + for (int p = 0; p < patterns.Length; p++) + { + string[] files = Directory.GetFiles(roots[i], patterns[p], SearchOption.AllDirectories); + if (files.Length > 0) + { + return files[0]; + } + } + } + + return null; + } + + private static Dictionary CreateMaterials() + { + Dictionary materialColors = new Dictionary + { + { "Mat_Floor", new Color(0.36f, 0.42f, 0.36f) }, + { "Mat_Workbench", new Color(0.42f, 0.25f, 0.12f) }, + { "Mat_Workbench_Dark", new Color(0.22f, 0.13f, 0.07f) }, + { "Mat_Wood", new Color(0.55f, 0.34f, 0.16f) }, + { "Mat_Stone", new Color(0.47f, 0.48f, 0.47f) }, + { "Mat_Iron", new Color(0.55f, 0.58f, 0.62f) }, + { "Mat_Fiber", new Color(0.48f, 0.66f, 0.34f) }, + { "Mat_Coal", new Color(0.05f, 0.05f, 0.05f) }, + { "Mat_Herb", new Color(0.18f, 0.62f, 0.25f) }, + { "Mat_Crystal", new Color(0.35f, 0.78f, 0.95f) }, + { "Mat_Player", new Color(0.20f, 0.44f, 0.82f) }, + { "Mat_Torch_Light", new Color(1.00f, 0.55f, 0.12f) } + }; + + Dictionary result = new Dictionary(); + foreach (KeyValuePair pair in materialColors) + { + string path = MaterialsPath + "/" + pair.Key + ".mat"; + Material material = AssetDatabase.LoadAssetAtPath(path); + if (material == null) + { + material = new Material(Shader.Find("Standard")); + AssetDatabase.CreateAsset(material, path); + } + + material.color = pair.Value; + EditorUtility.SetDirty(material); + result[pair.Key] = material; + } + + return result; + } + + private static Dictionary CreateItems() + { + ItemDefinition[] definitions = + { + new ItemDefinition("Wood", "wood", "Древесина", "Базовый строительный ресурс для рукоятей и простых инструментов.", 64, true, new Color(0.58f, 0.36f, 0.16f)), + new ItemDefinition("Stone", "stone", "Камень", "Твердый ресурс для лезвий и ударных частей инструментов.", 64, true, new Color(0.55f, 0.55f, 0.55f)), + new ItemDefinition("Stick", "stick", "Палка", "Готовая рукоять для простых рецептов.", 64, true, new Color(0.64f, 0.42f, 0.20f)), + new ItemDefinition("IronOre", "iron_ore", "Железная руда", "Металлическое сырье для прочных инструментов.", 64, true, new Color(0.56f, 0.58f, 0.64f)), + new ItemDefinition("Fiber", "fiber", "Волокно", "Гибкий материал для перевязки и связки деталей.", 64, true, new Color(0.72f, 0.78f, 0.48f)), + new ItemDefinition("Leather", "leather", "Кожа", "Прочный материал для креплений и украшений.", 32, true, new Color(0.45f, 0.25f, 0.12f)), + new ItemDefinition("Coal", "coal", "Уголь", "Горючий ресурс для факелов.", 64, true, new Color(0.08f, 0.08f, 0.08f)), + new ItemDefinition("Herb", "herb", "Трава", "Лечебное растение для простых расходников.", 64, true, new Color(0.20f, 0.66f, 0.24f)), + new ItemDefinition("Crystal", "crystal", "Кристалл", "Редкий магический ресурс.", 16, true, new Color(0.30f, 0.80f, 1.00f)), + new ItemDefinition("Axe", "axe", "Топор", "Инструмент для рубки древесины.", 1, false, new Color(0.76f, 0.72f, 0.62f)), + new ItemDefinition("Pickaxe", "pickaxe", "Кирка", "Инструмент для добычи камня и руды.", 1, false, new Color(0.65f, 0.68f, 0.72f)), + new ItemDefinition("Torch", "torch", "Факел", "Источник света, создается по две штуки за крафт.", 16, false, new Color(1.00f, 0.62f, 0.18f)), + new ItemDefinition("Bandage", "bandage", "Бинт", "Расходный предмет для восстановления здоровья.", 16, false, new Color(0.92f, 0.90f, 0.82f)), + new ItemDefinition("MagicAmulet", "magic_amulet", "Магический амулет", "Редкий предмет, усиливающий героя.", 1, false, new Color(0.55f, 0.35f, 0.95f)) + }; + + Dictionary items = new Dictionary(); + for (int i = 0; i < definitions.Length; i++) + { + ItemDefinition definition = definitions[i]; + string path = ItemsPath + "/" + definition.assetName + ".asset"; + ItemData item = AssetDatabase.LoadAssetAtPath(path); + if (item == null) + { + item = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(item, path); + } + + item.itemId = definition.itemId; + item.itemName = definition.itemName; + item.description = definition.description; + item.maxStack = definition.maxStack; + item.isResource = definition.isResource; + item.itemColor = definition.itemColor; + item.icon = null; + EditorUtility.SetDirty(item); + items[definition.assetName] = item; + } + + AssetDatabase.SaveAssets(); + return items; + } + + private static List CreateRecipes(Dictionary items) + { + RecipeDefinition[] definitions = + { + new RecipeDefinition("Recipe_Axe", "recipe_axe", "Топор", "Простой топор из древесины, палки и камня.", "Axe", 1, new[] { Pair("Wood", 2), Pair("Stick", 1), Pair("Stone", 1) }), + new RecipeDefinition("Recipe_Pickaxe", "recipe_pickaxe", "Кирка", "Прочная кирка для добычи твердых ресурсов.", "Pickaxe", 1, new[] { Pair("Wood", 2), Pair("Stick", 2), Pair("IronOre", 2) }), + new RecipeDefinition("Recipe_Torch", "recipe_torch", "Факел", "Два факела из палки, угля и волокна.", "Torch", 2, new[] { Pair("Stick", 1), Pair("Coal", 1), Pair("Fiber", 1) }), + new RecipeDefinition("Recipe_Bandage", "recipe_bandage", "Бинт", "Простая лечебная повязка из волокна и травы.", "Bandage", 1, new[] { Pair("Fiber", 2), Pair("Herb", 1) }), + new RecipeDefinition("Recipe_MagicAmulet", "recipe_magic_amulet", "Магический амулет", "Редкий амулет из кристаллов, кожи и железной руды.", "MagicAmulet", 1, new[] { Pair("Crystal", 2), Pair("Leather", 1), Pair("IronOre", 1) }) + }; + + List recipes = new List(); + for (int i = 0; i < definitions.Length; i++) + { + RecipeDefinition definition = definitions[i]; + string path = RecipesPath + "/" + definition.assetName + ".asset"; + RecipeData recipe = AssetDatabase.LoadAssetAtPath(path); + if (recipe == null) + { + recipe = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(recipe, path); + } + + recipe.recipeId = definition.recipeId; + recipe.recipeName = definition.recipeName; + recipe.description = definition.description; + recipe.result = items[definition.resultKey]; + recipe.resultAmount = definition.resultAmount; + recipe.unlockedByDefault = true; + recipe.ingredients = new Ingredient[definition.ingredients.Length]; + + for (int ingredientIndex = 0; ingredientIndex < definition.ingredients.Length; ingredientIndex++) + { + RecipeIngredientDefinition ingredientDefinition = definition.ingredients[ingredientIndex]; + recipe.ingredients[ingredientIndex] = new Ingredient + { + item = items[ingredientDefinition.itemKey], + amount = ingredientDefinition.amount + }; + } + + EditorUtility.SetDirty(recipe); + recipes.Add(recipe); + } + + AssetDatabase.SaveAssets(); + return recipes; + } + + private static RecipeIngredientDefinition Pair(string itemKey, int amount) + { + return new RecipeIngredientDefinition(itemKey, amount); + } + + private static void CreateWorld(Dictionary materials, Dictionary items, Font worldFont) + { + GameObject floor = GameObject.CreatePrimitive(PrimitiveType.Plane); + floor.name = "Floor"; + floor.transform.localScale = new Vector3(2.8f, 1f, 2.2f); + SetMaterial(floor, materials["Mat_Floor"]); + + GameObject tableTop = GameObject.CreatePrimitive(PrimitiveType.Cube); + tableTop.name = "Crafting Workbench"; + tableTop.transform.position = new Vector3(0f, 0.65f, 3.2f); + tableTop.transform.localScale = new Vector3(3.6f, 0.28f, 1.5f); + SetMaterial(tableTop, materials["Mat_Workbench"]); + + CreateCube("Workbench Leg FL", new Vector3(-1.45f, 0.25f, 3.72f), new Vector3(0.22f, 0.55f, 0.22f), materials["Mat_Workbench_Dark"]); + CreateCube("Workbench Leg FR", new Vector3(1.45f, 0.25f, 3.72f), new Vector3(0.22f, 0.55f, 0.22f), materials["Mat_Workbench_Dark"]); + CreateCube("Workbench Leg BL", new Vector3(-1.45f, 0.25f, 2.68f), new Vector3(0.22f, 0.55f, 0.22f), materials["Mat_Workbench_Dark"]); + CreateCube("Workbench Leg BR", new Vector3(1.45f, 0.25f, 2.68f), new Vector3(0.22f, 0.55f, 0.22f), materials["Mat_Workbench_Dark"]); + + CreateWorldLabel("Workbench Label", "Верстак: крафт через панель справа", new Vector3(0f, 1.25f, 3.2f), worldFont); + + CreatePickup("Wood Pickup A", items["Wood"], 2, PrimitiveType.Cube, new Vector3(-5.5f, 0.35f, -2.8f), new Vector3(0.55f, 0.28f, 0.28f), materials["Mat_Wood"], worldFont); + CreatePickup("Wood Pickup B", items["Wood"], 3, PrimitiveType.Cube, new Vector3(-4.8f, 0.35f, 1.8f), new Vector3(0.55f, 0.28f, 0.28f), materials["Mat_Wood"], worldFont); + CreatePickup("Stone Pickup", items["Stone"], 3, PrimitiveType.Sphere, new Vector3(-2.9f, 0.35f, -4.2f), new Vector3(0.48f, 0.34f, 0.48f), materials["Mat_Stone"], worldFont); + CreatePickup("Stick Pickup", items["Stick"], 2, PrimitiveType.Cube, new Vector3(2.2f, 0.22f, -4.7f), new Vector3(0.75f, 0.14f, 0.14f), materials["Mat_Wood"], worldFont); + CreatePickup("Iron Ore Pickup", items["IronOre"], 2, PrimitiveType.Sphere, new Vector3(5.1f, 0.35f, -2.0f), new Vector3(0.45f, 0.34f, 0.45f), materials["Mat_Iron"], worldFont); + CreatePickup("Fiber Pickup", items["Fiber"], 3, PrimitiveType.Cube, new Vector3(-3.7f, 0.20f, 4.8f), new Vector3(0.6f, 0.12f, 0.3f), materials["Mat_Fiber"], worldFont); + CreatePickup("Leather Pickup", items["Leather"], 1, PrimitiveType.Cube, new Vector3(4.4f, 0.22f, 3.8f), new Vector3(0.55f, 0.12f, 0.42f), materials["Mat_Workbench"], worldFont); + CreatePickup("Coal Pickup", items["Coal"], 2, PrimitiveType.Sphere, new Vector3(5.6f, 0.30f, 1.1f), new Vector3(0.38f, 0.28f, 0.38f), materials["Mat_Coal"], worldFont); + CreatePickup("Herb Pickup", items["Herb"], 2, PrimitiveType.Cube, new Vector3(-0.5f, 0.20f, -5.0f), new Vector3(0.45f, 0.12f, 0.45f), materials["Mat_Herb"], worldFont); + CreatePickup("Crystal Pickup", items["Crystal"], 2, PrimitiveType.Cylinder, new Vector3(2.9f, 0.45f, 5.1f), new Vector3(0.24f, 0.55f, 0.24f), materials["Mat_Crystal"], worldFont); + } + + private static void CreateCameraAndLight() + { + GameObject cameraObject = new GameObject("Main Camera"); + Camera camera = cameraObject.AddComponent(); + cameraObject.tag = "MainCamera"; + cameraObject.transform.position = new Vector3(0f, 8f, -7f); + cameraObject.transform.rotation = Quaternion.Euler(58f, 0f, 0f); + camera.clearFlags = CameraClearFlags.SolidColor; + camera.backgroundColor = new Color(0.12f, 0.14f, 0.16f); + camera.fieldOfView = 48f; + + GameObject lightObject = new GameObject("Directional Light"); + Light light = lightObject.AddComponent(); + light.type = LightType.Directional; + light.intensity = 1.05f; + lightObject.transform.rotation = Quaternion.Euler(50f, -30f, 0f); + } + + private static void CreateSystems(Dictionary items, List recipes, Dictionary materials, Font uiFont, GameObject recipeButtonPrefab, GameObject slotPrefab, GameObject hotbarSlotPrefab) + { + GameObject player = CreatePlayer(materials["Mat_Player"]); + Camera mainCamera = Camera.main; + if (mainCamera != null) + { + CameraFollow follow = mainCamera.gameObject.AddComponent(); + follow.target = player.transform; + } + + GameObject inventoryObject = new GameObject("Player Inventory"); + Inventory inventory = inventoryObject.AddComponent(); + inventory.maxSlots = 32; + inventory.testResources = new List + { + new InventorySlot(items["Wood"], 8), + new InventorySlot(items["Stone"], 6), + new InventorySlot(items["Stick"], 6), + new InventorySlot(items["IronOre"], 4), + new InventorySlot(items["Fiber"], 6), + new InventorySlot(items["Leather"], 2), + new InventorySlot(items["Coal"], 3), + new InventorySlot(items["Herb"], 3), + new InventorySlot(items["Crystal"], 2) + }; + + GameObject craftingObject = new GameObject("Crafting System"); + CraftingSystem craftingSystem = craftingObject.AddComponent(); + craftingSystem.playerInventory = inventory; + craftingSystem.availableRecipes = recipes; + craftingSystem.lastMessage = "Собирайте ресурсы на карте, затем создавайте предметы справа."; + + GameObject canvasObject = new GameObject("Canvas"); + Canvas canvas = canvasObject.AddComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + CanvasScaler scaler = canvasObject.AddComponent(); + scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; + scaler.referenceResolution = new Vector2(1280f, 720f); + scaler.matchWidthOrHeight = 0.5f; + canvasObject.AddComponent(); + + GameObject eventSystemObject = new GameObject("EventSystem"); + eventSystemObject.AddComponent(); + eventSystemObject.AddComponent(); + + RectTransform root = canvasObject.GetComponent(); + Text title = CreateText("Title", root, "Лабораторная работа №3 - Система крафта", uiFont, 22, FontStyle.Bold, TextAnchor.MiddleCenter, Color.white); + Anchor(title.rectTransform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -22f), new Vector2(720f, 36f)); + + Image inventoryPanel = CreatePanel("Inventory Panel", root, new Vector2(150f, -246f), new Vector2(272f, 374f), new Vector2(0f, 1f), new Color(0.055f, 0.070f, 0.085f, 0.97f)); + Image recipePanel = CreatePanel("Recipe Panel", root, new Vector2(-196f, -246f), new Vector2(364f, 374f), new Vector2(1f, 1f), new Color(0.055f, 0.070f, 0.085f, 0.97f)); + Image detailsPanel = CreatePanel("Recipe Details Panel", root, new Vector2(0f, 158f), new Vector2(500f, 116f), new Vector2(0.5f, 0f), new Color(0.055f, 0.070f, 0.085f, 0.96f)); + Image hotbarPanel = CreatePanel("Hotbar Panel", root, new Vector2(0f, 42f), new Vector2(878f, 68f), new Vector2(0.5f, 0f), new Color(0.045f, 0.055f, 0.070f, 0.97f)); + + Text inventoryTitle = CreateText("Inventory Title", inventoryPanel.rectTransform, "Инвентарь", uiFont, 20, FontStyle.Bold, TextAnchor.MiddleLeft, Color.white); + Anchor(inventoryTitle.rectTransform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 160f), new Vector2(240f, 30f)); + + Transform slotContainer = CreateVerticalContainer("Inventory Slots", inventoryPanel.rectTransform, new Vector2(0f, 4f), new Vector2(248f, 270f), new Vector2(0.5f, 0.5f), 4f, TextAnchor.UpperCenter); + + Text inventoryDebug = CreateText("Inventory Debug", inventoryPanel.rectTransform, "Ресурсов: 9", uiFont, 14, FontStyle.Normal, TextAnchor.LowerLeft, new Color(0.78f, 0.84f, 0.88f)); + Anchor(inventoryDebug.rectTransform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, -162f), new Vector2(240f, 24f)); + + Text recipeTitle = CreateText("Recipe Title", recipePanel.rectTransform, "Рецепты", uiFont, 20, FontStyle.Bold, TextAnchor.MiddleLeft, Color.white); + Anchor(recipeTitle.rectTransform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, 160f), new Vector2(336f, 30f)); + + Transform recipeContainer = CreateRectContainer("Recipe Buttons", recipePanel.rectTransform, new Vector2(0f, -8f), new Vector2(336f, 286f), new Vector2(0.5f, 0.5f)); + + Text recipeDebug = CreateText("Recipe Debug", recipePanel.rectTransform, "Рецептов: 5", uiFont, 14, FontStyle.Normal, TextAnchor.LowerLeft, new Color(0.78f, 0.84f, 0.88f)); + Anchor(recipeDebug.rectTransform, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0f, -162f), new Vector2(336f, 24f)); + + Text selectedRecipeText = CreateText("Selected Recipe Text", detailsPanel.rectTransform, "", uiFont, 12, FontStyle.Normal, TextAnchor.UpperLeft, new Color(0.88f, 0.92f, 0.95f)); + Stretch(selectedRecipeText.rectTransform, 12f, 8f, 12f, 34f); + + Text messageText = CreateText("Craft Message", detailsPanel.rectTransform, "Собирайте ресурсы на карте, затем создавайте предметы справа.", uiFont, 13, FontStyle.Bold, TextAnchor.MiddleLeft, new Color(0.74f, 0.95f, 0.70f)); + Anchor(messageText.rectTransform, new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(12f, 18f), new Vector2(-24f, 28f)); + + Text interactionText = CreateText("Interaction Text", root, "Подойдите к ресурсу и нажмите E", uiFont, 16, FontStyle.Bold, TextAnchor.MiddleCenter, new Color(1f, 0.92f, 0.62f)); + Anchor(interactionText.rectTransform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -56f), new Vector2(540f, 28f)); + + Text hintText = CreateText("Hint Text", root, "WASD - движение | E - подобрать | 1-9/0 - слот | F - использовать | R - тестовые ресурсы | C - UI", uiFont, 13, FontStyle.Normal, TextAnchor.MiddleCenter, new Color(0.84f, 0.89f, 0.92f)); + Anchor(hintText.rectTransform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -84f), new Vector2(850f, 24f)); + + Transform hotbarContainer = CreateHorizontalContainer("Hotbar Slots", hotbarPanel.rectTransform, new Vector2(0f, 0f), new Vector2(854f, 54f), new Vector2(0.5f, 0.5f), 6f, TextAnchor.MiddleCenter); + + InventoryUI inventoryUI = inventoryPanel.gameObject.AddComponent(); + inventoryUI.inventory = inventory; + inventoryUI.slotContainer = slotContainer; + inventoryUI.slotPrefab = slotPrefab; + + CraftingUI craftingUI = recipePanel.gameObject.AddComponent(); + craftingUI.craftingSystem = craftingSystem; + craftingUI.inventory = inventory; + craftingUI.recipeContainer = recipeContainer; + craftingUI.recipeButtonPrefab = recipeButtonPrefab; + craftingUI.messageText = messageText; + craftingUI.selectedRecipeText = selectedRecipeText; + craftingUI.uiFont = uiFont; + + PlayerMovement movement = player.GetComponent(); + PlayerInteractor interactor = player.AddComponent(); + interactor.inventory = inventory; + interactor.craftingSystem = craftingSystem; + interactor.interactionText = interactionText; + + ItemUseSystem itemUseSystem = player.AddComponent(); + itemUseSystem.inventory = inventory; + itemUseSystem.craftingSystem = craftingSystem; + itemUseSystem.playerMovement = movement; + itemUseSystem.playerTransform = player.transform; + itemUseSystem.torchLightMaterial = materials["Mat_Torch_Light"]; + itemUseSystem.statusText = messageText; + itemUseSystem.selectableSlotCount = 10; + + HotbarUI hotbarUI = hotbarPanel.gameObject.AddComponent(); + hotbarUI.inventory = inventory; + hotbarUI.itemUseSystem = itemUseSystem; + hotbarUI.slotContainer = hotbarContainer; + hotbarUI.slotPrefab = hotbarSlotPrefab; + hotbarUI.slotCount = 10; + + GameObject controllerObject = new GameObject("Crafting Demo Controller"); + CraftingDemoController controller = controllerObject.AddComponent(); + controller.inventory = inventory; + controller.craftingSystem = craftingSystem; + controller.craftingPanel = canvasObject; + controller.hintText = hintText; + controller.addStartingResources = false; + + ApplyFontToAllText(canvasObject, uiFont); + } + + private static GameObject CreateRecipeButtonPrefab(Font font) + { + GameObject root = new GameObject("RecipeButtonPrefab", typeof(RectTransform), typeof(Image), typeof(Button)); + RectTransform rect = root.GetComponent(); + rect.sizeDelta = new Vector2(336f, 52f); + Image image = root.GetComponent(); + image.color = new Color(0.13f, 0.18f, 0.20f, 0.98f); + Button button = root.GetComponent