본문으로 건너뛰기
cccskills
무료GitHub에 공개

unity-ui-patterns

Unity UI/UX pattern design-to-code translation. Screen flow architecture, View/ViewModel separation, HUD architecture, feedback & juice systems, dynamic list/grid views, transition & animation contracts. UI Toolkit only. DESIGN INTENT format: INTENT/WRONG/RIGHT/SCAFFOLD/DESIGN HOOK. Based on Unity 6.3 LTS.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

연동 도구Unity
설치 방법 보기

포함된 파일(2)

  • SKILL.md24.6 KB
  • references/ui-pattern-scaffolds.md30.9 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

UI/UX Patterns -- Design Translation Patterns

Prerequisite skills: unity-ui (UI Toolkit API, USS/UXML, data binding), unity-async-patterns (async transitions, cancellation), unity-game-architecture (events, Service Locator)

Claude builds UI as monolithic scripts mixing data fetching, display, and animation. Works for one screen, unmaintainable when a designer adds transitions, dynamic lists, or multiple screens. These patterns separate structure from behavior, make screens composable, and keep juice designer-configurable.


PATTERN: Screen Flow Architecture

DESIGN INTENT: Game has multiple screens (MainMenu, Settings, Inventory, HUD, PauseMenu) with navigation flows and back-button support.

WRONG:

// One MonoBehaviour per screen with direct references -- breaks on every new screen
public class MainMenu : MonoBehaviour
{
    [SerializeField] private GameObject settingsPanel;
    [SerializeField] private GameObject inventoryPanel;

    public void OnSettingsClicked()
    {
        gameObject.SetActive(false);
        settingsPanel.SetActive(true); // Direct coupling
    }

    public void OnBackFromSettings()
    {
        settingsPanel.SetActive(false);
        gameObject.SetActive(true); // Every screen knows every other screen
    }
}

RIGHT: ScreenManager with stack-based navigation (push/pop). Each screen implements IScreen with ShowAsync/HideAsync. Screens loaded from UXML templates via UIDocument. Navigation decoupled from screen logic -- screens never reference each other.

SCAFFOLD:

using System;
using System.Threading;
using UnityEngine;
using UnityEngine.UIElements;

/// <summary>
/// Contract for all navigable screens.
/// </summary>
public interface IScreen
{
    /// <summary>Display name used for debugging and stack inspection.</summary>
    string ScreenName { get; }

    /// <summary>Transition the screen into view.</summary>
    Awaitable ShowAsync(CancellationToken ct);

    /// <summary>Transition the screen out of view.</summary>
    Awaitable HideAsync(CancellationToken ct);
}

/// <summary>
/// Stack-based screen navigator. Push screens to go forward, pop to go back.
/// </summary>
public class ScreenManager : MonoBehaviour
{
    [SerializeField] private UIDocument uiDocument;

    private readonly System.Collections.Generic.Stack<IScreen> _screenStack = new();
    private bool _isTransitioning;

    public static ScreenManager Instance { get; private set; }

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
    static void ResetStatic() => Instance = null;

    void Awake()
    {
        if (Instance != null) { Destroy(gameObject); return; }
        Instance = this;
    }

    /// <summary>Push a new screen onto the stack, hiding the current one.</summary>
    public async Awaitable PushAsync(IScreen screen)
    {
        if (_isTransitioning) return;
        _isTransitioning = true;

        var ct = destroyCancellationToken;

        if (_screenStack.TryPeek(out var current))
        {
            await current.HideAsync(ct);
        }

        _screenStack.Push(screen);
        await screen.ShowAsync(ct);

        _isTransitioning = false;
    }

    /// <summary>Pop the current screen and reveal the one beneath it.</summary>
    public async Awaitable PopAsync()
    {
        if (_isTransitioning || _screenStack.Count <= 1) return;
        _isTransitioning = true;

        var ct = destroyCancellationToken;
        var leaving = _screenStack.Pop();
        await leaving.HideAsync(ct);

        if (_screenStack.TryPeek(out var revealed))
        {
            await revealed.ShowAsync(ct);
        }

        _isTransitioning = false;
    }
}

