【Unity】动作游戏开发实战详细分析-16-敌人AI设计


基本思想

本文来实现简单的敌人AI,使用协程来开发AI。如果想要使用行为树插件可自行学习使用。

代码实现

敌人的目标信息结构

用于存储所有的敌人可攻击的目标对象信息,包括游戏对象以及仇恨值。

并提供只读的目标列表,通过函数进行注册与反注册

public struct EnemyTargetInfo//目标信息结构
{public GameObject GameObject { get; set; }//目标public float Hatred { get; set; }//仇恨值
}
public class EnemyTargets
{static EnemyTargets mInstance;public static EnemyTargets Instance { get { return mInstance ?? (mInstance = new EnemyTargets()); } }readonly List<EnemyTargetInfo> mTargetList = new List<EnemyTargetInfo>();public IReadOnlyList<EnemyTargetInfo> TargetList { get { return mTargetList; } }//目标列表public void RegistTarget(GameObject target, float hatred)//注册目标{mTargetList.Add(new EnemyTargetInfo() { GameObject = target, Hatred = hatred });}public void UnregistTarget(GameObject target)//反注册目标{mTargetList.RemoveAll(m => m.GameObject == target);}
}

敌人共享数据段

用于所有敌人AI共享数据

public class EasyEnemyShareMemory
{static EasyEnemyShareMemory mInstance;//单例public static EasyEnemyShareMemory Instance { get { return mInstance ?? (mInstance = new EasyEnemyShareMemory()); } }int mAttackerCount;public int AttackCount { get { return mAttackerCount; } }//当前攻击者计数public void NoticeAttack()//通知攻击{mAttackerCount++;}public void EndOfAttack()//结束攻击{mAttackerCount--;}
}

简单的敌人AI

基本字段

  • 激活范围
  • 攻击范围
  • 移动速度
  • 当前的行为协程
  • 当前目标

行为逻辑说明:

游戏开始时,开启等待协程,原地等待,并不断检测是否有目标进入激活范围。如果进入激活范围,则停止当前协程,并进入激活协程,在激活协程中,通过共享数据段新增数据,并等待攻击协程。通过攻击协程进行攻击指令的触发,不断循环。

public class EasyEnemy : MonoBehaviour
{public Animator animator;public float activeRange = 8f;//激活范围public float attackRange = 2f;//攻击范围public float speed = 20f;//移动速度Coroutine mBehaviourCoroutine;//主协程GameObject mCurrentTarget;//当前目标void Start(){mBehaviourCoroutine = StartCoroutine(StandByBehaviour());}void Hit(){}IEnumerator StandByBehaviour(){var whileFlag = true;while (whileFlag){for (int i = 0, iMax = EnemyTargets.Instance.TargetList.Count; i < iMax; i++){var target = EnemyTargets.Instance.TargetList[i];if (IsInActiveRange(target.GameObject.transform))//有目标进入激活范围{StopCoroutine(mBehaviourCoroutine);//关闭当前协程mBehaviourCoroutine = StartCoroutine(ActiveBehaviour());//进入激活行为whileFlag = false;break;}}yield return null;}}IEnumerator ActiveBehaviour(){animator.SetBool("Moving", false);UpdateTarget();//更新目标,仇恨值筛选while (mCurrentTarget != null && IsInActiveRange(mCurrentTarget.transform))//确保目标没有离开{if (EasyEnemyShareMemory.Instance.AttackCount < 1)//攻击数量检测{EasyEnemyShareMemory.Instance.NoticeAttack();//增加攻击者统计yield return AttackBehaviour(mCurrentTarget.transform);//攻击EasyEnemyShareMemory.Instance.EndOfAttack();//减少攻击者统计}yield return null;}StartCoroutine(StandByBehaviour());//结束后回到待机行为}IEnumerator AttackBehaviour(Transform target){var flag = true;//确认攻击的flagwhile (!IsInAttackRange(target)){//若不在攻击范围则追踪if (!IsInActiveRange(target))//若逃离则跳出{flag = false;break;}var to = (target.position - transform.position);to = Vector3.ProjectOnPlane(to, Physics.gravity.normalized).normalized;transform.position += to * speed * Time.deltaTime;//执行移动transform.forward = to;//更新方向animator.SetBool("Moving", true);//更新动画yield return null;}animator.SetBool("Moving", false);if (flag){animator.SetTrigger("AttackTrigger");//执行攻击animator.Update(0);const string IDLE_TAG = "Idle";yield return new WaitUntil(() => !animator.IsInTransition(0) && animator.GetCurrentAnimatorStateInfo(0).IsTag(IDLE_TAG));//动画回到有空闲标签的状态,则退出攻击行为。}}void UpdateTarget()//更新当前目标{var maxHatred = -1f;for (int i = 0, iMax = EnemyTargets.Instance.TargetList.Count; i < iMax; i++){var currentTarget = EnemyTargets.Instance.TargetList[i];if (currentTarget.Hatred > maxHatred)//筛选最大仇恨值的目标{if (IsInActiveRange(currentTarget.GameObject.transform))//是否在激活范围{mCurrentTarget = currentTarget.GameObject;maxHatred = currentTarget.Hatred;//更新目标}}}}//判断激活范围bool IsInActiveRange(Transform target)//是否在激活范围内{return Vector3.Distance(transform.position, target.position) <= activeRange;}//判断攻击范围bool IsInAttackRange(Transform target)//是否在攻击范围内{return Vector3.Distance(transform.position, target.position) <= attackRange;}//绘制激活范围void OnDrawGizmos(){Color color = Gizmos.color;Gizmos.color = Color.white;Gizmos.DrawWireSphere(transform.position, activeRange);Gizmos.color = Color.red;Gizmos.DrawWireSphere(transform.position, attackRange);Gizmos.color = color;}
}

