📜 ⬆️ ⬇️

We add MVP to games on Unity3D

image 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:

image

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 .

Source: https://habr.com/ru/post/251543/


All Articles