← Назад к вопросам
Приведи пример где можно применить паттерн 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
- Тестируемость: Модель
InventoryModelможно протестировать юнит-тестами без Unity, так как она не зависит отMonoBehaviourи UI. - Гибкость: Можно легко заменить UI (
InventoryView) без изменения логики инвентаря. Например, перейти от сетки слотов к радиальному меню, обновив только View. - Разделение ответственности: Художник UI может настраивать префабы и анимации, не касаясь C# кода. Программист работает с логикой в Model и Controller.
- Расширяемость: Добавление новой функции (сортировка, поиск) локализовано. Сортировка — это метод в Model, а кнопка для неё — элемент в View, обрабатываемый Controller.
Этот паттерн особенно полезен для сложных UI-систем (меню настроек, диалоги NPC) и симуляций с состоянием (пошаговая стратегия, экономика города), где чёткое разделение данных и их отображения критично для долгосрочной поддержки проекта.