Unity3D | FPS游戏_敌人相关
书接上回,在上一篇中当说到有些交叉知识的时候,会进行简单标注,在这里围绕敌人来进行详细的解说。
####################
- 思路
- _有限状态机_
- 对象池
- 导航组件
- 实现
- AI
- 随机出生点
- 攻击判定
- 尚未解决的问题/拓展
首先会简单介绍一下敌人的逻辑以及行为,敌人会随机在地图上的五个出生点刷新和重生,敌人的数量开始会从0增加到10,并且在击杀之后持续重生保持数量。敌人会在各个出生点之间巡逻,当敌人与人物的距离到达一定限制或者人物在敌人一定范围内射击(可以理解为开枪吸引敌人),敌人会锁定人物,切换为追击状态。
思路
有限状态机
敌人的状态机和人物的类似,尤其特殊需要注意的一点是,敌人有一个终点状态,同时使用对象池管理
①在进入Dead状态之后,不会再进行任何状态转换:同时关闭所有协程(在本例中每次敌人受伤,会开启一个击退协程)
②在每次从对象池中取出,要对敌人的状态初始化:简单距离描述一个敌人的完整过程:实例化敌人 -> HP<=0 -> SetActive(false),进入对象池,此时的敌人状态是Dead -> 需要一个新的对象,同时池中存在对象 -> SetActive(true),同时状态需要切换为Idle
在初始状态机开始有一个关键性判断,实现上面的两点的注意
if (zombieState == ZombieState.Dead&& value != ZombieState.Idle){return;}
这段判断表示的意思:如果当前的状态是Dead,并且希望去往的状态不是Idle,则结束函数。这样一来既满足了Dead是终点状态的定义,也可以满足初始化时可以从Dead切换到Idle
初始化代码:
public void Init(){Debug.Log("初始化");animator.SetTrigger("init");capsuleCollider.enabled = true;hp = 100;ZombieState = ZombieState.Idle;}
动画开关init控制AnyState到Idle
对象池
这里首先演示使用对象池之后敌人的游戏项目管理
1.为什么使用对象池
这里个人理解会和两点有关:
①Destroy(gameObject);
根据老师的指导,个人增加了一些对Destory的理解,当代码中执行Destroy(gameObject);时候,虽然在Hierarchy中已经看不到该游戏项目,但是在Unity的底层中,并没有删除该项目,而是类似于给他打上一个删除的标签,之后游戏中的项目愈来愈多,到达内存和资源的占用越来越多都内存满,然后会检视有哪些是带有删除标签的,再将其删除。
综合上述的原理,如果是每击杀一个敌人之后,删除该游戏项目,再实例化一个新的敌人,内存占用会越来越大。
②需要重复使用对象
使用对象池,每次击杀的敌人会将其游戏项目SetActive(false);然后将其添加到对象池(队列Queue)下面作为子项目。这样之后每次需要一个新的对象时,会先去查看对象池是否为空,若不为空,则SetActive(true)使用;若为空,则实例化新的对象。
以下为代码管理敌人生成的协程,以及消灭之后进入对象池的函数
IEnumerator CheckZombie(){while(true){yield return new WaitForSeconds(1f);if(zombies.Count<10)//场景现存僵尸数量不够{if(zombiesPool.Count>0)//首先检查对象池中是否有{ZombieController zb = zombiesPool.Dequeue();//取出队列zb.transform.SetParent(transform);//从对象池中取出zb.transform.position=GameManager.instance.GetPoints();//随机出生点zb.gameObject.SetActive(true);zb.Init();//初始化僵尸状态zombies.Add(zb);//场景中存在的僵尸列表yield return new WaitForSeconds(3f);}else{//直接新建实例化GameObject zb=Instantiate(pre_Zombie,GameManager.instance.GetPoints(),Quaternion.identity,transform);zombies.Add(zb.GetComponent<ZombieController>());}}}}//死亡状态调用函数public void ZombieDead(ZombieController zombie){zombies.Remove(zombie);zombiesPool.Enqueue(zombie);zombie.gameObject.SetActive(false);zombie.transform.SetParent(Pool);}
联想:子弹是否可以使用对象池?
如果是在完整的大型多人FPS射击游戏,追求模拟真实的枪战效果,采用子弹对象,一定要使用对象池。
但是,因为游戏场景中的物体每一帧都在渲染,如果是子弹对象,模拟真实子弹射出的速度,有可能在这一帧,子弹在物体的正前方,但是下一帧会刷新到物体的后面。
导航组件
AI -> Navigation -> Bake ,会自动烘焙出地图上可以行走的区域
通过navMeshAgent.isStopped = true;来开关敌人的导航功能(初次接触,只是简单了解这个组件可以规划出世界中两个位置之间的路径,具体的有关属性还未使用,以后会深入了解)
实现
AI
主要表现为不同状态之间的转换,对应状态执行对应的行为函数,涉及AI的主要状态有:Idle(待机)、Walk(巡逻)、Run(追击)、Attack(攻击),因为状态和动画是同步的,讲解可以参考上图
待机 -> 巡逻
在敌人实例化或者从池子取出生成,会默认进入待机状态,在Idle状态中演示执行巡逻函数
case ZombieState.Idle:animator.SetBool("walk", false);animator.SetBool("run", false);//关闭导航组件navMeshAgent.isStopped = true;Invoke("GoWalk", Random.Range(1, 3));//每次巡逻待机的时长break;
void GoWalk(){//僵尸的去巡逻函数ZombieState = ZombieState.Walk;}
如果完成从一个出生点到另一个出生点的巡逻,并且中途没有发现人物,会在原地切换为Idle状态,形成状态的循环
//Walk状态初始化
//随机选择一个巡逻点
target = GameManager.instance.GetPoints();
navMeshAgent.SetDestination(target);//Walk状态Update
if (Vector3.Distance(transform.position, target) <= 1)
{//到达目标点,切换状态,改为待机ZombieState = ZombieState.Idle;
}
巡逻 -> 追击
出现这个状态的转换有两种情况:
①人物和敌人之间的距离到达一定限制
if (Vector3.Distance(transform.position, PlayerController.instance.transform.position) < dis){//追击玩家ZombieState = ZombieState.Run;return;}
②人物在敌人周围射击:
在Update中每帧计算人物和敌人之间的距离时,检测人物是否处于射击状态,若为是,则判定追击距离为30;若为否,则判定追击距离为10
//如果当前玩家的状态是Shoot,僵尸追击的范围会变大,变相实现听枪声追击float dis = PlayerController.instance.PlayerState == PlayerState.Shoot ? 30f : 10f;
Hurt -> 追击
当人物射击击中敌人时,在击退协程中,会首先进入Hurt状态,播放受伤动画,之后主动状态切换到追击,人物之后再次射击造成伤害,重复循环
追击 -> 攻击
当敌人到达人物一定范围内,会切换到攻击状态,执行一次完整攻击动画(中途即使受伤,不会播放受伤动画,除非HP<=0,则立即播放死亡动画,进入死亡状态),之后进入追击状态,再进行距离判断,形成循环
case ZombieState.Attack://参考换弹动画回归条件if (animator.GetCurrentAnimatorClipInfo(0)[0].clip.name == "Attack"&& animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1){ZombieState = ZombieState.Run;}break;
随机出生点
类似于之前“是男人就下一百层”,随机生成平台的思路,记录一个点,每次新平台的生成,在这个点的基础上,横坐标随机在一定范围内随机波动。同理,在场景世界中随机新建多个3D项目(例Cube),将组件取消勾选,通过GameManager中新建Transform的数组,来记录管理这些坐标,每次敌人的生成,会随机选择一个点的坐标
public Vector3 GetPoints(){return Points[Random.Range(0,Points.Length)].position;}
攻击判定
在攻击动画的播放过程中,添加Event,StartAttack()和EndAttack()
根据攻击动画,在合适的模型处添加碰撞体
代码逻辑:
//ZombieController
void StartAttack(){weapon.StartAttack();}
void EndAttack(){weapon.EndAttack();}
在敌人脚本中声明武器
因为动画事件在ZombieController中可以监听到,需要新建敌人武器的脚本来对武器的碰撞体进行监听控制
//Zombie_Weapon
public void StartAttack(){isAttack=false;boxCollider.enabled=true;}
public void EndAttack(){boxCollider.enabled=false;}
因为碰撞体判断是一段持续时间,有可能会出现一次攻击,造成多次伤害,为此的解决方法是:在Zombie_Weapon脚本中添加一个bool值,表示正在攻击,在每次开始攻击的时候为false,进入开关函数,添加判断条件(判判断碰撞的碰撞体的物体的tag是否为player,从而决定是否调用PlayerController中的受伤函数),修改为true;
private bool isAttack=false;//保证一次攻击,进行一次伤害判定
private void OnTriggerEnter(Collider other) {if(!isAttack &&other.gameObject.tag=="Player"){isAttack=true;PlayerController.instance.Hurt(10);} }
尚未解决的问题/拓展
问题1.敌人死亡动画异常
经过Debug证实,在快速射击的情况下,因为击退协程中存在yield return 语句,当HP已经满足死亡条件时,之前延迟的协程还在等待执行。暂时的想法是,在进入Dead状态的第一步,关闭所有的协程,解决未果。
拓展1.人物死亡
人物在HP<=0的时候,目前没有完善人物的Dead状态,暂时的想法时,做出类似CS的被击杀效果,主要是当前界面整体调为红色,人物的主摄像机模拟出倒下的效果。
Unity3D | FPS游戏_敌人相关相关推荐
- Unity3D | FPS游戏_人物相关
这次报名参加了训练营,初次尝试Unity3D的游戏开发,很庆幸的是有老师很详细的指导,拖了一些时间,也总算完成了.依照惯例,继续来写总结,同时这次几乎上学到的都是新知识,在不熟悉的前提下,还比较复杂散 ...
- Unity3d FPS游戏之武器切换
U3D武器系统切枪 多种武器切换教程 多种武器切换教程 我们要通过鼠标来实现切枪效果,我们要有几种思路: 1.通过值来索引对应武器数组下标的值,然后生成,在切换武器的时候,先销毁当前的武器,在生成新武 ...
- Unity3D引擎入门搭建一个FPS游戏Demo
目录 一.unity3d简介(平台,应用范围,经典游戏) 二.界面,基础操作(界面,创建场景,灯光系统,摄像机,组件,脚本语言) 1.界面 2.基本操作 三.demo制作(坐标系统,视角控制,移动,碰 ...
- Unity3D FPS Game:第一人称射击游戏(二)
耗时一周制作的第一人称射击游戏,希望能帮助到大家! 由于代码较多,分为三篇展示,感兴趣的朋友们可以点击查看! Unity3D FPS Game:第一人称射击游戏(一) Unity3D FPS Game ...
- Unity3D FPS Game:第一人称射击游戏(三)
耗时一周制作的第一人称射击游戏,希望能帮助到大家! 由于代码较多,分为三篇展示,感兴趣的朋友们可以点击查看! Unity3D FPS Game:第一人称射击游戏(一) Unity3D FPS Game ...
- FPS游戏自动瞄准敌人头部?是如何实现的(三)准星算法与实现自动瞄准
准星算法 知道了准星的变化规律: 1.准星水平位置摇摆角 正北是π,逆时针逐渐减小,正南是0,继续逆时针减小到正北为-π π和-π重叠 (正北方向Y轴逐渐增加,正东方向X轴逐渐增加) 2.准星高度位置 ...
- Unity3D FPS Game:第一人称射击游戏(一)
耗时一周制作的第一人称射击游戏,希望能帮助到大家! 由于代码较多,分为三篇展示,感兴趣的朋友们可以点击查看! Unity3D FPS Game:第一人称射击游戏(一) Unity3D FPS Game ...
- emfps游戏教程_【新教学上架】全面讲解FPS游戏第一人称动画制作 | Max动画——FPS游戏动作绑定实战案例教学...
原标题:[新教学上架]全面讲解FPS游戏第一人称动画制作 | Max动画--FPS游戏动作绑定实战案例教学 Max动画--FPS游戏动作绑定实战案例教学 本教程由Emperor-Honoka老师制作, ...
- lolfps高但画面不流畅_全面解析:高刷新率真能提高FPS游戏技术?
时隔多年再一次陪朋友座进网吧的时候,老实说我真的后悔了!这次网吧的游戏体验非常的糟糕,总感觉画面和动作并不流畅,经过几番探索之后我终于找到了原因,原来网吧电竞区的显示器刷新率居然只有60Hz!这对于游 ...
- unity游戏开发毕设_基于Unity3D射击游戏开发与实现
摘 要 unity3D是本次毕业设计的主要的软件,这个软件以使用,医用和跨平台为主,unity还具有一个性能非常领先的游戏引擎,价值不菲,是一个完全集成的专业应用,正由于它强大的专业游戏开发引擎能大 ...
最新文章
- selenium提取数据之driver对象的常用属性和方法
- 如何让图像过渡更自然 python_如何过渡至 Python 3
- 设置html初始值0,数组怎么初始化?
- python大神-国内某Python大神自创完整版,系统性学习Python
- Ubuntu下安装SSH服务
- P1156-垃圾陷阱【dp】
- asp.net 根据当前时间计算是否股票、期货、黄金交易日期
- MySQL视图的应用
- Strategy 定义一系列算法或策略,把它们封闭起来,并且使它们相互可以替换。各算法或策略可以独立于客户程序而变化。...
- Android 添加多个回调,如何在同一个片段中创建多个Retrofit回调 – android
- 划重点!Android 11 首个开发者预览版新功能抢先看
- HMM学习笔记_2(从一个实例中学习HMM前向算法)
- android模拟anr,Android ANR
- WeChat微信商户号JSAPI支付 支付授权目录无法添加:添加完成后不刷新再添加一遍
- 从零开始学统计 02 | 总体参数
- 广西工业职业技术学院计算机宿舍,广西工业职业技术学院2021年宿舍条件
- 用数组实现一个队列改进版
- 如果我恨一个人,我就领他到中关村买相机。
- 【js-xlsx和file-saver插件】前端html的table导出数据到excel的表格合并显示boder
- Python挑战游戏( PythonChallenge)闯关之路Level- 5
热门文章
- Pytorch(一) —— 相关库和函数
- 三、判断三元一次方程组是否有解及求解——(计算糖果)
- hdl四位二进制计数器_用Verilog HDL设计一个4位BCD码计数器
- gps校正 android,通过GPS时间同步Android设备?
- 【斯坦福大学公开课CS224W——图机器学习】二、图机器学习中的传统方法(2)
- 2019年 年终总结
- oracle数据库中汉字转化成拼音
- Discuz仿今日头条模板/Discuz新闻资讯商业版GBK模板
- 服务器分区有什么作用,MSR 分区有什么用_网站服务器运行维护
- jQuery实现打地鼠游戏