The Hon: Joseon — Development Architecture
v3.0 | Godot 4.4 | GDScript | 1920x1080 | 60fps
이 문서는 이 프로젝트의 구조와 구성을 정의합니다. 일반 Godot 4 패턴(State Machine, Component, Object Pooling, Autoload Singleton 등)은
/godot-gdscript-patternsskill을 참조하세요.
1. 설계 원칙
| 원칙 | 설명 | 적용 |
|---|---|---|
| Data-Driven | 모든 엔티티 스탯은 JSON에서 런타임 로드. 스크립트에 매직 넘버 금지 | game/data/*.json -> DataManager |
| Component Composition | 깊은 상속 대신 재사용 가능한 컴포넌트 조합으로 엔티티 구성 | HitboxComponent, HealthComponent 등 |
| Signal Decoupling (EventBus) | 시스템 간 통신은 글로벌 EventBus 시그널로. 직접 노드 참조 최소화 | EventBus.enemy_died.emit() |
| Object Pooling | 빈번히 스폰되는 오브젝트는 풀링. 핫 패스에서 queue_free() 금지 | PoolManager.get_instance() |
| Resource-based Config | Godot Resource 타입으로 무기/적/장비 정의. JSON 백엔드 | WeaponData extends Resource |
| State Machine Pattern | 복잡한 상태 전환은 명시적 상태 머신으로 관리 | GameManager, Boss phases |
| Single Responsibility | 각 스크립트는 하나의 책임만. Autoload당 하나의 도메인 | 시스템별 독립 Autoload |
각 패턴의 일반적 구현 방법:
/godot-gdscript-patternsskill 참조
2. 전체 디렉토리 구조
the-hon-joseon/
├── game/ # Godot 프로젝트 루트
│ ├── project.godot
│ ├── src/ # GDScript 소스코드
│ │ ├── main.gd # 엔트리 포인트 / 씬 전환
│ │ ├── main.tscn
│ │ │
│ │ ├── autoloads/ # 글로벌 싱글톤 (12개)
│ │ │ ├── achievement_manager.gd
│ │ │ ├── character_manager.gd
│ │ │ ├── data_manager.gd
│ │ │ ├── equipment_manager.gd
│ │ │ ├── event_bus.gd
│ │ │ ├── ftue_manager.gd
│ │ │ ├── game_manager.gd
│ │ │ ├── passive_manager.gd
│ │ │ ├── pool_manager.gd
│ │ │ ├── save_manager.gd
│ │ │ ├── shrine_manager.gd
│ │ │ └── ui_constants.gd
│ │ │
│ │ ├── components/ # 재사용 노드 컴포넌트
│ │ │ ├── ethereal_shield_component.gd
│ │ │ ├── health_component.gd / .tscn
│ │ │ ├── hitbox_component.gd / .tscn
│ │ │ ├── hurtbox_component.gd / .tscn
│ │ │ └── movement_component.gd
│ │ │
│ │ ├── player/ # 플레이어 엔티티
│ │ │ ├── player.gd
│ │ │ └── player.tscn
│ │ │
│ │ ├── weapons/ # 무기 시스템 (12종)
│ │ │ ├── weapon_base.gd # 추상 무기 베이스
│ │ │ ├── projectile_base.gd # 추상 투사체 베이스
│ │ │ ├── bell/ # 방울
│ │ │ ├── brush/ # 붓
│ │ │ ├── club/ # 육모방망이
│ │ │ ├── crossbow/ # 쇠뇌
│ │ │ ├── ginseng/ # 산삼
│ │ │ ├── goblin_fire/ # 도깨비불
│ │ │ ├── haegeum/ # 해금
│ │ │ ├── sling/ # 돌팔매
│ │ │ ├── talisman/ # 부적
│ │ │ ├── torch/ # 횃불
│ │ │ ├── twin_blades/ # 쌍칼
│ │ │ └── wind_horn/ # 풍각
│ │ │
│ │ ├── enemies/ # 적 시스템
│ │ │ ├── enemy_base.gd / .tscn
│ │ │ ├── spawn_manager.gd
│ │ │ └── bosses/ # 보스 (11종)
│ │ │ ├── boss_base.gd
│ │ │ ├── boss_projectile.gd / .tscn
│ │ │ ├── dragon.gd
│ │ │ ├── fallen_king.gd
│ │ │ ├── fox_clone.gd
│ │ │ ├── ghost_king.gd
│ │ │ ├── kings_shadow.gd
│ │ │ ├── samdo_master.gd
│ │ │ ├── thousand_fox.gd
│ │ │ ├── underworld_gate.gd
│ │ │ ├── underworld_gatekeeper.gd
│ │ │ └── yeomra.gd
│ │ │
│ │ ├── souls/ # 영혼 동행 시스템 (4종)
│ │ │ ├── soul_base.gd
│ │ │ ├── hwang_jin_i/ # 황진이
│ │ │ ├── im_kkeok_jeong/ # 임꺽정
│ │ │ ├── jeon_woo_chi/ # 전우치
│ │ │ └── yi_sun_sin/ # 이순신
│ │ │
│ │ ├── systems/ # 코어 게임 시스템 (12개)
│ │ │ ├── boss_spawn_system.gd
│ │ │ ├── capture_system.gd
│ │ │ ├── chunk_manager.gd
│ │ │ ├── curse_system.gd
│ │ │ ├── game_world.gd / .tscn
│ │ │ ├── level_up_system.gd
│ │ │ ├── obstacle.gd
│ │ │ ├── resonance_system.gd
│ │ │ ├── soul_growth_system.gd
│ │ │ ├── stage_manager.gd
│ │ │ ├── summon_system.gd
│ │ │ ├── weapon_evolution_system.gd
│ │ │ └── yin_yang_system.gd
│ │ │
│ │ ├── equipment/ # 장비 데이터 및 로직
│ │ │
│ │ ├── ui/ # UI 스크립트 (20+)
│ │ │ ├── achievement_panel.gd
│ │ │ ├── achievement_toast.gd
│ │ │ ├── boss_warning_ui.gd
│ │ │ ├── capture_popup.gd
│ │ │ ├── character_select_screen.gd
│ │ │ ├── curse_selection_screen.gd
│ │ │ ├── damage_popup.gd / _manager.gd
│ │ │ ├── ftue_overlay.gd
│ │ │ ├── game_over_panel.gd
│ │ │ ├── hud.gd
│ │ │ ├── inventory_panel.gd
│ │ │ ├── level_up_panel.gd
│ │ │ ├── pause_menu.gd
│ │ │ ├── settings_menu.gd
│ │ │ ├── soul_select_screen.gd
│ │ │ ├── summon_button.gd
│ │ │ └── title_screen.gd
│ │ │
│ │ ├── hub/ # 거점 (무당집)
│ │ │ ├── hub_screen.gd
│ │ │ ├── stage_select_screen.gd
│ │ │ ├── npc_blacksmith.gd
│ │ │ ├── npc_dialog.gd
│ │ │ ├── npc_grandma.gd
│ │ │ ├── npc_gumiho.gd
│ │ │ ├── npc_reaper.gd
│ │ │ └── npc_shrine.gd
│ │ │
│ │ └── pickups/ # 픽업 아이템
│ │ ├── equipment_drop.gd / .tscn
│ │ └── xp_gem.gd / .tscn
│ │
│ ├── data/ # 밸런스 JSON 데이터 (SSOT)
│ │ ├── achievements.json
│ │ ├── capture_codex.json
│ │ ├── characters.json
│ │ ├── curses.json
│ │ ├── enemies.json
│ │ ├── equipment.json
│ │ ├── ftue.json
│ │ ├── levelup.json
│ │ ├── npc_dialogs.json
│ │ ├── shrine.json
│ │ ├── souls.json
│ │ ├── stages.json
│ │ ├── unlocks.json
│ │ ├── weapons.json
│ │ └── waves/ # 웨이브 데이터 (스테이지별)
│ │ └── wave_01.json ~ wave_29.json
│ │
│ ├── res/ # 리소스 (에셋)
│ │ ├── assets.json # 에셋 매니페스트
│ │ ├── sprites/ # 픽셀 아트
│ │ │ ├── characters/
│ │ │ ├── enemies/
│ │ │ ├── bosses/
│ │ │ ├── weapons/
│ │ │ ├── effects/
│ │ │ ├── npcs/
│ │ │ └── ui/
│ │ ├── audio/ # 사운드
│ │ │ ├── bgm/
│ │ │ └── sfx/
│ │ ├── tilesets/ # 스테이지 타일셋
│ │ └── ui/ # UI 테마 및 폰트
│ │
│ ├── test/ # GdUnit4 테스트 (소스 미러 구조)
│ │ ├── autoloads/
│ │ ├── components/
│ │ ├── data/
│ │ ├── enemies/
│ │ ├── equipment/
│ │ ├── hub/
│ │ ├── pickups/
│ │ ├── player/
│ │ ├── souls/
│ │ ├── systems/
│ │ ├── ui/
│ │ ├── weapons/
│ │ └── test_user_flow.gd
│ │
│ └── addons/ # Godot 플러그인 (GdUnit4)
│
├── scripts/ # 자동화 스크립트
├── tools/dashboard/ # Docusaurus + 대시보드
├── docs/ # 문서
├── ops/ # 운영 데이터
└── .claude/ # 에이전트 설정
3. Autoload 시스템
모든 Autoload는 project.godot에 등록되며 전역으로 접근 가능합니다.
Autoload/Singleton 패턴의 일반적 구현:
/godot-gdscript-patternsskill 참조
| Autoload | 경로 | 책임 |
|---|---|---|
| EventBus | autoloads/event_bus.gd | 크로스-시스템 시그널 허브 |
| GameManager | autoloads/game_manager.gd | 게임 상태 머신 (MENU -> PLAYING -> GAME_OVER 등) |
| DataManager | autoloads/data_manager.gd | JSON 데이터 로딩, 캐싱, 조회 |
| PoolManager | autoloads/pool_manager.gd | 적, 투사체, 이펙트 오브젝트 풀링 |
| SaveManager | autoloads/save_manager.gd | 세이브/로드 |
| EquipmentManager | autoloads/equipment_manager.gd | 장비 관리 (인벤토리, 장착) |
| CharacterManager | autoloads/character_manager.gd | 캐릭터 선택/해금 관리 |
| PassiveManager | autoloads/passive_manager.gd | 패시브 아이템 효과 관리 |
| ShrineManager | autoloads/shrine_manager.gd | 사당 영구 업그레이드 |
| AchievementManager | autoloads/achievement_manager.gd | 업적 추적/해금 |
| FtueManager | autoloads/ftue_manager.gd | 초보자 가이드 (First Time User Experience) |
| UIConstants | autoloads/ui_constants.gd | UI 상수/테마 |
3.1 EventBus -- 시그널 카탈로그
이 프로젝트의 실제 시그널 목록:
## 플레이어
signal player_damaged(amount: float, source: Node2D)
signal player_healed(amount: float)
signal player_died
## 적
signal enemy_spawned(enemy: Node2D)
signal enemy_died(enemy: Node2D, position: Vector2)
signal enemy_damaged(enemy: Node2D, amount: float)
## 무기
signal weapon_fired(weapon_id: String)
signal weapon_leveled_up(weapon_id: String, new_level: int)
signal weapon_evolved(weapon_id: String, result_id: String)
## 전투
signal damage_dealt(target: Node2D, amount: float, source: Node2D)
signal xp_gained(amount: float, total_xp: float)
signal xp_collected(amount: float)
signal level_up(new_level: int)
signal levelup_choice_made(choice_type: String, choice_id: String)
## 영혼
signal soul_skill_activated(soul_id: String)
signal soul_xp_gained(soul_id: String, amount: float, total_xp: float)
signal soul_leveled_up(soul_id: String, new_level: int)
signal resonance_changed(value: float)
signal resonance_activated
signal resonance_deactivated
## 음양
signal yin_yang_changed(gauge: float, state: String)
signal yin_yang_state_changed(state: String)
## 장비
signal equipment_dropped(equipment_data: Dictionary, position: Vector2)
signal equipment_collected(equipment_data: Dictionary)
signal equipment_upgraded(result: Dictionary)
## 캐릭터
signal character_selected(character_id: String)
signal ethereal_shield_activated
signal ethereal_shield_consumed
## 포획
signal enemy_captured(enemy_id: String, position: Vector2)
signal capture_failed(enemy_id: String, position: Vector2)
signal boss_capture_attempt(boss_id: String)
## 보스
signal boss_spawned(boss_id: String)
signal boss_damaged(boss_id: String, hp_ratio: float)
signal boss_died(boss_id: String)
signal boss_warning_started(boss_id: String)
signal boss_warning_finished(boss_id: String)
## 소환
signal summon_activated(summon_id: String)
## 저주
signal curses_selected(curse_ids: Array)
## 거점
signal honbaek_earned(amount: int, multiplier: float)
signal npc_interacted(npc_id: String)
signal shrine_upgraded(upgrade_id: String, new_level: int)
signal stage_selected(stage_id: String)
signal soul_selected(soul_id: String)
## 게임 플로우
signal game_started
signal game_paused
signal game_resumed
signal game_over(is_victory: bool)
signal wave_started(wave_number: int)
signal scene_change_requested(scene_name: String)
3.2 GameManager -- 상태 머신
상태 전환 다이어그램:
MAIN_MENU --> HUB --> LOADING --> PLAYING --> PAUSED
| |
| <---------+
v
GAME_OVER --> SETTLEMENT --> HUB
run_data 구조:
run_data = {
"elapsed_time": 0.0, # 경과 시간 (초)
"kill_count": 0, # 처치 수
"xp": 0.0, # 현재 경험치
"level": 1, # 현재 레벨
"weapons": [], # 활성 무기 ID 목록
"passives": [], # 패시브 아이템 목록
"soul_id": "", # 선택된 영혼 ID
"resonance": 0.0, # 공명 게이지 (0~100)
"yang_ratio": 50.0, # 양 비율 (0~100)
"yin_ratio": 50.0, # 음 비율 (0~100)
"equipment_pending": [], # 정산 대기 장비
"curses": [], # 활성 저주 목록
}
3.3 PoolManager -- 풀 레지스트리
Object Pooling 패턴 구현:
/godot-gdscript-patternsskill 참조
이 프로젝트의 풀 레지스트리:
| 풀 이름 | 씬 | 초기 크기 | 최대 확장 | 비고 |
|---|---|---|---|---|
talisman_projectile | talisman_projectile.tscn | 30 | 60 | 가장 흔한 투사체 |
imp_enemy | enemy_base.tscn | 50 | 200 | 첫 적, 대량 스폰 |
xp_gem | xp_gem.tscn | 100 | 300 | 최다 픽업 |
damage_number | damage_number.tscn | 30 | 60 | 플로팅 데미지 |
hit_effect | hit_effect.tscn | 20 | 40 | 타격 VFX |
풀 라이프사이클:
get_instance()-> 풀에서 디큐 -> visible 설정 -> 컨테이너에 reparent -> 반환return_instance()-> invisible 설정 -> 풀로 reparent -> 인큐- 자동 확장: 풀이 비면 새 배치 인스턴스화 (현재 크기 2배, 최대치 캡)
- 게임플레이 중 절대
queue_free()금지 -- 항상 풀로 반환
4. 컴포넌트 시스템
일반 컴포넌트 패턴 (Health, Hitbox, Hurtbox 등) 구현 방법:
/godot-gdscript-patternsskill 참조
4.1 이 프로젝트의 컴포넌트 목록
| 컴포넌트 | 파일 | 역할 |
|---|---|---|
| HealthComponent | health_component.gd | HP 추적 + 사망 시그널 |
| HitboxComponent | hitbox_component.gd / .tscn | 데미지 가함 (Area2D) |
| HurtboxComponent | hurtbox_component.gd / .tscn | 데미지 수신 (Area2D) + 무적 프레임 |
| MovementComponent | movement_component.gd | 속도 기반 이동 (CharacterBody2D용) |
| EtherealShieldComponent | ethereal_shield_component.gd | 일회성 피격 무효화 방패 |
4.2 컴포넌트 상호작용 흐름
플레이어가 적을 공격:
1. 무기가 HitboxComponent를 가진 투사체 생성
2. 투사체의 HitboxComponent가 적의 HurtboxComponent에 진입
3. HurtboxComponent.hurt(hitbox) 시그널 발화
4. 적이 hitbox.damage 읽어서 HealthComponent.take_damage() 호출
5. HealthComponent가 health_changed / died 시그널 발화
6. died -> EventBus.enemy_died.emit(enemy, position)
7. EventBus 리스너 반응: XP 스폰, 공명 충전, 음양 변동, 포획 체크
적이 플레이어를 공격:
1. 적의 HitboxComponent가 플레이어의 HurtboxComponent에 진입
2. 동일 흐름 -> EventBus.player_damaged.emit(amount, source)
3. 음양 시스템: yin += 피격량 기반 값
4.3 충돌 레이어 맵
| Bit | 이름 | 용도 |
|---|---|---|
| 1 | world | 벽, 타일맵 충돌 |
| 2 | player_hitbox | 플레이어 공격 판정 |
| 3 | player_hurtbox | 플레이어 피격 판정 |
| 4 | enemy_hitbox | 적 공격 판정 |
| 5 | enemy_hurtbox | 적 피격 판정 |
| 6 | pickup | 경험치, 장비 드롭 |
5. 엔티티 아키텍처
5.1 Player
Player (CharacterBody2D) # player.gd
+-- Sprite2D # 캐릭터 스프라이트 (flip_h로 방향)
+-- CollisionShape2D # 물리 바디 (8x12 사각형)
+-- HurtboxComponent (Area2D) # 적으로부터 피격
| +-- CollisionShape2D
+-- HealthComponent (Node) # HP 풀
+-- MovementComponent (Node) # 8방향 이동
+-- Camera2D # 플레이어 추적, 월드 바운드
+-- WeaponMount (Node2D) # 무기 노드 동적 추가
+-- [Weapon nodes -- 런타임 추가]
상태: IDLE, MOVING, HURT, DEAD
5.2 Enemy
Enemy (CharacterBody2D) # enemy_base.gd
+-- Sprite2D
+-- CollisionShape2D
+-- HitboxComponent (Area2D) # 접촉 시 플레이어에 데미지
| +-- CollisionShape2D
+-- HurtboxComponent (Area2D) # 무기로부터 피격
| +-- CollisionShape2D
+-- HealthComponent (Node) # HP 추적
+-- MovementComponent (Node) # 추적 / 순찰 로직
행동 패턴 (JSON behavior 필드):
| 패턴 | 설명 | 적 |
|---|---|---|
chase | 플레이어 직선 추적 | 잡귀, 해골 |
fast_chase | 고속 추적 | 도깨비불 |
teleport | 순간이동 + 추적 | 처녀귀신 |
swarm | 무리 지어 이동 | 달걀귀신 |
ranged | 원거리 공격 | 저승사자 |
summoner | 하위 적 소환 | 대귀 |
5.3 Boss
적 베이스를 확장한 다중 페이즈 상태 머신.
Boss (CharacterBody2D) # boss_base.gd extends enemy_base.gd
+-- [Enemy 노드 전부]
+-- PhaseManager (Node) # 페이즈 전환 관리
+-- BossHealthBar (Control) # 화면 하단 보스 체력바
페이즈 시스템: HP 비율에 따라 자동 전환: 100%->66% P1, 66%->33% P2, 33%->0% P3
5.4 Projectile
Projectile (Area2D) # projectile_base.gd
+-- Sprite2D
+-- CollisionShape2D
+-- HitboxComponent (Area2D) # 적에게 데미지
| +-- CollisionShape2D
+-- VisibleOnScreenNotifier2D # 화면 밖 감지 -> 풀 반환
라이프사이클:
PoolManager.get_instance()-> 활성화- 위치/방향/데미지 초기화 (
setup()) - 매 프레임 이동 (
_physics_process) - 히트 또는 화면 밖 ->
PoolManager.return_instance()-> 비활성화
5.5 Pickup
Pickup (Area2D) # pickup: equipment_drop.gd, xp_gem.gd
+-- Sprite2D
+-- CollisionShape2D # pickup 레이어
+-- AnimationPlayer # 바운스/글로우 애니메이션
- XP 젬: 플레이어 접근 시 자석 효과 -> 흡수
- 장비 드롭: 자동 줍기,
run_data.equipment_pending에 추가
6. 무기 시스템
6.1 무기 목록 (12종)
| 무기 | 폴더 | 타입 | 특수 파일 |
|---|---|---|---|
| 부적 (Talisman) | talisman/ | 투사체 | projectile .gd/.tscn |
| 방울 (Bell) | bell/ | 범위 | |
| 육모방망이 (Club) | club/ | 근접 | |
| 붓 (Brush) | brush/ | 투사체 | projectile .gd/.tscn |
| 횃불 (Torch) | torch/ | 투사체 | projectile .gd/.tscn, ground_fire_effect.gd |
| 쇠뇌 (Crossbow) | crossbow/ | 투사체 | projectile .gd/.tscn |
| 풍각 (Wind Horn) | wind_horn/ | 범위 | |
| 산삼 (Ginseng) | ginseng/ | 범위 | poison_effect.gd |
| 도깨비불 (Goblin Fire) | goblin_fire/ | 궤도 | |
| 쌍칼 (Twin Blades) | twin_blades/ | 근접 | |
| 해금 (Haegeum) | haegeum/ | 범위 | |
| 돌팔매 (Sling) | sling/ | 투사체 | projectile .gd/.tscn |