This Unity package provides a foundational implementation of the Scriptable Object Architecture pattern, heavily inspired by Ryan Hipple's talk "Unite Austin 2017 - Game Architecture with Scriptable Objects" and similar concepts promoting decoupled and modular game development.
The core idea is to use Scriptable Objects not just for data storage, but as communication channels and shared state containers, significantly reducing direct dependencies between different parts of your game.
Inspiration:
- Unite Austin 2017 - Game Architecture with Scriptable Objects (Ryan Hipple)
- Unity Scriptable Object Game Events (Similar Concepts)
This package currently provides:
- Variables: Scriptable Objects representing shared primitive data types (Bool, Int, Float, String).
- References: A helper class allowing components to flexibly reference either a constant value or a Variable asset directly in the Inspector.
- Automatic Runtime Resets: Ensures Variables reset to their initial values when entering Play mode or before a scene loads.
- Build Preprocessor Resets: Ensures Variables are reset to their initial values before a build is made.
- Custom Editor Drawers: Enhanced Inspector UI for References.
- Decoupling: Reduce hard references between MonoBehaviours. Components interact indirectly through Scriptable Object assets.
- Modularity: Systems can be added or removed more easily.
- Designer Friendly: Allows designers to tweak values and hook up logic via the Inspector without code changes.
- Runtime Flexibility: Variables can be observed for changes, allowing reactive UI or game logic.
- Predictable State: Automatic reset mechanisms ensure consistent starting states during development and in builds.
The project now includes comprehensive unit tests for:
- Variables: Ensuring correct initialization, event triggering, and reset behavior.
- References: Validating that references correctly switch between constant values and variable asset values.
These tests are implemented using Unity's built-in test framework and NUnit, ensuring robustness and reliability of the Scriptable Object Architecture.
Using Unity Package Manager (Recommended)
- Open your Unity project.
- Go to
Window>Package Manager. - Click the
+button in the top-left corner. - Select
Add package from git URL.... - Enter the repository URL:
https://github.com/lfeq/Scriptable-Objects-Architecture.git - Click
Add. Unity will download and import the package.
Variables are Scriptable Object assets that hold a single value.
- In the Unity Editor's
Projectwindow, right-click or use theAssetsmenu. - Navigate to
Create>Scriptable Objects Architecture>Variables. - Choose the desired type (
Bool Value,Float Value,Int Value,String Value). - Name the newly created asset (e.g.,
PlayerHealth,Score,IsGameOver). - Select the asset and configure its
Initial Valuein the Inspector. This value will be used at the start and when the asset is reset.
Reference the Variable asset directly in your MonoBehaviours.
using UnityEngine;
using Scriptable_Objects_Architecture.Runtime.Variables; // Import the namespace
public class PlayerStats : MonoBehaviour
{
public IntVariable scoreVariable; // Assign your "Score" asset in the Inspector
public FloatVariable healthVariable; // Assign your "PlayerHealth" asset
void Start()
{
// Initialize UI or game state based on initial values
UpdateScoreUI(scoreVariable.Value);
UpdateHealthUI(healthVariable.Value);
// Subscribe to changes (optional)
scoreVariable.OnValueChange += UpdateScoreUI;
healthVariable.OnValueChange += UpdateHealthUI;
}
void OnDestroy()
{
// Unsubscribe to prevent memory leaks
scoreVariable.OnValueChange -= UpdateScoreUI;
healthVariable.OnValueChange -= UpdateHealthUI;
}
public void AddScore(int amount)
{
scoreVariable.Value += amount; // Setting the Value triggers OnValueChange
}
public void TakeDamage(float amount)
{
healthVariable.Value -= amount;
}
void UpdateScoreUI(int newScore)
{
Debug.Log("Score Updated: " + newScore);
// Update your score Text UI element here
}
void UpdateHealthUI(float newHealth)
{
Debug.Log("Health Updated: " + newHealth);
// Update your health bar UI element here
}
}References provide flexibility in the Inspector. You can choose whether a field uses a shared Variable asset or a
simple constant value.
using UnityEngine;
using Scriptable_Objects_Architecture.Runtime.References; // Import the namespace
public class EnemyController : MonoBehaviour
{
// This can be set to a constant OR link to a FloatVariable in the Inspector
public FloatReference moveSpeed;
// This will likely always be linked to the PlayerHealth Variable asset
public FloatReference playerHealthTarget;
void Update()
{
// Access the value transparently, regardless of constant or variable
transform.Translate(Vector3.forward * moveSpeed.Value * Time.deltaTime);
if (Input.GetKeyDown(KeyCode.Space)) // Example interaction
{
// Can modify the referenced Variable if not using a constant
if (!playerHealthTarget.UseConstant && playerHealthTarget.Variable != null)
{
playerHealthTarget.Value -= 10; // Damage the player (example)
Debug.Log($"Damaged player via Reference. Current Player Health: {playerHealthTarget.Value}");
}
else
{
Debug.Log($"Player Health Target is constant or null: {playerHealthTarget.Value}");
}
}
}
}In the Inspector, FloatReference (and other Reference types) will show a dropdown:
- Use Constant: Allows you to enter a fixed value directly (e.g.,
moveSpeed = 5.0f). - Use Variable: Shows an object field where you can drag & drop a corresponding
Variableasset (e.g., assign yourPlayerHealthFloatVariable asset toplayerHealthTarget).
RuntimeScriptableObject: The base class for Variables. It automatically registers instances and handles resetting them to theirinitialValueviaRuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad). This ensures a clean state every time you press Play in the editor.ResetRuntimeScriptableObjectsBuildPreprocessor: An editor script that hooks into the build process (IPreprocessBuildWithReport). Before a build is made, it finds allRuntimeScriptableObjectassets in the project and calls theirOnReset()method, ensuring the built game starts with the intended initial values.OnValueChangeEvent: Each Variable (BoolVariable,FloatVariable, etc.) exposes aUnityAction<T>event. Scripts can subscribe to this event to react whenever the Variable'sValueproperty is changed.- References (
BoolReference,FloatReference, etc.): These are simple C# classes marked[Serializable]. They contain fields forUseConstant,ConstantValue, and theVariableasset link. The customPropertyDrawerclasses (BoolReferenceDrawer, etc.) create the user-friendly Inspector UI. Implicit operators allow you to use a Reference directly where its underlying type is expected (e.g.,float speed = moveSpeedReference;).
This package is intended as a base. Future additions planned include:
- Game Events: Implement Scriptable Object based events for further decoupling (e.g.,
GameEventasset,GameEventListenercomponent). - Enum Variables/References: Add support for shared Enum states using the same Variable/Reference pattern.
Contributions are welcome! Please fork the repository and submit a pull request with your enhancements or open issues.
This project is licensed under the MIT License - see the LICENSE.md file for details.
- Ryan Hipple for the foundational talk on Scriptable Object Architecture.
- The Unity community for discussions and variations on these patterns.

