
Good day to all. In this article I would like to talk about how you can apply the MVP pattern in the process of developing games on the Unity3D platform. Using this template can help streamline code and improve project structure. It should be immediately noted that the article does not give a detailed description of the template itself, but assumes that the reader has basic knowledge about it.
As we all know, MVP is a template designed to separate the presentation logic from the application logic. In the case of Unity3D, the presentation can be a GameObject with a set of components attached to it that are necessary to implement the presentation logic (including the component of the presentation logic itself -
MonoBehaviour which implements the corresponding presentation interface (
View )).
Presenter can be any type of .NET that implements the logic of a specific part of an application and interacts with other parts of it, such as models, services, etc.
Go to practice
So, suppose before us, the task is to create a very simple game in which the player is presented with not the widest arsenal of possible actions: all he can do is click on the image of the colored rectangle. In response to the player's actions, the rectangle can jump, roll over and change its color to an arbitrary, depending on the logic of the game. This is a very simple example, but it is quite enough to understand how the MVP pattern can be applied when developing games on Unity3D.
')
Let's take a look at the possible organization of the project shown in the following figure:

Notice the
FunnyRectView script in the component list of the
FunnyRect object. This script is an implementation of presentation functionality. The
FunnyRectView type implements the
IFunnyRectView interface, and this interface is actively used by the presenter to interact with the presentation.
Below is the
FunnyRectView code.
public class FunnyRectView : MonoBehaviour, IFunnyRectView { private IFunnyRectPresenter _presenter; public void Awake() { _presenter = new FunnyRectPresenter(this); _presenter.Initialize(); } }
A presentation bunch with a presenter happens in Awake. Presenter, as a parameter of the constructor, accepts a link of type
IFunnyRectView . This interface serves as the bridge that connects the presentation with the presenter. It allows you to call methods defined in
FunnyRectView , subscribe and respond to its events. The presentation also has the ability to interact with the presenter via the link saved in the
_presenter .
Let's add the necessary for the normal functioning of the logic in
FunnyRectView .
public class FunnyRectView : MonoBehaviour, IFunnyRectView { private IFunnyRectPresenter _presenter; private bool _isInputEnabled; private Animator _animator; private const string JumpAnimationTriggerName = "JumpTrigger"; private const string RotateAnimationTriggerName = "RotateTrigger"; public void Awake() { _presenter = new FunnyRectPresenter(this); _animator = gameObject.GetComponent<Animator>(); _presenter.Initialize(); } public void OnDestroy() { _presenter.Uninitialize(); } public event EventHandler RotationEnd; public event EventHandler JumpEnd; public event EventHandler Clicked; public void OnMouseDown() { if (!_isInputEnabled) return; Clicked(this, EventArgs.Empty); } public void NotifyRotationEnded() { RotationEnd(this, EventArgs.Empty); } public void NotifyJumpEnded() { JumpEnd(this, EventArgs.Empty); } public void DisableInput() { _isInputEnabled = false; } public void EnableInput() { _isInputEnabled = true; } public void Rotate() { _animator.SetTrigger(RotateAnimationTriggerName); } public void Jump() { _animator.SetTrigger(JumpAnimationTriggerName); } public void ChangeColor(Color color) { var spriteRenderer = gameObject.GetComponent<SpriteRenderer>(); spriteRenderer.color = color; _isInputEnabled = true; } }
In the
Awake method, we get a link to the animator component. It is needed to play the jump animation and flip the rectangle. The
Clicked event
is necessary to notify the presenter that the player has clicked on the rectangle. This happens in
OnMousedDown (a collider is present on the rectangle). The
NotifyRotationEnded and
NotifyJumpEnded methods are called by the animator, at the moment when the animation of the jump or coup ends and generates
JumpEnd and
RotationEnd events, respectively. All other methods are invoked from the presenter.
public class FunnyRectPresenter : IFunnyRectPresenter { private readonly IFunnyRectView _view; private const int FunnyRectRotateActionCode = 0; private const int FunnyRectJumpActionCode = 1; private const int FunnyRectChangeColorActionCode = 2; public FunnyRectPresenter(IFunnyRectView view) { _view = view; } private void OnRectClicked(object sender, EventArgs e) { _view.DisableInput(); var action = GenerateRandomAction(); switch (action) { case FunnyRectRotateActionCode: _view.Rotate(); break; case FunnyRectJumpActionCode: _view.Jump(); break; case FunnyRectChangeColorActionCode: var color = GenerateRandomColor(); _view.ChangeColor(color); break; } } private void OnRotationEnd(object sender, EventArgs e) { _view.EnableInput(); } private void OnJumpEnd(object sender, EventArgs e) { _view.EnableInput(); } private Color GenerateRandomColor() { var random = new Random(); var c = Color.white; cr = (float)random.NextDouble(); cg = (float)random.NextDouble(); cb = (float)random.NextDouble(); return c; } private int GenerateRandomAction() { var random = new Random(); return random.Next(FunnyRectRotateActionCode, FunnyRectChangeColorActionCode+1); } public void Initialize() { _view.Clicked += OnRectClicked; _view.RotationEnd += OnRotationEnd; _view.JumpEnd += OnJumpEnd; _view.EnableInput(); } public void Uninitialize() { _view.Clicked -= OnRectClicked; _view.RotationEnd -= OnRotationEnd; _view.JumpEnd -= OnJumpEnd; } }
I think this code is fairly simple and straightforward. In
Initialize , subscription to events generated in the view occurs, in
Uninitialize there is a reply to them. In the constructor, save the link to the view. The
OnRectClicked method
is called each time a player clicks a rectangle. It generates a random action and calls its corresponding presentation method. It remains to look at the
IFunnyRectView and
IFunnyRectPresenter code .
IFunnyRectView:
public interface IFunnyRectView { event EventHandler RotationEnd; event EventHandler JumpEnd; event EventHandler Clicked; void EnableInput(); void DisableInput(); void Rotate(); void Jump(); void ChangeColor(Color color); }
IFunnyRectPresenter:
public interface IFunnyRectPresenter { void Initialize(); void Uninitialize(); }
The use of MVP enabled us to subsequently change the presentation logic at our discretion without affecting the application logic. It should also be noted that, based on the concepts of MVP, it is necessary to avoid directly calling methods and setting the properties of the presenter from the presentation. Instead, it must be notified of the occurrence of certain events.
The full source code with comments can be taken at
github.com/rumyancevpavel/FunnyRect .