书接上回,在上一篇中当说到有些交叉知识的时候,会进行简单标注,在这里围绕敌人来进行详细的解说。

####################

  • 思路
    • _有限状态机_
    • 对象池
    • 导航组件
  • 实现
    • 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游戏_敌人相关相关推荐

  1. Unity3D | FPS游戏_人物相关

    这次报名参加了训练营,初次尝试Unity3D的游戏开发,很庆幸的是有老师很详细的指导,拖了一些时间,也总算完成了.依照惯例,继续来写总结,同时这次几乎上学到的都是新知识,在不熟悉的前提下,还比较复杂散 ...

  2. Unity3d FPS游戏之武器切换

    U3D武器系统切枪 多种武器切换教程 多种武器切换教程 我们要通过鼠标来实现切枪效果,我们要有几种思路: 1.通过值来索引对应武器数组下标的值,然后生成,在切换武器的时候,先销毁当前的武器,在生成新武 ...

  3. Unity3D引擎入门搭建一个FPS游戏Demo

    目录 一.unity3d简介(平台,应用范围,经典游戏) 二.界面,基础操作(界面,创建场景,灯光系统,摄像机,组件,脚本语言) 1.界面 2.基本操作 三.demo制作(坐标系统,视角控制,移动,碰 ...

  4. Unity3D FPS Game:第一人称射击游戏(二)

    耗时一周制作的第一人称射击游戏,希望能帮助到大家! 由于代码较多,分为三篇展示,感兴趣的朋友们可以点击查看! Unity3D FPS Game:第一人称射击游戏(一) Unity3D FPS Game ...

  5. Unity3D FPS Game:第一人称射击游戏(三)

    耗时一周制作的第一人称射击游戏,希望能帮助到大家! 由于代码较多,分为三篇展示,感兴趣的朋友们可以点击查看! Unity3D FPS Game:第一人称射击游戏(一) Unity3D FPS Game ...

  6. FPS游戏自动瞄准敌人头部?是如何实现的(三)准星算法与实现自动瞄准

    准星算法 知道了准星的变化规律: 1.准星水平位置摇摆角 正北是π,逆时针逐渐减小,正南是0,继续逆时针减小到正北为-π π和-π重叠 (正北方向Y轴逐渐增加,正东方向X轴逐渐增加) 2.准星高度位置 ...

  7. Unity3D FPS Game:第一人称射击游戏(一)

    耗时一周制作的第一人称射击游戏,希望能帮助到大家! 由于代码较多,分为三篇展示,感兴趣的朋友们可以点击查看! Unity3D FPS Game:第一人称射击游戏(一) Unity3D FPS Game ...

  8. emfps游戏教程_【新教学上架】全面讲解FPS游戏第一人称动画制作 | Max动画——FPS游戏动作绑定实战案例教学...

    原标题:[新教学上架]全面讲解FPS游戏第一人称动画制作 | Max动画--FPS游戏动作绑定实战案例教学 Max动画--FPS游戏动作绑定实战案例教学 本教程由Emperor-Honoka老师制作, ...

  9. lolfps高但画面不流畅_全面解析:高刷新率真能提高FPS游戏技术?

    时隔多年再一次陪朋友座进网吧的时候,老实说我真的后悔了!这次网吧的游戏体验非常的糟糕,总感觉画面和动作并不流畅,经过几番探索之后我终于找到了原因,原来网吧电竞区的显示器刷新率居然只有60Hz!这对于游 ...

  10. unity游戏开发毕设_基于Unity3D射击游戏开发与实现

    摘  要 unity3D是本次毕业设计的主要的软件,这个软件以使用,医用和跨平台为主,unity还具有一个性能非常领先的游戏引擎,价值不菲,是一个完全集成的专业应用,正由于它强大的专业游戏开发引擎能大 ...

最新文章

  1. selenium提取数据之driver对象的常用属性和方法
  2. 如何让图像过渡更自然 python_如何过渡至 Python 3
  3. 设置html初始值0,数组怎么初始化?
  4. python大神-国内某Python大神自创完整版,系统性学习Python
  5. Ubuntu下安装SSH服务
  6. P1156-垃圾陷阱【dp】
  7. asp.net 根据当前时间计算是否股票、期货、黄金交易日期
  8. MySQL视图的应用
  9. Strategy 定义一系列算法或策略,把它们封闭起来,并且使它们相互可以替换。各算法或策略可以独立于客户程序而变化。...
  10. Android 添加多个回调,如何在同一个片段中创建多个Retrofit回调 – android
  11. 划重点!Android 11 首个开发者预览版新功能抢先看
  12. HMM学习笔记_2(从一个实例中学习HMM前向算法)
  13. android模拟anr,Android ANR
  14. WeChat微信商户号JSAPI支付 支付授权目录无法添加:添加完成后不刷新再添加一遍
  15. 从零开始学统计 02 | 总体参数
  16. 广西工业职业技术学院计算机宿舍,广西工业职业技术学院2021年宿舍条件
  17. 用数组实现一个队列改进版
  18. 如果我恨一个人,我就领他到中关村买相机。
  19. 【js-xlsx和file-saver插件】前端html的table导出数据到excel的表格合并显示boder
  20. Python挑战游戏( PythonChallenge)闯关之路Level- 5

热门文章

  1. Pytorch(一) —— 相关库和函数
  2. 三、判断三元一次方程组是否有解及求解——(计算糖果)
  3. hdl四位二进制计数器_用Verilog HDL设计一个4位BCD码计数器
  4. gps校正 android,通过GPS时间同步Android设备?
  5. 【斯坦福大学公开课CS224W——图机器学习】二、图机器学习中的传统方法(2)
  6. 2019年 年终总结
  7. oracle数据库中汉字转化成拼音
  8. Discuz仿今日头条模板/Discuz新闻资讯商业版GBK模板
  9. 服务器分区有什么作用,MSR 分区有什么用_网站服务器运行维护
  10. jQuery实现打地鼠游戏