Unity does not provide much direction on how to transfer game state between scenes. Some community suggestions are singletons, static variables or DontDestroyOnLoad. But these have other effects that you might not want. Here, I will share a pattern inspired by functional programming I call Stateless Scenes. This pattern, inspired by asynchronous APIs, leads to a much cleaner design.

We consider a scene to be: a long running operation that, given its inputs, eventually produces an outcome. We can rephrase that as: a function that accepts a callback (a.k.a. Action) in its argument list:

To actually implement this, we have to make a call to SceneManager.LoadScene and transfer the MySceneParams to a MonoBehaviour within the scene, along with the callback. It’s simpler to include the callback in the Params, so we just transfer a single variable across the LoadScene divide. To transfer this state, we use the lesser evil of a single static variable for a short period of time.

The nice thing is that the caller of the scene now gets to decide what comes afterwards (inversion of control). This makes testing much easier, as the test harness can use the scene in a very different way to how the main game might.

Inversion of Control – Hermetic Scenes

This is the main point of the pattern. The responsibility of the scene is encoded in the Param type signature, and its expressivity is captured in the return type. Furthermore, the scene has to know when it finishes… BUT NOT WHAT COMES NEXT.

It is the caller of the scene that decides which scene to run next, allowing the scenes to be rearranged and upgraded very easily. While it’s natural to think of the main game scene, most scenes are actually smaller in scope (e.g. a level end stats screen). These scenes do not need access to lots of game state and services to function.

Simple levels need simple inputs and outputs. Some levels are complex and require global services like account persistence. It is not necessary these complex services be passed to simple scenes. I have found having a single MainGame that sets up global services, and then passing references to scenes that require these services only when necessary is cleanest. In fact, I try to avoid scenes being exposed to global services in general, and instead do the service calls in the MainGame for value data that is passed around works nicely.

Ergonomic default scene runs

Another side benefit is that if the scene is run directly from the editor, the params will not be overwritten and will keep their values to what is defined in the scene. This lets you test individual scenes without scene switching.

In Corepox I use the variable names Request/Response, but here is me setting the Params in the Unity Editor

I hope you enjoy this pattern, its brought sanity to my game and I hope it will help your’s too!