using UnityEngine; using UnityEditor; using TMPro; /// /// Инструмент создания предметов. /// Tools > Item Builder /// public class ItemBuilderTool : EditorWindow { // Основные данные private string itemName = "NewItem"; private string itemId = ""; private string description = ""; private ItemType itemType = ItemType.Material; // Визуал private GameObject modelPrefab; private Sprite icon; private Color previewColor = Color.gray; // Свойства private int maxStack = 64; private int value = 1; private float weight = 1f; private bool isDroppable = true; private bool isStackable = true; // Pickup private float interactRange = 2.5f; [MenuItem("Tools/Item Builder")] public static void ShowWindow() { GetWindow("Конструктор Предметов"); } void OnGUI() { GUILayout.Label("Создание предмета", EditorStyles.boldLabel); // Основные данные GUILayout.Label("Основное", EditorStyles.miniBoldLabel); itemName = EditorGUILayout.TextField("Название", itemName); itemId = EditorGUILayout.TextField("ID (оставь пустым для GUID)", itemId); description = EditorGUILayout.TextField("Описание", description); itemType = (ItemType)EditorGUILayout.EnumPopup("Тип предмета", itemType); GUILayout.Space(10); // Визуал GUILayout.Label("Визуал", EditorStyles.miniBoldLabel); modelPrefab = (GameObject)EditorGUILayout.ObjectField("3D модель (префаб)", modelPrefab, typeof(GameObject), false); icon = (Sprite)EditorGUILayout.ObjectField("Иконка", icon, typeof(Sprite), false); if (icon == null) previewColor = EditorGUILayout.ColorField("Цвет иконки (заглушка)", previewColor); GUILayout.Space(10); // Свойства GUILayout.Label("Свойства", EditorStyles.miniBoldLabel); maxStack = EditorGUILayout.IntField("Макс. стек", maxStack); value = EditorGUILayout.IntField("Цена", value); weight = EditorGUILayout.FloatField("Вес", weight); isDroppable = EditorGUILayout.Toggle("Можно выбросить", isDroppable); isStackable = EditorGUILayout.Toggle("Стекируемый", isStackable); GUILayout.Space(10); GUILayout.Label("Подбор", EditorStyles.miniBoldLabel); interactRange = EditorGUILayout.FloatField("Дальность подбора (E)", interactRange); GUILayout.Space(20); // Кнопки GUI.color = Color.green; if (GUILayout.Button("Создать ItemData (ScriptableObject)")) CreateItemData(); GUI.color = Color.cyan; if (GUILayout.Button("Создать WorldItem в сцене")) CreateWorldItem(); GUI.color = Color.yellow; if (GUILayout.Button("Создать WorldItem как префаб")) CreateItemPrefab(); GUI.color = Color.white; GUILayout.Space(10); EditorGUILayout.HelpBox( "Подбор предметов — только по кнопке E (авто-pickup при касании убран намеренно).\n" + "1. Заполните данные → Создайте ItemData\n" + "2. Назначьте ItemData в InventoryManager.itemDatabase\n" + "3. Создайте WorldItem для размещения предметов на сцене", MessageType.Info); } void CreateItemData() { if (string.IsNullOrEmpty(itemName)) { EditorUtility.DisplayDialog("Ошибка", "Введите название предмета", "OK"); return; } var item = ScriptableObject.CreateInstance(); item.id = string.IsNullOrEmpty(itemId) ? System.Guid.NewGuid().ToString() : itemId; item.itemName = itemName; item.description = description; item.type = itemType; item.modelPrefab = modelPrefab; item.icon = icon ?? CreatePlaceholderIcon(); item.maxStack = maxStack; item.value = value; item.weight = weight; item.isDroppable = isDroppable; item.isStackable = isStackable; string folder = "Assets/Inventory/Items"; if (!System.IO.Directory.Exists(folder)) System.IO.Directory.CreateDirectory(folder); string path = $"{folder}/{itemName}.asset"; AssetDatabase.CreateAsset(item, path); AssetDatabase.SaveAssets(); Selection.activeObject = item; Debug.Log($"[ItemBuilder] Создан: {itemName} → {path}"); } void CreateWorldItem() { var itemGO = new GameObject(itemName); // Визуал GameObject visual; if (modelPrefab != null) { visual = (GameObject)PrefabUtility.InstantiatePrefab(modelPrefab, itemGO.transform); } else { visual = GameObject.CreatePrimitive(PrimitiveType.Cube); visual.transform.SetParent(itemGO.transform, false); visual.transform.localScale = Vector3.one * 0.3f; DestroyImmediate(visual.GetComponent()); } visual.name = "Visual"; visual.transform.localPosition = Vector3.zero; // WorldItem компонент var worldItem = itemGO.AddComponent(); worldItem.interactRange = interactRange; // Автоматически создаём/находим ItemData и назначаем var data = EnsureItemData(); if (data != null) { worldItem.itemData = data; AddToInventoryDatabase(data); } // Trigger-коллайдер для OverlapSphere (не для авто-pickup!) var col = itemGO.AddComponent(); col.radius = 0.5f; col.isTrigger = true; itemGO.transform.position = Vector3.zero; Selection.activeGameObject = itemGO; Debug.Log($"[ItemBuilder] Создан WorldItem: {itemName}"); } void CreateItemPrefab() { string path = EditorUtility.SaveFilePanelInProject( "Сохранить WorldItem префаб", itemName, "prefab", "Выберите место сохранения"); if (string.IsNullOrEmpty(path)) return; var prefabGO = new GameObject(itemName); // Визуал GameObject visual; if (modelPrefab != null) { visual = (GameObject)PrefabUtility.InstantiatePrefab(modelPrefab, prefabGO.transform); } else { visual = GameObject.CreatePrimitive(PrimitiveType.Cube); visual.transform.SetParent(prefabGO.transform, false); visual.transform.localScale = Vector3.one * 0.3f; DestroyImmediate(visual.GetComponent()); } visual.name = "Visual"; visual.transform.localPosition = Vector3.zero; var worldItem = prefabGO.AddComponent(); worldItem.interactRange = interactRange; var data = EnsureItemData(); if (data != null) { worldItem.itemData = data; AddToInventoryDatabase(data); } var col = prefabGO.AddComponent(); col.radius = 0.5f; col.isTrigger = true; GameObject savedPrefab = PrefabUtility.SaveAsPrefabAsset(prefabGO, path); DestroyImmediate(prefabGO); Selection.activeObject = savedPrefab; Debug.Log($"[ItemBuilder] Префаб сохранён: {path}"); } // Находит существующий ItemData ассет или создаёт новый — без диалогов ItemData EnsureItemData() { string folder = "Assets/Inventory/Items"; string path = $"{folder}/{itemName}.asset"; var existing = AssetDatabase.LoadAssetAtPath(path); if (existing != null) return existing; if (!System.IO.Directory.Exists(folder)) System.IO.Directory.CreateDirectory(folder); var item = ScriptableObject.CreateInstance(); item.id = string.IsNullOrEmpty(itemId) ? System.Guid.NewGuid().ToString() : itemId; item.itemName = itemName; item.description = description; item.type = itemType; item.modelPrefab = modelPrefab; item.icon = icon ?? CreatePlaceholderIcon(); item.maxStack = maxStack; item.value = value; item.weight = weight; item.isDroppable = isDroppable; item.isStackable = isStackable; AssetDatabase.CreateAsset(item, path); AssetDatabase.SaveAssets(); Debug.Log($"[ItemBuilder] ItemData автоматически создан: {path}"); return item; } // Добавляет ItemData в InventoryManager.itemDatabase если его там нет void AddToInventoryDatabase(ItemData data) { var im = Object.FindObjectOfType(); if (im == null) return; if (im.itemDatabase == null) im.itemDatabase = new System.Collections.Generic.List(); if (!im.itemDatabase.Contains(data)) { im.itemDatabase.Add(data); EditorUtility.SetDirty(im); Debug.Log($"[ItemBuilder] {data.itemName} добавлен в InventoryManager.itemDatabase"); } } Sprite CreatePlaceholderIcon() { var tex = new Texture2D(64, 64); var colors = new Color[64 * 64]; for (int i = 0; i < colors.Length; i++) colors[i] = previewColor; tex.SetPixels(colors); tex.Apply(); return Sprite.Create(tex, new Rect(0, 0, 64, 64), new Vector2(0.5f, 0.5f)); } }