21 KiB
设计文档
概述
本项目是一款基于 Godot 4.x 引擎开发的 2D 多人在线 AI 小镇游戏,采用客户端-服务器架构。游戏的核心特性是持久化的多人世界,玩家创建的角色在玩家离线时会作为 NPC 继续存在于游戏世界中。游戏优先支持网页端(HTML5 导出),并预留移动端适配接口。
技术栈
- 游戏引擎: Godot 4.x
- 编程语言: GDScript
- 网络协议: WebSocket (用于实时通信)
- 数据格式: JSON (用于数据序列化)
- 导出平台: HTML5 (Web), 预留 Android/iOS 支持
- 后端服务: 简单的 WebSocket 服务器 (可使用 Node.js + ws 或 Python + websockets)
架构
整体架构
游戏采用客户端-服务器架构,分为以下主要层次:
┌─────────────────────────────────────────┐
│ 客户端 (Godot HTML5) │
├─────────────────────────────────────────┤
│ 表现层 (UI/Rendering) │
│ ├─ 场景渲染 │
│ ├─ UI 界面 │
│ └─ 动画系统 │
├─────────────────────────────────────────┤
│ 游戏逻辑层 │
│ ├─ 角色控制器 │
│ ├─ 对话系统 │
│ ├─ 输入处理 │
│ └─ 状态管理 │
├─────────────────────────────────────────┤
│ 网络层 │
│ ├─ WebSocket 客户端 │
│ ├─ 消息序列化/反序列化 │
│ └─ 状态同步 │
└─────────────────────────────────────────┘
↕ WebSocket
┌─────────────────────────────────────────┐
│ 服务器 (WebSocket Server) │
├─────────────────────────────────────────┤
│ 连接管理 │
│ ├─ 客户端连接池 │
│ ├─ 身份验证 │
│ └─ 心跳检测 │
├─────────────────────────────────────────┤
│ 游戏状态管理 │
│ ├─ 世界状态 │
│ ├─ 角色状态 │
│ └─ 消息广播 │
├─────────────────────────────────────────┤
│ 数据持久化 │
│ ├─ 角色数据存储 │
│ ├─ 世界状态存储 │
│ └─ JSON 文件系统 │
└─────────────────────────────────────────┘
客户端架构
客户端采用 Godot 的场景树结构,主要节点组织如下:
Main (Node)
├─ NetworkManager (Node) - 网络连接管理
├─ GameStateManager (Node) - 游戏状态管理
├─ UILayer (CanvasLayer) - UI 层
│ ├─ LoginScreen (Control) - 登录界面
│ ├─ CharacterCreation (Control) - 角色创建界面
│ ├─ HUD (Control) - 游戏内 UI
│ └─ DialogueBox (Control) - 对话框
└─ GameWorld (Node2D) - 游戏世界
├─ TileMap (TileMap) - 场景地图
├─ Characters (Node2D) - 角色容器
│ ├─ PlayerCharacter (CharacterBody2D) - 本地玩家
│ └─ RemoteCharacter (CharacterBody2D) - 其他角色
└─ Camera (Camera2D) - 摄像机
服务器架构
服务器采用事件驱动架构,主要模块包括:
- ConnectionManager: 管理 WebSocket 连接
- AuthenticationService: 处理玩家身份验证
- WorldState: 维护游戏世界状态
- CharacterManager: 管理所有角色(在线/离线)
- MessageRouter: 路由和广播消息
- PersistenceService: 数据持久化服务
组件和接口
1. 网络管理器 (NetworkManager)
职责: 管理客户端与服务器的 WebSocket 连接
接口:
class_name NetworkManager extends Node
signal connected_to_server()
signal disconnected_from_server()
signal connection_error(error: String)
signal message_received(message: Dictionary)
func connect_to_server(url: String) -> void
func disconnect_from_server() -> void
func send_message(message: Dictionary) -> void
func is_connected() -> bool
消息协议: 所有消息使用 JSON 格式,包含以下字段:
{
"type": "message_type",
"data": {},
"timestamp": 1234567890
}
消息类型包括:
auth_request: 身份验证请求auth_response: 身份验证响应character_create: 创建角色character_move: 角色移动character_state: 角色状态更新dialogue_send: 发送对话world_state: 世界状态同步
2. 角色控制器 (CharacterController)
职责: 处理角色的移动、动画和状态
接口:
class_name CharacterController extends CharacterBody2D
var character_id: String
var character_name: String
var is_online: bool
var move_speed: float = 200.0
func move_to(direction: Vector2) -> void
func set_position_smooth(target_pos: Vector2) -> void
func play_animation(anim_name: String) -> void
func set_online_status(online: bool) -> void
状态:
idle: 静止状态walking: 行走状态talking: 对话状态
3. 对话系统 (DialogueSystem)
职责: 管理角色之间的对话交互
接口:
class_name DialogueSystem extends Node
signal dialogue_started(character_id: String)
signal dialogue_ended()
signal message_received(sender: String, message: String)
func start_dialogue(target_character_id: String) -> void
func send_message(message: String) -> void
func end_dialogue() -> void
func show_bubble(character_id: String, message: String, duration: float) -> void
对话模式:
- 直接对话: 玩家与另一个角色一对一对话
- 气泡对话: 附近角色的对话以气泡形式显示
4. 输入处理器 (InputHandler)
职责: 处理多平台输入(键盘、触摸、虚拟摇杆)
接口:
class_name InputHandler extends Node
signal move_input(direction: Vector2)
signal interact_input()
signal ui_input(action: String)
func get_move_direction() -> Vector2
func is_interact_pressed() -> bool
func setup_virtual_controls() -> void
输入映射:
- 桌面端: WASD/方向键移动,E 键交互
- 移动端: 虚拟摇杆移动,交互按钮
5. 游戏状态管理器 (GameStateManager)
职责: 管理游戏的全局状态和场景切换
接口:
class_name GameStateManager extends Node
enum GameState {
LOGIN,
CHARACTER_CREATION,
IN_GAME,
DISCONNECTED
}
var current_state: GameState
var player_data: Dictionary
func change_state(new_state: GameState) -> void
func save_player_data() -> void
func load_player_data() -> Dictionary
6. 世界管理器 (WorldManager)
职责: 管理游戏世界中的所有角色和对象
接口:
class_name WorldManager extends Node
var characters: Dictionary = {} # character_id -> CharacterController
func spawn_character(character_data: Dictionary) -> void
func remove_character(character_id: String) -> void
func update_character_state(character_id: String, state: Dictionary) -> void
func get_nearby_characters(position: Vector2, radius: float) -> Array
数据模型
角色数据 (Character Data)
{
"id": "unique_character_id",
"name": "角色名称",
"owner_id": "player_account_id",
"position": {
"x": 100.0,
"y": 200.0
},
"is_online": true,
"appearance": {
"sprite": "character_01",
"color": "#FFFFFF"
},
"created_at": 1234567890,
"last_seen": 1234567890
}
世界状态 (World State)
{
"scene_id": "datawhale_office",
"characters": [
// 角色数据数组
],
"timestamp": 1234567890
}
对话消息 (Dialogue Message)
{
"sender_id": "character_id",
"receiver_id": "character_id", // 可选,为空表示广播
"message": "对话内容",
"timestamp": 1234567890
}
正确性属性
属性是一个特征或行为,应该在系统的所有有效执行中保持为真——本质上是关于系统应该做什么的正式陈述。属性作为人类可读规范和机器可验证正确性保证之间的桥梁。
属性 1: 角色创建唯一性
对于任意两个成功创建的角色,它们的角色 ID 应该是唯一的,不会发生冲突 验证需求: 1.5
属性 2: 角色移动能力
对于任意创建或加载到场景的角色,该角色应该具有基础移动能力(可以响应移动指令) 验证需求: 2.1
属性 3: 位置更新同步
对于任意角色的移动操作,执行移动后角色的位置坐标应该被更新,并且该更新应该同步到所有连接的客户端 验证需求: 2.2
属性 4: 碰撞检测
对于任意角色和障碍物,当角色尝试移动到障碍物位置时,系统应该阻止该移动,角色位置保持不变 验证需求: 2.4
属性 5: 并发移动独立性
对于任意多个同时移动的角色,每个角色的移动应该独立处理,一个角色的移动不应影响其他角色的移动逻辑 验证需求: 2.5
属性 6: 对话触发
对于任意两个角色,当一个角色接近另一个角色并触发交互时,系统应该显示对话界面 验证需求: 3.1
属性 7: 消息传递完整性
对于任意对话消息,当玩家发送消息时,该消息应该被传递给接收方,并在发送方和接收方的界面上都正确显示 验证需求: 3.3
属性 8: 对话状态恢复
对于任意进行中的对话,当对话结束时,系统应该关闭对话界面并将游戏状态恢复到对话前的正常状态 验证需求: 3.4
属性 9: 对话可见性
对于任意两个角色之间的对话,附近的其他角色应该能够看到对话气泡显示在对话角色上方 验证需求: 3.5
属性 10: 场景碰撞检测
对于任意玩家角色和场景元素(如桌椅、墙壁),当玩家尝试移动到场景元素位置时,系统应该正确处理碰撞并阻止穿透 验证需求: 4.5
属性 11: 键盘输入响应
对于任意有效的键盘移动输入(方向键或 WASD),系统应该将玩家角色移动到相应方向,并将移动数据同步到服务器 验证需求: 5.1
属性 12: 触摸输入响应
对于任意有效的触摸移动输入(虚拟摇杆),系统应该将玩家角色移动到指定方向,并将移动数据同步到服务器 验证需求: 5.2
属性 13: 移动动画同步
对于任意角色的移动操作,系统应该播放相应的移动动画,并在所有客户端同步显示该动画 验证需求: 5.5
属性 14: 响应式布局适配
对于任意不同分辨率的浏览器窗口,游戏画面应该自动调整尺寸以适应窗口大小,保持可玩性 验证需求: 6.3, 7.2
属性 15: 窗口动态调整
对于任意浏览器窗口大小的改变,游戏系统应该动态调整游戏画面比例,保持界面元素的可访问性 验证需求: 6.4
属性 16: 数据序列化往返
对于任意游戏数据对象,序列化为 JSON 后再反序列化应该得到等价的对象(往返一致性) 验证需求: 7.3, 9.5
属性 17: 设备类型检测
对于任意设备类型(桌面或移动),系统应该正确检测设备类型并应用相应的控制方案(键盘或触摸) 验证需求: 7.5
属性 18: 角色数据持久化
对于任意创建的角色,角色数据应该被保存到服务器,后续登录时应该能够恢复相同的角色数据 验证需求: 9.1, 9.2
属性 19: 状态同步
对于任意角色位置或状态的改变,系统应该将更新同步到服务器,确保数据一致性 验证需求: 9.4
属性 20: 移动设备 UI 适配
对于任意在移动设备上运行的游戏,UI 元素大小应该调整以适应触摸操作(按钮足够大,间距合理) 验证需求: 10.4
属性 21: 错误提示显示
对于任意操作失败的情况,系统应该显示明确的错误提示信息,告知用户失败原因 验证需求: 10.5
属性 22: 在线角色显示
对于任意玩家进入游戏场景时,系统应该显示所有当前在线玩家的角色 验证需求: 11.1
属性 23: 离线角色显示
对于任意玩家进入游戏场景时,系统应该显示所有离线玩家的角色作为 NPC 验证需求: 11.2
属性 24: 上线状态切换
对于任意玩家上线事件,系统应该在场景中显示该玩家的角色,并将其标记为在线状态 验证需求: 11.3
属性 25: 下线状态切换
对于任意玩家下线事件,系统应该将该玩家的角色转换为离线 NPC 状态,但角色继续存在于场景中 验证需求: 11.4
属性 26: 角色状态视觉区分
对于任意显示的角色,系统应该通过视觉标识(如颜色、图标)区分在线玩家和离线角色 验证需求: 11.5
属性 27: 网络连接建立
对于任意玩家登录操作,系统应该尝试建立与服务器的网络连接 验证需求: 12.1
属性 28: 断线重连
对于任意网络连接中断事件,系统应该显示断线提示并自动尝试重新连接到服务器 验证需求: 12.3
属性 29: 操作确认
对于任意玩家执行的操作,系统应该将操作数据发送到服务器并等待接收确认响应 验证需求: 12.4
属性 30: 服务器更新同步
对于任意服务器推送的更新消息,客户端应该实时更新本地游戏状态以反映服务器的变化 验证需求: 12.5
错误处理
网络错误处理
连接失败:
- 显示友好的错误提示
- 提供重试按钮
- 记录错误日志用于调试
连接中断:
- 自动尝试重连(最多 3 次)
- 显示断线状态指示器
- 缓存未发送的操作,重连后重新发送
消息发送失败:
- 重试机制(指数退避)
- 超时后通知用户
- 保持本地状态一致性
数据验证错误
角色创建验证:
- 名称长度限制(2-20 字符)
- 禁止特殊字符和空白字符
- 检查名称唯一性
输入验证:
- 对话消息长度限制(1-500 字符)
- 过滤恶意输入
- 防止注入攻击
状态不一致处理
客户端-服务器状态不一致:
- 定期同步状态(每 5 秒)
- 服务器状态为权威状态
- 客户端预测 + 服务器校正
角色位置冲突:
- 服务器检测位置冲突
- 强制回退到有效位置
- 通知客户端更新
测试策略
单元测试
使用 Godot 的 GUT (Godot Unit Test) 框架进行单元测试。
测试覆盖范围:
- 数据模型的序列化/反序列化
- 输入处理逻辑
- 状态管理器的状态转换
- 消息协议的编码/解码
示例测试:
# test_character_data.gd
extends GutTest
func test_character_serialization():
var character = {
"id": "test_123",
"name": "测试角色",
"position": {"x": 100.0, "y": 200.0}
}
var json_str = JSON.stringify(character)
var parsed = JSON.parse_string(json_str)
assert_eq(parsed["id"], character["id"])
assert_eq(parsed["name"], character["name"])
属性基础测试
使用 GDScript 实现简单的属性测试框架,或使用社区提供的属性测试库。
测试库: 自定义实现或使用 Godot 社区的属性测试工具
测试配置: 每个属性测试至少运行 100 次迭代
测试标注格式: # Feature: godot-ai-town-game, Property X: [属性描述]
属性测试覆盖:
-
属性 1: 角色创建唯一性
- 生成多个随机角色
- 验证所有角色 ID 唯一
-
属性 2: 角色移动能力
- 生成随机角色
- 验证角色可以响应移动指令
-
属性 3: 位置更新同步
- 生成随机移动操作
- 验证位置正确更新
-
属性 4: 碰撞检测
- 生成随机障碍物和移动路径
- 验证碰撞正确阻止移动
-
属性 16: 数据序列化往返
- 生成随机游戏数据对象
- 验证序列化后反序列化得到等价对象
-
属性 18: 角色数据持久化
- 生成随机角色数据
- 验证保存后加载得到相同数据
示例属性测试:
# test_properties.gd
extends GutTest
# Feature: godot-ai-town-game, Property 16: 数据序列化往返
func test_property_serialization_roundtrip():
for i in range(100):
var random_data = generate_random_character_data()
var serialized = JSON.stringify(random_data)
var deserialized = JSON.parse_string(serialized)
assert_eq_deep(deserialized, random_data,
"序列化往返应该保持数据一致性")
func generate_random_character_data() -> Dictionary:
return {
"id": "char_" + str(randi()),
"name": "角色" + str(randi() % 1000),
"position": {
"x": randf_range(0, 1000),
"y": randf_range(0, 1000)
},
"is_online": randi() % 2 == 0
}
集成测试
场景加载测试:
- 测试 Datawhale 办公室场景正确加载
- 验证所有必需的节点存在
- 检查碰撞层设置正确
网络集成测试:
- 启动测试服务器
- 测试客户端连接流程
- 验证消息收发正确
多客户端测试:
- 模拟多个客户端连接
- 测试状态同步
- 验证角色互动
性能测试
帧率测试:
- 测试不同数量角色时的帧率
- 目标: 30+ FPS (10 个角色)
网络延迟测试:
- 测试不同网络条件下的响应时间
- 目标: 操作响应 < 200ms
内存使用测试:
- 监控长时间运行的内存使用
- 检测内存泄漏
跨平台测试
浏览器兼容性:
- Chrome (最新版本)
- Firefox (最新版本)
- Safari (最新版本)
- Edge (最新版本)
设备测试:
- 桌面 (1920x1080, 1366x768)
- 平板 (iPad, Android 平板)
- 手机 (iOS, Android)
输入测试:
- 键盘输入
- 鼠标输入
- 触摸输入
- 虚拟摇杆
场景设计
Datawhale 办公室场景
场景尺寸: 2000x1500 像素
主要区域:
- 入口区域: 门、欢迎标识
- 工作区: 办公桌、电脑、椅子
- 会议区: 会议桌、白板
- 休息区: 沙发、茶水间
- 展示区: Datawhale logo、成就墙
碰撞层设置:
- Layer 1: 墙壁和固定障碍物
- Layer 2: 家具(可选择性碰撞)
- Layer 3: 角色
- Layer 4: 交互区域
视觉风格:
- 2D 俯视角(45度等距视角可选)
- 简洁的像素艺术或矢量风格
- Datawhale 品牌色: 蓝色系为主
资源需求
图像资源:
- 角色精灵图(4 方向行走动画)
- 场景瓦片集(地板、墙壁、家具)
- UI 元素(按钮、对话框、图标)
- Datawhale logo 和品牌元素
音频资源 (可选):
- 背景音乐
- 脚步声
- UI 交互音效
- 对话提示音
部署和构建
Web 导出配置
Godot 导出设置:
- 目标平台: HTML5
- 导出模板: Godot 4.x Web
- 线程支持: 启用(如果浏览器支持)
- VRAM 压缩: 启用
Web 服务器要求:
- 支持 WebSocket
- HTTPS (用于生产环境)
- CORS 配置正确
服务器部署
推荐部署方案:
- 静态文件: Nginx/Apache 或 CDN
- WebSocket 服务器: Node.js 或 Python
- 数据存储: JSON 文件或轻量级数据库
环境变量:
SERVER_URL: WebSocket 服务器地址PORT: 服务器端口DATA_DIR: 数据存储目录
移动端预留
响应式设计:
- 使用 Godot 的 Viewport 缩放
- UI 元素使用相对定位
- 字体大小动态调整
输入抽象:
- 统一的输入接口
- 自动检测输入设备
- 虚拟控制器自动显示/隐藏
跨端数据同步:
- 统一的数据格式
- 服务器端状态管理
- 客户端无状态设计
扩展性考虑
未来功能预留
AI 对话系统:
- 预留 AI 接口(如 OpenAI API)
- 对话历史记录
- 角色个性设置
更多场景:
- 场景管理器支持多场景
- 场景切换机制
- 场景间传送
社交功能:
- 好友系统
- 私聊功能
- 角色关系网络
性能优化预留
对象池:
- 角色对象复用
- UI 元素复用
- 减少内存分配
网络优化:
- 消息批处理
- 状态差异同步
- 区域兴趣管理(AOI)
渲染优化:
- 视锥剔除
- LOD (细节层次)
- 纹理压缩