DESIGN HOOK: New screens = implement IScreen + UXML template; navigation via ScreenManager.PushAsync(screen) / PopAsync(). Screens know nothing about each other.

GOTCHA: Screen stack must handle edge cases: pushing same screen twice, popping last screen, pushing during transition. The scaffold guards against transition overlap with _isTransitioning. To prevent duplicate pushes, check _screenStack.Peek() != screen before pushing.


PATTERN: View/ViewModel Separation

DESIGN INTENT: UI displays data from multiple systems (health, inventory, quests) and updates reactively.

WRONG:

// UI script queries game systems directly every frame
public class InventoryUI : MonoBehaviour
{
    [SerializeField] private UIDocument doc;

    void Update()
    {
        // Polling every frame, tightly coupled to game internals
        var root = doc.rootVisualElement;
        var items = GameManager.Instance.Player.Inventory.Items;
        for (int i = 0; i < items.Count; i++)
        {
            root.Q<Label>($"item-{i}").text = items[i].Name;
            root.Q<Label>($"count-{i}").text = items[i].Count.ToString();
        }
    }
}

RIGHT: ViewModel (plain C# class) adapts game data to display data, exposes C# events on change. View binds VisualElements to ViewModel properties. ViewModel has no dependency on VisualElement. Game systems update the ViewModel; View reacts.

SCAFFOLD:

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;

/// <summary>
/// ViewModel adapting inventory data for display.
/// No dependency on UnityEngine.UIElements.
/// </summary>
public class InventoryViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private string _selectedItemName = "";
    private int _selectedItemCount;
    private int _totalItems;

    /// <summary>Name of the currently selected item.</summary>
    public string SelectedItemName
    {
        get => _selectedItemName;
        set { _selectedItemName = value; OnPropertyChanged(); }
    }

    /// <summary>Stack count of the selected item.</summary>
    public int SelectedItemCount
    {
        get => _selectedItemCount;
        set { _selectedItemCount = value; OnPropertyChanged(); }
    }

    /// <summary>Total number of distinct items in inventory.</summary>
    public int TotalItems
    {
        get => _totalItems;
        set { _totalItems = value; OnPropertyChanged(); }
    }

    private void OnPropertyChanged([CallerMemberName] string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }
}
using UnityEngine;
using UnityEngine.UIElements;

/// <summary>
/// View that binds VisualElements to InventoryViewModel properties.
/// </summary>
public class InventoryView : MonoBehaviour
{
    [SerializeField] private UIDocument uiDocument;

    private InventoryViewModel _viewModel;
    private Label _itemNameLabel;
    private Label _itemCountLabel;
    private Label _totalLabel;

    /// <summary>Bind the view to a ViewModel instance.</summary>
    public void Bind(InventoryViewModel viewModel)
    {
        if (_viewModel != null)
            _viewModel.PropertyChanged -= OnPropertyChanged;

        _viewModel = viewModel;
        _viewModel.PropertyChanged += OnPropertyChanged;

        var root = uiDocument.rootVisualElement;
        _itemNameLabel = root.Q<Label>("item-name");
        _itemCountLabel = root.Q<Label>("item-count");
        _totalLabel = root.Q<Label>("total-items");

        RefreshAll();
    }

    private void OnPropertyChanged(object sender,
        System.ComponentModel.PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case nameof(InventoryViewModel.SelectedItemName):
                _itemNameLabel.text = _viewModel.SelectedItemName;
                break;
            case nameof(InventoryViewModel.SelectedItemCount):
                _itemCountLabel.text = _viewModel.SelectedItemCount.ToString();
                break;
            case nameof(InventoryViewModel.TotalItems):
                _totalLabel.text = _viewModel.TotalItems.ToString();
                break;
        }
    }

    private void RefreshAll()
    {
        _itemNameLabel.text = _viewModel.SelectedItemName;
        _itemCountLabel.text = _viewModel.SelectedItemCount.ToString();
        _totalLabel.text = _viewModel.TotalItems.ToString();
    }

    void OnDestroy()
    {
        if (_viewModel != null)
            _viewModel.PropertyChanged -= OnPropertyChanged;
    }
}

