Files
2025-12-05 19:00:14 +08:00

21 KiB
Raw Permalink Blame History

设计文档

概述

本项目是一款基于 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. 属性 1: 角色创建唯一性

    • 生成多个随机角色
    • 验证所有角色 ID 唯一
  2. 属性 2: 角色移动能力

    • 生成随机角色
    • 验证角色可以响应移动指令
  3. 属性 3: 位置更新同步

    • 生成随机移动操作
    • 验证位置正确更新
  4. 属性 4: 碰撞检测

    • 生成随机障碍物和移动路径
    • 验证碰撞正确阻止移动
  5. 属性 16: 数据序列化往返

    • 生成随机游戏数据对象
    • 验证序列化后反序列化得到等价对象
  6. 属性 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 像素

主要区域:

  1. 入口区域: 门、欢迎标识
  2. 工作区: 办公桌、电脑、椅子
  3. 会议区: 会议桌、白板
  4. 休息区: 沙发、茶水间
  5. 展示区: 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 (细节层次)
  • 纹理压缩