docs: 重新组织文档结构,按开发阶段分类

新的目录结构:
  01-项目入门/     # 新人必读,项目基础
  02-开发规范/     # 编码标准和规范
  03-技术实现/     # 具体开发指导
  04-高级开发/     # 进阶开发技巧
  05-部署运维/     # 发布和部署
  06-功能模块/     # 特定功能文档

 新增导航文档:
- docs/README.md - 完整的文档导航和使用指南
- 各目录下的README.md - 分类说明和使用指导

 优化效果:
- 开发者可以按阶段快速定位需要的文档
- 新人有清晰的学习路径
- 不同角色有针对性的文档推荐
- 提供了问题导向的快速查找功能
This commit is contained in:
2025-12-31 18:02:16 +08:00
parent 2998fd2d11
commit 1ff677b3b2
22 changed files with 449 additions and 0 deletions

View File

@@ -0,0 +1,388 @@
# 实现细节规范
本文档详细说明了WhaleTown项目中各种游戏对象的具体实现要求和技术细节。
## 🎮 玩家实现规范
### 基础要求
- **节点类型**: 必须使用 `CharacterBody2D`
- **移动系统**: 使用 `move_and_slide()` 方法
- **相机集成**: 必须包含 `Camera2D` 子节点
### 玩家节点结构
```
Player (CharacterBody2D)
├── Sprite2D # 玩家精灵
├── CollisionShape2D # 碰撞形状
├── Camera2D # 玩家相机
│ └── [相机配置]
├── InteractionArea (Area2D) # 交互检测区域
│ └── CollisionShape2D
└── AnimationPlayer # 动画播放器
```
### 相机配置要求
```gdscript
# Player.gd 中的相机设置
@onready var camera: Camera2D = $Camera2D
func _ready() -> void:
# 必须启用位置平滑
camera.position_smoothing_enabled = true
camera.position_smoothing_speed = 5.0
# 设置相机边界基于TileMap
_setup_camera_limits()
func _setup_camera_limits() -> void:
# 获取当前场景的TileMap
var tilemap = get_tree().get_first_node_in_group("tilemap")
if tilemap and tilemap is TileMap:
var used_rect = tilemap.get_used_rect()
var tile_size = tilemap.tile_set.tile_size
# 计算世界坐标边界
camera.limit_left = used_rect.position.x * tile_size.x
camera.limit_top = used_rect.position.y * tile_size.y
camera.limit_right = used_rect.end.x * tile_size.x
camera.limit_bottom = used_rect.end.y * tile_size.y
```
### 移动实现模板
```gdscript
extends CharacterBody2D
class_name Player
@export var move_speed: float = 200.0
@export var acceleration: float = 1000.0
@export var friction: float = 1000.0
@onready var sprite: Sprite2D = $Sprite2D
@onready var camera: Camera2D = $Camera2D
func _ready() -> void:
# 设置相机
camera.position_smoothing_enabled = true
_setup_camera_limits()
func _physics_process(delta: float) -> void:
_handle_movement(delta)
func _handle_movement(delta: float) -> void:
# 获取输入方向
var input_direction := Input.get_vector(
"move_left", "move_right",
"move_up", "move_down"
)
# 应用移动
if input_direction != Vector2.ZERO:
velocity = velocity.move_toward(input_direction * move_speed, acceleration * delta)
else:
velocity = velocity.move_toward(Vector2.ZERO, friction * delta)
move_and_slide()
# 发送移动事件
if velocity.length() > 0:
EventSystem.emit_event(EventNames.PLAYER_MOVED, {
"position": global_position,
"velocity": velocity
})
```
## 🤖 NPC实现规范
### 基础要求
- **节点类型**: 使用 `CharacterBody2D``StaticBody2D`
- **交互区域**: 必须包含名为 `InteractionArea``Area2D`
- **事件通信**: 通过 `EventSystem` 触发交互
### NPC节点结构
```
NPC (CharacterBody2D)
├── Sprite2D # NPC精灵
├── CollisionShape2D # 物理碰撞
├── InteractionArea (Area2D) # 交互检测区域
│ └── CollisionShape2D # 交互碰撞形状
├── DialogueComponent # 对话组件
└── AnimationPlayer # 动画播放器
```
### NPC实现模板
```gdscript
extends CharacterBody2D
class_name NPC
@export var npc_name: String = "NPC"
@export var dialogue_resource: DialogueResource
@onready var interaction_area: Area2D = $InteractionArea
@onready var sprite: Sprite2D = $Sprite2D
signal interaction_available(npc: NPC)
signal interaction_unavailable(npc: NPC)
func _ready() -> void:
# 连接交互区域信号
interaction_area.body_entered.connect(_on_interaction_area_entered)
interaction_area.body_exited.connect(_on_interaction_area_exited)
# 监听交互事件
EventSystem.connect_event(EventNames.INTERACT_PRESSED, _on_interact_pressed)
func _on_interaction_area_entered(body: Node2D) -> void:
if body.is_in_group("player"):
interaction_available.emit(self)
# 显示交互提示
_show_interaction_hint()
func _on_interaction_area_exited(body: Node2D) -> void:
if body.is_in_group("player"):
interaction_unavailable.emit(self)
# 隐藏交互提示
_hide_interaction_hint()
func _on_interact_pressed(data: Dictionary = {}) -> void:
# 检查玩家是否在交互范围内
if _is_player_in_range():
start_dialogue()
func start_dialogue() -> void:
EventSystem.emit_event(EventNames.NPC_TALKED, {
"npc": self,
"npc_name": npc_name,
"dialogue": dialogue_resource
})
func _is_player_in_range() -> bool:
var bodies = interaction_area.get_overlapping_bodies()
for body in bodies:
if body.is_in_group("player"):
return true
return false
```
## 🗺️ TileMap图层规范
### 图层配置要求
TileMap必须按以下标准配置图层
#### 图层0地面层 (Ground)
- **用途**: 地面纹理、道路、草地等
- **碰撞**: 禁用物理层
- **渲染**: 最底层渲染
- **Y排序**: 禁用
```gdscript
# 设置地面层
var ground_layer = tilemap.get_layer(0)
tilemap.set_layer_name(0, "Ground")
tilemap.set_layer_enabled(0, true)
tilemap.set_layer_y_sort_enabled(0, false)
# 不设置物理层
```
#### 图层1障碍层 (Obstacles)
- **用途**: 墙壁、树木、建筑等不可通过的障碍
- **碰撞**: 启用物理层
- **渲染**: 中间层
- **Y排序**: 禁用
```gdscript
# 设置障碍层
tilemap.set_layer_name(1, "Obstacles")
tilemap.set_layer_enabled(1, true)
tilemap.set_layer_y_sort_enabled(1, false)
# 设置物理层用于碰撞检测
tilemap.set_layer_physics_enabled(1, true)
```
#### 图层2装饰层 (Decoration)
- **用途**: 装饰物、前景元素
- **碰撞**: 根据需要设置
- **渲染**: 最上层
- **Y排序**: 启用(重要!)
```gdscript
# 设置装饰层
tilemap.set_layer_name(2, "Decoration")
tilemap.set_layer_enabled(2, true)
tilemap.set_layer_y_sort_enabled(2, true) # 启用Y排序
tilemap.set_layer_y_sort_origin(2, 16) # 设置排序原点
```
### TileMap设置模板
```gdscript
extends TileMap
class_name GameTileMap
func _ready() -> void:
# 设置TileMap为tilemap组
add_to_group("tilemap")
# 配置图层
_setup_layers()
# 通知相机系统更新边界
EventSystem.emit_event(EventNames.TILEMAP_READY, {
"tilemap": self,
"used_rect": get_used_rect()
})
func _setup_layers() -> void:
# 确保有足够的图层
while get_layers_count() < 3:
add_layer(-1)
# 配置地面层 (0)
set_layer_name(0, "Ground")
set_layer_y_sort_enabled(0, false)
# 配置障碍层 (1)
set_layer_name(1, "Obstacles")
set_layer_y_sort_enabled(1, false)
set_layer_physics_enabled(1, true)
# 配置装饰层 (2)
set_layer_name(2, "Decoration")
set_layer_y_sort_enabled(2, true)
set_layer_y_sort_origin(2, tile_set.tile_size.y / 2)
```
## 🎯 交互物实现规范
### 可收集物品
```gdscript
extends Area2D
class_name CollectibleItem
@export var item_name: String = "Item"
@export var item_value: int = 1
@onready var sprite: Sprite2D = $Sprite2D
@onready var collision: CollisionShape2D = $CollisionShape2D
func _ready() -> void:
body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node2D) -> void:
if body.is_in_group("player"):
collect_item(body)
func collect_item(collector: Node2D) -> void:
# 发送收集事件
EventSystem.emit_event(EventNames.ITEM_COLLECTED, {
"item_name": item_name,
"item_value": item_value,
"collector": collector,
"position": global_position
})
# 播放收集动画
_play_collect_animation()
func _play_collect_animation() -> void:
var tween = create_tween()
tween.parallel().tween_property(self, "scale", Vector2.ZERO, 0.3)
tween.parallel().tween_property(self, "modulate:a", 0.0, 0.3)
await tween.finished
queue_free()
```
### 可交互对象
```gdscript
extends StaticBody2D
class_name InteractableObject
@export var interaction_text: String = "按E交互"
@export var can_interact: bool = true
@onready var interaction_area: Area2D = $InteractionArea
@onready var sprite: Sprite2D = $Sprite2D
var player_in_range: bool = false
func _ready() -> void:
interaction_area.body_entered.connect(_on_interaction_area_entered)
interaction_area.body_exited.connect(_on_interaction_area_exited)
EventSystem.connect_event(EventNames.INTERACT_PRESSED, _on_interact_pressed)
func _on_interaction_area_entered(body: Node2D) -> void:
if body.is_in_group("player") and can_interact:
player_in_range = true
_show_interaction_prompt()
func _on_interaction_area_exited(body: Node2D) -> void:
if body.is_in_group("player"):
player_in_range = false
_hide_interaction_prompt()
func _on_interact_pressed(data: Dictionary = {}) -> void:
if player_in_range and can_interact:
interact()
func interact() -> void:
# 子类重写此方法实现具体交互逻辑
print("", name, " 交互")
EventSystem.emit_event(EventNames.OBJECT_INTERACTED, {
"object": self,
"interaction_type": "default"
})
```
## 🎨 资源过滤设置
### 纹理过滤规范
所有像素艺术资源必须使用最近邻过滤:
```gdscript
# 在导入设置中或代码中设置
func _setup_pixel_perfect_texture(texture: Texture2D) -> void:
if texture is ImageTexture:
var image = texture.get_image()
image.generate_mipmaps(false)
# 在导入设置中设置Filter为Off
```
### 导入设置模板
对于所有精灵资源,在导入设置中:
- **Filter**: Off (关闭)
- **Mipmaps**: Off (关闭)
- **Fix Alpha Border**: On (开启)
## 🔧 性能优化要求
### 节点缓存
```gdscript
# ✅ 正确:使用@onready缓存节点引用
@onready var sprite: Sprite2D = $Sprite2D
@onready var collision: CollisionShape2D = $CollisionShape2D
func _process(delta: float) -> void:
sprite.modulate.a = 0.5 # 使用缓存的引用
# ❌ 错误在_process中重复获取节点
func _process(delta: float) -> void:
$Sprite2D.modulate.a = 0.5 # 每帧都要查找节点
```
### 事件频率控制
```gdscript
# 控制事件发送频率
var last_event_time: float = 0.0
const EVENT_INTERVAL: float = 0.1 # 100ms间隔
func _process(delta: float) -> void:
var current_time = Time.get_time_dict_from_system()
if current_time - last_event_time >= EVENT_INTERVAL:
EventSystem.emit_event(EventNames.POSITION_UPDATE, {
"position": global_position
})
last_event_time = current_time
```
---
**记住:遵循这些实现细节规范可以确保游戏对象的一致性和性能!**