DESIGN HOOK: New data sources = new ViewModel adapting game system; View binds with zero knowledge of game internals. ViewModels are testable without Unity.

GOTCHA: Don't use Unity's built-in data binding for gameplay UI -- it is designed for Editor tools and has overhead/limitations that make it unsuitable for runtime game UI. Use explicit C# event subscriptions for control and debuggability.


PATTERN: HUD Architecture

DESIGN INTENT: HUD elements (health bar, ammo counter, minimap markers, crosshair) always visible during gameplay, update at different rates.

WRONG:

// All HUD logic in one script, polling every frame
public class HUD : MonoBehaviour
{
    [SerializeField] private UIDocument doc;

    void Update()
    {
        var root = doc.rootVisualElement;
        root.Q<ProgressBar>("health-bar").value = Player.Instance.Health;
        root.Q<Label>("ammo-count").text = Player.Instance.Ammo.ToString();
        root.Q<Label>("score").text = ScoreManager.Instance.Score.ToString();
        // Everything updates every frame whether it changed or not
    }
}

RIGHT: Each HUD widget implements IHudWidget with Initialize/Dispose lifecycle. Widgets subscribe to game events (SO channels from unity-game-architecture), self-update only on change. HudController manages widget registration and lifecycle.

SCAFFOLD:

using System;
using UnityEngine.UIElements;

/// <summary>
/// Contract for HUD widgets. Each widget owns a section of the HUD.
/// </summary>
public interface IHudWidget : IDisposable
{
    /// <summary>Bind to VisualElements and subscribe to game events.</summary>
    void Initialize(VisualElement hudRoot);
}
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

/// <summary>
/// Manages HUD widget lifecycle. Attach to the HUD UIDocument GameObject.
/// </summary>
public class HudController : MonoBehaviour
{
    [SerializeField] private UIDocument hudDocument;

    private readonly List<IHudWidget> _widgets = new();

    /// <summary>Register and initialize a widget.</summary>
    public void RegisterWidget(IHudWidget widget)
    {
        widget.Initialize(hudDocument.rootVisualElement);
        _widgets.Add(widget);
    }

    void OnDestroy()
    {
        foreach (var widget in _widgets)
            widget.Dispose();
        _widgets.Clear();
    }
}
using UnityEngine;
using UnityEngine.UIElements;

/// <summary>
/// Health bar widget. Updates only when health changes via event subscription.
/// </summary>
public class HealthBarWidget : IHudWidget
{
    // ScriptableObject event channel (see unity-game-architecture)
    private readonly FloatEventChannel _healthChanged;
    private ProgressBar _healthBar;

    public HealthBarWidget(FloatEventChannel healthChanged)
    {
        _healthChanged = healthChanged;
    }

    public void Initialize(VisualElement hudRoot)
    {
        _healthBar = hudRoot.Q<ProgressBar>("health-bar");
        _healthChanged.OnEventRaised += UpdateHealth;
    }

    private void UpdateHealth(float normalizedHealth)
    {
        _healthBar.value = normalizedHealth * 100f;
    }

    public void Dispose()
    {
        _healthChanged.OnEventRaised -= UpdateHealth;
    }
}

DESIGN HOOK: New widgets = implement IHudWidget; add element to HUD UXML; register with HudController. Widgets are self-contained, testable, and only update when their data changes.

GOTCHA: HUD root must use a separate UIDocument from gameplay screens -- sortingOrder on the UIDocument component controls layering. HUD should have a higher sortingOrder than screen UI so it renders on top. Multiple UIDocument components can coexist in a scene.


PATTERN: Feedback & Juice Systems

DESIGN INTENT: UI reacts to game events with visual feedback (damage numbers, screen shake, color flash, scale punch).

WRONG:

// Hardcoded feedback inline in combat logic
public class CombatManager : MonoBehaviour
{
    [SerializeField] private UIDocument hudDoc;

    public void ApplyDamage(float amount)
    {
        // Combat script directly drives UI -- tight coupling
        var healthBar = hudDoc.rootVisualElement.Q("health-bar");
        healthBar.style.backgroundColor = Color.red;
        // No way to revert, no timing, no designer control
    }
}

RIGHT: UIFeedbackPlayer component with UIFeedback SO assets. Game code fires events, feedback system subscribes, applies visual effects. Feedback sequence: USS class add, transition plays via USS transition-* properties, USS class remove after duration.

SCAFFOLD:

using UnityEngine;

/// <summary>
/// Designer-configurable UI feedback effect.
/// </summary>
[CreateAssetMenu(menuName = "UI/Feedback Effect")]
public class UIFeedback : ScriptableObject
{
    [Tooltip("USS class to add that triggers the visual effect")]
    public string ussClassName = "feedback--damage";

    [Tooltip("Duration in seconds before removing the USS class")]
    public float duration = 0.3f;

    [Tooltip("Optional: target element name. Null = apply to root.")]
    public string targetElementName;
}
using System.Threading;
using UnityEngine;
using UnityEngine.UIElements;

/// <summary>
/// Plays UIFeedback effects on a UIDocument's visual tree.
/// </summary>
public class UIFeedbackPlayer : MonoBehaviour
{
    [SerializeField] private UIDocument uiDocument;

    /// <summary>Play a feedback effect, adding then removing a USS class.</summary>
    public async Awaitable PlayAsync(UIFeedback feedback)
    {
        var ct = destroyCancellationToken;
        var root = uiDocument.rootVisualElement;

        var target = string.IsNullOrEmpty(feedback.targetElementName)
            ? root
            : root.Q(feedback.targetElementName);

        if (target == null) return;

        // Add USS class -- USS transition-* properties handle the animation
        target.AddToClassList(feedback.ussClassName);

        await Awaitable.WaitForSecondsAsync(feedback.duration, ct);

        // Remove USS class -- transition plays in reverse
        target.RemoveFromClassList(feedback.ussClassName);
    }
}

Example USS for damage flash:

/* Base state */
.health-bar {
    transition-property: background-color, scale;
    transition-duration: 0.15s;
    transition-timing-function: ease-out;
    background-color: rgb(50, 180, 50);
    scale: 1 1;
}

/* Feedback state -- applied by UIFeedbackPlayer */
.health-bar.feedback--damage {
    background-color: rgb(220, 40, 40);
    scale: 1.1 1.1;
}

DESIGN HOOK: Designers create UIFeedback SOs; assign USS classes for visual effects. New juice = new SO + USS rule. No code changes required.

GOTCHA: USS transitions use transition-duration and transition-timing-function -- you must set the transition properties on the base state (not the triggered class), or the transition won't play. The triggered class only changes the target property values.


PATTERN: Dynamic List/Grid Views

DESIGN INTENT: Inventory, shop, leaderboard -- scrollable lists of variable-length data with efficient rendering.

WRONG:

// Instantiate one VisualElement per data item
public void PopulateInventory(List<ItemData> items)
{
    var container = doc.rootVisualElement.Q("item-container");
    container.Clear();
    foreach (var item in items)
    {
        // 1000 items = 1000 elements in the visual tree -- laggy scroll
        var element = new VisualElement();
        element.Add(new Label(item.Name));
        element.Add(new Label(item.Count.ToString()));
        container.Add(element);
    }
}

RIGHT: UI Toolkit ListView with makeItem/bindItem callbacks for element recycling. Data source is List<T> or IList. Item template from UXML. Only visible items exist in the visual tree.

SCAFFOLD:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

/// <summary>
/// Sets up a ListView with element recycling for efficient scrolling.
/// </summary>
public class InventoryListView : MonoBehaviour
{
    [SerializeField] private UIDocument uiDocument;
    [SerializeField] private VisualTreeAsset itemTemplate;

