HTN框架介绍、开发心得

20 篇文章 13 订阅

分层任务网络:HTN

1. 特点

  • HTN和GOAP有很多相似共通之处,这类模型的AI可以创造他们自己的方法去解决问题
  • HTN支持推演
    • 以子弹和枪为例,一个任务是拿子弹,一个是拿枪,要求同时拿到子弹和枪的总时间最少,这样一个简单的需求可能就会卡掉状态机和行为树了。状态机和行为树只能硬编码,而HTN只需要编写好两个任务即可。
  • HTN可以在众多方案中选出最优方案执行
    • 通过条件检测、情景预测,选出代价最小的合法计划进行执行

这个Case无论先到最近的球或者方块都不行
在这里插入图片描述
最佳路径为
在这里插入图片描述

2. 世界状态(World State)

预测是推演的基石,为此,我们需要具体化三件事情来支持预测,你会发现后面的GOAP也做了这样的处理:

  • 世界的状态
  • 行为的条件
  • 行为的影响

HTN需要使用数值去描述这个世界中,与AI决策相关的状态,例如自身各属性、目标各属性、是否有枪、有多少颗子弹。这些数值的集合,便是HTN内的世界状态——World State。

在抽象出World State后,我们可以使用World State去描绘行为的条件和影响。例如,开枪需要子弹和枪,并将减少子弹的数量。

当预测了开枪的条件和影响后,我们就可以借此进行推演。例如,我可能需要先去找子弹,再开枪,子弹的数量能不能打死目标。

3. 任务(Task)

任务就是行为的抽象,每个任务都可以有条件和影响

  • 条件:需要在什么样的世界状态下才能执行
  • 影响:执行后会对世界状态造成什么改变

例如:

任务条件影响
寻敌/获得目标
移动至目标有目标在目标附近
攻击有目标、在目标附近降低目标生命值

任务的条件和影响与世界状态的交织,架构起了整个HTN的框架。

把条件判断内聚到任务内可以降低心智负担,不需要在编写HTN时对每个任务的条件进行判断,因为在制定计划时会逐一检测。

4. 计划(Plan)

计划 = = = 任务链

4.1 推演(合法性)

我们使用以下流程进行计划的推演

  • 拷贝一份当前的WorldState
  • 判断WorldState是否满足第一个任务的条件
  • 若满足,将该任务的影响施加到WorldState上
  • 后方的任务在此WorldState的基础上重复判断条件 - 施加影响

经过推演,寻敌 → \to 移动至目标 → \to 攻击 的计划合法,可以被执行

4.2 抉择(优先级)

多个任务的排列组合,组成了众多计划,在不同情况下,排列组合的最优化策略不同。HTN需要在众多计划中,做出抉择,定下当前的最佳计划。

计划由天平判断优劣,而具体化后的代价,就是HTN做出抉择的砝码。例如,移动的代价就可以是时间开销。

在代价非负的情况下可以使用堆优化BFS进行优化,一边推演一边抉择。

4.3 代价(Cost)

小任务的不同排列组合之间怎么决定优先级是一个比较复杂的问题。

例如该攻击还是移动,这个事情真的可以通过所谓的代价来决定吗?我们需要限制Cost对MakePlan的影响,凡是不能量化比较的指标都要避免进行Cost的比较。

4.3.1 如何使用代价

攻击行为的指标可以是对所有敌方造成的伤害之和,移动行为的指标可以是花费的时间。指标都不一样,那把攻击和移动拉到同一纬度比较就很奇怪了。

那么回到前面的话题,该攻击还是移动怎么控制?大体两种,一种是在编写HTN的适合进行一个优先级分配,如果可以攻击就攻击,不能攻击就移动,当然这个也可以通过条件Cost做到;第二种是脱离HTN,在控制器内进行控制,通过修改某个数据进行对HTN行为的影响。

