Building a Score Popup out of Scriptable Objects and Events
A peek into Scriptable Object Architecture - Who needs Singletons when you can Store all your Variables and Behaviors in the Files System?
This is post #7 of my journey to release a game on Steam this year. You may check out my original plan here:
I’ve been thoroughly enjoying the Scriptable Object Architecture asset. Maybe a little too much.
Today, I thought I’d make the post about the type of architecture I’ve been using to implement the features of my game.
In specific, I’d like to cover it by making a “tutorial” on how I implemented a new little but important feature to the game: an indicator that shows up whenever a value is increased, on top of the charm that increased it!
What are Scriptable Objects
According to Unity’s official documentation,
A ScriptableObject is a data container that you can use to save large amounts of data, independent of class instances. One of the main use cases for ScriptableObjects is to reduce your project’s memory usage by avoiding copies of values.
The beauty of scriptable objects is that they live inside the files system alongside the rest of your assets, next to things like images or sound files. And you can configure them to store any type of data: numbers, strings, or even references to other scriptable objects!
This makes them perfect for storing data for systems such as dialog systems, loot tables, or statistics for enemies or weapons. Any type of immutable data with no extra logic attached to it, to then feed into
What is Scriptable Object Architecture?
Scriptable Object Architecture dares ask the question: what would happen if you stored mutable data in scriptable objects?
This asset allows you to store variables and collections as scriptable objects living in the files system. Meaning they can be accessed directly by any class who’d want to use them, without any other extra dependencies.
Moreover, it also allows for the creation of custom events, which can be called and listened to by items with references to it.
If you’re interested in looking more into how the general architecture works, you can watch the Unite Austin talk that this asset is based on.
Implementing Score Popups
As mentioned before, to walk through the three main features of this architecture - variables, collections, and events - I’ll show the steps to implement that popup system!
This system is completely decoupled from the charms and the scoring, and can be toggled on, off, and even completely removed without affecting the rest of the code. It’s completely modular. That’s the beauty of scriptable object architecture.
Step 1: The Variables
The game’s core variables are stored as scriptable objects. Specifically, the ones related to scoring are stored as Doubles (in order to accomodate for really large numbers).
In this case, points are stored internally as “PValue”.
I store a separate variable (PDelta) to indicate the change in points whenever its value is increased or decreased. So, if PValue goes from 5 to 10, PDelta will become 5. This will be important for later.
Step 2: The Collections
I use collections for the charm targeting system, the tags (each tag is a collection of all charms with that certain tag), and also the actions. In this case, we care about the actions.
All “action instances” are stored in a collection of “triggered actions”, which contain information on which action was executed, which charm executed it, which round/turn it was executed in…

Most importantly, one of the data we have access to is the world position of the charm that triggered the effect. This will also be useful later.
Step 3: The Events
Events also live as scriptable objects. These can be called by any object, and code can be executed as a consequence of calling the event anywhere from the code.
I set up an easy system to call events depending on changes to a variable.
One of these is an event that gets called whenever points are increased.

Step 4: Putting it All Together
There is an object in the scene in charge of spawning the indicators.
This object contains a reference to the collection of triggered actions and the prefabs (premade game objects) for each indicator.
It contains a public method to spawn an instance of an object at the position the last action was called from (remember, we’re storing the position of the charm alongside the action it executes!).
This method is called externally, through an “event listener”. This listener is listening to the “OnPointsIncreased” event. Whenever it is called, that method will be executed. No coupling or dependencies here, it’s an event being called and then listened to, so both systems can still live independently!

The indicator comes in the form of a “prefab” (a game object). When we spawn the indicator, we “instantiate” a copy of that object at the right position. Afterwards, the indicator does the rest.
The indicator prefab contains text that, when instanced, is automatically formatted to the PDelta value (the difference in points since the last change). Once again, this is completely decoupled from the other systems, even the indicator spawner itself!

Repeat a similar process for Mult and Gold, and you’ve got it! I’m planning on using a similar system to spawn visual effects.
Drawbacks of SO Architecture
Now, this architecture does have a few quirks that need to be worked around.
First of all, you really, really need to think about how you’re handling your data structures. Due to the game being data-driven, it can feel like I’m constantly juggling references to be able to access key data from certain nested classes.
I’m currently carrying a “MasterGameData” object with references to key systems to access them from various places. This isn’t super elegant, but I’m committed to it for now.

Secondly, you need some discipline of cleaning up after each execution. Scriptable Objects, due to being files, keep their changes in the editor, but NOT in the build. So, to replicate how they’d function in a build, extra functionality is added to, for example, reset the correct collections and variables before and after each execution, to prevent strange behavior on future executions.
Lastly, as with all event-based architecture, you need to sequence your calls properly to avoid race conditions. For example, trying to simultaneously print and change a variable after a single event call could have unintended consequences.
Progress Check - What Else Have I Done?
I am still hard at work at figuring out the visual style for the game. I’ve been drawing and photobashing more mockups, trying to find the right feel for the game.
Compared to last time, this is where I am now. The main changes are lightly colored outlines for charms, and a woody background.
I’m particularly proud of landing on the idea of making the background some type of wood. I think this already helps give the game a more cozy aesthetic without taking away the arcade-y abstract feel. I’m at a loss on the UI though… That’s going to be the next step to figure out.
I’ve also been working on getting the game in an actually playable state. Up until now, the mechanics laid out nothing more than a toy. But now I’ve got a start with the scoring and rounds system, with some UI to take a peek at the related variables.
Also, there is now a popup that lets you know what a charm does when you’re hovering it!
Needless to say, all UI here is still temporary. But it feels amazing to actually lay out the systems that communicate the mechanics of the game to the player. (Also, thanks to the scriptable object architecture, these are super modular and completely decoupled from the systems they rely on!)
Lastly, the tech for charm effects themselves is getting more advanced. Charms can now trigger conditionally outside of their usual execution order, move around, destroy themselves, and have their effects improved based on various parameters, all seamlessly. Here’s an out-of-context preview of some new behaviors from my debug testing scene!.
Despite not having any win/lose condition just yet, this is already beginning to feel like a game. I like that.
What’s Next?
I’ve decided that I will first be working on finalizing the core mechanics first. Win/lose conditions are still left to code, but the main behemoth to tackle and design is the shop system. Honestly, the whole shop system and general economy is still really unclear and undefined in terms of how it should work, so that’s something left to design.
Then, I will work on visuals, sounds, UI, and probably even some polish through effects, animations, and shaders. Hopefully, this step will include visually laying out extra information needed to understand what exactly is going on in the game.
I will be doing this alongside some initial “packages” of charms in the game, to test a few synergies and make sure I didn’t miss any big behaviors or functionalities to code.
After this, I should have something that I could call a “vertical slice”. I am hoping to finish this before mid-March, and use it to make an initial announcement trailer and put up a Steam page.
Once the Steam page is up, start working on promoting the game more thoroughly, finishing up the planned content, and implementing the game’s metaprogression.
Sounds like a plan? I hope it does.
Thank you so much for reading this far. Feel free to share any questions or suggestions you may have.
Also, if you enjoyed reading this, I’d appreciate if you subscribe. It’s free, and only requires your email!