什么是行为机

顾名思义,类比状态机每个节点是一个状态,行为机每个节点是描述一种行为。行为机每个节点之间是互斥的,并且节点相互之间完全不用关心是怎么切换的。这里就不讲状态机跟行为树是怎么做ai的了,这里只讲用行为机怎么做一个ai。举个例子 mmo中的小怪策划案,大致会这么写:

小怪在出生点周围巡逻。发现周围有玩家则选择一个玩家做目标,追击该目标玩家,追到目标玩家则攻击目标玩家,发现距离出生点太远则返回,返回到出生点则继续巡逻

1.定义ai的各种行为

我们首先定义好怪物有哪些行为。很简单,我们直接根据策划案中的字面意思,怪物大致有这么几种行为:
a.巡逻
b.选择一个玩家追击并且攻击
e.返回出生点。

注意很多状态机会把移动作为一种状态,这在行为机中是不对的,因为巡逻,追击,返回都会有移动,移动只是玩家行为节点中的一个部分,移动跟巡逻,追击,返回并不是互斥的。

节点不要拆的太细,因为每个行为是个协程,我们可以在行为节点中写十分复杂的逻辑,比如有些同学可能会把 选择一个玩家追击并且攻击 这一个节点拆成 a 选择目标 b 追击目标 c 攻击目标.

甚至还有人会把巡逻拆的更细,拆成a.寻找一个点 b.移动 c.等待一定时间。这都是状态机跟行为树的思维,因为状态机跟行为树可能希望移动节点可以共用。这都是增加了麻烦,不合理,不要用状态机跟行为树的思维去想行为机。行为机中,节点只是描述一种行为,并不需要共用,共用的永远是各种函数。

2.填充满足行为的条件

我们把每个行为确定好对应的条件,一旦条件满足则会进入该行为,取消上一个行为(协程)
a. 巡逻的条件:身上没有玩家目标,周围没有玩家,距离出生点 < 10米
b. 选择一个玩家追击并且攻击: 周围有玩家,距离出生点 < 10米
c. 返回出生点: 距离出生点 > 20米
其实条件一旦列出,那么节点中的Check方法自然也就实现了

3.实现行为

a. 巡逻的伪代码:

while (true)
{
pos = 出生点周围找一个点
bool ret = await MoveToAsync(pos,cancelToken);
if (!ret) // false表示协程取消, 则需要return,停止整个协程
{
return;
}
// 移动到了,随机等待2-4秒
randomTime = RandomHelper.Random(2000, 4000);
bool ret = await TimeComponent.Instance.Wait(randomTime, cancelToken)
if (!ret) // false表示协程取消, 则需要return,停止整个协程
{
return;
}
}

这样,如果b c条件不满足的话,怪物就永远在巡逻节点协程中,不停的找一个点移动,等待,移动,等待

b. 选择目标追击并且攻击目标节点的伪代码:

while (true)
{
target = SelectTarget()
while (true)
{
while (true)
{
// 追击目标
pos = 计算离目标0.2米的一个点
// 这里不能以目标作为移动目标,因为怪物要距离玩家稍远一点
await MoveToAsync(pos, cancelToken);
if (!ret) // false表示协程取消, 则需要return,停止整个协程
{
return;
}
// 距离玩家 < 0.5米,表示追到了玩家,就不需要追了
if (距离玩家<0.5米)
{
break;
}
}
// 追击到了,攻击玩家
while (true)
{
//
spellId = SelectSpell();
if (spellId == 0) // 可能技能在cd,等待500ms再试
{
bool ret = await TimeComponent.Instance.Wait(500, cancelToken)
if (!ret) // false表示协程取消, 则需要return,停止整个协程
{
return;
}
continue;
}
await CastSpell(target);
// 攻击完成后停止一段时间
bool ret = await TimeComponent.Instance.Wait(1000, cancelToken)
if (!ret) // false表示协程取消, 则需要return,停止整个协程
{
return;
} // 距离玩家 > 0.5米, 距离玩家远了,break攻击循环,继续追击
if (距离玩家<0.5米)
{
break;
}
}
} // 这里加个time,防止上面两个while循环没有进入,结果就会导致一直执行 target = 选择一个目标 这句话,会导致死循环
bool ret = await TimeComponent.Instance.Wait(100, cancelToken)
if (!ret) // false表示协程取消, 则需要return,停止整个协程
{
return;
}
}

