An Introduction

After starting game development, I saw that the disciplines in native applications are in a way that we are not used to in game engines because game engines should appeal to as many people as possible and be flexible. This is a conscious choice of engine developers because it is very difficult to predict what a game will need.
For simple needs, legacy a use is both a fast and uncomplicated solution. While this is the case, forcing a person with such a need to use such patterns will only tire the developer. The best example of this is WPF. .net developers who are accustomed to the classical form structure, encountered WPF, and even if what they wanted to do was simple, they had a hard time doing it.
In this series of articles, I will describe the existing patterns and the problems they solve, as well as their limitations. It will be a long series, I hope you find it useful.

When talking about the MVVM Design Pattern, of course, the first thing that comes to mind is WPF/Silverlight. They provide data binding (Data Binding), command (Command) and all other functions for good implementation of MVVM. Two-way data binding is provided by Data Binding, and the command splits the generic Code Behind into events in the ViewModel as Command.

Mvvm UML

Implementing MVVM in WPF

In WPF you define a ViewModel to handle the View. For example as follows:

public class BookViewModel : INotifyPropertyChanged
{
    #region Construction
    /// Constructs the default instance of a BookViewModel
    public BookViewModel()
    {
        _book = new Book { WriterName = "Unknown", BookTitle = "Unknown" };
    }
    #endregion

    #region Members
    Book _book;
    #endregion

    #region Properties
    public Book Book
    {
        get
        {
            return _book;
        }
        set
        {
            _book = value;
        }
    }

    public string WriterName
    {
        get { return Book.WriterName; }
        set
        {
            if (Book.WriterName != value)
            {
                Book.WriterName = value;
                RaisePropertyChanged("WriterName");
            }
        }
    }
    #endregion

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

    #region Methods

    private void RaisePropertyChanged(string propertyName)
    {
        // take a copy to prevent thread issues
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    #endregion
}

You also have to use Binding in the View to bind the property of the ViewModel to the control of the content like follow:

<TextBox Content="{Binding WriterName}" />

Linking ViewModel and View both ways makes no sense. ViewModel should implement INotifyPropertyChanged interface. Because WPF Framework allows control to monitor PropertyChanged event. When the Property value changes, the PropertyChanged event is triggered, meaning control automatically gets the latest value. Conversely, when the control’s value changes, for example, when the TextBox is triggered, the OnTextChanged event is triggered, so that the value of the corresponding property in the ViewModel is equalized.

MVP & MVVM

Unity3D is different from Silverlight/WPF. Unity3D does not provide a Data Binding mechanism and View syntax like XAML. So how do we implement MVVM in this case? In the Asp.net Form era, there was no Asp.net MVC and MVP was often used to separate the UI and presentation layer. The drawing below shows the MVP pattern.

Mvp UML

The main idea of MVP is to abstract the UI by defining a View. MVP doesn’t care about the source of the data, in other words, it doesn’t care about performing the Business Logic that will happen when a button is clicked. MVP only cares about UI iterations. This is a typical prioritization.
The thing is, this is what I want to talk about. As long as Unity3D does not provide a Data Binding mechanism, we can reference MVP’s philosophy.
The UI is abstracted with a View to which it is attached, and Component-oriented development is transformed into View-oriented development. Each View is controlled by an independent ViewModel. The diagram below illustrates this.

Phone UI

Unity3D can implement Data Binding, Conveter, Command etc similar to abstract View in WPF as it does not have XAML and Data Binding technology.
It should be noted here: For Data Binding in MVP, the View object is sent to the Presenter, while in MVVM the change of data is updated with a triggered event.

Implementing and Designing MVVM in Unity3D

Let’s first take a look at how to write a ViewModel in WPF. The ViewModel provides the data the View needs and inherits the INotifyPropertyChanged interface. When the data changes, the PropertyChanged event is triggered and the control’s value is updated by the event’s response method.
Once we understand this, we can think about how to implement this in Unity3D. Let’s say we’re making a registration form… First we need to define a View that abstracts the UI elements. So which UI elements are we going to abstract? Let’s create a registration page for this example. This requires InputField, Text, Slider, Toggle and Button elements.

public class RegisterView
{
    public InputField BookInputField;
    public Text BookText;//Label

    public InputField BookWriterInputField;
    public Text BookWriterText;//Label
    
