obsidian/笔记文件/2.笔记/游戏中的AI算法_第四章.md
2025-03-26 00:02:56 +08:00

4.6 KiB
Raw Permalink Blame History

获取目标就是通过label标签从字典容器提取

!Pasted image 20240410140653.png

寻找目标,参考LINQ使用Orderby、ThenBy实现多字段的排序先OrderByDescending降序排列然后从当前激活的目标队列_activeGoals中拿到索引为0的第一个目标即可

!Pasted image 20240410141054.png

两个函数分别是调用所有目标的UpdateData更新函数有包含激活和失效的和完成当前目标的赋值

!Pasted image 20240410142055.png

目标管理类的,更新数据函数,就是调用以上接口即可

!Pasted image 20240410142239.png

而构造函数,完成各项初始化即可

!Pasted image 20240410142313.png

用于实现IPlanner计划接口的具体逻辑类其中声明了对应实现的_agent代理在构造函数中完成传参赋值即可

!Pasted image 20240410143111.png

设置当前状态节点的逻辑入口这里引入了TreeNode树节点的传参和逻辑 通过GetNotExistKeys接口判断当前总的状态数据合集容器_dataTable是否包含传参树节点的GoalState状态 如果不存在,就加到,该树节点,对应的当前状态即可;

!Pasted image 20240410143507.png

回到State脚本完善上述逻辑接口

!Pasted image 20240410143628.png

再回到Planner脚本这是获取对于节点的当前状态不一样的其余状态的总数

!Pasted image 20240410143904.png

回到State状态类实现获取状态不同的value队列

!Pasted image 20240410143936.png

回到Planner类定义的cost消耗是等于节点本身的cost加上不同状态总数再加上节点对应的Action动作的cost

!Pasted image 20240410144131.png

这个逻辑是用来解析当前currentNode所包含的后续影响、先决条件、目标生成一个subNode节点后续会用来获取其中包含的所有cost消耗跟当前currentNode对比拿到更低消耗的那个节点

!Pasted image 20240410144458.png

IAgent基类接口完善各声明

!Pasted image 20241004100246.png

Planner类注意先加上后续使用到的排序相关命名空间

!Pasted image 20241004100431.png

然后是获取所有子节点ActionHandler行为事件容器的函数接口 通过映射后续影响Effect的EffectsAndActionMap容器是否包含目标状态GoalState和当前状态CurrentState的差异key键值遍历EffectsAndActionMap容器拿到其中的value也就是目标的动作事件加到汇总的handlers队列容器中 使用OrderByDescending降序排列后返回的是目标状态与当前状态有差异的动作Handler行为队列容器

!Pasted image 20240410145227.png

比较拿到更低消耗节点的GetCheapestNode函数接口有几种情况的判断核心的是先判空然后消耗Cost字段的比较如果相等就对应优先级Priority字段的比较

!Pasted image 20240410145920.png

创建树的最初节点逻辑也是调用Tree的CreateTopNode接口完成树节点的创建然后把目标的后续影响通过Set接口设置到节点的目标状态 再完成消耗的计算将最初节点topNode设置为当前状态

!Pasted image 20240410154044.png

IsEnd函数通过检测node节点是否还存在不包含在状态总集的状态 如果数量为0说明就是遍历到最终node节点了

!Pasted image 20241004101839.png

构造函数构建动作计划的逻辑入口先新建一个Tree树然后基于goal目标创建最初树节点 声明一个最便宜节点cheapestNode最开始的时候是null空值还有一个subNode子节点用来配合当前节点CurrentNode计算和比较Cost消耗 通过GetSubHandlers接口拿到所有的目标动作事件然后遍历动作事件使用树Tree的CreateNode接口创建一个TreeNode树节点作为subNode子节点 然后通过SetNodeState接口把当前节点currentNode通过CopyState复制给subNode还有当前节点的后续影响、先决条件也是在上述解析的SetNodeState接口完成在subNode的整合 通过GetCost接口拿到subNode的消耗设置一下父节点 再通过GetCheapestNode接口在当前currentNode节点、subNode子节点之间拿到消耗最低的节点设为最新的当前currentNode节点完成Plan计划的逻辑构建拿到消耗最低的动作树节点

!Pasted image 20240410153932.png

拿到消耗最低的动作树节点后通过BuildPlan接口塞进总的Queue数据容器即可

!Pasted image 20240410155924.png