因为代价是正数,我们会把正面影响部分和一个固定代价并列。 例如在恰当时机放一个技能应该是正面收益,但是直接放到HTN内部则还是会有代价,还是会被“可不做就不做”。所以相对的,我们给技能部分并列一个代价,例如100,那么如果给合适的技能代价设为低于100,则会有限考虑这部分。

4.4 重计划(Re-Plan)

计划赶不上变化,世界状态变化后,无法保证当前计划的可行性。

所以,需要在执行计划过程中判断是否需要重计划,有以下两种可能:

  • 当前运行的任务,条件不符合
  • 计划里的某个任务,条件不符合

5. 额外开销

为了赋予AI推演的能力,HTN需要付出很多开销:

  • HTN需要额外空间记录世界状态,并且与AI决策相关的状态都需要被记录
  • 为了满足不同分支的推导,在执行一步时,所有状态都需要进行一次拷贝,用于容纳后续任务的修改。
  • 凡是会影响世界状态的行为,都需要在计划时预测好执行后对世界状态的影响。
  • 无论如何优化,为了达到代价最小,都需要从众多计划中选择,任务树较为复杂时,决策时间复杂度较高。

6. 局限性

作为一个基于计划的模型,无论计划多么完美,都避免不了计划之外的事情发生(游戏里最大的随机因素就是玩家输入)。

计划越长线,冲突的概率就越大。所以,HTN制定的计划一般是中线计划

作为基于计划的模型,在使用的时候需要尽可能避免当前计划被打断。和行为树的尝试有着本质的区别,开发者需要尽可能保证制定的计划完整、有效得执行

不能随机应变是HTN的最大缺陷,当计划失败时只能重计划。

eg:
假设有先后执行的两个任务组A和B,制定好任务并开始执行。A任务组执行完后,实际的情况和预想的不一样,导致B任务组内定下的任务不再满足要求。此时会重新进行制定任务并再次执行A,而不是从B任务组内重新选择一个任务执行。

总结就是:失败时,无法动态修改部分任务,不像行为树中子树内一个失败后会继续找下一个。

当然在大部分情况下这个缺陷不会很明显

eg:
以移动和攻击(例子从抽象的角度去理解,不考虑什么Tick Recheck等)。移动只不过是攻击的铺垫,即便移动完成后攻击节点失败,再次移动也无可厚非,从AI的角度也没有问题。此处我们将移动看成是攻击的铺垫或是辅助。

当然这种说法还有一个限制,就是随机性,如果在任务组添加随机属性,那么多次打断可以就会出现一种情况:执行X的铺垫部分,再执行Y的铺垫部分……就会显得比较蠢了。这种情况可能需要将随机成分移出HTN内部,例如在控制器内随机并存储随机结果,保证打断后可以回到原来的分支。

7. 改造

7.1 局部重计划

针对上述的局限性,我对HTN进行了改造,加入了局部重计划的功能。即,从B点开始的后续任务树会在执行计划时再次计划,然后执行任务树内,面向当前状态的最佳计划。

7.2 加入行为树

这里只提供一种思路,没有实施过。尝试把行为树糅合进来,进行末端行为的控制,从而做到随机应变。

最简单的实现便是把某些Task替换为BT,运行到了就跑一个完整的行为树。

任务的关键在于条件和影响,其中难点在于影响的预测。最理想的当然是根据当前的worldstate,模拟一遍行为树,进行预测影响。但是实际开发时,对于精准预测的要求并不是很高。

7.3 局部延迟计划

针对大任务树,将其某些子树进行包装,在计划时不去考虑代价,而是在运行时进行计划。

这样可以降低计划的计算量级,提升性能,并且做到了局部重计划类似的效果。相比之下,局部重计划可以在一开始制定计划时,展望全局,也就是先定下一个全局较优的计划。

这个需要前置任务链不是子树的前置行为。否则前置任务完成后发现后序子树无法执行,那么可以会一直执行前置任务,表现不好。

8. 参考文献

HTN论文,很详细且易懂
Advanced Real-Time Hierarchical Task Networks A New Approach
各种AI模型
斯坦福AI PDF
HTN论文
HTN插件介绍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值