Using Facebook’s Flux architectural style for game development in Unity 3D

I decided to work again in an old game project. The game is named “Shotem” and the mechanic is quite simple: coins start falling from the sky and you need to “shot them” (got it?). Tap on a coin to shoot it, you gain score if you hit and you lose score if you miss the shot or if a coin falls off the screen (in case you didn’t tap it). You have seven bullets to use, each shot (finger tap) consumes a bullet and you need to shake the mobile device to reload your bullets.

The game is still a work in progress, but I added the following video in order to show that it actually works in real life:

I’ll write the history of the development process of this game. It started in a very naive form, then I converted it to Flux style and left it alone for three months. Now I’m coming back to it and had a pleasant surprise to read the code after forgetting about it. I think Flux style made it easy to continue the project were I left it. This post is 100% opinion based. Please, take a seat.

The first version I coded was very straightforward. Each coin was a GameObject with rendering and game behavior logic. All game state were inside a Singleton and two other objects were responsible for creating coins and handling user taps. Not organized at all.

I was interested in how to code the “reload by shaking” feature. As soon as I got it working, I lost my interest in the game mechanic itself. This happened quite fast actually, Unity 3D has a very nice class to use for dealing with the mobile device gyroscope: Input.gyro.

At the same time, I got in touch with Facebook’s Flux architectural style. I tried it in some web projects and had a nice feeling about it. It seemed easier to write, maintain and understand code written in Flux style than the code written in MVC fashion. So, I decided to rewrite the code of Shotem in Flux style.

At first, it didn’t seem to fit into Unity’s way of doing things. But I worked it out and coded it anyway. It worked fine, then I left the code alone.

Three months later I’m coming back to this project and I didn’t remember that I tested Flux style in it. At first I thought: “oh no, I’ll need to delete this project and start it over, why did I do it?” Then I did read the code and look at the project structure: I was amazed at how easy it was to get things back on track, I were able to get productive again in less than five minutes! What a pleasant surprise!

Now, I feel that I should share this with the rest of the world. I appreciate feedback, so if you feel that using Flux style in game development is madness or if you feel like giving it a try too, leave a comment.

From now on, I’ll use some terms from Flux, e.g. Store, Dispatcher, Action. I won’t explain what are their meanings, so feel free to take a little detour and read the official Facebook’s Flux website to get a summary of it.

I created a Dispatcher class to register the Stores, dispatch actions, track dependencies between Stores and ensure that the Actions do not cascade. The Dispatcher, in it’s full glory, is here:

using System;
using System.Collections.Generic;

using Shotem.Actions;

namespace Shotem.Flux {

    public class Dispatcher {
     
        private Dictionary<int, Store> stores;
        private HashSet<int> calledStores;
        private ShotemAction handlingAction;
        private int lastKey;
        private bool inDispatch;

        public Dispatcher() {
            stores = new Dictionary<int, Store>();
            calledStores = new HashSet<int>();
            handlingAction = null;
            lastKey = 0;
            inDispatch = false;
        }

        public int Register(Store store) {
            int key = lastKey + 1;
            stores.Add(key, store);
            lastKey = key;
            return key;
        }

        public void Dispatch(ShotemAction action) {
            if (inDispatch) {
                throw new Exception("double dispatch");
            }

            inDispatch = true;

            calledStores.Clear();
            handlingAction = action;
            foreach (int k in stores.Keys) {
                if (calledStores.Contains(k)) {
                    continue;
                }
                calledStores.Add(k);
                stores[k].Handle(action);
            }

            inDispatch = false;
        }

        public void WaitFor(int registrationKey) {
            if (!inDispatch) {
                throw new Exception("wait out of dispatch");
            }

            if (calledStores.Contains(registrationKey)) {
                return;
            }
            calledStores.Add(registrationKey);
            stores[registrationKey].Handle(handlingAction);
        }

    }

}

The core game mechanic is encoded in three Stores: CoinStore, handling all coins in the game; GunStore, handling player’s ammunition; ScoreStore, tracking user score.

Player input may dispatch two Actions in the game: ShootAction and ReloadGunAction. To handle the physics of the game (falling coins), one Action is sent every FixedUpdate: PhysicsUpdateAction.

The Stores handle each action according to simple rules:

PhysicsUpdateAction is handled by CoinStore by spawning new coins, moving the coins in the screen and removing coins that are off the screen. ScoreStore waits for the CoinStore to finish and then updates the score by decrementing the amount of coins that were removed from CoinStore, because they have fallen off the screen. GunStore ignores this action.

ShootAction is handled by GunStore by decrementing player’s ammunition. CoinStore waits for the GunStore and ignores the action if no ammunition was used, otherwise it checks for overlapping coins in the shot area and removes them. ScoreStore waits for GunStore and CoinStore then increases the score by each coin removed or decreases the score in case an ammunition was used and no coin was removed.

ReloadGunAction is handled by GunStore to reset the player’s ammunition to seven. CoinStore and ScoreStore ignores this action.

