← Назад к вопросам

Приведи пример где можно применить паттерн MVC

1.7 Middle🔥 131 комментариев
#Паттерны проектирования

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Пример применения паттерна MVC в Unity: Инвентарь игрока

Паттерн Model-View-Controller (MVC) идеально подходит для организации кода в системах с чётким разделением данных, логики и отображения. В Unity он помогает создавать масштабируемые и поддерживаемые модули, такие как инвентарь, диалоговые системы или меню. Рассмотрим реализацию системы инвентаря игрока.

Архитектура MVC для инвентаря

  • Model (Модель): Содержит структуры данных — список предметов, их количество, свойства (вес, ценность). Отвечает за бизнес-логику: добавление/удаление предметов, проверку перегруза.
  • View (Представление): Визуальное отображение модели на UI Canvas (слоты, иконки, текст количества). Полностью пассивно — только отражает текущее состояние модели.
  • Controller (Контроллер): Посредник между Model и View. Обрабатывает пользовательский ввод (клики по предметам, кнопки "выбросить") и делегирует логику Model, после чего обновляет View.

Пример кода

Model: данные и логика инвентаря

// ItemData.cs
[System.Serializable]
public class ItemData
{
    public string Id;
    public string DisplayName;
    public Sprite Icon;
    public int MaxStack = 1;
    public float Weight;
}

// InventoryModel.cs
public class InventoryModel
{
    // Структура для хранения предмета и его количества в слоте
    public class InventorySlot
    {
        public ItemData Item;
        public int Count;
    }

    public List<InventorySlot> Slots { get; private set; } = new List<InventorySlot>(20);
    public float MaxWeight { get; private set; } = 100f;

    // Логика добавления предмета. Возвращает успешность операции.
    public bool TryAddItem(ItemData newItem, int count = 1)
    {
        if (CalculateTotalWeight() + newItem.Weight * count > MaxWeight)
        {
            Debug.Log("Инвентарь перегружен!");
            return false;
        }

        // Попытка добавить в существующий стек
        foreach (var slot in Slots)
        {
            if (slot.Item == newItem && slot.Count < slot.Item.MaxStack)
            {
                int canAdd = slot.Item.MaxStack - slot.Count;
                int addAmount = Mathf.Min(canAdd, count);
                slot.Count += addAmount;
                count -= addAmount;
                if (count <= 0) return true;
            }
        }

        // Добавление в новый слот
        while (count > 0 && Slots.Count < 20)
        {
            int addAmount = Mathf.Min(newItem.MaxStack, count);
            Slots.Add(new InventorySlot { Item = newItem, Count = addAmount });
            count -= addAmount;
        }

        return count <= 0;
    }

    private float CalculateTotalWeight()
    {
        return Slots.Sum(slot => slot.Item.Weight * slot.Count);
    }
}

View: отображение состояния модели

// InventoryView.cs
public class InventoryView : MonoBehaviour
{
    [SerializeField] private Transform _slotsContainer;
    [SerializeField] private GameObject _slotPrefab;

    private List<InventorySlotUI> _uiSlots = new List<InventorySlotUI>();

    // Инициализация UI с заданным количеством слотов
    public void Initialize(int slotCount)
    {
        for (int i = 0; i < slotCount; i++)
        {
            var slotGO = Instantiate(_slotPrefab, _slotsContainer);
            _uiSlots.Add(slotGO.GetComponent<InventorySlotUI>());
        }
    }

    // Обновление конкретного слота на основе данных из модели
    public void UpdateSlot(int index, Sprite icon, int count, bool isEmpty)
    {
        if (index < 0 || index >= _uiSlots.Count) return;

        _uiSlots[index].SetItem(icon, count, isEmpty);
    }

    // Полная перерисовка инвентаря
    public void RefreshView(List<InventoryModel.InventorySlot> modelSlots)
    {
        for (int i = 0; i < _uiSlots.Count; i++)
        {
            if (i < modelSlots.Count && modelSlots[i] != null)
            {
                UpdateSlot(i, modelSlots[i].Item.Icon, modelSlots[i].Count, false);
            }
            else
            {
                UpdateSlot(i, null, 0, true);
            }
        }
    }
}

Controller: связующее звено

// InventoryController.cs
public class InventoryController : MonoBehaviour
{
    [SerializeField] private InventoryView _view;
    private InventoryModel _model = new InventoryModel();

    private void Start()
    {
        _view.Initialize(20); // Создаём 20 слотов в UI
        RefreshUI(); // Первоначальная синхронизация
    }

    // Публичный метод для добавления предмета извне (например, по поднятию)
    public void AddItemToInventory(ItemData item)
    {
        bool success = _model.TryAddItem(item);
        if (success)
        {
            Debug.Log($"Предмет {item.DisplayName} добавлен.");
            RefreshUI();
        }
    }

    // Обработчик клика по UI-слоту
    public void OnSlotClicked(int slotIndex)
    {
        // Здесь может быть логика использования или выброса предмета
        Debug.Log($"Клик по слоту {slotIndex}");
        // Например, вызов _model.RemoveItem(slotIndex);
        // После чего обязательно RefreshUI();
    }

    // Ключевой метод: синхронизация View с текущим состоянием Model
    private void RefreshUI()
    {
        _view.RefreshView(_model.Slots);
    }
}

Преимущества такого подхода в Unity

  1. Тестируемость: Модель InventoryModel можно протестировать юнит-тестами без Unity, так как она не зависит от MonoBehaviour и UI.
  2. Гибкость: Можно легко заменить UI (InventoryView) без изменения логики инвентаря. Например, перейти от сетки слотов к радиальному меню, обновив только View.
  3. Разделение ответственности: Художник UI может настраивать префабы и анимации, не касаясь C# кода. Программист работает с логикой в Model и Controller.
  4. Расширяемость: Добавление новой функции (сортировка, поиск) локализовано. Сортировка — это метод в Model, а кнопка для неё — элемент в View, обрабатываемый Controller.

Этот паттерн особенно полезен для сложных UI-систем (меню настроек, диалоги NPC) и симуляций с состоянием (пошаговая стратегия, экономика города), где чёткое разделение данных и их отображения критично для долгосрочной поддержки проекта.

Приведи пример где можно применить паттерн MVC | PrepBro