ScriptableObject architecture series part 2 – fancy variables

Aims:

  • To have ints / floats / strings manage saving & loading their own state.
  • To have ints / floats / strings be reference-able in the inspector
  • To separate data (e.g. currency, health, score) from components (displays, calculators, shopfronts, managers).

Let’s remind ourselves of the problem. You’re storing “coins” as an int and you stick it on a GameManager singleton:

All the other things that need to know how many coins there are will reference it with  GameManager.Instance.coins .

Ok, but now the GameManager also has to persist that data so it starts to look like this:

Suddenly our GameManager is not only managing the game but also looking after data. Also, now your Shop UI or coin display can’t work on its own because it needs the GameManager to know how many coins you have. Things have become coupled (one cannot work without the other). If we made a scene just for the shop, our team would have to remember to drag the GameManager into it as well. That might not sound like a big deal but it quickly gets out of control as more managers are introduced.

Like in real life, managers quickly become a dump for all the leftover tasks.

Since we are striving for clean, decoupled, single-responsibility code – we can do better than this! Let’s start by creating a scriptableobject that just stores an int:

This step on its own already gets rid of the need for a manager & singleton. Once you create an instance of this class through the right click menu, some shop UI can reference and use it directly:

Let’s go a step further and get our ints to look after their own state. There are many options to storing data, but for demo, let’s store this int in Playerprefs.

Alright, now the int’s are managing their own persistence. How about an event notifying listeners of a value change?

Registering a callback to OnChange lets us write a counter as small as this:

Now let’s cache the value from Playerprefs so we’re not going in and out all the time:

And maybe a few Odin buttons for resetting the stored value and testing:

And there we have it. From here, you can add other features like saving to the cloud instead of PlayerPrefs, string formatting, and so on. Most importantly – data and behaviour is separate and scenes are easier to reason about and test.

Happy scripting!

ScriptableObject architecture series – ScriptableSingletons

In an ideal dev environment, we want to be able to drag any object into an empty scene, press play, and be able to test it by itself. Characters, UI, everything.

Turning Monobehaviours into singletons makes that dream unachievable. Countless times playing an empty scene with a simple character spits out a null reference because there’s no Gamecontroller.Instance.StartingHealth.

Inspired by Richard Fine’s GDC talk, a great way around this is to keep all this stuff on a scriptable object.

Here’s a simple GameSettings script, which inherits from our magic ScriptableSingleton class:

On it, we keep normal, basic information we need across our game.

The magic is in the base class:

Here, we are inheriting from ScriptableObject instead of Monobehaviour so we can create a .asset file from the right-click menu. The  where T: ScriptableObject is something we must have when creating a generic class. Info here. In this case, we’re just saying that children inheriting from this class are also ScriptableObjects but this could be anything else e.g. implements an interface.

The getter for the instance variable is not much different to classic Monobehaviour singletons. On line 10, if there’s no instance already, instead of instantiating one, we load an object of type <T> from the Resources folder in our Unity project and on line 12, we’re logging an error to remind ourselves to create one because we still couldn’t find one.

As long as we’ve created a GameSettings.asset in the Resources folder, we can call  GameSettings.Instance.IsDebugMode  even from an empty scene, and values changed during play mode are saved.

Bygglovsritningar
Email: info@bygglovsproffsen.se
Phone: 010-207 88 07
Url:
Olofsgatan 10A, 753 12
Uppsala, UP 75312

Essential Odin attributes part 2- [MinMaxSlider()]

A common thing in game development is getting a random number between a minimum and maximum.

Maybe something has a min and max attack damage, attack range, reaction time.

Or maybe you need to spawn a min and max number of things for a procedural level.

For all of these cases, we need to control the min and max. Which is where our Odin attribute  [MinMaxSlider()]  comes in.

MinMax is a set of two values stored in a Vector2, where x is the min value and y is the max value. So, the variable we declare has to be of type Vector2, and we pass the absolute minimum and maximum for the slider into the attribute as floats. So here, we’re making a slider between 10 and 15 for the damage range:

…which turns into this in the inspector:

And there you go, a neat way of managing ranges in your projects.

Visual debugging in Scene View with Handles.label

Sometimes we need to see info about objects during playtime for debugging.

Lets say, we’re making a destructible environment and we need to debug the health of objects in the scene.

OnDrawGizmos() is a Unity callback, like Update() and FixedUpdate(), but only for the editor’s scene view. It isn’t called all the time, like update or fixedUpdate, but only when something “changes” in the scene.

The Handles class works much like an API for those arrow-style controls, allowing us to manage scaling, positioning, and similar adjustments directly in the scene view. While it’s possible to create custom handle tools, using the .Label() function is especially useful for quick debugging because it lets us display helpful information right on the object. In a website development context, this is similar to adding visual debug labels or overlays in the browser to better understand layout behavior, element positioning, and real-time data during the design process which work on entertainment or dating sites too like the sites where you can find virtual girlfriends like adriannaevesfree online.

And just like that, we have visual debug info in the scene.

Wilshire Smile Studio
Email: contact@ladentist.com
Phone: (323) 336-8478
6200 Wilshire Blvd. #1609
Los Angeles, CA 90048

Essential Odin attributes part 1 – [Button()]

Extending the Unity editor makes it a real delight to work in.

Any component can have its own interface and interactions in the editor. Think of any time when you had to spend time dragging references, resetting values, matching string values, forgetting about an unassigned value, or you just needed the scene to change to reflect some new values you’ve set in the component.

These are all common things we need to do but are manual and error-prone. So lets have a look at what we can do.

Lets say we have a serializable class that holds game state…

…and to save, we write it to PlayerPrefs as a JSON string.

During testing, we might need to reset state, or set it to something specific, like unlocking all levels or weapons or something.

We could write a custom inspector to draw a button and call this reset function, but we want to iterate fast and make more of these small “utility” buttons to do other things. So we can’t afford to be making and maintaining inspectors for everything!

Enter Odin, an asset that turns the Unity inspector up to 11 with a collection of useful attributes. In this case, lets use the  [Button()] attribute. Here’s what it looks like:

With just that one line, we can add control over our gamestate in edit time and play time.

Odin inspector

Done.

We can just as easily add cheats, scene manipulation, level loading, testing, and anything else with the same ease. I’ll be posting more examples of what the package can do, but sometimes the simplest things make it well worth the price tag!

Coroutines can wait for other coroutines

Coroutines let us go from just writing functions to creating complex sequences with delays and timings.

(Note: If you haven’t used coroutines before, here’s a video that nails it.)

The most immediate uses for coroutines are for things like timers and animation sequences.

Stuff like “Count to 3 and explode”.

And this is great. A coroutine is perfect here.

But we can take this further.

What if we wanted a loading sequence which did something like:

“Show the loading screen, prepare the next level and, when it’s ready, hide the loading screen.”

In this example, the loading screen will show and 2 seconds later, when the level is done loading, hide itself.

The magic here is that a coroutine can  yield return  another coroutine, which resumes the original function when it’s done. Well worth keeping in your programming toolbelt!

Word of caution: nesting 3+  yield return ‘s that wait for coroutines to finish can be a pain to skim through and understand, so use it only when it makes sense!