可控的随机行为

这里提供一些示例的随机代码

float Random01_Fall()
{var r1 = Random.value;return Random.Range(r1, 1);
}float Random01_Rise()
{var r1 = Random.value;return Random.Range(0, 1 - r1);
}//模拟正态分布
float Random01_Arc(float averageOffset = 0,float alpha = 2f)
{var r1 = Random.value;var t1 = Mathf.Lerp(0, 1, r1);var t2 = Mathf.Lerp(1, 0, r1);var tFinal = Mathf.Lerp(t1, t2, r1) * alpha;return Mathf.Lerp(r1, 0.5f, tFinal) + averageOffset;
}
//不会与上一次重复
int EliminateRepeatRandom(int last,int min,int max)
{var current = Random.Range(min, max);if (last == current){return (current + (int)Mathf.Sign(Random.value) * Random.Range(min + 1, max - 1)) % max;}elsereturn current;
}

场景信息获取

AI编写时,经常有需要获取场景信息的需求,例如BOSS飞到场地中央释放技能等等。

之前,我们的角色都是通过SpawnPoint创建,因此,我们只需要通过SpawnPoint绑定场景的配置信息即可。

我们添加一个接口

所以实现了该接口的目标都会实现发送Spawn场景信息

public interface ISpawnPointCallBack
{void OnSpawn(SpawnPoint sender);
}

修改SpawnPoint的Spawn函数,并进行回调执行

protected virtual void Spawn()
{...var spawnPointCallback = mSpawnedGO.GetComponent<ISpawnPointCallBack>();if (spawnPointCallback != null)spawnPointCallback.OnSpawn(this);...
}

