Files
Colosseum/AGENTS.md

12 KiB
Raw Permalink Blame History

Colosseum - Unity Game Project

Project Overview

Multiplayer arena game built with Unity 6000.3.10f1 and Unity Netcode for GameObjects.

  • Language: C# 9.0
  • Target Framework: .NET Standard 2.1
  • Root Namespace: Colosseum
  • Assembly Definition: Colosseum.Game (Assets/Scripts/Colosseum.Game.asmdef)

Game Design Documentation

Design docs are maintained in Obsidian Vault: /mnt/smb/Obsidian Vault/Colosseum

  • Always use the shared Obsidian Vault at /mnt/smb/Obsidian Vault/Colosseum for project documentation updates.
  • 현재 환경에서 접근 가능한 셸(bash, zsh 등) 기준으로 Obsidian Vault 파일을 열고 수정해도 된다.
  • Unless the user explicitly names another file, 체크리스트 means /mnt/smb/Obsidian Vault/Colosseum/개발/프로토타입 체크리스트.md.
  • After completing work, always check whether the prototype checklist should be updated and reflect the new status when needed.
  • If the work also changes a domain-specific checklist such as boss/player/combat, sync that checklist too.
  • When editing user-authored Obsidian notes, preserve the user's original text structure as much as possible.
  • 에이전트가 작성자가 아닌 보완 메모나 제안을 추가할 때는 보완 메모 또는 제안 같이 명확히 구분되는 섹션에 작성하여 원본과 구별되도록 한다.
  • Cross-session design agreements that should be checked before related work are summarized in /mnt/smb/Obsidian Vault/Colosseum/개발/세션 공통 합의.md.

Game Concept

  • Genre: Online multiplayer co-op action RPG (3rd person)
  • Theme: Gladiator survival boss raid in Colosseum
  • Roles: Multi-role system - players can hybridize (e.g., Tank 0.5 + DPS 0.5) instead of fixed Tank/DPS/Healer

Stats System

Stat Abbr Description Derived Formula
Strength STR Weapon damage Physical Damage = STR × 2
Dexterity DEX Ranged aim/damage, melee speed Ranged Damage = DEX × 2
Intelligence INT Magic damage Magic Damage = INT × 2
Vitality VIT Max health Max HP = VIT × 10
Wisdom WIS Healing power Heal Power = WIS × 1.5
Spirit SPI Max mana Max MP = SPI × 5

Damage Calculation

Final Damage = baseDamage + (statDamage × statScaling)
DamageType Base Stat Description
Physical STR Melee weapon damage
Magical INT Spell damage
Ranged DEX Bow/ranged damage
True None Fixed damage, no stat scaling

Stat Modifier System

Modifiers are applied in order:

  1. Flat: Add fixed value
  2. PercentAdd: Sum percentages, then multiply
  3. PercentMult: Multiply individually
Final = (Base + FlatSum) × (1 + PercentAddSum) × PercentMult1 × PercentMult2...

Skill System

  • Active Skills: 6 slots (L-click, R-click, 1, 2, 3, 4)
  • Passive Skills: Tree-based progression from center
  • Effects: Triggered via animation events (OnEffect(index))
  • Animation: Start clip + optional end clip

Build/Run Commands

This is a Unity project. Use Unity Editor for building and testing.

# Open in Unity Editor (requires Unity Hub)
# Build via: File > Build Settings > Build

# Run tests in Unity Editor
# Window > General > Test Runner > EditMode / PlayMode

Build from Command Line (Linux)

# Build Linux standalone (adjust paths as needed)
"/path/to/Unity/Editor/Unity" -batchmode -projectPath . -buildLinux64Player ./Builds/Linux/Colosseum -quit

Project Structure

Assets/
  Scripts/
    Abnormalities/    # Buff/debuff system
    Editor/           # Unity editor extensions
    Network/          # Network management
    Player/           # Player controllers
    Skills/           # Skill system
      Effects/        # Skill effects (damage, heal, etc.)
    Stats/            # Character statistics
    UI/               # User interface

