新的目录结构: 01-项目入门/ # 新人必读,项目基础 02-开发规范/ # 编码标准和规范 03-技术实现/ # 具体开发指导 04-高级开发/ # 进阶开发技巧 05-部署运维/ # 发布和部署 06-功能模块/ # 特定功能文档 新增导航文档: - docs/README.md - 完整的文档导航和使用指南 - 各目录下的README.md - 分类说明和使用指导 优化效果: - 开发者可以按阶段快速定位需要的文档 - 新人有清晰的学习路径 - 不同角色有针对性的文档推荐 - 提供了问题导向的快速查找功能
11 KiB
11 KiB
实现细节规范
本文档详细说明了WhaleTown项目中各种游戏对象的具体实现要求和技术细节。
🎮 玩家实现规范
基础要求
- 节点类型: 必须使用
CharacterBody2D - 移动系统: 使用
move_and_slide()方法 - 相机集成: 必须包含
Camera2D子节点
玩家节点结构
Player (CharacterBody2D)
├── Sprite2D # 玩家精灵
├── CollisionShape2D # 碰撞形状
├── Camera2D # 玩家相机
│ └── [相机配置]
├── InteractionArea (Area2D) # 交互检测区域
│ └── CollisionShape2D
└── AnimationPlayer # 动画播放器
相机配置要求
# 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
移动实现模板
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实现模板
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排序: 禁用
# 设置地面层
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排序: 禁用
# 设置障碍层
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排序: 启用(重要!)
# 设置装饰层
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设置模板
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)
🎯 交互物实现规范
可收集物品
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()
可交互对象
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"
})
🎨 资源过滤设置
纹理过滤规范
所有像素艺术资源必须使用最近邻过滤:
# 在导入设置中或代码中设置
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 (开启)
🔧 性能优化要求
节点缓存
# ✅ 正确:使用@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 # 每帧都要查找节点
事件频率控制
# 控制事件发送频率
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
记住:遵循这些实现细节规范可以确保游戏对象的一致性和性能!