2 Awesome Uses for UniRx
Working smarter, not harder with reactive code
If you break it down, a game is really just a series of events that is executed in a certain order based on sets of conditions and input from players — and there are multiple ways we can manage this chaos using code.
We could just implement these conditions and events simply in an imperative way. Imperative code consists of statements that change the game, describing how it should operate. Below is an example of what this looks like:
As new features and requirements are added, this can get complex over time and the implicit order of events may not be as clear to new developers joining the project.
At Mighty Bear, we prefer to write things in a declarative manner. Declarative code expresses logic without describing control flow. Rather, it describes what the game needs to accomplish — as a result, it is easier to understand the explicit order from declarative code and therefore easier to expand on later if required.
To help achieve this, we use the Unity plugin UniRx¹ a lot! Here are some of the ways it helps support our development process:
Reactive Properties & Commands for UI
We use the established Model-View-ViewModel pattern² in our UI framework. The motivation here is that this approach reduces coupling between presentation logic and application logic, improving overall testability. The UI framework is loosely based on reactiveui.net³ so that we can compose our Model-View-ViewModel UI in a declarative manner.
What follows is a stripped down example of a UI view that shows information on a character in a game. First, some UI framework setup:
There are two possible commands with this view: navigate back or show character progress. In our example game, you can only progress a character after giving consent to go online. You might notice that there isn’t any Unity UI code in the ViewModel or the ViewModelProvider.
We can test the requirement to have consent to go online before being able to see character progress by running a mock offline session, triggering the character progress command and checking if the “Go Online” request is triggered.
Setting these reactive patterns up makes retrieving information use more boilerplate, but it helps by making it easier both to test the UI and to keep UI views up to date with changing data.
Automated tests can help save a lot of time usually spent debugging. You might think that values in a data stream emitted asynchronously over time might not be testable by a discrete test. However, this TestObserver makes it easy for us to test observables in Unity’s Edit Mode Tests:
Here is a simple example of how the TestObserver can be used in a test:
You can also simulate the passing of time using custom schedulers. This PeriodicTestScheduler gives the option to advance time forward in a test. The code for TestScheduler⁴ and VirtualTestScheduler⁵ can be found in the footnotes to this article!
We want to continue to be able to support our games beyond launch with fun new features and content for a long time after launch! Writing maintainable code allows us to make changes easier; writing testable code makes sure we don’t break existing functionalities as the game grows in complexity.
If you are interested to find out more about our game development Engineering practices, our Senior Engineer, Khalid, has written about how automated tests can save your code.
If you are more interested in the simple spawning code at the start of the article, our Senior Engineer, Pauline, recently contributed the Spawn Doctor series of articles which describes our tooling around setting up spawn points for our games.
If you have any additional questions or comments on UniRx and its uses, fire away in the comments below!