【Unity】动作游戏开发实战详细分析-16-敌人AI设计相关推荐

  1. 【Unity】动作游戏开发实战详细分析-07-连续技与组合技功能设计

    [Unity]动作游戏开发实战详细分析-07-连续技与组合技功能设计 基本思路 在一些动作游戏中,存在着连续技这一功能,具体来说就是连续按下规定的按键会触发能力的功能,或者是长按触发等等. 实现解析 ...

  2. 【Unity】动作游戏开发实战详细分析-06-技能系统设计

    [Unity]动作游戏开发实战详细分析-06-技能系统设计 基本思想 不同的技能可以设计为技能模版,当角色释放技能时,会通过模版ID将它进行实例化,这个实例技能类可以是一个挂载的MonoBehavio ...

  3. 【Unity】动作游戏开发实战详细分析-25-角色残影效果的实现

    [Unity]动作游戏开发实战详细分析-25-角色残影效果的实现 基本思路 Unity中的蒙皮网格组件提供了一个接口BakeMesh,允许我们拿到当前动画帧的网格数据,借此可对烘焙网格使用半透明的边缘 ...

  4. 【Unity】动作游戏开发实战详细分析-15-可扩展的战斗系统

    [Unity]动作游戏开发实战详细分析-15-可扩展的战斗系统 系统设计 攻击信息传递 通常情况下,伤害.属性.判定都会被封装到类中,在触发动画事件后将战斗信息发送给受击者. 我们可以结合Unity碰 ...

  5. 【Unity】动作游戏开发实战详细分析-24-流血喷溅程序

    [Unity]动作游戏开发实战详细分析-24-流血喷溅程序 溅落血迹效果 实现思路 利用对象池的代码设计思路,通过随机性来实现随机的溅落血迹效果. 代码 public class DripBloodF ...

  6. 五分钟详述:一文理解动作游戏开发中的攻击逻辑和受击逻辑

    前言: 本文节选自机械工业出版社出版的<Unity3D动作游戏开发实战>一书,略有改动. 攻击逻辑 一个好的操作手感对于战斗非常重要,玩家会根据当前动画状态来确定下一步输入,若手感混乱则会 ...

  7. 【游戏开发实战】Unity从零开发多人视频聊天功能,无聊了就和自己视频聊天(附源码 | Mirror | 多人视频 | 详细教程)

    文章目录 一.前言 二.思考问题与解决方案 1.思考问题 2.解决方案 2.1.Unity中如何开启摄像头并对图像进行采样 2.2.图像如何中转给其他客户端 2.3.如何实现清晰度切换 2.4.客户端 ...

  8. 《Unity 2D与3D手机游戏开发实战》简介

    #好书推荐##好书奇遇季#<Unity 2D与3D手机游戏开发实战>,京东当当天猫都有发售.彩色印制,定价89元,网店打折销售更便宜.本书配套源码.PPT课件,适合Unity游戏开发初学者 ...

  9. 《Unity 5.x游戏开发实战》一1.9 添加一个水平面

    本节书摘来异步社区<Unity 5.x游戏开发实战>一书中的第1章,第1.9节,作者: Alan Thorn 译者: 李华峰 责编: 胡俊英,更多章节内容可以访问云栖社区"异步社 ...

最新文章

  1. flowable更换数据源与连接池
  2. tomcat向weblogic移植需要注意的问题
  3. 51Talk音视频技术实践和独特挑战
  4. echo回声不能用了_回声消除的昨天、今天和明天
  5. centos7.3下apache搭建django[未成功]
  6. 百度编辑器图片上传 java_百度编辑器粘贴图片自动上传到服务器(Java版)
  7. Python高级——Web静态服务器(面向对象)
  8. numpy.repeat作用,语法,参数解读以及实例
  9. 宝塔可以修改服务器内存限制吗,宝塔内存使用率很高的解决方法 cpu过高这样做!...
  10. ajax提交手机号到php,ajax怎样申请手机号到数据库验证并且返回数据的状态值
  11. 猜数字的算法的一个简单实现
  12. 十大硬盘数据恢复软件简评
  13. 遥感计算机解释技术PPT,梅安新 遥感导论.ppt
  14. 顶级域名、一级域名、二级域名与IP
  15. xlsx表格怎么筛选重复数据_怎样在excel2010中筛选出重复数据呢?
  16. 微信公众号开启服务器配置流程及注意事项
  17. 计算机运行快捷方式,电脑运行快捷键有哪些 电脑运行快捷键介绍
  18. 如何理解第三方支付清算和结算?
  19. linux内存不足导致tomcat宕机
  20. 汽车电子点火控制系统

热门文章

  1. 上海2021高考成绩什么时候可以查询,关于2021年上海高考成绩什么时候出来
  2. ArcGIS制作经纬格网地图
  3. 解密车厂押注车载人机交互背后:更丰富的形式,更激烈的战场
  4. ​势头强劲的 Python PK 强大的 C++,究竟谁更胜一筹?
  5. 看懂Python爬虫框架,所见即所得一切皆有可能
  6. level1和level2行情的区别
  7. 2020年中国智能物联网(AIoT)白皮书
  8. VHDL设计一个同步清零的JK触发器
  9. Ilog、Drools、Jess规则引擎的Rule Language 对比
  10. Springboot集成第三方登录(facebook,linkedin,github)