unity3d
Wzorce projektowe
Szukaj…
Wzorzec projektowy kontrolera widoku modelu (MVC)
Kontroler widoku modelu jest bardzo powszechnym wzorcem projektowym, który istnieje już od dłuższego czasu. Ten wzór koncentruje się na zmniejszeniu kodu spaghetti poprzez podzielenie klas na części funkcjonalne. Ostatnio eksperymentowałem z tym wzorcem projektowym w Unity i chciałbym podać podstawowy przykład.
Projekt MVC składa się z trzech podstawowych części: modelu, widoku i kontrolera.
Model: Model to klasa reprezentująca część danych twojego obiektu. Może to być gracz, ekwipunek lub cały poziom. Jeśli jest odpowiednio zaprogramowany, powinieneś być w stanie wziąć ten skrypt i używać go poza Unity.
Zwróć uwagę na kilka rzeczy na temat modelu:
- Nie powinien dziedziczyć po Monobehaviour
- Nie powinien zawierać specyficznego dla Unity kodu dla przenośności
- Ponieważ unikamy wywołań interfejsu Unity API, może to utrudnić takie rzeczy, jak niejawne konwertery w klasie Model (wymagane są obejścia)
Player.cs
using System;
public class Player
{
public delegate void PositionEvent(Vector3 position);
public event PositionEvent OnPositionChanged;
public Vector3 position
{
get
{
return _position;
}
set
{
if (_position != value) {
_position = value;
if (OnPositionChanged != null) {
OnPositionChanged(value);
}
}
}
}
private Vector3 _position;
}
Vector3.cs
Niestandardowa klasa Vector3 do użytku z naszym modelem danych.
using System;
public class Vector3
{
public float x;
public float y;
public float z;
public Vector3(float x, float y, float z)
{
this.x = x;
this.y = y;
this.z = z;
}
}
Widok: widok jest klasą reprezentującą część podglądu powiązaną z modelem. Jest to odpowiednia klasa wywodząca się z Monobehaviour. Powinien zawierać kod, który współdziała bezpośrednio z interfejsami API specyficznymi dla Unity, w tym OnCollisinEnter
, Start
, Update
itp.
- Zazwyczaj dziedziczy po Monobehaviour
- Zawiera kod specyficzny dla Unity
PlayerView.cs
using UnityEngine;
public class PlayerView : Monobehaviour
{
public void SetPosition(Vector3 position)
{
transform.position = position;
}
}
Kontroler: Kontroler to klasa, która łączy ze sobą zarówno Model, jak i Widok. Kontrolery synchronizują model i widok, a także interakcję napędu. Kontroler może nasłuchiwać zdarzeń od dowolnego partnera i odpowiednio aktualizować.
- Wiąże zarówno model, jak i widok, synchronizując stan
- Może stymulować interakcję między partnerami
- Kontrolery mogą, ale nie muszą być przenośne (może być konieczne użycie kodu Unity tutaj)
- Jeśli zdecydujesz się nie przenosić kontrolera na inny, rozważ ustawienie Monobehaviour, aby pomóc w sprawdzaniu edytora
PlayerController.cs
using System;
public class PlayerController
{
public Player model { get; private set; }
public PlayerView view { get; private set; }
public PlayerController(Player model, PlayerView view)
{
this.model = model;
this.view = view;
this.model.OnPositionChanged += OnPositionChanged;
}
private void OnPositionChanged(Vector3 position)
{
// Sync
Vector3 pos = this.model.position;
// Unity call required here! (we lost portability)
this.view.SetPosition(new UnityEngine.Vector3(pos.x, pos.y, pos.z));
}
// Calling this will fire the OnPositionChanged event
private void SetPosition(Vector3 position)
{
this.model.position = position;
}
}
Ostateczne użycie
Teraz, gdy mamy już wszystkie główne elementy, możemy stworzyć fabrykę, która wygeneruje wszystkie trzy części.
PlayerFactory.cs
using System;
public class PlayerFactory
{
public PlayerController controller { get; private set; }
public Player model { get; private set; }
public PlayerView view { get; private set; }
public void Load()
{
// Put the Player prefab inside the 'Resources' folder
// Make sure it has the 'PlayerView' Component attached
GameObject prefab = Resources.Load<GameObject>("Player");
GameObject instance = GameObject.Instantiate<GameObject>(prefab);
this.model = new Player();
this.view = instance.GetComponent<PlayerView>();
this.controller = new PlayerController(model, view);
}
}
I wreszcie możemy zadzwonić do fabryki od kierownika ...
Manager.cs
using UnityEngine;
public class Manager : Monobehaviour
{
[ContextMenu("Load Player")]
private void LoadPlayer()
{
new PlayerFactory().Load();
}
}
Załącz skrypt Managera do pustego GameObject w scenie, kliknij komponent prawym przyciskiem myszy i wybierz „Load Player”.
W przypadku bardziej złożonej logiki można wprowadzić dziedziczenie z abstrakcyjnymi klasami podstawowymi i interfejsami w celu poprawy architektury.