refactor:重构项目架构为分层结构 #3

Merged
moyin merged 3 commits from qbb0530/whale-town-front:refactor/项目结构重构 into main 2025-12-31 16:35:25 +08:00
Showing only changes of commit b9182bbc2e - Show all commits

157
CLAUDE.md
View File

@@ -1,77 +1,96 @@
# Claude Code Rules - Godot 4.x Town Project
# 🎯 CLAUDE.md - WhaleTown Project Instructions
## Project Overview
A 2D top-down pixel art RPG built in Godot 4.x using a layered architecture.
- **Core**: `_Core/` (SceneManager, EventSystem, GameManager)
- **Gameplay**: `Scenes/` (Maps, Entities, Components)
- **UI**: `UI/` (Windows, Overlays)
## 1. Project Vision & Context
- **Project**: "WhaleTown" - A 2D top-down pixel art RPG.
- **Engine**: Godot 4.2+ (Strictly NO Godot 3.x syntax).
- **Architecture**: Strictly layered: `_Core` (Framework), `Scenes` (Gameplay), `UI` (Interface).
- **Core Principle**: "Signal Up, Call Down". High decoupling via `EventSystem`.
## Coding Standards (GDScript)
- **Type Safety**: Use static typing strictly (e.g., `var speed: float = 100.0`, `func _ready() -> void`).
## 2. 🛠 Command Reference & Setup
- **Input Map (Required Configuration)**:
- `move_left`, `move_right`, `move_up`, `move_down` (WASD / Arrows)
- `interact` (E Key / Space)
- `pause` (ESC)
- **Run Game**: `godot --path .`
- **Run Tests (GUT)**: `godot --headless -s addons/gut/gut_cmdline.gd -gdir=res://tests/ -ginclude_subdirs`
- **Init Structure**: `mkdir -p _Core/managers _Core/systems Scenes/Maps Scenes/Entities Scenes/Components UI/Windows UI/HUD Assets/Sprites tests/unit tests/integration`
## 3. 📂 File Path Rules (STRICT LOWERCASE)
*Claude: Root folders MUST be lowercase. Scripts and Scenes MUST stay together.*
- **Core Managers**: `_Core/managers/[Name].gd`
- **Core Systems**: `_Core/systems/[Name].gd`
- **Entities**: `Scenes/Entities/[EntityName]/[EntityName].tscn` (Script `.gd` in same folder).
- **Maps**: `Scenes/Maps/[map_name].tscn`
- **Components**: `Scenes/Components/[ComponentName].gd` (Reusable logic nodes).
- **UI Windows**: `UI/Windows/[WindowName].tscn`
- **Tests**: `tests/[unit|integration]/test_[name].gd` (Folder is lowercase `tests`).
## 4. 📋 Coding Standards (The Law)
- **Type Safety**: ALWAYS use strict static typing: `var speed: float = 100.0`, `func _ready() -> void`.
- **Naming Conventions**:
- Classes: `PascalCase` (using `class_name`).
- Variables/Functions: `snake_case`.
- Constants: `SCREAMING_SNAKE_CASE`.
- Signals: `snake_case` (e.g., `signal player_interacted(data)`).
- **Node Access**: Use `@onready var name = $Path` or `%UniqueName`. Prefer `%UniqueName` for UI and internal scene components.
- **Best Practices**:
- Use `await` for signals and timers instead of `yield`.
- Use `Callable` for callbacks.
- Follow the "Signal Up, Call Down" rule: Parent calls methods on children; children emit signals to parents.
- Use `Input.get_vector("left", "right", "up", "down")` for 2D movement.
- `class_name PascalCase` at the top of every script.
- Variables/Functions: `snake_case`. Constants: `SCREAMING_SNAKE_CASE`.
- Private members: Prefix with underscore `_` (e.g., `var _health: int`).
- **Node Access**: Use `%UniqueName` for UI and internal scene components.
- **Signals**: Use "Signal Up, Call Down". Parent calls child methods; Child emits signals.
- **Forbidden Patterns**:
- ❌ NO `yield()` -> Use `await`.
- ❌ NO `get_node()` in `_process` -> Cache with `@onready`.
- ❌ NO Linear Filter -> All Sprite2D/TileMap resources MUST use **Nearest** filter.
## Project Architecture Rules
- **Decoupling**: Never reference singleton managers directly in low-level entities. Use `EventSystem` to communicate across modules.
- **Scene Management**: All scene transitions must go through `SceneManager.change_scene(scene_key)`.
- **Components**: Logic like "Health", "Interactions", or "Movement" should be encapsulated in `Scenes/Components/` as reusable nodes.
## 5. 🏛 Architecture & Communication
- **EventSystem**: Use `_Core/systems/EventSystem.gd` for cross-module messaging.
- **Event Registry**: Use `class_name EventNames` in `_Core/EventNames.gd`.
```gdscript
class_name EventNames
const PLAYER_MOVED = "player_moved"
const INTERACT_PRESSED = "interact_pressed"
const NPC_TALKED = "npc_talked"
Singletons: Only GameManager, SceneManager, EventSystem allowed as Autoloads.
Decoupling: Low-level entities MUST NOT reference GameManager. Use events.
6. 🏗 Implementation Details
Player: CharacterBody2D. Must include Camera2D with position_smoothing_enabled = true.
NPC/Interactables: Use Area2D named InteractionArea. Trigger via EventSystem.
TileMap Layers:
Layer 0: Ground (No collision).
Layer 1: Obstacles (Physics Layer enabled).
Layer 2: Decoration (Y-Sort enabled).
Camera: Must auto-calculate limits via TileMap.get_used_rect().
7. 🧪 Testing Requirements (MANDATORY)
Coverage: Every Manager/System in _Core/ MUST have a GUT test.
Naming: Test files must start with test_ and extend GutTest.
Example:
code
Gdscript
extends GutTest
func test_event_emission():
var sender = Node.new()
watch_signals(EventSystem)
EventSystem.emit_event(EventNames.PLAYER_MOVED, {})
assert_signal_emitted(EventSystem, "event_raised")
8. 🧘 The Zen of Development
Juice or Death: Every interaction (UI popup, NPC talk) MUST have a Tween placeholder.
Zero Magic Numbers: All speeds/timers MUST be @export or defined in Config/.
Simplicity: If a function does two things, split it.
Back of the Fence: Hidden logic (like ResponseHandler.gd) must be as clean as the HUD.
9. 📝 Code Template (Entity Pattern)
code
Gdscript
extends CharacterBody2D
class_name Player
## Implementation Specifics (Town Map)
# 1. Exports & Constants
@export var move_speed: float = 200.0
### 1. Entity Structure
- **Player**: Must be a `CharacterBody2D`. Include a `Camera2D` with `position_smoothing_enabled = true`.
- **NPC**: Must be a `StaticBody2D` or `CharacterBody2D` with an `Area2D` named `InteractionArea`.
- **Interactables**: Use a shared `InteractableComponent` (Area2D based) for building entrances and NPCs.
# 2. Node References
@onready var sprite: Sprite2D = %Sprite2D
### 2. Interaction System
- NPC/Entrance interaction must trigger via `EventSystem`.
- Interaction logic:
1. Player enters `InteractionArea`.
2. Player presses "E" or "UI_Accept".
3. Emit `EventSystem.notify_interaction_triggered(interactable_data)`.
# 3. Lifecycle
func _physics_process(delta: float) -> void:
_move(delta)
### 3. TileMap Rules
- Use `TileMap` (or `TileMapLayer` if Godot 4.3+).
- Layer 0: Ground (No collision).
- Layer 1: Obstacles/Walls (With Physics Layer collision).
- Layer 2: Decorations (Y-Sort enabled).
## File Organization
- New maps: `Scenes/Maps/[map_name].tscn`
- New entities: `Scenes/Entities/[entity_name]/[entity_name].tscn`
- New scripts: Same folder as their scene files.
- Assets: Always reference assets from `res://Assets/Sprites/`.
## Command Reference
- Build/Run: `godot --path .`
- Export (Linux): `godot --path . --export-release "Linux/X11"`
## Testing Standards
- **Framework**: Use GUT (Godot Unit Test) for all tests.
- **Test Location**: All test scripts must be in `res://test/unit/` or `res://test/integration/`.
- **File Naming**: Test files must start with `test_` and end with `.gd` (e.g., `test_player_movement.gd`).
- **Class Structure**: Every test file must `extends GutTest`.
- **Requirements**:
- Every new core logic (in `_Core`) must have a corresponding unit test.
- Player movement and NPC interactions must have integration tests using `yield_for` or `await`.
- Use `assert_eq`, `assert_true`, and `assert_signal_emitted` for validations.
## Commands
- **Run all tests**: `godot --headless -s addons/gut/gut_cmdline.gd -gdir=res://test/ -ginclude_subdirs`
- **Run specific test**: `godot --headless -s addons/gut/gut_cmdline.gd -gtest=res://test/unit/test_example.gd`
## The Zen of Development (Pro Tips)
- **Simplicity is the ultimate sophistication**: If a function does two things, it's one thing too many. Split it.
- **The "Back of the Fence" Rule**: Even parts the player never sees (like the EventSystem logic) must be clean and beautiful. No "quick hacks".
- **Feedback Loop**: Every interaction MUST have a placeholder for juice (Tween, Sound, or Particle). Code is the skeleton, but juice is the soul.
- **Zero Hard-Coding**: All paths, configuration values, and magic numbers must be defined as constants or @export variables.
- **The "It Just Works" Camera**: Camera logic must automatically find TileMap limits. The player should never see the "gray void" outside the map.
# 4. Private Methods
func _move(_delta: float) -> void:
var dir := Input.get_vector("move_left", "move_right", "move_up", "move_down")
velocity = dir * move_speed
move_and_slide()