That’s the whole game logic. Each Store is observable, the Views register themselves to receive update notifications. When the Store handles an Action that changes its state, it triggers a notification. I’ve created an abstract class Store to handle this common behavior:

using Shotem.Actions;

namespace Shotem.Flux {

    public abstract class Store {

        public delegate void StoreObserver();

        public event StoreObserver Observers;

        protected void Notify() {
            if (Observers != null) {
                Observers();
            }
        }

        public abstract void Handle(ShotemAction action);

    }

}

The listing below is the code of ScoreStore, the other Stores follow the same style:

using Shotem.Flux;
using Shotem.Actions;

namespace Shotem.Stores {

    public class ScoreStore : Store {

        private Dispatcher dispatcher;
        private CoinStore coinStore;
        private GunStore gunStore;
        private int lastKnownCoinsCount;
        private int lastKnownCartridgesLeft;

        public ScoreStore(CoinStore coinStore, GunStore gunStore) {
            Score = 0;
            this.coinStore = coinStore;
            this.gunStore = gunStore;
            lastKnownCoinsCount = coinStore.Coins.Count;
            lastKnownCartridgesLeft = gunStore.CartridgesLeft;
        }

        public void RegisterAt(Dispatcher dispatcher) {
            this.dispatcher = dispatcher;
            dispatcher.Register(this);
        }

        public int Score { 
            get; 
            private set; 
        }

        public override void Handle(ShotemAction action) {
            dispatcher.WaitFor(coinStore.DispatcherIndex);
            dispatcher.WaitFor(gunStore.DispatcherIndex);
            int coinsDelta = lastKnownCoinsCount - coinStore.Coins.Count;
            int cartridgesDelta = lastKnownCartridgesLeft - gunStore.CartridgesLeft;
            lastKnownCoinsCount = coinStore.Coins.Count;
            lastKnownCartridgesLeft = gunStore.CartridgesLeft;

            switch (action.Type) {
                case ActionType.SHOOT:
                    // Add points for each coin that is destroyed with a shoot
                    // Remove a point if used a cartridge and did not hit a coin
                    if (coinsDelta > 0) {
                        Score += coinsDelta;
                    } else if (coinsDelta == 0 && cartridgesDelta > 0) {
                        Score -= 1;
                    }
                    break;

                case ActionType.PHYSICS_UPDATE:
                    // Remove points for each coin that is destroyed with a physics update
                    if (coinsDelta > 0) {
                        Score -= coinsDelta;
                    }
                    break;

                default:
                    return;
            }

            Notify();
        }

    }

}

Setup code is done in a Singleton GameObject’s Awake method, I called it StoreHub because I use this to get the reference to the Stores from other Views:

using UnityEngine;

using Shotem.Flux;
using Shotem.Stores;
using Shotem.Actions;

namespace Shotem.Views {

    public class StoreHub : MonoBehaviour {

        void Awake() {
            Dispatcher = new Dispatcher();

            GunStore = new GunStore();
            GunStore.RegisterAt(Dispatcher);

            CoinStore = new CoinStore(GunStore);
            CoinStore.RegisterAt(Dispatcher);

            ScoreStore = new ScoreStore(CoinStore, GunStore);
            ScoreStore.RegisterAt(Dispatcher);

            ActionGenerator = new ShotemActionGenerator(Dispatcher, CoinStore, GunStore, ScoreStore);
        }

        public Dispatcher Dispatcher { 
            get; 
            private set; 
        }
        
        public CoinStore CoinStore { 
            get; 
            private set; 
        }
        
        public ScoreStore ScoreStore { 
            get; 
            private set; 
        }
        
        public GunStore GunStore { 
            get; 
            private set; 
        }
        
        public ShotemActionGenerator ActionGenerator { 
            get; 
            private set; 
        }

    }

}

ShotemActionGenerator is a helper class to create Actions and send them to Dispatcher, nothing fancy.

The View classes are also simple, for example, the game may play three different sound effects when the GunStore is updated: when a bullet is shot, when user tries to shot without ammunition left and when the gun is reloaded:

using UnityEngine;

using System.Collections;

using Shotem.Flux;
using Shotem.Stores;

namespace Shotem.Views {

    public class GunSoundsView : MonoBehaviour {

        public AudioClip ShootClip;
        public AudioClip ReloadClip;
        public AudioClip EmptyGunClip;
        private GunStore gunStore;
        private int lastKnownCartridgesLeft;
        private AudioSource audioSource;

        void Start() {
            audioSource = GetComponent<AudioSource>();
            gunStore = FindObjectOfType<StoreHub>().GunStore;
            lastKnownCartridgesLeft = gunStore.CartridgesLeft;
            gunStore.Observers += OnGunStoreUpdate;
        }

