221 lines
7.9 KiB
C#
221 lines
7.9 KiB
C#
using UnityEngine;
|
||
using UnityEditor;
|
||
using TMPro;
|
||
|
||
/// <summary>
|
||
/// Инструмент создания предметов.
|
||
/// Tools > Item Builder
|
||
/// </summary>
|
||
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<ItemBuilderTool>("Конструктор Предметов");
|
||
}
|
||
|
||
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<ItemData>();
|
||
|
||
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 != null ? 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<Collider>());
|
||
}
|
||
visual.name = "Visual";
|
||
visual.transform.localPosition = Vector3.zero;
|
||
|
||
// WorldItem компонент
|
||
var worldItem = itemGO.AddComponent<WorldItem>();
|
||
worldItem.interactRange = interactRange;
|
||
|
||
// Trigger-коллайдер для OverlapSphere (не для авто-pickup!)
|
||
var col = itemGO.AddComponent<SphereCollider>();
|
||
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<Collider>());
|
||
}
|
||
visual.name = "Visual";
|
||
visual.transform.localPosition = Vector3.zero;
|
||
|
||
var worldItem = prefabGO.AddComponent<WorldItem>();
|
||
worldItem.interactRange = interactRange;
|
||
|
||
var col = prefabGO.AddComponent<SphereCollider>();
|
||
col.radius = 0.5f;
|
||
col.isTrigger = true;
|
||
|
||
GameObject savedPrefab = PrefabUtility.SaveAsPrefabAsset(prefabGO, path);
|
||
DestroyImmediate(prefabGO);
|
||
|
||
Selection.activeObject = savedPrefab;
|
||
Debug.Log($"[ItemBuilder] Префаб сохранён: {path}");
|
||
}
|
||
|
||
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));
|
||
}
|
||
}
|