Extend ScriptableObjects with reactive events, runtime reference keeping, editor debugging tools, and clean data separation between edit-time and play-mode. Ships with an inspector panel and a creation wizard.
- Features
- Overview
- Scriptables Panel
- Quick Start
- Pin Feature
- Interfaces
- Install
- Support
- Thanks
- License
- Creation Wizard β step-by-step UI to create new scriptable assets with data type configuration and auto-generated script files
- Scriptables Panel β centralised window to browse, search, filter, and inspect every scriptable asset in the project
- Reactive Events β observable data containers that notify subscribers when values change; emit with zero allocation
- Pin System β keep ScriptableObjects alive across domain reloads with
IPinnable/ReferenceKeeper - Subscriber Tracking β every reactive inspector includes a live observer list showing all current subscribers; no more guessing who is listening to your events
- Description Field β rich text descriptions on every asset, visible in both panel and inspector
- Dual Data System β separate editor-configured data from runtime data with automatic reset on play mode transitions
- Runtime ScriptableObjects β volatile data that resets on every play session; ideal for shared game state
- Settings ScriptableObjects β persistent data that survives play mode; ideal for configuration and constants
- Standard Interfaces β
ISubscribable<T>,ISettable<T>,IEmitable,IResettable,IInitializable<T>,IPinnableβ code against contracts, not concrete types - Custom Inspectors β per-type editors with real-time data visualisation, reset buttons, and printer utilities
- Type-Safe Generics β no more
Database class constraint; freely generic on any serializable type - Zero per-frame allocations β all editor UI uses cached
GUIContent/GUIStylestatics
The package organises ScriptableObjects into four specialised categories:
- Persistent data configured at edit time, available at runtime
- Survives play mode β changes in editor are serialized
- Can be updated via remote config, repositories, or any external means
- Ideal for game configuration, constants, balance parameters
- Volatile data shared across systems during a session
- Automatically resets when exiting play mode
- No persistence β every run starts fresh
- Perfect for game state, temporary variables, inter-system communication
- Observable data that notifies listeners automatically on change
- Play-mode data is isolated from edit-time defaults
- Event-driven architecture for decoupled communication
- Also available as NoParamsReactive (signal-only, no payload)
Open via Tools > Scriptables > Panel. The panel provides:
- Tab navigation: Scriptables, Settings, Runtime, Reactive, ScriptableObject
- Search / filter: real-time filtering by name, type, or category
- Pagination: browse large collections efficiently
- Details card: inspect description, type, path, and metadata of the selected asset
- Quick actions: Open asset, Ping in Project window
The fastest way to create a new scriptable asset:
- Open Tools > Scriptables > Wizard or Create > Scriptables > Wizard
- Choose a category (Settings, Runtime, Reactive, No-Parameter Reactive)
- Configure the data type and asset name
- Click Create β the asset and script file are generated automatically
New script files can also be generated directly inside the wizard by selecting the "New" option.
[CreateAssetMenu(fileName = nameof(GameConfigSettings), menuName = "MyGame/Settings/" + nameof(GameConfigSettings))]
public class GameConfigSettings : ScriptableSettings<GameConfigSettings.GameConfigData>
{
[Serializable]
public class GameConfigData
{
public float MusicVolume = 0.8f;
public int MaxPlayers = 4;
}
}public class SettingsUser : MonoBehaviour
{
[SerializeField]
private GameConfigSettings gameConfigSettings;
private void Start()
{
// Initialise with runtime values
gameConfigSettings.Initialize(new GameConfigSettings.GameConfigData
{
MusicVolume = 0.5f,
MaxPlayers = 2
});
}
}[CreateAssetMenu(fileName = nameof(PlayerStateRuntime), menuName = "MyGame/Runtime/" + nameof(PlayerStateRuntime))]
public class PlayerStateRuntime : ScriptableRuntime<PlayerStateRuntime.PlayerStateData>
{
[Serializable]
public class PlayerStateData
{
public int Health = 100;
public Vector3 Position;
}
}public class PlayerController : MonoBehaviour
{
[SerializeField]
private PlayerStateRuntime playerStateRuntime;
private void Update()
{
PlayerStateRuntime.PlayerStateData playerStateData = playerStateRuntime.Data;
playerStateData.Position = transform.position;
playerStateRuntime.Set(playerStateData); // overwrites runtime data
}
}[CreateAssetMenu(fileName = nameof(ScoreReactive), menuName = "MyGame/Reactives/" + nameof(ScoreReactive))]
public class ScoreReactive : ScriptableReactive<Score>
{
// Score is an existing class
}Ensure the type is marked
[Serializable]to appear in the inspector.
public class ScoreController : MonoBehaviour
{
[SerializeField]
private ScoreReactive scoreReactive;
private void OnEnable()
{
scoreReactive.Subscribe(OnScoreChange);
}
private void OnDisable()
{
scoreReactive.Unsubscribe(OnScoreChange);
}
private void OnScoreChange(Score newScore) { ... }
}To set values and trigger events:
scoreReactive.Set(new Score(...)) // Sets and notifies all subscribers
scoreReactive.SetSilently(new Score(...)) // Sets the value silently β no notification
scoreReactive.Emit(); // Emits the current value without changing itA signal-only reactive that fires an event without passing data:
[CreateAssetMenu(fileName = nameof(GameOverSignal), menuName = "MyGame/Signals/" + nameof(GameOverSignal))]
public class GameOverSignal : NoParamsReactive { }public class GameManager : MonoBehaviour
{
[SerializeField]
private GameOverSignal gameOverSignal;
public void EndGame()
{
gameOverSignal.Emit();
}
}public class UIManager : MonoBehaviour
{
[SerializeField]
private GameOverSignalReactive gameOverSignalReactive;
private void OnEnable()
{
gameOverSignalReactive.Subscribe(ShowGameOverScreen);
}
private void OnDisable()
{
gameOverSignalReactive.Unsubscribe(ShowGameOverScreen);
}
private void ShowGameOverScreen() { ... }
}Ready-to-use reactive and runtime assets are available via Create > Scriptables:
| Reactive | Runtime | Settings |
|---|---|---|
BooleanReactive |
BooleanRuntime |
BooleanSettings |
ColorReactive |
ColorRuntime |
ColorSettings |
DoubleReactive |
DoubleRuntime |
DoubleSettings |
FloatReactive |
FloatRuntime |
FloatSettings |
GameObjectReactive |
GameObjectRuntime |
GameObjectSettings |
IntReactive |
IntRuntime |
IntSettings |
QuaternionReactive |
QuaternionRuntime |
QuaternionSettings |
StringReactive |
StringRuntime |
StringSettings |
TransformReactive |
TransformRuntime |
TransformSettings |
Vector2Reactive |
Vector2Runtime |
Vector2Settings |
Vector3Reactive |
Vector3Runtime |
Vector3Settings |
ScriptableObjects are normally unloaded when no scene references point to them. The pin system prevents that:
public class GameManager : MonoBehaviour
{
[SerializeField]
private PlayerStateRuntime playerStateRuntime;
private void Awake() => playerStateRuntime.Pin(); // keep alive
private void OnDestroy() => playerStateRuntime.Unpin(); // release
}Pinned objects survive domain reloads and remain functional across play mode transitions. The ReferenceKeeper internally uses a HashSet and a hidden scene ScriptableCache MonoBehaviour.
All base classes implement standard interfaces so you can code against contracts:
| Interface | Method | Applied To |
|---|---|---|
ISubscribable<T> |
Subscribe(Action<T>) / Unsubscribe(Action<T>) |
ScriptableReactive<T> |
ISubscribable |
Subscribe(Action) / Unsubscribe(Action) |
NoParamsReactive |
IEmitable |
Emit() |
ScriptableReactive<T>, NoParamsReactive |
ISettable<T> |
Set(T) |
ScriptableReactive<T>, ScriptableRuntime<T> |
ISilentSettable<T> |
SetSilently(T) |
ScriptableReactive<T> |
IResettable |
Reset() |
ScriptableRuntime<T>, ScriptableReactive<T> |
IInitializable<T> |
Initialize(T) |
ScriptableSettings<T> |
IPinnable |
Pin() / Unpin() |
All base classes |
-
Copy the git URL:
https://github.com/thisaislan/scriptables.git -
In Unity Editor, click Window > Package Manager
-
Click the + button and select Add package from git URL...
-
Paste the URL and click Add
Please submit any queries, bugs or issues, to the Issues page on this repository. All feedback is appreciated as it not just helps myself find problems I didn't otherwise see, but also helps improve the project.
My friends and family, and you for having come here!
Copyright (c) 2024-present Aislan Tavares (@thisaislan) and Contributors. Scriptables is free and open-source software licensed under the MIT License.