Code Style Guidelines

Namespaces

Follow Colosseum.{Subnamespace} pattern:

namespace Colosseum.Player { }
namespace Colosseum.Skills { }
namespace Colosseum.Skills.Effects { }
namespace Colosseum.Stats { }
namespace Colosseum.Network { }
namespace Colosseum.Abnormalities { }

Using Statements Order

Organize imports in this order, separated by blank lines:

  1. System namespaces
  2. UnityEngine namespaces
  3. Unity.Netcode / Unity packages
  4. Colosseum namespaces
using System;
using System.Collections.Generic;

using UnityEngine;

using Unity.Netcode;

using Colosseum.Stats;
using Colosseum.Player;

Naming Conventions

Element Convention Example
Classes PascalCase PlayerNetworkController
Interfaces IPascalCase IDamageable
Methods PascalCase TakeDamageRpc()
Public Properties PascalCase MaxHealth, IsStunned
Private Fields camelCase currentHealth, characterStats
Constants PascalCase or SCREAMING_SNAKE SKILL_STATE_NAME, MaxValue
Enum values PascalCase DamageType.Physical

Serialization & Inspector

Use [SerializeField] with [Header] and [Tooltip] for organization:

[Header("References")]
[Tooltip("CharacterStats component (auto-searched if null)")]
[SerializeField] private CharacterStats characterStats;

[Header("Settings")]
[Min(0f)] [SerializeField] private float baseDamage = 10f;
[SerializeField] private DamageType damageType = DamageType.Physical;

Documentation

Use XML documentation comments in Korean:

/// <summary>
/// 플레이어 네트워크 상태 관리 (HP, MP 등)
/// </summary>
public class PlayerNetworkController : NetworkBehaviour
{
    /// <summary>
    /// 대미지 적용 (서버에서만 실행)
    /// </summary>
    [Rpc(SendTo.Server)]
    public void TakeDamageRpc(float damage)
    {
        // ...
    }
}

Network Code Patterns

Use Unity Netcode patterns:

// Network variables for synchronized state
private NetworkVariable<float> currentHealth = new NetworkVariable<float>(100f);

// Server RPCs for client-to-server calls
[Rpc(SendTo.Server)]
public void TakeDamageRpc(float damage)
{
    currentHealth.Value = Mathf.Max(0f, currentHealth.Value - damage);
}

// Check authority before modifying
if (IsServer)
{
    currentHealth.Value = MaxHealth;
}

Expression Body Members

Use for simple properties and methods:

public float MaxHealth => vitality.FinalValue * 10f;
public bool IsStunned => stunCount > 0;
public bool CanAct => !IsStunned;

Switch Expressions

Prefer switch expressions for concise mapping:

public CharacterStat GetStat(StatType statType)
{
    return statType switch
    {
        StatType.Strength => strength,
        StatType.Dexterity => dexterity,
        StatType.Intelligence => intelligence,
        _ => null,
    };
}

ScriptableObjects for Data

Use ScriptableObject for configuration data:

[CreateAssetMenu(fileName = "NewSkill", menuName = "Colosseum/Skill")]
public class SkillData : ScriptableObject
{
    [SerializeField] private string skillName;
    [SerializeField] private List<SkillEffect> effects;
    
    public string SkillName => skillName;
    public IReadOnlyList<SkillEffect> Effects => effects;
}

Error Handling

  • Use Debug.LogWarning() for recoverable issues
  • Use Debug.LogError() for critical failures
  • Null-check parameters in public methods
public void ApplyAbnormality(AbnormalityData data, GameObject source)
{
    if (data == null)
    {
        Debug.LogWarning("[Abnormality] ApplyAbnormality called with null data");
        return;
    }
    // ...
}

Events

Use C# events with Action<T> or custom delegates:

public event Action<ActiveAbnormality> OnAbnormalityAdded;
public event Action<ActiveAbnormality> OnAbnormalityRemoved;
public event Action OnAbnormalitiesChanged;

