Files
my-project/Assets/Inventory/Scripts/InventoryItemFactory.cs
unknown 057f5d3d1e test
2026-04-15 20:04:28 +05:00

284 lines
9.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
/// <summary>
/// Конфигурация для фабрики предметов - создать в Assets > Create > Inventory > Item Factory Config
/// </summary>
[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;
}
/// <summary>
/// Фабрика для создания префабов предметов - заменяет прямой Instantiate и Resources.Load
/// </summary>
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<string, GameObject> _prefabCache = new Dictionary<string, GameObject>();
void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
/// <summary>
/// Создать предмет в мире (WorldItem)
/// </summary>
/// <param name="itemData">Данные предмета</param>
/// <param name="amount">Количество</param>
/// <param name="position">Позиция спавна</param>
/// <param name="rotation"> rotation ( Quaternion.identity по умолчанию)</param>
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<SphereCollider>();
collider.radius = 0.3f;
collider.isTrigger = true;
var rb = go.AddComponent<Rigidbody>();
rb.isKinematic = true;
rb.useGravity = false;
}
else
{
go = Instantiate(config.worldItemPrefab, position, rotation);
}
var worldItem = go.GetComponent<WorldItem>();
if (worldItem == null)
worldItem = go.AddComponent<WorldItem>();
worldItem.Setup(itemData, amount);
if (logSpawns)
Debug.Log($"[Factory] Created WorldItem: {itemData.itemName} x{amount} at {position}");
return worldItem;
}
/// <summary>
/// Создать предмет в мире с настраиваемым временем жизни
/// </summary>
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;
}
/// <summary>
/// Создать спавнер предмета (точка появления)
/// </summary>
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<ItemSpawner>();
if (spawner == null)
spawner = go.AddComponent<ItemSpawner>();
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<ItemSpawner>();
spawner.Setup(itemData, amount, config != null ? config.despawnTime : 300f);
return spawner;
}
/// <summary>
/// Выбросить предмет из инвентаря (drop)
/// </summary>
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<Rigidbody>();
if (rb == null)
rb = worldItem.gameObject.AddComponent<Rigidbody>();
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<Collider>();
if (col != null) GameObject.Destroy(col);
}
return worldItem;
}
/// <summary>
/// Предмет подбирается игроком - вызывается из WorldItem
/// </summary>
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;
}
}
/// <summary>
/// Создать префаб из GameObject в рантайме (динамические предметы)
/// </summary>
public GameObject CreateItemModel(GameObject modelPrefab, Transform parent)
{
if (modelPrefab == null)
return null;
return Instantiate(modelPrefab, parent);
}
}
#if UNITY_EDITOR
/// <summary>
/// Кастомный редактор для быстрой настройки фабрики
/// </summary>
[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<ItemFactoryConfig>();
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