        private void OnGunStoreUpdate() {
            int cartridgesLeft = gunStore.CartridgesLeft;
            if (cartridgesLeft == lastKnownCartridgesLeft && cartridgesLeft == 0) {
                audioSource.PlayOneShot(EmptyGunClip);
            } else if (cartridgesLeft < lastKnownCartridgesLeft) {
                audioSource.PlayOneShot(ShootClip);
            } else {
                audioSource.PlayOneShot(ReloadClip);
            }
            lastKnownCartridgesLeft = cartridgesLeft;
        }

    }

}

Another view shows the quantity of ammunition remaining, which are just children GameObjects with Renderers that I enable or disable based on the GunStore state:

using UnityEngine;

using Shotem.Flux;
using Shotem.Stores;

namespace Shotem.Views {

    public class CartridgesLeftView : MonoBehaviour {

        private GunStore gunStore;
        private Renderer[] bullets;

        void Start() {
            bullets = GetComponentsInChildren<Renderer>();
            gunStore = FindObjectOfType<StoreHub>().GunStore;
            gunStore.Observers += OnGunStoreUpdate;
            OnGunStoreUpdate();
        }

        private void OnGunStoreUpdate() {
            int cartridgesLeft = gunStore.CartridgesLeft;
            for (int i = 0; i < cartridgesLeft; ++i) {
            	bullets[i].enabled = true;
            }
            for (int i = cartridgesLeft; i < bullets.Length; ++i) {
            	bullets[i].enabled = false;
            }
        }

    }

}

There is a View to show the score, one to show a tip to remind the player to reload the gun when it’s empty, another to handle the renderers of each coin visible in the screen, etc. The entire game is made in the same solid base, or may I say “solid flux“. Data always flows in a single direction without cascading, there is always an Action that enters the system and triggers something, nothing happens if no Action is dispatched.

I don’t know if this design can scale to a big game project, as the style as it is now depends on it running in a single core. But, none of my games are that intense, so I will try to finish this game with Flux. I guess that using Flux will also make it easier to create automated tests of the game mechanic, as it is self-contained. Maybe I’ll post some follow up thoughts on it.

Below is an overview image of the current flux of the game. I decided to call “Inception” the layer that generates the Actions, Flux do not give it a name.

Thanks for your time,

Advertisements

4 thoughts on “Using Facebook’s Flux architectural style for game development in Unity 3D

  1. I had the same thought when I learned Flux this week. I've got a fairly large game project (over 170 different classes) that has started to become tangled and pondered the benefits Flux could have on its maintainability. It's very exciting to see that at least one other person had the crazy idea to apply this web-based pattern to a Unity game. After all, it's essentially an elaboration on the Command Pattern (GOF), so why not? In fact, it could increase performance since it allows the game to have fewer GameObjects and fewer GameObject find operations as well.

    I do wonder about two things. Firstly, there's the direct references that the Stores have between each other – can this coupling be eliminated? My first thought was that Stores could subscribe to StateChange events, but this would require the same cycle-prevention that the Dispatcher implements for Action handling, and that redundancy would be worse than the coupling. On second thought, Interfaces could nicely decouple the Stores without any additional overhead.

    My second thought was on the action enums and the switches that handle them in the views. It seems like the number of objects being passed around could be greatly reduced if those were replaced with C# Event Delegates. I think the enum/switch system was invented for Flux because function signature checking in JavaScript is a convoluted and expensive thing to do. I'm not exactly sure how this would be carried out without diving in – I'll try to remember to let you know if I ever pull it off!

    Like

  2. Nice to read from you! I should write a follow-up post about this one, as I did find some stuff to be hard to do in Flux.

    My fear about Stores listening to changes of other Stores is that this could create a cascading effect: Store A notifies Store B, Store B updates some internal state and trigger a new notification. This could end up in a cycle. Did I get it right? Unless, as you said, you have a “notification-dispatcher” that prevents this, this seems a bit “overkill” to me, but I would take a try at it to see how the code lays out.

    I don't get your second tought about it. Could you elaborate a bit?

    I think it would be very interesting to replace the enum/switch system by delegates. At first tought, my concern is: how would you implement the “WaitFor” mechanic? The dispatcher would still use a List of listeners and the Events would only be at the stores?

    Thanks for the comment, really appreciate it.

    Like

  3. Right, I was just thinking out loud about Stores listening to Stores. Definitely not viable.

    I'm stumped myself as to how to avoid sending every Action to every Store and running them all through switch statements. It seems like a waste of passing data around, especially if we get over 100 stores. It could turn out to be negligible, but I haven't had the opportunity to profile it on something of that size.

    There's also the issue of action parameters. The original Flux relies on Javascript's lack of signature or type checking. As soon as you're in a strongly typed language, how do you include parameters with actions? It looks like in your code, none of your actions include accompanying data. I handled this in TypeScript by creating a Data Interface that's common to all Actions, but in a larger application Actions would have very different data interfaces.

    I'm very eager to hear more about which things you found hard to do in Flux.

    Like

  4. I do pass data around, I send it as an object and cast it to the right type at each handler. So, the “Action” is always a pair of “ActionType” and “ActionData”, the data part hacing a dynamic type according to which type it has.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s