c. 返回出生点伪代码:

while (true)
{
// 整个返回过程是无敌的
using (Buff buff = AddBuff(无敌))
{
pos = 找到离出生点10米的点
bool ret = await MoveToAsync(pos,cancelToken);
if (!ret) // false表示协程取消, 则需要return,停止整个协程
{
return;
}
// 移动到了, buff会删除,或者切换成其它状态,协程退出也会删除无敌buff
}
}

其实巡逻跟返回两个节点也可以合并成一个节点,这个大家自己去尝试尝试。可以看出,行为机编写是非常简单,代码是非常易读的。就这么一个怪物的逻辑,用行为树来编写,节点就很多了,而且并不好阅读跟重构。整个逻辑可能还有些瑕疵,不过意思应该很明白了。

总结

  1. 行为机节点并不需要共用,行为机的节点只是表示一段逻辑,可以做的非常非常非常庞大,比如做机器人的时候,一个做任务,只有一个节点。里面代码调用了无数的协程方法。
  2. 行为机共用的是函数,不是节点,不要想着这个节点应该抽出来共用,这个想法是错误的。
  3. 行为机永远只关注当前行为,永远不需要关心上一个行为,当满足行为条件就直接打断上个协程,执行当前节点的协程即可。所以只需要定义好玩家有哪些行为,ai自然而然的就写出来了。
  4. 节点不要拆的太细,没有必要

    ET开源地址地址:egametang/ET: Unity3D Client And C# Server Framework (github.com)   qq群:474643097

ET介绍——更为便捷高效的AI框架-行为机(Behavior Machine)的更多相关文章

  1. AI框架类FAQ

    AI框架类FAQ 数据处理 问题:如何在训练过程中高效读取数量很大的数据集? 答复:当训练时使用的数据集数据量较大或者预处理逻辑复杂时,如果串行地进行数据读取,数据读取往往会成为训练效率的瓶颈.这种情 ...

  2. AI框架精要:设计思想

    AI框架精要:设计思想 本文主要介绍飞桨paddle平台的底层设计思想,可以帮助用户理解飞桨paddle框架的运作过程,以便于在实际业务需求中,更好的完成模型代码编写与调试及飞桨paddle框架的二次 ...

  3. AI框架中图层IR的分析

    摘要:本文重点分析一下AI框架对IR有什么特殊的需求.业界有什么样的方案以及MindSpore的一些思考. 本文分享自华为云社区<MindSpore技术专栏 | AI框架中图层IR的分析> ...

  4. FoxOne---一个快速高效的BS框架--数据访问(Dao)

    FoxOne---一个快速高效的BS框架--(1) FoxOne---一个快速高效的BS框架--(2) FoxOne---一个快速高效的BS框架--(3) FoxOne---一个快速高效的BS框架-- ...

  5. RabbitMQ和Kafka,更加便捷高效的消息队列使用方式,请放心食用

    一.RabbitMQ实例介绍RabbitMQ实例由华为云分布式消息服务(DMS)团队打造,实例采用物理隔离的方式部署,租户独占RabbitMQ实例.一键式部署,完全兼容开源RabbitMQ的使用方式, ...

  6. 以Spring Cache扩展为例介绍如何进行高效的源码的阅读

    摘要 日常开发中,需要用到各种各样的框架来实现API.系统的构建.作为程序员,除了会使用框架还必须要了解框架工作的原理.这样可以便于我们排查问题,和自定义的扩展.那么如何去学习框架呢.通常我们通过阅读 ...

  7. 在windows上极简安装GPU版AI框架(Tensorflow、Pytorch)

    在windows上极简安装GPU版AI框架 如果我们想在windows系统上安装GPU版本的AI框架,比如GPU版本的tesnorflow,通常我们会看到类似下面的安装教程 官方版本 安装CUDA 安 ...

  8. AI框架外部用户贡献代码

    AI框架外部用户贡献代码 概述 飞桨是百度自主研发的一款开源的深度学习框架,是主流深度学习框架中首个完全国产化的产品,已经在农业.医疗.林业.科研.服务等领域成功应用.无论是已入职场的深度学习从业者. ...

  9. 中国人工智能AI框架自主研发

    中国人工智能AI框架自主研发 中国AI界争相构建AI开源框架的背后,技术和业务层面的考量因素当然重要,但也不应忽视国家层面的政策支持.对于AI基础设施的建设,中国政府在<新一代人工智能发展规划& ...

  10. 昇思MindSpore全场景AI框架 1.6版本,更高的开发效率,更好地服务开发者

    摘要:本文带大家快速浏览昇思MindSpore全场景AI框架1.6版本的关键特性. 全新的昇思MindSpore全场景AI框架1.6版本已发布,此版本中昇思MindSpore全场景AI框架易用性不断改 ...

