Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Проектирование системы инвентаря в Unity
Реализация системы инвентаря — комплексная задача, требующая проработки архитектуры данных, UI и логики взаимодействия. Я поделюсь проверенным подходом, основанным на компонентно-ориентированном дизайне.
1. Базовая архитектура данных
Первым делом создаём структуры для хранения данных предметов и слота инвентаря. Использую ScriptableObject для настраиваемых параметров предметов.
// Базовый класс предмета как ScriptableObject
[CreateAssetMenu(fileName = "New Item", menuName = "Inventory/Item")]
public class Item : ScriptableObject
{
public string itemName = "New Item";
public Sprite icon = null;
public int maxStack = 1;
public GameObject physicalPrefab;
[TextArea(5, 10)]
public string description;
public virtual void Use()
{
// Базовая логика использования предмета
Debug.Log($"Using {itemName}");
}
}
2. Класс инвентаря и управления слотами
Реализую InventoryManager как синглтон для глобального доступа и InventorySlot для представления каждого слота.
// Класс для управления инвентарём
public class InventoryManager : MonoBehaviour
{
public static InventoryManager Instance;
public List<InventorySlot> slots = new List<InventorySlot>();
public int capacity = 20;
public UnityEvent<Item, int> onItemChanged;
private void Awake()
{
if (Instance == null)
Instance = this;
else
Destroy(gameObject);
InitializeSlots();
}
void InitializeSlots()
{
for (int i = 0; i < capacity; i++)
{
slots.Add(new InventorySlot());
}
}
public bool AddItem(Item item, int amount = 1)
{
// Поиск стекающегося предмета или пустого слота
foreach (var slot in slots)
{
if (slot.item == item && slot.amount < item.maxStack)
{
slot.amount += amount;
onItemChanged?.Invoke(item, slot.amount);
return true;
}
}
foreach (var slot in slots)
{
if (slot.item == null)
{
slot.item = item;
slot.amount = amount;
onItemChanged?.Invoke(item, amount);
return true;
}
}
Debug.LogWarning("Inventory is full!");
return false;
}
}
3. Система слота инвентаря
// Класс, представляющий один слот инвентаря
[System.Serializable]
public class InventorySlot
{
public Item item;
public int amount;
public bool IsEmpty => item == null;
public void Clear()
{
item = null;
amount = 0;
}
}
4. UI компоненты инвентаря
Для UI использую Grid Layout Group и префабы слотов. Ключевые компоненты:
- InventoryUI — контроллер всего интерфейса инвентаря
- InventorySlotUI — UI представление одного слота
- ItemTooltip — всплывающая подсказка с описанием
// Контроллер UI слота
public class InventorySlotUI : MonoBehaviour, IPointerClickHandler
{
public Image icon;
public Text amountText;
private InventorySlot slot;
public void Setup(InventorySlot slot)
{
this.slot = slot;
UpdateUI();
}
void UpdateUI()
{
if (slot.IsEmpty)
{
icon.enabled = false;
amountText.enabled = false;
}
else
{
icon.sprite = slot.item.icon;
icon.enabled = true;
amountText.text = slot.amount > 1 ? slot.amount.ToString() : "";
amountText.enabled = slot.amount > 1;
}
}
public void OnPointerClick(PointerEventData eventData)
{
if (eventData.button == PointerEventData.InputButton.Left)
{
// Логика использования предмета
if (!slot.IsEmpty)
slot.item.Use();
}
else if (eventData.button == PointerEventData.InputButton.Right)
{
// Логика выброса предмета
Debug.Log($"Dropping {slot.item.itemName}");
}
}
}
5. Расширенные возможности
Для полноценной системы добавляю:
Систему быстрых слотов (Hotbar):
public class Hotbar : MonoBehaviour
{
public InventorySlot[] quickSlots = new InventorySlot[8];
private int selectedIndex = 0;
void Update()
{
// Переключение быстрых слотов цифровыми клавишами
for (int i = 0; i < quickSlots.Length; i++)
{
if (Input.GetKeyDown(KeyCode.Alpha1 + i))
{
SelectSlot(i);
}
}
}
}
Сериализацию для сохранения: Использую JSONUtility или BinaryFormatter для сохранения состояния инвентаря между сессиями.
6. Оптимизация и best practices
- Object Pooling для UI элементов слотов при больших инвентарях
- Event-driven архитектура через UnityEvents для уменьшения связности
- Lazy initialization для загрузки иконок предметов
- Кэширование ссылок на компоненты в Awake() для производительности
- Разделение логики на Inventory (данные) и InventoryUI (представление)
7. Гибкая настройка через Inspector
Использую кастомные редакторы для удобной настройки:
#if UNITY_EDITOR
[CustomEditor(typeof(InventoryManager))]
public class InventoryManagerEditor : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("Test Add Random Item"))
{
// Тестовая функциональность для дебага
}
}
}
#endif
Ключевые принципы моей реализации:
- Разделение ответственности — данные, логика и UI изолированы
- Гибкость через ScriptableObject — легко добавлять новые типы предметов
- Производительность — минимизация поисков по спискам, кэширование
- Расширяемость — виртуальные методы для переопределения логики
- Тестируемость — компоненты можно тестировать изолированно
Эта архитектура проверена в нескольких проектах и масштабируется от простых инвентарей до сложных систем с категориями, фильтрами, крафтингом и торговлей. Главное — начинать с минимально рабочей версии и постепенно добавлять фичи, сохраняя код чистым и модульным.