    private ListView _listView;
    private List<ItemData> _items = new();

    void OnEnable()
    {
        _listView = uiDocument.rootVisualElement.Q<ListView>("inventory-list");

        _listView.makeItem = () => itemTemplate.Instantiate();

        _listView.bindItem = (element, index) =>
        {
            var item = _items[index];
            element.Q<Label>("item-name").text = item.Name;
            element.Q<Label>("item-count").text = item.Count.ToString();
        };

        _listView.unbindItem = (element, index) =>
        {
            // Clean up any subscriptions or dynamic content
            element.Q<Label>("item-name").text = "";
            element.Q<Label>("item-count").text = "";
        };

        _listView.itemsSource = _items;
        _listView.fixedItemHeight = 60f;
        _listView.selectionType = SelectionType.Single;

        _listView.selectionChanged += OnSelectionChanged;
    }

    /// <summary>Update the data source and refresh the list.</summary>
    public void SetItems(List<ItemData> items)
    {
        _items = items;
        _listView.itemsSource = _items;
        _listView.RefreshItems();
    }

    private void OnSelectionChanged(IEnumerable<object> selection)
    {
        foreach (var obj in selection)
        {
            if (obj is ItemData item)
                Debug.Log($"Selected: {item.Name}");
        }
    }

    void OnDisable()
    {
        _listView.selectionChanged -= OnSelectionChanged;
    }
}

DESIGN HOOK: Item visual = UXML template; data binding in bindItem callback; new list types reuse pattern with different data/template. Designers control item layout via UXML + USS.

GOTCHA: Call ListView.RefreshItems() (not Rebuild()) after data changes -- Rebuild recreates all elements from scratch, RefreshItems rebinds existing recycled elements. Use Rebuild() only when the item template itself changes.


PATTERN: Transition & Animation Contracts

DESIGN INTENT: Screen transitions (fade, slide, scale) are consistent and designer-configurable.

WRONG:

// Manual alpha lerps per screen, no standard contract
public async Awaitable ShowMainMenu(VisualElement root)
{
    root.style.opacity = 0;
    root.style.display = DisplayStyle.Flex;
    float t = 0;
    while (t < 0.3f)
    {
        t += Time.deltaTime;
        root.style.opacity = t / 0.3f;
        await Awaitable.NextFrameAsync();
    }
    // Every screen duplicates this. Duration/easing hardcoded. No designer control.
}

RIGHT: IScreenTransition interface with PlayAsync(VisualElement target, CancellationToken ct). Implementations use USS transitions (opacity, translate, scale). Transitions are SO assets with duration/easing config. ScreenManager accepts transition per navigation call.

SCAFFOLD:

using System.Threading;
using UnityEngine.UIElements;

/// <summary>
/// Contract for screen transitions.
/// </summary>
public interface IScreenTransition
{
    /// <summary>Play the transition on the target element.</summary>
    Awaitable PlayAsync(VisualElement target, CancellationToken ct);
}
using System.Threading;
using UnityEngine;
using UnityEngine.UIElements;

/// <summary>
/// Fade transition using USS opacity. Designer-configurable duration.
/// </summary>
[CreateAssetMenu(menuName = "UI/Transitions/Fade")]
public class FadeTransition : ScriptableObject, IScreenTransition
{
    [Tooltip("Fade duration in seconds")]
    [SerializeField] private float duration = 0.3f;

    [Tooltip("True = fade in (0 to 1), False = fade out (1 to 0)")]
    [SerializeField] private bool fadeIn = true;

