本文へ移動
cccskills
無料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.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md7.8 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

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.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

adopt

無料

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.

日本語の概要は準備中です。原文の説明を表示しています。

IdoCohen560/claude-unity-game-studio222026年7月8日 更新

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.

日本語の概要は準備中です。原文の説明を表示しています。

IdoCohen560/claude-unity-game-studio222026年7月8日 更新

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.

日本語の概要は準備中です。原文の説明を表示しています。

IdoCohen560/claude-unity-game-studio222026年7月8日 更新

art-bible

無料

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.

日本語の概要は準備中です。原文の説明を表示しています。

IdoCohen560/claude-unity-game-studio222026年7月8日 更新

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

日本語の概要は準備中です。原文の説明を表示しています。

IdoCohen560/claude-unity-game-studio222026年7月8日 更新

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.

日本語の概要は準備中です。原文の説明を表示しています。

IdoCohen560/claude-unity-game-studio222026年7月8日 更新

IdoCohen560 のスキルをすべて見る

このスキルの問題を報告する