工人任务分配系统

在矮人要塞 like 的游戏中,都有一套基于工人的任务分发系统。玩家通常不能像 RTS 中那样直接操作工人去工作,而是对要做的事情下达任务,等着工人自主去完成。

由于任务数量通常远多于工人数量,这个任务分发系统中大多配有优先级设置,可以让诸多任务有条不紊的进行。调整优先级变成玩家主动操控的渠道。初玩这类游戏,会有点不习惯:感觉难以在微观层面直接做自己像做的事情。像捡块石头放进指定仓库这件事,无法像玩 RTS 游戏那样,先点选工人,再针对石头发出拾取指令…… 但习惯之后,恐怕又回不去了。比如我在玩 Ratopia 时,就对操控鼠王直接干活烦躁不已。

这类游戏,我玩的时间比较长的有三个,按时长排序为:缺氧 (ONI) 、边缘世界 (Rimworld)、矮人要塞 (DF)。其它如 Songs of Syx 、Prison Architect 等很多也有所涉猎。其实,这些游戏在设计工人任务系统的细节上也有所不同。

以我游戏时长最长的缺氧和边缘世界相比较,同样是提供玩家主动操控的能力:Rimworld 可以给工人的任务队列直接下达指令(这更接近 RTS 的玩法),而 ONI 则是通过给单个任务本身排优先级实现的。ONI 设计了警报级任务,可以越过一切优先级设定,强制立刻完成。虽然 ONI 也保留了指挥单个小人移动到指定位置,但实际游戏中几乎没什么用。

对于拾取物品,Rimworld 可以封禁、解禁单个物品,而 ONI 没有这个设计。ONI 的工人几乎不会主动把地上的东西搬入仓库,除非下达清扫指令。

这些细节的不同,可能来源于作者设计时的思维轨迹,很大程度上也取决于游戏的其他玩法。例如 Rimworld 偏重手控成分很重的战斗,而 ONI 没有战斗成分。Rimworld 强调人物之间的情感联系,ONI 里的都是工具人。

我比较喜欢 ONI 的系统,打算用这个规则打底设计自己的游戏。下面是设计的草稿:

游戏场景中代做的事情全部被视为任务,任务需要由工人完成。

任务构成要素主要由对象和行为构成。对象大多为场景中的建筑,也可以是其它一些活动角色,例如某个工人或敌人。

行为决定了任务的类型,而每种任务类型有一个预设的“类型权重”;玩家可以对任务所属对象设置一个“对象权重”。

每个工人有自己的任务队列。工人可以设置对任务类型的“偏好权重”,任务对象在场景中的位置和工人之间的距离决定了任务的“位置权重”。将每个任务的所有权重相乘,得到任务分配给每个工人的最终权重。同一个任务会分别进入每个工人的任务队列中。

有些任务是分配给特定工人的。例如,工人需要周期进食,氧气不足时会就近补充氧气,等等。也会排入对应的任务队列。

各个任务队列定期刷新,将归属的任务以权重排序。工人从高到低依次完成任务。因为一个任务可以被分配到多个队列中,所以,可以出现工人当前任务被取消的情况。如果扩展战斗系统,而攻击敌人也属于任务的话,同一个任务也可以被并行执行。

比较特殊的是搬运任务,它通常是建造任务的一个环节,即给建造任务提供原料。它需要把原料从一个地点搬运到建造蓝图的地方。这种搬运任务有两个地点。但给建造蓝图供料时,原料可以有很多候选。我想到两种实现方法:

第一,当一个建造任务被发布后,所有可用的原料均被发布一个供应的子任务,根据和原料和建造任务的距离,给予不同的权重。这样,工人再根据自身的位置,如果开始就近执行一个搬运任务,就立刻把其它搬运任务取消。第二,不考虑不同原料的位置,只要原料可达,就发布一个供应任务,每个工人在考量建造任务权重时,只考虑自己和建造工地的距离。

看起来,第一个方案看起来会有更聪明的表现。因为会倾向于让离原料近的工人就近拿到原料开始搬运。但从实现上看,第二个方案更简单。因为它没有把搬运任务做特殊处理。只在工人执行任务时,再寻找原料。寻早原料变成执行任务的过程,而不在计算权重和分配任务阶段进行。

关于寻路

昨天我实现了基本的寻路模块。一开始,我认为需要的基本功能是:标记出场景中从 A 点到 B 点的路径。由于场景是玩家一点点搭建起来,随时变化,所以很难对场景做充分的预处理。这样一个寻路模块的时间及空间复杂度都不会太低,而在游戏中恐怕不会太低频使用它。我实现了两版都不是很满意,所以又回头来回顾需求。

结合任务系统来考虑,我突然发现,其实真正需要的不是找到任务 A B 两点间的通行路径,而是找到从同一个点出发,到场景中很多点的路径。因为,任务本身是有位置属性的,这个位置属性决定了它在每个工人的队列中的不同权重;或者从工人角度看,他同时会面对多个不同位置的任务,需要根据距离远近排序。所以,更合理的基础功能应该是:针对每个工人的当前位置,计算出距离可达区域每个位置的行动路线,这用几行代码就可以生成。同理,如果像找到一个建筑任务的所有原料供应点的远近,也可以使用相同的算法。

工人只在开始准备一个新任务时,才需要做一次计算。而一旦他处在执行一个任务的过程中,就不再需要实时计算距离其它任务地的路径。

文章来源:

Author:云风
link:https://blog.codingnow.com/2024/07/task_system.html