    public async Awaitable PlayAsync(VisualElement target, CancellationToken ct)
    {
        float startOpacity = fadeIn ? 0f : 1f;
        float endOpacity = fadeIn ? 1f : 0f;

        // Set initial state without transition
        target.style.transitionDuration = new List<TimeValue> { new(0, TimeUnit.Second) };
        target.style.opacity = startOpacity;

        // Wait one frame for the initial state to apply
        await Awaitable.NextFrameAsync(ct);

        // Set transition duration then animate
        target.style.transitionProperty = new List<StylePropertyName> { new("opacity") };
        target.style.transitionDuration = new List<TimeValue> { new(duration, TimeUnit.Second) };
        target.style.transitionTimingFunction =
            new List<EasingFunction> { new(EasingMode.EaseInOut) };
        target.style.opacity = endOpacity;

        await Awaitable.WaitForSecondsAsync(duration, ct);
    }
}
using System.Threading;
using UnityEngine;
using UnityEngine.UIElements;

/// <summary>
/// Slide transition using USS translate. Configurable direction and duration.
/// </summary>
[CreateAssetMenu(menuName = "UI/Transitions/Slide")]
public class SlideTransition : ScriptableObject, IScreenTransition
{
    [SerializeField] private float duration = 0.4f;
    [SerializeField] private bool slideIn = true;
    [SerializeField] private Vector2 offset = new(100f, 0f); // percentage

    public async Awaitable PlayAsync(VisualElement target, CancellationToken ct)
    {
        var start = slideIn
            ? new Translate(Length.Percent(offset.x), Length.Percent(offset.y))
            : new Translate(Length.Percent(0), Length.Percent(0));
        var end = slideIn
            ? new Translate(Length.Percent(0), Length.Percent(0))
            : new Translate(Length.Percent(offset.x), Length.Percent(offset.y));

        // Set initial state
        target.style.transitionDuration = new List<TimeValue> { new(0, TimeUnit.Second) };
        target.style.translate = new StyleTranslate(start);

        await Awaitable.NextFrameAsync(ct);

        // Animate
        target.style.transitionProperty = new List<StylePropertyName> { new("translate") };
        target.style.transitionDuration = new List<TimeValue> { new(duration, TimeUnit.Second) };
        target.style.transitionTimingFunction =
            new List<EasingFunction> { new(EasingMode.EaseOut) };
        target.style.translate = new StyleTranslate(end);

        await Awaitable.WaitForSecondsAsync(duration, ct);
    }
}

DESIGN HOOK: New transitions = implement IScreenTransition as a ScriptableObject; designers pick transition SO per screen change. ScreenManager.PushAsync(screen, transition) applies it.

GOTCHA: USS transition-property must include ALL animated properties -- if you animate opacity and translate, both must be listed. Use transition-property: all as a fallback, but be aware it will animate every property change (including layout changes you don't intend to animate).


Anti-Patterns Summary

Anti-PatternProblemPattern Fix
Monolithic screen scriptsEvery screen knows every other screen; adding one screen changes five filesScreen Flow Architecture -- stack-based navigation
Direct system polling in UpdateWasted cycles, tight coupling between UI and game logicView/ViewModel -- event-driven updates
Single HUD MonoBehaviourOne change breaks all HUD elements; everything polls every frameHUD Architecture -- independent widgets with event subscriptions
Hardcoded feedback in game logicCombat scripts drive UI directly; no designer controlFeedback & Juice -- SO-driven USS class effects
Manual element instantiation1000 items = 1000 VisualElements; laggy scrollDynamic ListView -- recycling with makeItem/bindItem
Per-screen animation codeEvery screen duplicates transition logic; no consistencyTransition Contracts -- SO-based IScreenTransition

Related Skills

  • unity-ui -- UI Toolkit fundamentals, USS/UXML authoring, VisualElement hierarchy
  • unity-async-patterns -- Awaitable, CancellationToken, async lifecycle management
  • unity-game-architecture -- Event channels, Service Locator, ScriptableObject patterns
  • unity-scene-assets -- Scene loading for screen-per-scene architectures

Additional Resources

  • skills/unity-ui-patterns/references/ui-pattern-scaffolds.md -- Complete implementations for all patterns
  • Unity Manual: UI Toolkit runtime usage
  • Unity Manual: ListView and virtualization
  • Unity Manual: USS transitions and animations

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

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의 스킬 모두 보기

이 스킬의 문제 신고