Skip to content

Scriptable Architecture

yenmoc edited this page Jul 3, 2023 · 24 revisions

What

Scriptable Objects are an immensely powerful yet often underutilized feature of Unity. Learn how to get the most out of this versatile data structure and build more extensible systems and data patterns. In this talk, Schell Games shares specific examples of how they have used the Scriptable Object for everything from a hierarchical state machine, to an event system, to a method of cross scene communication.

Leverages the power of scriptable objects, which are native objects from the Unity Engine. Scriptable objects are usually used for data storage. However, because they are assets, they can be referenced by other assets and are accessible at runtime and in editor. See GDC talk of Ryan Hipple

It was useful mostly for and while making Casual and Hyper casual game. Because the nature of these games is simple and the power of ScriptableObject is flexibility. For more complex games it can be hindered by the complexity spike due to the amount of scriptables generated and easy to lose control, but this can still be avoided by simply applying apply it to the part of the game where you want to reduce dependencies.

Characteristic

Solve dependencies

Avoid coupling your classes by using a reference to a common Scriptable object as a bridge or by using Scriptable Events. This is particularly true in the Hypercasual market for several reasons:

  • Many features are enabled/disabled through independent AB Tests. Therefore, we want to avoid noisy code and hardcoding our features into the core of our game. Having them “hooked” with an independent architecture makes it easy to add or remove them.
  • Because you make a lot of games quickly, you want to reuse features as much as you can. Therefore, having features as “drag and drop” can be a huge time saver over the long run for things that generally don’t change too much across games

Efficiency

  • Subscribe to what you need and have each class handle itself.
  • Reduce code complexity.
  • Avoid useless managers. Avoid creating new classes for simple behaviors
  • Be able to debug visually and have the game react in real time

Member

Scriptable Variable

A scriptable variable is a scriptable object of a certain type containing a value. Example string variable look like:

image

  • Value: the current value of the variable. Can be changed in inspector, at runtime, by code or in unity events. Changing the value will trigger an event “OnValueChanged” that can be registered to by code.

  • Debug Log Enabled: if true, will log in the console whenever this value is changed.

  • Saved: If true, the value of the variable will be saved to Data when it changes.

  • Default Value: If “Saved” is true, then you can set a default value. This is used the first time you load from Data if there is no save yet.

  • Reset On: When is this variable reset (or loaded, if saved)?

    • Scene Loaded: Whenever a scene is loaded. Ignores Additive scene loading. Use this if you want the variable to be reset if it is only used in a single scene.
    • Application Start: Reset once when the game starts. Useful if you want changes made to the variable to persist across scenes.
  • Is Clamped: Specific to FloatVariable and IntVariable, gives you the ability to clamp it if you need.

  • Reset to initial value which lets you quickly reset the value of the variable to the initial value.

  • Notes: In the Editor, ScriptableVariables automatically reset to their initial value (the value in the inspector before entering play mode) when exiting play mode.

To create a new variable, access via menu Create > Pancake > Scriptable > ScriptableVariables

image

Or via short-cut Alt + 1

image

Or use button create in property to create a new instance of that scriptable variable in the folder (_Root/Scripts/Generated).

image

When you are in play mode, the objects (and their component) that have registered to the OnValueChanged Event of a scriptable variable are display in the inspector.

image

As you see 1 object registered to this variable. Object name is Main Camera and script is Sample

Scriptable List

Lists are useful to avoid need a manager hold that list and we must access to manager to get list (high-coupling).

Screenshot_2 Screenshot_3

  • Reset On : When is this list cleared?
    • Scene Loaded: whenever a scene is loaded. Ignores addtive scene loading
    • Application Start: Reset once when the game starts

In play mode, you can see all the elements that populate the list.

In the Editor, ScriptableLists automatically clear themselves when exiting play mode

You can create Scriptable List via menu Create > Pancake > Scriptable > ScriptableLists

Screenshot_1

Scriptable Event

image

This ScriptableObject-based event system is our solution to create a solid game architecture and make objects communicate with each other, at the same time avoiding the use of the Singleton pattern.

The reasons why we want to avoid using Singletons are multiple. They create rigid connections between different systems in the game, and that makes it so they can't exist separately and they will always depend on each other (i.e. SystemA always requires SystemB in the scene to work, and so on). This is of course hard to maintain and reuse, and makes testing individual systems harder without testing the whole game.

How it works At the base of the system we have a series of ScriptableObjects that we call "Event Channels". They act as channels (like a radio) on which scripts can "broadcast" events. Other scripts can in turn listen to a specific channel, and they would pick that event up, and implement a reaction (callback) to it. The graph at the top of this page visualises this structure.

Both the script that fires the event and the event listener are Monobehaviours. Since we are using the ScriptableObject Event Channel (which is an asset) to connect the 2 systems, those Monobehaviours can live in 2 different scenes in a completely independent way.

For instance, an event can be raised when pressing a button, and broadcasted on a "Button_X_Pressed" Event Channel ScriptableObject. On the other hand, we can have one or more objects listening to this event and react with different functionality: one of them spawns some particles, one plays a sound, one starts a cutscene.

You can create Scriptable Event via menu Create > Pancake > Scriptable > Scriptable Event

image

  • There are two types of scriptable events:

    • Event with out param

    image

    • Event with param (int, bool, float, string, vector2 ...)

    image

Events can be triggered by code, unity actions or the inspector (via the raise button).

Raising events in the inspector can be useful to quickly debug your game.

When you in play mode, you can see all object that registered to that event:

image

Event Listener

To listen to these events when they are fired, you need to attach an Event Listener component (of the same type) to your GameObjects

image

  • Binding

    • UNTIL_DESTROY: Will register in Awake() and unsubscribe OnDestroy()
    • UNTIL_DISABLE: Will register in OnEnable() and unsubscribe OnDisable()
  • Disable after subscribing: If true, will deactivate the GameObject after registering to the event. Useful for UI elements

  • Event Respones: here you can add multiple events and trigger things with unity events when they are fired. You can also register to events directly from code.

Clone this wiki locally