从 0 到 1:一个 SLG 游戏的基础架构、Tick 优化与扩展方向

在写 SLG(策略类)游戏时,真正决定系统能走多远的,从来不是玩法堆得有多满,而是底层结构是否稳定、是否可扩展

这篇文章记录的是我在实现一套 SLG 服务端基础架构过程中的完整思路:

  • 如何从 0 到 1 搭建一个最小可运行的 SLG 骨架
  • 在规模出现之前,提前解决 tick 驱动下的性能问题
  • 以及这套结构未来可以如何自然扩展

一、设计起点:先保证结构能活下去

在一开始,我并没有急着实现复杂玩法,而是先给整个系统定下了几个不会轻易动摇的前提:

  • 所有时间统一使用离散 tick,1 tick = 1 秒
  • 地图结构与业务逻辑彻底解耦
  • 玩家不是地图实体,队伍才是
  • 行军、战斗都不做全量遍历,而是事件驱动

SLG 的复杂度,本质来自于:

大量对象 × 长时间等待 × 少量瞬时活跃

如果一开始就用“每 tick 扫一遍所有数据”的方式实现,后面规模一上来,几乎必然推倒重写。


二、地图层:节点是唯一中心

slg_map

slg_map 是整个地图系统的总入口,负责协调:

  • 节点信息
  • 行军调度
  • 战斗调度

它本身不承载具体业务规则,而是作为调度和聚合中心存在。


graph:动态无权无向图

地图使用的是动态无权无向图

graph 只负责一件事:

  • 管理节点与节点之间是否连通

它不关心:

  • 节点归属
  • 行军时间
  • 战斗规则

相邻节点之间的距离是一个逻辑约定值:

  • 固定为 90 tick

这样做的好处是:

  • 地图结构足够稳定
  • 后续加入地形、科技、BUFF 修正时,不需要修改 graph 本身

node_info:节点业务信息

所有与节点相关的业务数据,都集中在 node_info 中,包括:

  • 节点类型(大本营、小城、中城、大城、都城)
  • 当前归属
  • 是否存在守军
  • 节点的战斗状态与战斗队列

在这套设计中,节点是 SLG 一切行为的中心


三、行军系统:队伍才是地图实体

行军规则

  • 玩家本身没有位置信息
  • 只有队伍(team)可以行军
  • 队伍从己方大本营节点出发

行军是按节点推进的:

  • 每跨越一个相邻节点消耗 90 tick
  • 到达节点后立即判断节点状态

节点处理规则非常明确:

  • 空节点:直接占领
  • 己方节点:允许继续行军
  • 他方节点:停止行军,进入战斗流程

march_mgr:行军管理器

最直观、也最危险的实现方式是:

  • 每个 tick 遍历所有正在行军的队伍

在队伍数量较少时问题不大,但这类实现方式几乎注定无法扩展

因此,在行军调度上,引入了时间轮机制。


四、第一次优化:行军时间轮

行军的本质,是一个延迟触发事件:

  • 队伍会在未来某个 tick 抵达下一个节点

march_mgr 使用时间轮来管理行军:

  • 行军阶段注册为时间轮任务
  • 时间轮随 tick 推进
  • 只有到期的行军任务才会被处理

这样做的结果是:

  • 不再遍历所有行军队伍
  • tick 推进只处理“现在该发生的事情”
  • 调度复杂度从 O(N) 降为 O(K)

这是系统从“能跑”走向“能长期跑”的关键一步。


五、战斗系统:节点驱动,而不是全图扫描

battle_unit 抽象

所有可参与战斗的单位统一抽象为 battle_unit,包括:

  • 玩家队伍
  • 节点守军 NPC

它们共享统一的战斗接口、状态和冷却规则。


npc:节点守军

npc 继承自 battle_unit,只存在于节点之上:

  • 节点被占领时生成
  • 被击败后移除

战斗基本规则

战斗以节点为单位调度:

  • 一个 tick 内,一个节点最多处理 5 场 1v1 战斗
  • 一个队伍在一个 tick 内只能参与一次战斗

当行军队伍进入敌方节点时:

  • 行军终止
  • 队伍进入节点战斗队列

六、第二次优化:节点战斗时间轮

如果每个 tick 都遍历所有节点检查战斗状态,性能同样不可接受。

因此,battle_mgr 同样采用时间轮思想:

  • 只有存在战斗队列的节点才注册战斗任务
  • 时间轮触发时才执行战斗
  • 每次最多处理 5 场

若战斗尚未结束:

  • 再注册下一次 tick 的战斗事件

最终效果是:

  • 没有战斗的节点完全不消耗计算资源
  • 热点节点才成为性能消耗点
  • 战斗调度完全事件化

七、战斗结算与闭环

战斗结算规则保持简单一致:

  • 胜利方占领节点
  • 节点生成新的守军
  • 失败方队伍返回大本营

队伍后续状态重新交由 march_mgr 管理,形成完整闭环。


八、为什么 Tick + 时间轮非常适合 SLG

SLG 中大量行为都具有共同特征:

  • 行军等待
  • 战斗排队
  • 建筑完成
  • Buff 到期

它们都是:

  • 延迟触发
  • 长时间等待
  • 少量同时活跃

时间轮的优势正好与之匹配:

  • 不扫描
  • 不轮询
  • 只处理到期事件

九、后续扩展方向

在当前架构基础上,可以非常自然地扩展:

  • 地形、科技对行军时间的修正
  • 多队伍混战与协同作战
  • 联盟协防与援军机制
  • 节点战斗优先级与排队策略
  • 世界事件、赛季事件的 Tick 注入

这些扩展都不需要推翻结构,只是在节点与时间轮之上叠加规则。


结语

SLG 的难点不在于系统数量,而在于规模增长后的稳定性。

只要做到:

  • 地图结构足够纯粹
  • 行军与战斗完全事件化
  • tick 推进不做全量遍历

这套 SLG 架构,就可以从 0 跑到 1,再从 1 稳定地跑到 N。

--完--