Skip to content
cccskills
FreePublished on GitHub

uw-scriptable-object-arch

Generate ScriptableObject-based systems including data containers, event channels, and runtime sets. Use when creating game data (weapons, enemies, items, abilities), cross-system events, or live object tracking. Triggers on requests like "create weapon data", "add an event system", "make a runtime set", "decouple these systems", "create item database", "add a damage event", "track active enemies", "create a config SO", "add game settings data", or any task involving ScriptableObject data containers, event-driven communication, or runtime object tracking. Reads ProjectConfig.yaml -> architecture_pattern to determine SO-first vs DI-first context.

The English summary is being prepared. Showing the original description.

Works withUnity
See how to install

Files included (1)

  • SKILL.md7.8 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

ScriptableObject Architecture

Generate SO patterns for data, events, and runtime collections.

Before You Start

  1. Read docs/ProjectConfig.yaml for:
    • architecture_pattern — "so-first" (default, this skill is primary) or "di-first" (SOs still hold data, but events become Commands — see uw-dependency-injection).
    • mcp.unity_mcp — if true, call refresh_unity after creating files.
  2. Read docs/TDD_Template.md for data architecture decisions.
  3. Read docs/NAMING_CONVENTIONS.md for SO file naming (PascalCase: SwordData.asset).
  4. Ensure the feature has an .asmdef — create with uw-unity-feature-scaffold if needed.

Three Patterns

1. Data Container

Store game-tunable values in a ScriptableObject. Designers edit them in the Inspector without touching code.

using UnityEngine;

[CreateAssetMenu(fileName = "New WeaponData", menuName = "Game/Data/Weapon Data")]
public class WeaponData : ScriptableObject
{
    [Header("Combat")]
    [SerializeField] private float _damage = 10f;
    [SerializeField] private float _fireRate = 0.5f;
    [SerializeField] private float _range = 15f;

    [Header("Ammo")]
    [SerializeField] private int _maxAmmo = 30;
    [SerializeField] private float _reloadTime = 1.5f;

    [Header("Feel")]
    [SerializeField] private ShakeProfile _hitShakeProfile;
    [SerializeField] private AudioClip _fireSound;
    [SerializeField] private GameObject _muzzleFlashPrefab;

    // Read-only properties — game code reads, designers tune in Inspector
    public float Damage => _damage;
    public float FireRate => _fireRate;
    public float Range => _range;
    public int MaxAmmo => _maxAmmo;
    public float ReloadTime => _reloadTime;
    public ShakeProfile HitShakeProfile => _hitShakeProfile;
    public AudioClip FireSound => _fireSound;
    public GameObject MuzzleFlashPrefab => _muzzleFlashPrefab;
}

2. Event Channel

Decouple systems by broadcasting events through a ScriptableObject asset. Any system can subscribe without knowing who raises the event.

Parameterless event:

using System;
using UnityEngine;

[CreateAssetMenu(fileName = "New GameEvent", menuName = "Game/Events/Game Event")]
public class GameEvent : ScriptableObject
{
    private event Action _onRaise;

    public void Raise() => _onRaise?.Invoke();
    public void Subscribe(Action listener) => _onRaise += listener;
    public void Unsubscribe(Action listener) => _onRaise -= listener;
}

Typed event channels for events that carry data:

using System;
using UnityEngine;

[CreateAssetMenu(fileName = "New IntEvent", menuName = "Game/Events/Int Event")]
public class IntEvent : ScriptableObject
{
    private event Action<int> _onRaise;

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

[CreateAssetMenu(fileName = "New FloatEvent", menuName = "Game/Events/Float Event")]
public class FloatEvent : ScriptableObject
{
    private event Action<float> _onRaise;

    public void Raise(float value) => _onRaise?.Invoke(value);
    public void Subscribe(Action<float> listener) => _onRaise += listener;
    public void Unsubscribe(Action<float> listener) => _onRaise -= listener;
}

Usage:

// Publisher (doesn't know who listens)
[SerializeField] private IntEvent _onScoreChanged;
_onScoreChanged.Raise(newScore);

// Subscriber (doesn't know who publishes)
[SerializeField] private IntEvent _onScoreChanged;
private void OnEnable() => _onScoreChanged.Subscribe(UpdateScoreUI);
private void OnDisable() => _onScoreChanged.Unsubscribe(UpdateScoreUI);

3. Runtime Set

Track live objects (active enemies, collectibles, spawned projectiles) without FindObjectsOfType. Objects register themselves on spawn and unregister on destroy.

using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "New RuntimeSet", menuName = "Game/Sets/Runtime Set")]
public class RuntimeSet<T> : ScriptableObject
{
    private readonly List<T> _items = new();
    public IReadOnlyList<T> Items => _items;
    public int Count => _items.Count;

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

    public void Remove(T item) => _items.Remove(item);

