- UNC 경로 처리 문제로 cmd/powershell/bash 대신 파일 도구 사용 규칙 명시 Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
353 lines
12 KiB
Markdown
353 lines
12 KiB
Markdown
# 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: `\\Truenas\smb\Obsidian Vault\Colosseum`
|
||
|
||
- Always use the shared Obsidian Vault at `\\Truenas\smb\Obsidian Vault\Colosseum` for project documentation updates.
|
||
- **Obsidian Vault 파일은 반드시 파일 도구(read/write/edit)로만 접근한다.** cmd, powershell, bash 등은 UNC 경로를 정상적으로 처리하지 못하므로 사용하지 않는다.
|
||
- Do not write Colosseum design notes to `C:\Users\dal4s\OneDrive\문서\Obsidian Vault\Colosseum`; that path is not the active vault for this project.
|
||
- Unless the user explicitly names another file, `체크리스트` means `\\Truenas\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.
|
||
- If Codex adds interpretation, clarification, proposals, or implementation notes to a user-authored note, put them in a clearly labeled separate section such as `Codex 보완 메모` or `Codex 제안` so the added content is visibly distinguishable from the user's original writing.
|
||
- Avoid silently rewriting or blending Codex-authored guidance into the user's original bullet lists unless the user explicitly asks for a rewrite.
|
||
- Cross-session design agreements that should be checked before related work are summarized in `\\Truenas\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.
|
||
|
||
```bash
|
||
# 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 (Windows)
|
||
|
||
```bash
|
||
# Build Windows standalone (adjust paths as needed)
|
||
"C:\Program Files\Unity\Hub\Editor\6000.3.10f1\Editor\Unity.exe" -batchmode -projectPath . -buildWindows64Player ./Builds/Windows/Colosseum.exe -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:
|
||
|
||
```csharp
|
||
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
|
||
|
||
```csharp
|
||
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:
|
||
|
||
```csharp
|
||
[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**:
|
||
|
||
```csharp
|
||
/// <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:
|
||
|
||
```csharp
|
||
// 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:
|
||
|
||
```csharp
|
||
public float MaxHealth => vitality.FinalValue * 10f;
|
||
public bool IsStunned => stunCount > 0;
|
||
public bool CanAct => !IsStunned;
|
||
```
|
||
|
||
### Switch Expressions
|
||
|
||
Prefer switch expressions for concise mapping:
|
||
|
||
```csharp
|
||
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:
|
||
|
||
```csharp
|
||
[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
|
||
|
||
```csharp
|
||
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:
|
||
|
||
```csharp
|
||
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
|
||
|
||
```csharp
|
||
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
|
||
|
||
```csharp
|
||
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.
|
||
- 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로 읽히게 한다. 차단 진행도나 정확한 누적 수치 노출은 명시 요청이 없으면 피한다.
|