分享一下队列的妙用

队列系统 · 0.5可视化

让游戏流程清晰到如同水洗过一样!

最近在折腾自己的游戏框架,最初我想把项目流程做成一张流程图——用节点加连线,搞点可视化展示。
实践下来却发现:

  • 项目太简单时,画图纯属多余;
  • 项目一复杂,连线多到吓死人,根本理不清。

后来试着用了队列,瞬间觉得“真香”!于是干脆在队列上做文章,把整个流程串起来。


举个实际例子:回合制战斗

假设角色 A 攻击怪物 B。

1. 最简流程

A 攻击 B → 造成伤害 → 死亡判定
若 B 没死,继续循环回合。

2. 加一点复杂度:B 有反伤能力

A 攻击 B → 伤害计算 → 死亡判定 → 反伤计算 → 再判定 A 是否被反伤击杀。

3. 再加一点:A 有吸血能力

A 攻击 B → 伤害计算 → 吸血计算 + 反伤计算
问题来了:吸血和反伤谁先算?
如果反伤先算,A 被反到血量归零,还能靠吸血起死回生吗?

4. 再再复杂一点:多段攻击 + 概率吸血

  • A 的一次攻击可能是多段的;
  • 吸血效果可能是概率触发的。

那么:

  • 前几段就击杀敌人时,后续攻击还继续吗?
  • 前几段就被反伤弹死时,后续攻击触发的回血能把自己拉回来吗?

老练的开发者会仔细推敲这些细节,但现实往往是——“先这样试试”,几天后让你改机制。


队列机制如何优雅解决?

队列的核心思想很简单:将任务封装、入队、依次执行。
因为任务都在队列里,所以可以方便地插队——这是最强大的功能。

示例队列 ①(吸血无法起死回生)

攻击任务 → 伤害计算 → 反伤计算 → 敌人死亡判定 → 玩家死亡判定 → 吸血计算

吸血在死亡判定之后,所以无法起死回生。

示例队列 ②(吸血先于反伤)

攻击任务 → 伤害计算 → 吸血计算 → 反伤计算 → 敌人死亡判定 → 玩家死亡判定

吸血在反伤之前,先吸血再计算反伤。

示例队列 ③(多轮攻击,允许鞭尸和弹后复活)

攻击任务 → 攻击轮次计数 → 伤害计算 → 反伤计算 → 吸血计算 → 轮次切换任务
(轮次未满时,插入 伤害计算、反伤计算;满了就跳过)→ 死亡判断任务

死亡判断在多轮攻击之后,所以多轮攻击不会中断,允许鞭尸,也可能被弹死后靠后续回血起死回生。


更进一步:任务嵌套与子任务

为了更好地处理插队和清除,我将任务队列本身也设计成任务类,允许一个任务拥有子任务。
这样结构更清晰,也便于批量移除某一条子队列。

举个实际结构:

battle_task
 └─ player_turn_task
     ├─ player_attack
     │   ├─ 单段攻击任务
     │   │   ├─ damage_task
     │   │   │   ├─ (vampire_task 可插入此处)
     │   │   │   └─ (反伤 task 可插入此处)
     │   │   └─ check_dead_task
     │   │       └─ 若敌人死亡 → battle_task 清空
     │   │          (非单一敌人时,增加 check_win_task)
     │   └─ 段数切换任务
     │       └─ 递增段数,未满则再插入一轮单段攻击
     └─ turn_switch_task
         └─ 切换阵营,插入 player_turn_task 或 enemy_turn_task + turn_switch_task

根据角色是否吸血/敌人是否反伤,player_attack 中会动态插入 vampire_task 或反伤判定任务。
也可以固定插入,然后在任务内部根据条件跳过。


更复杂的场景也能 Hold 住

在实际工作中,我还遇到过:

  • 反击:敌人被攻击后可能触发反击,反击带有攻击附带的各类效果判定,比单纯反伤复杂得多;
  • 援击:你攻击时,队友有概率触发援击,帮你补一发。

这些机制在任务队列系统下,都能清晰地拆分和插入。
你只需要考虑 任务拆到什么粒度,以及 在哪个位置插入 即可。


0.5 可视化的含义

任务执行时,每一步的任务树结构都可以通过命令逐步打印出来,运行流程一目了然。

相比传统的流程图,你无法一眼看到完整的全貌,但你可以逐步看到当前正在执行和准备执行的任务——这就是我所说的 “0.5 可视化”。


适用场景

