Skip to content

mab09/Unreal-MVVM-Pattern-Examples

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Unreal MVVM Pattern Examples

This is a project where I tested out and learned how to use the UMG ViewModel Plugin, through this I was able to make a MVVM-based UI architecture for one of the larger projects I was working on which I will touch upon at the end.

Important

ViewModels in Unreal is a beta feature with not alot of documentation and examples out there showing the different ways you can set it up.
In this project I have tried to tackle one of the areas that I had the most trouble with initially which was how to link your widgets/views to your viewmodels.
Hopefully this can help whoever is trying to setup their UI with MVVM and show the possible approaches they can take.

That said, there are some great resources other than Unreal's own documentation that helped me understand the Hows and the Whys when implementing this. I won't be going into the basics of what MVVM is and its advantages over traditional MVC, so I recommend going through the links below.

Project Information

  • This project uses the ThirdPerson template and implements some basic UI elements:
    • Health Bar: Showing health from a AC_Vitals component on the character (A debug key 'H' that reduces the health to show the UI updating)
    • Stamina Bar: Showing stamina from AC_Vitals (The player can sprint while pressing 'Shift' causing the stamina to deplete and then regenerate when released)
    • Pause Menu: A pause menu with options to resume or quit the game (press 'esc')
    • Map: A mini map and a full screen map that can be cycled through (press 'M')
  • The main assets to look at are in Content/ThirdPerson/Blueprints and Content/UI
  • This project is all blueprints, since the goal is to show how to link a viewmodel to a view/widget which are usually blueprints. You can have the ViewModels be C++ classes instead, how they are accessed by the widgets remains the same.
  • Made in Unreal Engine 5.6.1

Note

This project is not an example of best UI/UMG practices or what an ideal architecture should be, the goal is to showcase the different ways viewmodels can be setup to work with your widgets.

The Model, The View and The ViewModel

Here is a simple diagram showing how the data flows between the different UI elements in this project as per the MVVM design.

Who Creates the ViewModels and How it Connects to the Widgets?

This is the hard part of setting up the MVVM UI in your project, not because it is complicated but because there are multiple ways you can do it (called the "Creation Type" when adding a viewmodel in the widget), and each of these require different setup.


For the most part in this project, all the widgets and the viewmodels are created and initialized in the HUD_MVVM (Check the CreateWidgets() and InitializeVMs() functions), how it all comes together depends on the creation type.

Tip

The HUD in my opinion is a good place to create and initialize these UI related objects, another way is to have something like a UI manager component on the player controller or player state.

Creation Type 1: Manual

The widget initializes with the Viewmodel as null, and you need to manually create an instance and assign it.

This is the most straightforward way to link your viewmodel to a widget. There is no active example of this one in the project but the screenshots below should be enough to show how to implement.


Since this creation type always creates a public setter for the viewmodel in the widget therefore the reference to the viewmodel can be passed in at the time of widget creation. Just make sure your viewmodel is created and initialized before widget creation.

Tip

Since the widget links to a viewmodel before Construct() it is recommended that your viewmodels are created and ideally initialized before the respective widget is created.

Creation Type 2: Create Instance

The widget automatically creates its own instance of the Viewmodel.

The Health Bar UI is implemented through this type.
Since the ViewModel is created locally in the widget we must also initialize it here with the model (AC_Vitals)

Note

This is a pretty easy way to setup the viewmodel but it is questionable with respect to MVVM decoupling since the View/Widget itself has a Model reference

Creation Type 3: Global ViewModel Collection

Refers to a globally-available Viewmodel that can be used by any widget in your project. Requires a Global Viewmodel Identifier.

The Map UI is implemented through this type.
In this method, after initialization, the viewmodel is stored in a global collection of viewmodels with a unique identifier. This identifier is later used by the widget looking to find this viewmodel.



Tip

Through this method, your widget blueprint is completely free of any model related information, allowing a highly decoupled architecture.
In a larger project this might become harder to maintain considering the number of viewmodels that might exist, in that case, this method can be used for parent viewmodels that encapsulate a bunch of other small ones, which in turn may use a different creation type like 'Resolver'

Creation Type 4: Property Path

At initialization, execute a function to find the Viewmodel. The Viewmodel Property Path uses member names separated by periods. For example: GetPlayerController.Vehicle.ViewModel Property paths are always relative to the widget.

The Pause Menu UI is implemented through this type.
In this method, the viewmodel may be initialized anywhere as long as the widget can find its reference through a chain of functions/properties. Here we initialize the viewmodel in the HUD and then the widget finds it through a local helper function GetViewModel()





Tip

  • This function must be pure and const
  • We need not have the viewmodel finding logic in the widget, it could be in any blueprint as long as the widget has access to it. We could techinically use the path "GetOwningPlayer.GetHUD.pauseMenuVm" but we can't cast to our custom HUD in the property path and this has to be a design time resolution.

Creation Type 5: Resolver

The Stamina Bar UI is implemented through this type.
In this method, each ViewModel comes with a ViewModel Resolver (VMR), The resolver's sole purpose is to locate the ViewModel.
Here the stamina ViewModel is intialized in the player HUD and then the resolver finds that viewmodel using a blueprint interface implemented on the HUD.




Closing Thoughts

BP_MVVM_Prototype.mp4

As I mentioned before, this project looks at a very specific aspect of setting up MVVM in unreal, which seemed to have been a common point of confusion and difficulty (atleast at the time I made this), So I hope this project covers that aspect extensively enough to clear the confusion.
There are other aspects like how to setup the bindings which I won't be writing about here but you can go through the widgets to have a look.

Overall here are some of the things you might want to consider when setting up your MVVM architecture:

  • The ViewModel to Widget relationship can be one-to-many or many-to-one.
    • Many-to-One is useful for stuff like the StatusBar widget where one widget needs information of different things like the Health and Stamina.
    • One-to-Many is useful if one piece of information might be needed by different widgets like health is needed by the health bar and maybe also a screen effect UI.
  • Property bindings can be One Time to Widget, One Way To Widget, One Way to ViewModel or Two Way , out of which I was not able to fully utilize Two Way in this project, that should be useful for highly interactive widgets like an inventory menu.
  • Property bindings have in-built conversion functions, so you can do basic math, string conversion and much more in the binding itself without having to write custom logic for it.
  • I believe that the 'Global ViewModel Collection' and 'Resolver' creation types give the maximum decoupling, a combination of the two might be the best approach. But note that achieving the maximum decoupling might be overkill for small projects and smaller teams. In my opinion, this is mostly beneficial if you have different people working on the UI functionality vs the UI visuals.
  • An MVVM style architecture itself may be overkill for a project. While it is possible to base your entire UI on MVVM, it might be more beneficial to use it in certain UI elements, while sticking to traditional ways in others. Remember, this is still a beta feature so basing everything around it may restrict the usage of other valueable features like CommonUI.

Example of an Advanced Architecture

Here is one of early iterations of the UI architecture I implemented in a larger project that I was working on. We made quite a few changes later on but this is the first working example I made of a fully MVVM based UI architecture in Unreal.
This involved nested viewmodels where the master viewmodel and the viewmodels for different UI layers were setup using the global viewmodel collection while the viewmodels for the individual UI elements were setup using resolvers

Advanced MVVM Architecture

About

A project with examples of how to use Unreal's ViewModel plugin to implement simple UI elements

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published