// Invoke with null-conditional
OnAbnormalityAdded?.Invoke(newAbnormality);

Key Dependencies

Package Purpose
Unity.Netcode.Runtime Multiplayer networking
Unity.InputSystem New input system
Unity.TextMeshPro Text rendering
Unity.Networking.Transport Low-level networking

Common Patterns

MonoBehaviour Components

public class ExampleComponent : MonoBehaviour
{
    [Header("References")]
    [SerializeField] private Animator animator;
    
    public Animator Animator => animator;
    
    private void Awake()
    {
        if (animator == null)
            animator = GetComponentInChildren<Animator>();
    }
}

NetworkBehaviour Components

public class NetworkedComponent : NetworkBehaviour
{
    private NetworkVariable<int> value = new NetworkVariable<int>();
    
    public override void OnNetworkSpawn()
    {
        // Initialize networked state
    }
    
    public override void OnNetworkDespawn()
    {
        // Cleanup
    }
}

Notes

  • For Unity work, prefer Unity MCP for active scene inspection, runtime verification, prefab checks, and console review when it is available in the session.
  • Never edit code, scenes, prefabs, components, or Unity asset settings while the Unity Editor is in play mode. Stop play mode first, then edit.
  • CRITICAL: After any code change (edit, create, delete), always perform a force refresh with compile request and wait for ready before entering play mode. Failing to do so causes mid-play compilation which can leave network ports occupied on the next run. Use refresh_unity(mode="force", compile="request", wait_for_ready=true).
  • After Unity-related edits, check the Unity console for errors before proceeding.
  • For networked play tests, prefer a temporary non-conflicting test port when needed and restore the default port after validation.
  • The user has a strong project preference that play mode must be stopped before edits because network ports can remain occupied otherwise.
  • Unless the user explicitly asks to stop before push, when the user requests a commit, create the commit, show the message, and push by default. Treat approval to run git push as the push confirmation. Ask separately only for destructive push flows such as force-push or history rewrite.
  • Commit messages should follow the recent project history style: use a type prefix such as feat:, fix:, or chore: and write the subject in Korean so the gameplay/UI/system change is clear from the log alone.
  • When the change is substantial, include a blank line after the subject and add Korean bullet points that summarize the work by feature/purpose, similar to commit 0889bb0 (feat: 드로그 집행 개시 패턴 및 낙인 디버프 추가).
  • Prefer the format type: 한글 요약 for the subject, then - 변경 사항 bullet lines for the body. Use feat: for feature work, fix: for bug fixes, and chore: for non-feature maintenance such as scene/prefab cleanup, asset reorganization, or other miscellaneous upkeep. The body should mention key implementation scope, affected systems/assets, and validation or rollback notes when relevant.
  • All code comments and documentation should be in Korean
  • Use [Min()] attribute for numeric minimums in Inspector
  • Use [TextArea] for multi-line string fields
  • Private fields should use camelCase (no m_ or _ prefix)
  • Prefer IReadOnlyList<T> for exposing collections
  • 프로젝트 용어로는 필살기보다 고위력 기술을 우선 사용한다.
  • 보호막은 단일 값이 아니라 타입별 독립 인스턴스로 취급한다. 같은 타입은 자기 자신만 갱신되고, 서로 다른 타입은 공존하며, 흡수 순서는 적용 순서를 따른다.
  • 보스 시그니처 전조는 가능한 한 정확한 진행 수치 UI보다 명확한 모션/VFX로 읽히게 한다. 차단 진행도나 정확한 누적 수치 노출은 명시 요청이 없으면 피한다.

Runtime Content Agent Rules

Purpose: Keep boss YAML compatible with game runtime expectations.

Always:

  • validate boss YAML shape before commit
  • preserve boss_id stability
  • keep runtime field names machine-readable and snake_case
  • prefer additive changes over destructive changes

Never:

  • change mechanic intent to satisfy schema formatting
  • silently rename IDs
  • auto-fill required combat values without a design source