这种方法特别适合:
:white_check_mark: 流程复杂
:white_check_mark: 需要频繁修改机制
:white_check_mark: 希望快速调整执行顺序和插入新逻辑

如果你也在纠结复杂的游戏流程,不妨试试队列思路,或许会让你豁然开朗 :smile:

3赞

吸血是第一次伤害的附带性质。反伤是在承受了第一次伤害没死以后的第二次伤害流程。

没有先反伤再吸血这种设定的。肯定是伤害同时加血-没死反伤。这样才是合理的。

关于战斗,可以参考知乎这篇 https://zhuanlan.zhihu.com/p/416805924?share_code=nwg0oFTg2ZYR&utm_psn=2080638912100307801

我觉得它的设计是相对比较合理的。

你的看法我赞同,不过我不是和你讨论怎么设计合理。 吸血可能是先吸再反伤合理。如果换一个,击杀敌人触发回血,你觉得是先被弹死还是回血回回来? :yum: 实际工作中,很容易出现这类设计上不明确的问题。虽然这主要是策划的工作,不过往往会因为策划设计时无法预先考虑好具体情况,导致中途要改。 甚至有时候,预先考虑好的机制大家都觉得是对的,但是实际游玩的时候,这种机制玩家不喜欢。

刚刚我反思了一下,确实有些游戏设计是可以先反伤再吸血,这是游戏机制设计的取舍。

不过话题跑偏了,队列能支持这种设计的改动。

嗯,你说的是方案的鲁棒性问题。

额,没咋看懂

pipeline+循环~

额,这样说吧,就等于把你原本的一个个函数,变成了一个个对象,然后放在了队列中。你可以清晰地看到某些函数是在哪个过程中被插入。 可以看到后续应该做什么。 由于程序中经常会有if else等分支逻辑,完整地一个流程图是会很复杂的。 但是这种队列结构,有一点可视化,但是又没有将整个流程全一口气展示出来,可以比较清晰地展示你现在的逻辑流程。 并且你可以通过控制插入执行的顺序,快速修改逻辑机制。

一个行为树就处理了 不需要那么麻烦!!!

回合制有个经典反击问题,兽王鹰击长空打五下,第二下时,被人反击打死了,队列机制能不能处理

你的要求如若是第二下反击时死亡立即终止,那就将死亡判定task放在每一轮攻击后。 死亡判定一旦确认死亡,则将后续的task都清除。

我不清楚你说的行为树是怎么实现这种需求的。在我看来用这种队列已经是非常简洁了,我不觉得这样算麻烦。 方便说一下行为树如何实现这类需求吗?

对于多种怪物,多种英雄的情况,队列可以处理吗,比如有的英雄有吸血,有的英雄有反伤,有的英雄有复活,然后有的怪物特殊技能,特殊机制,独有机制,类似这样的情况

你说的多种怪物,多种英雄是什么情况?举个例子给我。

分析的很好哦,在编程领域这是数据结构的一种,叫队列,在工程领域,这叫工作流,在工厂,这叫流水线…,它的本质就是时序的分工组合。但它本身只是众多结构中的一种,并不能单独完成更复杂的需求,随着需求越来越复杂,这种结构就会演变成树形或图形,而这个“队列”只是主轴。

我用过工作流。 不知道是不是我理解的不一样,我看到的工作流和流程图的感觉类似,都是一整个完整的图。我在使用队列的时候,感受到的亮点是,它没有将完整的流程完全展示出来,这样反而避免了项目体量大的时候,各个分工节点之间复杂的连线造成的视觉混乱。 所以目前我很喜欢用这个队列的方式,只要稍微加上自己的一点封装,将每一步任务的插入、执行记录下来,增加接口可以查看每一步队列里的状态的变化,就可以比较清晰地查看出机制上的问题。

很好的分享,这种队列的方式对“即时回合制”适用吗? 因为即时回合制很多机制和时间相关,并不是简单的轮次或者顺序关系, 你这个队列设计方便加入时间/延迟/定时器的设计吗?

我这套做过一定的实践验证,之前几周时间在疯狂复刻杀戮尖塔2。 但是你说的即时类的我没有试过,目前没有加上时间相关的封装。 你可以举一个具体一点的例子吗? 我来思考一下如何优化。 这的确也是一个很好的扩充点。

命令模式 + 组合模式 + 队列。真正有价值的是把行为 Task 化并组合起来,队列更多只是负责按顺序执行。

请问能分享一点具体一点的经验吗?有没有什么实践案例?