using UnityEngine; using System.Collections.Generic; #if UNITY_EDITOR using UnityEditor; #endif /// /// Конфигурация для фабрики предметов - создать в Assets > Create > Inventory > Item Factory Config /// [CreateAssetMenu(fileName = "ItemFactoryConfig", menuName = "Inventory/Item Factory Config")] public class ItemFactoryConfig : ScriptableObject { [Header("World Item Prefabs")] public GameObject worldItemPrefab; // Префаб предмета в мире [Header("Spawner Prefab")] public GameObject itemSpawnerPrefab; // Префаб спавнера [Header("Pickup Settings")] public float pickupRadius = 1.5f; public float despawnTime = 300f; // 5 минут [Header("Drop Settings")] public float dropForce = 2f; public float dropRandomness = 0.5f; } /// /// Фабрика для создания префабов предметов - заменяет прямой Instantiate и Resources.Load /// public class InventoryItemFactory : MonoBehaviour { public static InventoryItemFactory Instance { get; private set; } [Header("Configuration")] [Tooltip("Создать в Assets > Create > Inventory > Item Factory Config")] public ItemFactoryConfig config; [Header("Debug")] public bool logSpawns = false; // Кэшированные ссылки для быстрого доступа private Dictionary _prefabCache = new Dictionary(); void Awake() { if (Instance == null) { Instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } } /// /// Создать предмет в мире (WorldItem) /// /// Данные предмета /// Количество /// Позиция спавна /// rotation ( Quaternion.identity по умолчанию) public WorldItem CreateWorldItem(ItemData itemData, int amount, Vector3 position, Quaternion rotation = default) { GameObject go; if (config == null || config.worldItemPrefab == null) { // Fallback - создаём без префаба go = new GameObject($"WorldItem_{itemData.itemName}"); go.transform.position = position; go.transform.rotation = rotation; // Добавляем компоненты для физики var collider = go.AddComponent(); collider.radius = 0.3f; collider.isTrigger = true; var rb = go.AddComponent(); rb.isKinematic = true; rb.useGravity = false; } else { go = Instantiate(config.worldItemPrefab, position, rotation); } var worldItem = go.GetComponent(); if (worldItem == null) worldItem = go.AddComponent(); worldItem.Setup(itemData, amount); if (logSpawns) Debug.Log($"[Factory] Created WorldItem: {itemData.itemName} x{amount} at {position}"); return worldItem; } /// /// Создать предмет в мире с настраиваемым временем жизни /// public WorldItem CreateWorldItem(ItemData itemData, int amount, Vector3 position, float lifetime, Quaternion rotation = default) { var worldItem = CreateWorldItem(itemData, amount, position, rotation); if (worldItem != null && lifetime > 0) { Destroy(worldItem.gameObject, lifetime); } return worldItem; } /// /// Создать спавнер предмета (точка появления) /// public ItemSpawner CreateItemSpawner(ItemData itemData, int amount, Vector3 position) { if (config == null || config.itemSpawnerPrefab == null) { // Создаём базовый спавнер без префаба return CreateSpawnerFallback(itemData, amount, position); } var go = Instantiate(config.itemSpawnerPrefab, position, Quaternion.identity); var spawner = go.GetComponent(); if (spawner == null) spawner = go.AddComponent(); spawner.Setup(itemData, amount, config.despawnTime); if (logSpawns) Debug.Log($"[Factory] Created Spawner: {itemData.itemName} x{amount}"); return spawner; } private ItemSpawner CreateSpawnerFallback(ItemData itemData, int amount, Vector3 position) { var go = new GameObject($"Spawner_{itemData.itemName}"); go.transform.position = position; var spawner = go.AddComponent(); spawner.Setup(itemData, amount, config != null ? config.despawnTime : 300f); return spawner; } /// /// Выбросить предмет из инвентаря (drop) /// public WorldItem DropItem(ItemData itemData, int amount, Vector3 dropPosition, Vector3? velocity = null) { // Добавляем небольшое случайное смещение Vector3 offset = Vector3.zero; if (config != null) { offset = new Vector3( Random.Range(-config.dropRandomness, config.dropRandomness), 0.5f, Random.Range(-config.dropRandomness, config.dropRandomness) ); } Vector3 spawnPos = dropPosition + offset; Quaternion rotation = Quaternion.Euler( Random.Range(0, 360), Random.Range(0, 360), Random.Range(0, 360) ); var worldItem = CreateWorldItem(itemData, amount, spawnPos, rotation); // Добавляем начальную скорость (для выброса из инвентаря) if (velocity.HasValue && worldItem != null) { var rb = worldItem.GetComponent(); if (rb == null) rb = worldItem.gameObject.AddComponent(); rb.linearVelocity = velocity.Value + (Vector3.up * (config?.dropForce ?? 2f)); } // Если у ItemData нет связанной модели — создаём простой визуал (куб) if (itemData != null && itemData.modelPrefab == null && worldItem != null) { var visual = GameObject.CreatePrimitive(PrimitiveType.Cube); visual.name = "Visual"; visual.transform.SetParent(worldItem.transform, false); visual.transform.localPosition = Vector3.zero; visual.transform.localScale = Vector3.one * 0.3f; // Удаляем лишний коллайдер на визуале var col = visual.GetComponent(); if (col != null) GameObject.Destroy(col); } return worldItem; } /// /// Предмет подбирается игроком - вызывается из WorldItem /// public bool PickupItem(WorldItem worldItem, MonoBehaviour picker) { if (worldItem == null || worldItem.itemData == null) return false; int remaining = InventoryManager.Instance.AddItem(worldItem.itemData, worldItem.amount); if (remaining <= 0) { if (logSpawns) Debug.Log($"[Factory] Picked up: {worldItem.itemData.itemName} x{worldItem.amount}"); Destroy(worldItem.gameObject); return true; } else { // Частичный подбор worldItem.amount = remaining; return false; } } /// /// Создать префаб из GameObject в рантайме (динамические предметы) /// public GameObject CreateItemModel(GameObject modelPrefab, Transform parent) { if (modelPrefab == null) return null; return Instantiate(modelPrefab, parent); } } #if UNITY_EDITOR /// /// Кастомный редактор для быстрой настройки фабрики /// [CustomEditor(typeof(InventoryItemFactory))] public class InventoryItemFactoryEditor : Editor { public override void OnInspectorGUI() { base.OnInspectorGUI(); var factory = (InventoryItemFactory)target; GUILayout.Space(10); if (factory.config == null) { EditorGUILayout.HelpBox( "Создайте ItemFactoryConfig: Assets > Create > Inventory > Item Factory Config", MessageType.Warning); } if (GUILayout.Button("Создать ItemFactoryConfig")) { var config = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(config, "Assets/Inventory/Configs/ItemFactoryConfig.asset"); AssetDatabase.SaveAssets(); factory.config = config; Debug.Log("Создан ItemFactoryConfig"); } GUILayout.Space(5); if (GUILayout.Button("Создать тестовый предмет в сцене")) { if (InventoryManager.Instance?.itemDatabase?.Count > 0) { var item = InventoryManager.Instance.itemDatabase[0]; factory.CreateWorldItem(item, 5, Vector3.zero + Vector3.forward * 2); } else { Debug.LogWarning("Сначала добавьте предметы в InventoryManager"); } } } } #endif