【Unity】动作游戏开发实战详细分析-16-敌人AI设计
【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设计相关推荐
- 【Unity】动作游戏开发实战详细分析-07-连续技与组合技功能设计
[Unity]动作游戏开发实战详细分析-07-连续技与组合技功能设计 基本思路 在一些动作游戏中,存在着连续技这一功能,具体来说就是连续按下规定的按键会触发能力的功能,或者是长按触发等等. 实现解析 ...
- 【Unity】动作游戏开发实战详细分析-06-技能系统设计
[Unity]动作游戏开发实战详细分析-06-技能系统设计 基本思想 不同的技能可以设计为技能模版,当角色释放技能时,会通过模版ID将它进行实例化,这个实例技能类可以是一个挂载的MonoBehavio ...
- 【Unity】动作游戏开发实战详细分析-25-角色残影效果的实现
[Unity]动作游戏开发实战详细分析-25-角色残影效果的实现 基本思路 Unity中的蒙皮网格组件提供了一个接口BakeMesh,允许我们拿到当前动画帧的网格数据,借此可对烘焙网格使用半透明的边缘 ...
- 【Unity】动作游戏开发实战详细分析-15-可扩展的战斗系统
[Unity]动作游戏开发实战详细分析-15-可扩展的战斗系统 系统设计 攻击信息传递 通常情况下,伤害.属性.判定都会被封装到类中,在触发动画事件后将战斗信息发送给受击者. 我们可以结合Unity碰 ...
- 【Unity】动作游戏开发实战详细分析-24-流血喷溅程序
[Unity]动作游戏开发实战详细分析-24-流血喷溅程序 溅落血迹效果 实现思路 利用对象池的代码设计思路,通过随机性来实现随机的溅落血迹效果. 代码 public class DripBloodF ...
- 五分钟详述:一文理解动作游戏开发中的攻击逻辑和受击逻辑
前言: 本文节选自机械工业出版社出版的<Unity3D动作游戏开发实战>一书,略有改动. 攻击逻辑 一个好的操作手感对于战斗非常重要,玩家会根据当前动画状态来确定下一步输入,若手感混乱则会 ...
- 【游戏开发实战】Unity从零开发多人视频聊天功能,无聊了就和自己视频聊天(附源码 | Mirror | 多人视频 | 详细教程)
文章目录 一.前言 二.思考问题与解决方案 1.思考问题 2.解决方案 2.1.Unity中如何开启摄像头并对图像进行采样 2.2.图像如何中转给其他客户端 2.3.如何实现清晰度切换 2.4.客户端 ...
- 《Unity 2D与3D手机游戏开发实战》简介
#好书推荐##好书奇遇季#<Unity 2D与3D手机游戏开发实战>,京东当当天猫都有发售.彩色印制,定价89元,网店打折销售更便宜.本书配套源码.PPT课件,适合Unity游戏开发初学者 ...
- 《Unity 5.x游戏开发实战》一1.9 添加一个水平面
本节书摘来异步社区<Unity 5.x游戏开发实战>一书中的第1章,第1.9节,作者: Alan Thorn 译者: 李华峰 责编: 胡俊英,更多章节内容可以访问云栖社区"异步社 ...
最新文章
- flowable更换数据源与连接池
- tomcat向weblogic移植需要注意的问题
- 51Talk音视频技术实践和独特挑战
- echo回声不能用了_回声消除的昨天、今天和明天
- centos7.3下apache搭建django[未成功]
- 百度编辑器图片上传 java_百度编辑器粘贴图片自动上传到服务器(Java版)
- Python高级——Web静态服务器(面向对象)
- numpy.repeat作用,语法,参数解读以及实例
- 宝塔可以修改服务器内存限制吗,宝塔内存使用率很高的解决方法 cpu过高这样做!...
- ajax提交手机号到php,ajax怎样申请手机号到数据库验证并且返回数据的状态值
- 猜数字的算法的一个简单实现
- 十大硬盘数据恢复软件简评
- 遥感计算机解释技术PPT,梅安新 遥感导论.ppt
- 顶级域名、一级域名、二级域名与IP
- xlsx表格怎么筛选重复数据_怎样在excel2010中筛选出重复数据呢?
- 微信公众号开启服务器配置流程及注意事项
- 计算机运行快捷方式,电脑运行快捷键有哪些 电脑运行快捷键介绍
- 如何理解第三方支付清算和结算?
- linux内存不足导致tomcat宕机
- 汽车电子点火控制系统
热门文章
- 上海2021高考成绩什么时候可以查询,关于2021年上海高考成绩什么时候出来
- ArcGIS制作经纬格网地图
- 解密车厂押注车载人机交互背后:更丰富的形式,更激烈的战场
- ​势头强劲的 Python PK 强大的 C++,究竟谁更胜一筹?
- 看懂Python爬虫框架,所见即所得一切皆有可能
- level1和level2行情的区别
- 2020年中国智能物联网(AIoT)白皮书
- VHDL设计一个同步清零的JK触发器
- Ilog、Drools、Jess规则引擎的Rule Language 对比
- Springboot集成第三方登录(facebook,linkedin,github)