Unity Scriptable Objects for shared state

Unity Scriptable Objects for shared state

Unity has a rarely used feature known as Scriptable Objects. These are essentially very light weight components, allowing one to edit them in the editor, set pre-defined states, and place them in components as required. Instead of inheriting from MonoBehavior, inherit from ScriptableObjects. ScriptableObjects do not have all of the same states that Monobehaviors do, they can be validated, but do not have the Update/Start/Etc methods required by MonoBehavior. Materials are actually Scriptable Objects, along with several other components in the Unity Architecture.

One of the simplest uses of Scriptable Objects is that of a shared state. Let’s say that you have a score that many different objects need to know in the game. There are basically 3 ways that you can do that: use a static object or Singleton, use a single component that keeps the state, or use scriptable objects.

Static Interfaces are not well supported in Unity. If one changes the code, Unity cannot hot swap the state. They can change the entire system, but it can sometimes be unclear what in the system can change things. The also suffer from a difficulty in testing individual components. For instance, what happens if you have a score board, and you want to test that it is working right. With a static object, there are no really ways you can directly test this, at best you can add a new component that reads the static and changes the values, but that can be unclean.

Using a single component and sharing it out has similar difficulties as a singleton, although it can work a bit better. The biggest difference is it is more expensive to do a search for a type of component then it is to reference a singleton interface.

Using a Scriptable Object can be the cleanest way to do this. A single ScriptableObject can be created, and placed in to each object that cares about the state. Prefabs can be used for these items, and can be easily unit tested by simply plugging in a specific state in to a custom Unit Test ScriptableObject. Such a script might look like this:

Given this, we can then create a shared integer, and then attach it to any component that needs to either change the score, or use it in some way. Let’s get back to that score display. We will create a simple script to display any Shared Integer, shown below.

We then create an shared integer, which I will name Score. Right click in your Assets package, just as you would do to create a material.

Let’s then drag the new score in to the score item in the inspector.

Once this is in, we have a score that can be controlled via the inspector. Let’s say the max score is 999,999, and we want to make sure that displays correctly. We can simply set it in the ScriptableObject, and let it automatically update. We can then determine if the score box is large enough.

 

If you have an object that can change the score, or uses it, just drop in the same scriptable object in to any object you desire, and you can change it by changing the score.val integer.

This is how one can use the state in my Screensaver Manager Asset Pack, which was discussed in a previous blog post. The manager sets scriptable objects for the screensaver state variables, which any object can use to determine if we are operating in screensaver or settings mode. I use this state to store many of the settings in my simulation of Elon Musk’s Tesla Roadster traveling in space.

More complex items can be done as well. Some projects I have worked on have one or more common state ScriptableObjects that can be used to change all of the game’s basic state. It can be used to store images to use a certain type of image display, and many other things. For more information, I recommend watching the following video:

Ben Pearson is the developer of Colonial Sea Trader, a Unity game that he is producing under the company name Old Ham Media, as well as the website Where is Roadster?. Ben learned Unity development with much assistance with gamedev.tv. You can subscribe to updates at his Google Group, like him on Facebook, check his videos out on YouTube or watch him stream game development on Twitch. He has been a programmer since a young age, although only recently is learning programming with game engines. He has completed the the Complete Unity Developer Course and Pass the Unity Certification courses, and is working through the Complete Blender Developer Course, RPG Core Combat Creator, and Unity Game Physics courses. He is hoping to soon start Unreal Courses soon. Follow him on Twitter @KD7UIY.

 

4 Comments

  1. Brian K. Trotter says:

    Great tutorial. I have a quick question… after further reading on the subject (Unity’s “fantastic” documentation, mostly), it was made clear that the values saved in the Assets will not persist from game to game (i.e. don’t rely on it to save the state), but what about from scene to scene…
    Example: I set a SharedInteger score, and when I load the next scene, it’s already carried over the score from the last scene…
    (I’m hoping this will be a better method of persisting data scene to scene than rather sloppy singletons.

  2. Brian K Trotter says:

    Aha, watching the video above, it indeed DOES persist from scene to scene “very useful, for example, for player HP” (around 28:00 or so).
    I’m going to tear apart my Project Boost game (my version has powerups to improve thrust and turnspeed, a score, and a number of “lives”) and refactor it with ScriptableObjects… I started last night before I watched the video, and I had it set up with a full data structure of all of the possible variables… but I realized after watching the video that ONE variable per ScriptableObject instance is better, so that each object is only dealing with the things it’s interested in.
    Thanks for this post, Ben!

Leave a Reply

Your email address will not be published. Required fields are marked *