← All patterns
Architectural

MVP

Split UI code into a dumb View, a pure-data Model and a Presenter that mediates between them.

What Is It?

Model–View–Presenter divides a screen into three roles. The Model holds state and rules (gold, items, "can I afford this?") as plain C#. The View is a MonoBehaviour that owns the Buttons and TMP_Texts and nothing else. The Presenter sits in between and is the only piece that talks to both.

Traffic flows in a loop. A click in the View raises an event like BuyClicked; the Presenter turns that into model.TryBuy(potion); the Model changes and raises Changed; the Presenter reads the new state and calls view.Render(gold, items). The View never holds a reference to the Model.

Think of a shop screen: the shopkeeper prices, the player wallet and the inventory rules live in an InventoryModel you can unit test without a scene, while the InventoryView could be swapped from uGUI to UI Toolkit without touching a single game rule.

When Is It Used?

Use it for any UI with real logic behind it — shops, inventories, crafting screens, settings menus with validation.

It pays off when you want automated tests for UI rules, or when designers frequently rework layouts and you do not want those changes to break gameplay code.

For a static pause menu with three buttons that just call SceneManager.LoadScene, MVP is ceremony. Keep it simple until state and rules appear.

Interactive Demo

Click the controls and watch the objects collaborate. The console mirrors what the C# code below would log.

Code

Assets / Scripts / Architectural/ MVP ›InventoryBootstrap.cs
using UnityEngine;

namespace Patterns.Architectural.MVP
{
    /// <summary>
    /// Builds the triad for the shop screen: creates the Model, finds the
    /// View in the scene and hands both to a new Presenter.
    /// </summary>
    public class InventoryBootstrap : MonoBehaviour
    {
        [SerializeField] private InventoryView view;
        [SerializeField] private int startingGold = 50;

        private InventoryPresenter presenter;

        private void Start()
        {
            var model = new InventoryModel(startingGold);
            presenter = new InventoryPresenter(model, view);
        }

        private void OnDestroy()
        {
            presenter?.Dispose();
            presenter = null;
        }
    }
}
4 files · namespace Patterns.Architectural.MVPC# · UTF-8 · LF

Advantages & Disadvantages

+ Advantages

  • Game rules live in a plain C# Model that can be tested in Edit Mode.
  • Views become thin and replaceable — swap uGUI for UI Toolkit by writing a new View.
  • The data flow is explicit and one-directional: click → presenter → model → event → presenter → view.

− Disadvantages

  • More classes and more wiring per screen than a single MonoBehaviour would need.
  • Presenters can bloat into "god objects" if every screen concern is dumped into them.
  • Forgetting to unsubscribe in Dispose leaves presenters alive after their screen is gone.

Tips

  1. 01Give the View an interface (IInventoryView) so the Presenter can be tested with a fake view.
  2. 02Have the View expose C# events (event Action BuyClicked) rather than public Button fields, so the Presenter never touches Unity UI types.
  3. 03Make the Presenter IDisposable and unhook all events in Dispose(); call it from the bootstrap in OnDestroy.
  4. 04Render from the Model's current state on every change instead of patching individual labels — it keeps the View consistent.
  5. 05MVVM with UI Toolkit data binding is a close cousin; pick MVP when you want explicit control over each refresh.