随机推荐

  1. AppBox快速开发框架(开源)开发流程介绍

    pre { overflow-y: auto; max-height: 300px }   目前很多低代码平台都是基于Web用拖拽方式生成界面,确实可以极大的提高开发效率,但也存在一些问题: 大部分平 ...

  2. Xposed 原理

    Xposed 使用替换app_process的方式(这是个二进制文件) xposed 的 app_main2.cpp中做了xposed的初始化 /** Initialize Xposed (unles ...

  3. 学习go语言编程之函数

    函数定义 函数的基本组成:关键字func,函数名,参数列表,返回值,函数体,返回语句. 示例如下: func Add(a int, b int) (ret int, err error) { if a ...

  4. java轻量级规则引擎easy-rules使用介绍

    我们在写业务代码经常遇到需要一大堆if/else,会导致代码可读性大大降低,有没有一种方法可以避免代码中出现大量的判断语句呢? 答案是用规则引擎,但是传统的规则引擎都比较重,比如开源的Drools,不 ...

  5. 硬件开发笔记(五): 硬件开发基本流程,制作一个USB转RS232的模块(四):创建CON连接器件封装并关联原理图元器件

    前言   有了原理图,可以设计硬件PCB,在设计PCB之间还有一个协同优先动作,就是映射封装,原理图库的元器件我们是自己设计的.为了更好的表述封装设计过程,本文描述了一个创建CON标准连接件封装,创建 ...

  6. 【八股cover#2】CPP语法 Q&A与知识点

    CPP语法 Q&A与知识点 简历cover 1.熟练使用C的指针应用及内存管理 指针与引用的区别 指针是一个存储地址的变量,可以有多级,可以为空,并且在初始化后可以改变指向: 引用是原变量的别 ...

  7. 【Python OO其一】构造函数__init__()

    Python对象包括三个部分:id(identity识别码).type(对象类型).value(对象的值) __ init __()构造函数 __ init __()方法应用定义构造函数,作用是在实例 ...

  8. 【算法day4】堆结构、堆排序、比较器以及桶排

    堆与堆结构(优先级队列结构) 知识点: 堆结构就是用数组实现的完全二叉树结构 完全二叉树中如果每棵子树的最大值都在顶部就是大根堆 完全二叉树中如果每棵子树的最小值都在顶部就是小根堆 堆结构的heapl ...

  9. Nebula 在 Akulaku 智能风控的实践:图模型的训练与部署

    本文整理自 Akulaku 反欺诈团队在 nMeetup·深圳场的演讲,B站视频见:https://www.bilibili.com/video/BV1nQ4y1B7Qd 这次主要来介绍下 Nebul ...

  10. SpringCloud Nacos

    1.Nacos简介 SpringCloud Alibaba 由来: 因为原先Spring Cloud 的许多组件都是对Netflix 公司的各种框架进行封装, 然后因为Netflix公司对后续更新的各 ...