    private void OnDisable() => _items.Clear(); // Prevent stale refs across play sessions
}

Concrete sets (needed because Unity can't serialize open generics):

[CreateAssetMenu(fileName = "New EnemySet", menuName = "Game/Sets/Enemy Set")]
public class EnemyRuntimeSet : RuntimeSet<EnemyController> { }

Self-registering pattern:

public class EnemyController : MonoBehaviour
{
    [SerializeField] private EnemyRuntimeSet _activeEnemies;

    private void OnEnable() => _activeEnemies.Add(this);
    private void OnDisable() => _activeEnemies.Remove(this);
}

Naming Convention

PatternNamingExample
Data Container{Type}DataWeaponData, EnemyData
Event Channel{Action}EventOnDeathEvent, ScoreChangedEvent
Typed Event{Type}EventIntEvent, FloatEvent, StringEvent
Runtime Set{Type}Set or {Type}RuntimeSetEnemySet, CollectibleRuntimeSet

After Setup

  • Add game feel: Use uw-game-feel-integrator — SO data containers pair well with feel parameters (ShakeProfile, tween durations stored in the SO).
  • Write tests: Use uw-unity-test-runner — test event channels (subscribe, raise, verify callback) and runtime sets (add, remove, clear) in EditMode tests.
  • Custom inspectors: Use uw-unity-editor-tools for designer-friendly SO inspectors (preview panels, validation).
  • Graduate to DI: If the project outgrows SO events, use uw-dependency-injection — SO data stays, events become Commands.

When to Consider DI Instead

If your project needs any of these, check ProjectConfig.yaml -> architecture_pattern and consider switching to di-first with the uw-dependency-injection skill:

  • Cross-feature orchestration beyond simple events (Command pattern)
  • Constructor injection for testability with mock services
  • Scoped lifetimes (per-scene, per-level services)
  • Factory patterns with automatic dependency resolution

DI + SO coexist: In DI-first projects, ScriptableObjects still hold data — they're injected into the container as instances. Events are replaced by Commands.

Rules

  • Always use [CreateAssetMenu] on every ScriptableObject.
  • Always use [SerializeField] private + public read-only properties for data fields.
  • Use Action/Action<T> delegates for events, not UnityEvents (better performance, no boxing).
  • Runtime sets must clear on OnDisable() to prevent stale references across play sessions.
  • Always unsubscribe from events in OnDisable — matching every Subscribe with an Unsubscribe.
  • All SO code must live inside an .asmdef.
  • SO file names use PascalCase (per NAMING_CONVENTIONS.md): SwordData.asset.
  • If ProjectConfig.yaml -> mcp.unity_mcp is true, call refresh_unity after creating files.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

adopt

Free

Brownfield onboarding — audits existing project artifacts for template format compliance (not just existence), classifies gaps by impact, and produces a numbered migration plan. Run this when joining an in-progress project or upgrading from an older template version. Distinct from /project-stage-detect (which checks what exists) — this checks whether what exists will actually work with the template's skills.

The English summary is being prepared. Showing the original description.

IdoCohen560/claude-unity-game-studio22Updated Jul 8, 2026

Creates an Architecture Decision Record (ADR) documenting a significant technical decision, its context, alternatives considered, and consequences. Every major technical choice should have an ADR.

The English summary is being prepared. Showing the original description.

IdoCohen560/claude-unity-game-studio22Updated Jul 8, 2026

Validates completeness and consistency of the project architecture against all GDDs. Builds a traceability matrix mapping every GDD technical requirement to ADRs, identifies coverage gaps, detects cross-ADR conflicts, verifies engine compatibility consistency across all decisions, and produces a PASS/CONCERNS/FAIL verdict. The architecture equivalent of /design-review.

The English summary is being prepared. Showing the original description.

IdoCohen560/claude-unity-game-studio22Updated Jul 8, 2026

Guided, section-by-section Art Bible authoring. Creates the visual identity specification that gates all asset production. Run after /brainstorm is approved and before /map-systems or any GDD authoring begins.

The English summary is being prepared. Showing the original description.

IdoCohen560/claude-unity-game-studio22Updated Jul 8, 2026

Audits game assets for compliance with naming conventions, file size budgets, format standards, and pipeline requirements. Identifies orphaned assets, missing references, and standard violations.

The English summary is being prepared. Showing the original description.

IdoCohen560/claude-unity-game-studio22Updated Jul 8, 2026

Generate per-asset visual specifications and AI generation prompts from GDDs, level docs, or character profiles. Produces structured spec files and updates the master asset manifest. Run after art bible and GDD/level design are approved, before production begins.

The English summary is being prepared. Showing the original description.

IdoCohen560/claude-unity-game-studio22Updated Jul 8, 2026

See all skills by IdoCohen560

Report a problem with this skill