    public InputField PublisherInputField;
    public Text PublisherText;//Label
    
    public Slider RateBookSlider;
    public Text RateBookText;//Label
    
    public Toggle LiveToggle;
    public Button SaveButton;
    public Button DraftButton;
}

As you can see we have created a simple View for the book registration. Now we have to define a ViewModel that manages the View to provide the properties and methods in the required form.
It is useful to say; Properties in ViewModel are not private. It should have a method of notifying its members when the data changes. So, how to notify members? Of course, this mechanism is event in C# language. Then let’s name this property as BindableProperty.

public class BindableProperty<T>
{
    public delegate void ValueChangedHandler(T oldValue, T newValue);

    public ValueChangedHandler OnValueChanged;

    private T _value;
    public T Value
    {
        get
        {
            return _value;
        }
        set
        {
            if (!object.Equals(_value, value))
            {
                T old = _value;
                _value = value;
                ValueChanged(old, _value);
            }
        }
    }

    private void ValueChanged(T oldValue, T newValue)
    {
        if (OnValueChanged != null)
        {
            OnValueChanged(oldValue, newValue);
        }
    }

    public override string ToString()
    {
        return (Value != null ? Value.ToString() : "null");
    }
}

Next, let’s define a ViewModel that contains the Properties and methods.

public class RegisterViewModel : ViewModel
{
    public BindableProperty<string> Name = new BindableProperty<string>();
    public BindableProperty<string> Writer = new BindableProperty<string>();
    public BindableProperty<int> Publisher = new BindableProperty<int>();
    public BindableProperty<float> Rate = new BindableProperty<float>();
    public BindableProperty<State> State = new BindableProperty<State>();
}

Together with the View and the ViewModel we have to find solution about two more points.

  • How to specify a ViewModel for a View.
  • How do we subscribe to the OnValueChanged event, which is triggered to update the View data when the value of a property in the ViewModel changes.

Based on the above two points, we can define a generic View. We smell its name UnityUIView.

public interface IView
{
    ViewModel BindingContext { get; set; }
}

public class UnityUIView:MonoBehaviour,IView
{
    public readonly BindableProperty<ViewModel> ViewModelProperty = new BindableProperty<ViewModel>();
    public ViewModel BindingContext
    {
        get { return ViewModelProperty.Value; }
        set { ViewModelProperty.Value = value; }
    }
    protected virtual void OnBindingContextChanged(ViewModel oldViewModel, ViewModel newViewModel)
    {
    }
    public UnityUIView()
    {
        this.ViewModelProperty.OnValueChanged += OnBindingContextChanged;
    }
}
  • As seen in the code above, BindingContext is provided as a context similar to DataContext in WPF. BindingContext property is defined as BindableProperty, not a simple property. When a ViewModel object is specified for a View’s BindingContext, the OnValueChanged event will be triggered as soon as the object is created.
  • We can listen for events in OnBindingContextChanged, which is the response function in the ViewModel, to update the data.

We are editing our View Class to derive from the main View class we defined earlier.

public class RegisterView:UnityUIView
{
   //Daha önceki kodlar. . .

   public RegisterViewModel ViewModel { get { return (RegisterViewModel)BindingContext; } }

   protected override void OnBindingContextChanged(ViewModel oldViewModel, ViewModel newViewModel)
    {

        base.OnBindingContextChanged(oldViewModel, newViewModel);

        RegisterViewModel oldVm = oldViewModel as RegisterViewModel;
        if (oldVm != null)
        {
            oldVm.Name.OnValueChanged -= BookValueChanged;
            //. . .
        }
        if (ViewModel!=null)
        {
            ViewModel.Name.OnValueChanged += BookValueChanged;
            //. . .
        }
        UpdateControls();
    }

    private void BookValueChanged(string oldvalue, string newvalue)
    {
        BookText.text = newvalue.ToString();
    }
}
public RegisterView bookRegisterView;
void Start()
{
    //Binding Context
    bookRegisterView.BindingContext = new RegisterViewModel();
}

Thanks to the overridden OnBindingContextChanged method, when a value changes in the ViewModel, the triggered event will be captured here and the final value will be transferred to the UI. Likewise, the OnTextBoxValueChanged event response method can be added to the View so that the value in the UI is synchronized in the ViewModel. I leave it to my readers for now. Here’s how we use what we’ve done so far.