AgentStack
SKILL verified MIT Self-run

Scriptable Objects

skill-xeldaralz-everything-claude-unity-scriptable-objects · by XeldarAlz

ScriptableObject architecture patterns — event channels, variable references, runtime sets, factory pattern, data containers. The backbone of data-driven Unity architecture.

No reviews yet
0 installs
12 views
0.0% view→install

Install

$ agentstack add skill-xeldaralz-everything-claude-unity-scriptable-objects

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Scriptable Objects? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

ScriptableObject Architecture

ScriptableObjects (SOs) are Unity's most powerful architectural tool. They are asset-based data containers that live outside scenes, enabling data-driven design, loose coupling, and designer-friendly workflows.

Pattern 1: Data Container

The simplest and most common use — define game data as assets.

[CreateAssetMenu(fileName = "NewWeapon", menuName = "Game/Weapon Definition")]
public sealed class WeaponDefinition : ScriptableObject
{
    [Header("Identity")]
    [SerializeField] private string _displayName;
    [SerializeField] private Sprite _icon;
    [TextArea]
    [SerializeField] private string _description;

    [Header("Stats")]
    [SerializeField] private float _damage = 10f;
    [SerializeField] private float _fireRate = 0.5f;
    [SerializeField] private int _ammoCapacity = 30;
    [SerializeField] private GameObject _prefab;

    public string DisplayName => _displayName;
    public Sprite Icon => _icon;
    public float Damage => _damage;
    public float FireRate => _fireRate;
    public int AmmoCapacity => _ammoCapacity;
    public GameObject Prefab => _prefab;
}

Use for: Items, abilities, enemy configs, level data, audio events, UI themes.

Pattern 2: Event Channel

Zero-coupling communication between systems. No direct references needed.

[CreateAssetMenu(fileName = "NewVoidEvent", menuName = "Events/Void Event")]
public sealed class VoidEventChannel : ScriptableObject
{
    private System.Action _onRaised;

    public void Raise()
    {
        _onRaised?.Invoke();
    }

    public void Subscribe(System.Action listener)
    {
        _onRaised += listener;
    }

    public void Unsubscribe(System.Action listener)
    {
        _onRaised -= listener;
    }
}

// Generic version for typed events
[CreateAssetMenu(fileName = "NewIntEvent", menuName = "Events/Int Event")]
public sealed class IntEventChannel : ScriptableObject
{
    private System.Action _onRaised;

    public void Raise(int value) => _onRaised?.Invoke(value);
    public void Subscribe(System.Action listener) => _onRaised += listener;
    public void Unsubscribe(System.Action listener) => _onRaised -= listener;
}

Usage:

// Publisher (e.g., ScoreSystem)
[SerializeField] private IntEventChannel _onScoreChanged;
_onScoreChanged.Raise(newScore);

// Subscriber (e.g., ScoreUI) — wire the SAME SO asset in inspector
[SerializeField] private IntEventChannel _onScoreChanged;
private void OnEnable() => _onScoreChanged.Subscribe(UpdateDisplay);
private void OnDisable() => _onScoreChanged.Unsubscribe(UpdateDisplay);

Pattern 3: Variable Reference

Inspector-tweakable values that can be shared or overridden per instance.

[System.Serializable]
public sealed class FloatReference
{
    [SerializeField] private bool _useConstant = true;
    [SerializeField] private float _constantValue;
    [SerializeField] private FloatVariable _variable;

    public float Value => _useConstant ? _constantValue : _variable.Value;
}

[CreateAssetMenu(fileName = "NewFloatVar", menuName = "Variables/Float")]
public sealed class FloatVariable : ScriptableObject
{
    [SerializeField] private float _value;
    public float Value { get => _value; set => _value = value; }
}

Use for: Player health, move speed, gravity — values that designers tweak and multiple systems read.

Pattern 4: Runtime Set

Track all active instances of a type without FindObjectsOfType.

[CreateAssetMenu(fileName = "NewRuntimeSet", menuName = "Sets/Transform Set")]
public sealed class TransformRuntimeSet : ScriptableObject
{
    private readonly List _items = new();

    public IReadOnlyList Items => _items;
    public int Count => _items.Count;

    public void Add(Transform item)
    {
        if (!_items.Contains(item))
        {
            _items.Add(item);
        }
    }

    public void Remove(Transform item)
    {
        _items.Remove(item);
    }
}

// Usage: enemies register themselves
public sealed class Enemy : MonoBehaviour
{
    [SerializeField] private TransformRuntimeSet _enemySet;

    private void OnEnable() => _enemySet.Add(transform);
    private void OnDisable() => _enemySet.Remove(transform);
}

Pattern 5: Factory Configuration

[CreateAssetMenu(fileName = "NewSpawnConfig", menuName = "Game/Spawn Config")]
public sealed class SpawnConfiguration : ScriptableObject
{
    [SerializeField] private GameObject _prefab;
    [SerializeField] private int _poolSize = 10;
    [SerializeField] private float _spawnRate = 2f;
    [SerializeField] private float _spawnRadius = 15f;

    public GameObject Prefab => _prefab;
    public int PoolSize => _poolSize;
    public float SpawnRate => _spawnRate;
    public float SpawnRadius => _spawnRadius;
}

Anti-Patterns

  1. Mutable state not reset between plays — SOs persist across play sessions in Editor. If you write to an SO at runtime, it keeps the value when you stop playing. Use OnEnable or OnDisable to reset, or use a separate runtime copy.
  1. SO as singleton — Don't use Resources.Load to access SOs globally. Use [SerializeField] references or DI.
  1. Too much logic in SOs — SOs are data and events. Keep complex logic in systems/services.
  1. Circular SO references — SO A references SO B which references SO A. Can cause infinite loops in serialization.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.