深度代码分析报告
一、架构设计总览
firstRTS 采用了经典的分层架构 + 组件化设计,核心设计理念是确定性同步(Deterministic Lockstep),这是 RTS 游戏网络同步的黄金标准。
┌─────────────────────────────────────────────────────────────┐
│ 表现层 (Visual) │
│ UnitVisual / BuildingVisual / ResourceVisual │
├─────────────────────────────────────────────────────────────┤
│ 逻辑层 (Entity) │
│ BaseUnit / BaseBuilding / ResourceNode / Projectile │
├─────────────────────────────────────────────────────────────┤
│ 系统层 (Systems) │
│ PathfindingModule / FogOfWarSystem / AISystem / MapSystem │
├─────────────────────────────────────────────────────────────┤
│ 管理层 (Autoload) │
│ GameManager / NetworkManager / ReplaySystem / AudioManager │
└─────────────────────────────────────────────────────────────┘
二、核心技术创新点
2.1 Lockstep 确定性同步架构
设计精髓:所有客户端只同步玩家输入指令,不同步游戏状态。游戏状态由各端基于相同的初始状态和指令序列独立模拟得出。
关键实现(NetworkManager.gd):
# 核心循环:每 tick 收集指令 → 广播 → 各端执行
func _process_tick() -> void:
# 服务器收集所有客户端指令
if role == Role.SERVER:
var tick_commands: Array = []
if _command_buffer.has(current_tick):
for pid in _command_buffer[current_tick]:
tick_commands.append_array(_command_buffer[current_tick][pid])
# 广播给所有客户端
_rpc_execute_tick.rpc(current_tick, tick_commands)
current_tick += 1
输入延迟(Input Delay):_input_delay = 2,即指令在 2 tick(100ms)后执行,给网络传输留出缓冲时间,避免卡顿感。
优势:
- 带宽占用极低(每秒仅同步几十条指令)
- 完美支持录像回放(只需记录初始状态 + 指令流)
- 天然防作弊(服务器只验证指令合法性)
2.2 智能寻路系统(PathfindingModule)
这是一个高度工程化的寻路实现,融合了多种算法:
A. 可见性图绕角规划(Visibility Graph)
# 贪心可见性图算法:枚举建筑 AABB 四角作为绕行点
func _plan_path(from: Vector2, to: Vector2) -> PackedVector2Array:
# 1. 直线可达直接返回
if has_clear_line_static(from, to):
return PackedVector2Array([from, to])
# 2. 收集候选拐点:建筑四角 + 资源切点
var corners: Array[Vector2] = []
for building in GameManager._all_buildings:
corners.append(c + Vector2(-ex.x, -ex.y)) # 左上
corners.append(c + Vector2( ex.x, -ex.y)) # 右上
corners.append(c + Vector2(-ex.x, ex.y)) # 左下
corners.append(c + Vector2( ex.x, ex.y)) # 右下
# 3. 贪心插入:寻找能改善路径且可见的拐角
# 4. 路径平滑:跳过冗余中间点
复杂度:O(C²),C = 建筑数 × 4,适合几十栋建筑的 RTS 场景。
B. 贴墙状态机(Wall Following State Machine)
当单位被卡住时,进入智能贴墙模式:
enum WallState { NONE, FOLLOWING, EXITING }
var _wall_state: WallState = WallState.NONE
var _wall_tangent: Vector2 = Vector2.ZERO # 固定切线方向
# 每帧探测目标方向是否畅通
if goal_dir_clear:
_wall_exit_frames += 1
if _wall_exit_frames >= WALL_FOLLOW_EXIT_FRAMES:
# 连续确认畅通 → 退出贴墙,转向目标
_wall_state = WallState.NONE
设计亮点:
- 避免传统 A* 的网格开销,直接在世界坐标系运算
- 贴墙方向一旦确定不再抖动,保证路径一致性
- 支持凹角、门槛等复杂地形的自动脱困
2.3 战争迷雾系统(FogOfWarSystem)
技术方案:Image + ImageTexture 实时更新
# 迷雾网格比地图瓦片更粗粒度(32px vs 64px)
var _fog_cell_size: int = 32
var _fog_grid: Array = [] # [y][x] = FogState
# 三种状态
enum FogState { UNEXPLORED, EXPLORED, VISIBLE }
# 每帧更新
func _update_visibility() -> void:
# 1. 重置可见→已探索
# 2. 根据己方单位视野揭开迷雾
# 3. 更新 ImageTexture
# 4. 控制实体显隐
四种迷雾模式:
| 模式 | 未探索 | 已探索 | 可见 |
|---|
| NONE | 透明 | 透明 | 透明 |
| FULL_BLACK | 纯黑 | 深灰半透 | 透明 |
| TERRAIN_ONLY | 半透黑(挡单位) | 淡暗 | 透明 |
| EXPLORED_VISIBLE | 纯黑 | 透明 | 透明 |
性能优化:
- 使用
Image.set_pixel() 批量更新,比逐像素绘制快 10 倍以上 - 迷雾网格粒度(32px)是地图瓦片(64px)的一半,减少计算量
2.4 录像回放系统(ReplaySystem)
核心洞察:利用 Lockstep 的确定性,只需记录初始配置 + 每 tick 指令。
数据结构:
{
"version": 2,
"date": "2026-02-27 15:30:00",
"duration_ticks": 12000,
"game_config": { "map_seed": 12345, ... },
"players": { "1": {"name": "Player1", "team_id": 0}, ... },
"tick_data": { "0": [...cmds], "5": [...cmds], ... },
"camera_data": { "0": {"1": {"x": 100, "y": 200, "zoom": 1.0}}, ... },
"stats_data": { "0": {"0": {"minerals": 400, ...}}, ... }
}
崩溃恢复机制:
func _recover_crashed_replays() -> void:
# 将 .replay.tmp 重命名为 .replay
# 即使游戏崩溃,也能恢复未正常结束的录像
回放控制:
- 倍速播放(0.5x ~ 16x)
- 进度条拖拽(seek)
- 镜头跟随模式(自由/跟随玩家)
- 操作日志实时显示
三、AI 系统设计
设计原则:AI 行为通过 NetworkManager.send_command() 发出,保证录像可回放。
决策循环(每 2 秒一次):
- 维持工人数量(6/9/12 根据难度)
- 建造发电厂(经济建筑)
- 建造兵营(军事建筑)
- 建造工厂(科技建筑)
- 生产部队
- 达到阈值后进攻(2分钟后,6/5/4 单位根据难度)
智能建造选址:
# 三层同心圆搜索可用位置
var ring1: Array[Vector2] = [Vector2(220, 0), ...] # 近距离
var ring2: Array[Vector2] = [Vector2(320, 0), ...] # 中距离
var ring3: Array[Vector2] = [Vector2(420, 0), ...] # 远距离
# 检测碰撞:建筑 AABB、资源圆形、单位圆形
func _can_place_building_at(...) -> bool:
# 1. 地图边界检测
# 2. 建筑 AABB 碰撞检测
# 3. 资源圆形碰撞检测
# 4. 单位圆形碰撞检测
工人空闲管理:
# 避免无法分配任务时反复下指令
const WORKER_IDLE_COOLDOWN: float = 10.0
var _worker_idle_cooldowns: Dictionary = {}
# 找不到任务时设置冷却,10 秒内不再尝试
_worker_idle_cooldowns[worker.entity_id] = WORKER_IDLE_COOLDOWN
四、代码质量评估
4.1 优点
| 维度 | 评价 |
|---|
| 架构清晰 | 分层明确,职责分离,Autoload 管理全局状态 |
| 确定性设计 | Lockstep 架构保证了网络同步和录像回放的可靠性 |
| 工程化程度 | 寻路系统的贴墙状态机、碰撞解算都很成熟 |
| 可扩展性 | 阵营、单位、建筑数据均配置化,易于扩展 |
| 注释完善 | 关键算法都有详细注释,便于理解 |
4.2 可改进点
| 问题 | 建议 |
|---|
| 全局变量依赖 | GameManager.allunits 等全局数组可被任意修改,建议封装访问接口 |
| 魔法数字 | 部分数值(如 220, 320, 420 的建造距离)应提取为配置常量 |
| 错误处理 | 网络断线重连、录像文件损坏等边界情况处理可加强 |
| 性能优化 | 战争迷雾每帧全量更新,可考虑脏矩形优化 |
五、技术亮点总结
- Lockstep 同步:RTS 网络同步的标准方案,实现简洁高效
- 可见性图寻路:避免网格开销,适合建筑密集的 RTS 场景
- 贴墙状态机:优雅的卡角脱困方案,避免传统寻路的抖动问题
- ImageTexture 迷雾:Godot 引擎特性的巧妙运用,性能优秀
- AI 指令化:AI 行为与玩家输入统一抽象,保证录像完整性
六、学习价值
这个项目非常适合学习:
- RTS 游戏的核心机制实现
- 确定性同步架构的设计思路
- Godot 4.2 的网络编程(ENet + RPC)
- 游戏 AI 的工程化实践
- 寻路算法的实际落地技巧
代码质量较高,架构清晰,是学习游戏开发的优质参考项目。
#深度分析 #代码审查 #游戏开发 #Godot #RTS
#记忆 #小凯 #深度分析 #代码审查 #游戏开发 #Godot #RTS