文章目录

  • 今天实现的内容:
    • 枪械功能的继续实现:
      • 弹药装填逻辑已实现
      • 开火
      • 实例化子弹
      • 优化代码结构
    • 新素材的导入:
      • 子弹抛壳
    • 新动画:
      • 开火动画
      • 换弹动画
  • BUG以及缺陷:
  • 值得注意的:

今天实现的内容:

枪械功能的继续实现:

  • 弹药装填逻辑已实现

在这个项目中弹药装填被设计为当弹仓中还有子弹时换弹会多一发子弹的情况。其实我很想设计成换弹就直接将整个弹匣扔掉,这样显得硬核一点。但是还是设计成大多数竞技射击游戏那样,弹匣少了多少弹药就补充多少弹药的形式。

        // 装填 protected override void Reload(){// 首先要有子弹可以换if (currentAmmoCarried > 0){// 其次是确实需要换弹并且没有在换弹if(currentAmmoInMag <= ammoInMag && !m_isReloading){// 将换弹layer的权重设置为1gunAnimator.SetLayerWeight(1, 1);// 判断当前弹仓里是否有子弹m_isBulletLeft = (currentAmmoInMag > 0);// 当前需要播放哪个动画?gunAnimator.SetTrigger(m_isBulletLeft ? "ReloadLeft" : "ReloadOutOfAmmo");// 设置状态m_isReloading = true;// 将所有的子弹放到currentAmmoCarriedcurrentAmmoCarried += currentAmmoInMag;// 将currentAmmoInMag设置为0 一是为了方便接下来的计算 二是为了让换弹时无法射击currentAmmoInMag = 0;// 开始执行换弹协程 只有动画快要播放完成时才真正换弹StartCoroutine(CheckReloadAmmoAnimationEnd()); }else{// 不需要换弹#if UNITY_EDITORDebug.Log("Dont need to reload!");#endif}}else{// 备用子弹打光了#if UNITY_EDITORDebug.Log("Out of Ammo!");#endifreturn;}}

首先,在没有子弹可以换的时候,在确实需要换弹,也就是子弹没有装满的时候,在正在换弹的时候,我们是不会执行换弹的。Reload函数仅仅是播放动画,设置状态m_isReloading,将所有子弹都放到currentAmmoCarried,将currentAmmoInMag清零,以及开启真正的换弹方法,该方法是一个协程。

// 检查换弹动画是否播放完成// 如果播放完成了就可以从逻辑上换弹了private IEnumerator CheckReloadAmmoAnimationEnd(){while (true){yield return null;// 一定要在每帧赋值 才能得到current stategunStateInfo = gunAnimator.GetCurrentAnimatorStateInfo(1);if (gunStateInfo.IsTag("ReloadAmmo")) //这个基本上肯定是true 因为我们先设置了播放换弹动画{if (gunStateInfo.normalizedTime >= 0.9f) //当换弹动画快要播完时{     if(m_isBulletLeft) //如果我们装填时弹仓中有子弹{// 剩下的子弹能不能填满完整个弹匣?currentAmmoInMag = (currentAmmoCarried > ammoInMag + 1) ? ammoInMag + 1 : currentAmmoCarried;// 装了多少就减多少currentAmmoCarried -= currentAmmoInMag;}else //如果我们装填时弹仓中没子弹{// 剩下的子弹能不能填满完整个弹匣?currentAmmoInMag = (currentAmmoCarried > ammoInMag) ? ammoInMag : currentAmmoCarried;// 装了多少就减多少currentAmmoCarried -= currentAmmoInMag;}m_isReloading = false;yield break;}}}}

这是实际的换弹方法,之所以使用协程是因为装填动画需要时间,一定要等装填动画快完成了我们才能执行换弹的真正逻辑。具体实现思路看注释吧,不想写了。

  • 开火

弹匣里有弹药才能开火,并且还要看满不满足射速的条件,在开火执行时,弹匣中的子弹数减一,直接播放开火动画,创建子弹(目前只有轨迹),播放枪口粒子特效,播放抛壳粒子特效,最后重置上一次发射的时间来用于下次射击的射速条件判断。

        // 发射子弹 需要派生类来具体实现protected override void Shooting(){// 如果弹匣里有弹药if (currentAmmoInMag <= 0) return;// 并且能够继续射击if (!isAllowShooting()) return;// 弹药减一currentAmmoInMag -= 1;// 播放开火动画gunAnimator.Play("Fire", 0, 0);// 创建子弹轨迹CreateBullet();// 播放枪口粒子特效muzzleParticle.Play();// 播放抛壳粒子特效casingParticle.Play();// 重置上一次发射时间lastFireTime = Time.time;}
  • 实例化子弹

看看就行了

        // 实例化子弹protected override void CreateBullet(){GameObject temp_bullet = Instantiate(bulletPrefab, muzzlePoint.position, muzzlePoint.rotation);temp_bullet.AddComponent<Rigidbody>().velocity = temp_bullet.transform.forward* 100f;}
  • 优化代码结构

为了方便子类修改Shooting代码以实现不同的攻击方式,将原本写在DoAttack中的代码写到AssaultRifle中的Shooting中。本来我们在之前写的就是突击步枪的攻击方式。这样做没有问题。可以这么理解,武器的功能是发动攻击(DoAttack),枪械类发动攻击的方式是射击(Shooting),不同的枪械有不同的射击方式,所以需要在具体的枪械子类中来实现具体的Shooting,以及CreateBullet。
下面是到今天为止的AssaultRifle类和Firearms类源代码

using UnityEngine;
using System.Collections;
using System.Collections.Generic;namespace Scripts.Weapon
{public class AssaultRifle : Firearms{// 是否正在装填private bool m_isReloading;// 弹仓里是否有子弹private bool m_isBulletLeft;protected override void Start(){base.Start();m_isReloading = false;}// 发射子弹 需要派生类来具体实现protected override void Shooting(){// 如果弹匣里有弹药if (currentAmmoInMag <= 0) return;// 并且能够继续射击if (!isAllowShooting()) return;// 弹药减一currentAmmoInMag -= 1;// 播放开火动画gunAnimator.Play("Fire", 0, 0);// 创建子弹轨迹CreateBullet();// 播放枪口粒子特效muzzleParticle.Play();// 播放抛壳粒子特效casingParticle.Play();// 重置上一次发射时间lastFireTime = Time.time;}// 装填 protected override void Reload(){// 首先要有子弹可以换if (currentAmmoCarried > 0){// 其次是确实需要换弹并且没有在换弹if(currentAmmoInMag <= ammoInMag && !m_isReloading){// 将换弹layer的权重设置为1gunAnimator.SetLayerWeight(1, 1);// 判断当前弹仓里是否有子弹m_isBulletLeft = (currentAmmoInMag > 0);// 当前需要播放哪个动画?gunAnimator.SetTrigger(m_isBulletLeft ? "ReloadLeft" : "ReloadOutOfAmmo");// 设置状态m_isReloading = true;// 将所有的子弹放到currentAmmoCarriedcurrentAmmoCarried += currentAmmoInMag;// 将currentAmmoInMag设置为0 一是为了方便接下来的计算 二是为了让换弹时无法射击currentAmmoInMag = 0;// 开始执行换弹协程 只有动画快要播放完成时才真正换弹StartCoroutine(CheckReloadAmmoAnimationEnd()); }else{// 不需要换弹#if UNITY_EDITORDebug.Log("Dont need to reload!");#endif}}else{// 备用子弹打光了#if UNITY_EDITORDebug.Log("Out of Ammo!");#endifreturn;}}// 实例化子弹protected override void CreateBullet(){GameObject temp_bullet = Instantiate(bulletPrefab, muzzlePoint.position, muzzlePoint.rotation);temp_bullet.AddComponent<Rigidbody>().velocity = temp_bullet.transform.forward* 100f;}// 检查换弹动画是否播放完成// 如果播放完成了就可以从逻辑上换弹了private IEnumerator CheckReloadAmmoAnimationEnd(){while (true){yield return null;// 一定要在每帧赋值 才能得到current stategunStateInfo = gunAnimator.GetCurrentAnimatorStateInfo(1);if (gunStateInfo.IsTag("ReloadAmmo")) //这个基本上肯定是true 因为我们先设置了播放换弹动画{if (gunStateInfo.normalizedTime >= 0.9f) //当换弹动画快要播完时{     if(m_isBulletLeft) //如果我们装填时弹仓中有子弹{// 剩下的子弹能不能填满完整个弹匣?currentAmmoInMag = (currentAmmoCarried > ammoInMag + 1) ? ammoInMag + 1 : currentAmmoCarried;// 装了多少就减多少currentAmmoCarried -= currentAmmoInMag;}else //如果我们装填时弹仓中没子弹{// 剩下的子弹能不能填满完整个弹匣?currentAmmoInMag = (currentAmmoCarried > ammoInMag) ? ammoInMag : currentAmmoCarried;// 装了多少就减多少currentAmmoCarried -= currentAmmoInMag;}m_isReloading = false;yield break;}}}}protected void Update(){// 开火if (Input.GetMouseButton(0)){DoAttack();}// 换弹if(Input.GetKeyDown(KeyCode.R)){Reload();}}}
}

子类AssaultRifle来具体定义Shooting,Reload,以及CreateBullet是干嘛

using UnityEngine;namespace Scripts.Weapon
{// 枪械类public abstract class Firearms : MonoBehaviour,IWeapon{// 枪口位置 用于生成子弹public Transform muzzlePoint;// 抛出蛋壳的位置public Transform casingPoint;// 枪焰粒子效果public ParticleSystem muzzleParticle; 枪焰火光效果//public Light muzzleLight;// 抛壳粒子效果public ParticleSystem casingParticle;// 弹匣弹药量public int ammoInMag = 30;// 能携带的最大弹药数public int maxAmmoCarried = 120;// 子弹预制体public GameObject bulletPrefab;// 射速速率 一秒钟能打几发public float fireRate;// 上一次开火的时间protected float lastFireTime;// 当前弹匣弹药量protected int currentAmmoInMag = 30;// 当前携带的弹药数protected int currentAmmoCarried = 120;// 枪械动画protected Animator gunAnimator;// 枪械动画机的信息protected AnimatorStateInfo gunStateInfo;// virtual修饰符方便子类修改protected virtual void Start(){currentAmmoInMag = ammoInMag;currentAmmoCarried = maxAmmoCarried;gunAnimator = GetComponent<Animator>();}// 执行攻击public void DoAttack(){// 对于枪械来说 发动攻击的方式就是发射子弹Shooting();}// 发射子弹 需要派生类来具体实现protected abstract void Shooting();// 装填 protected abstract void Reload();// 是否能够射击 用于武器的射速限制protected bool isAllowShooting(){return (Time.time - lastFireTime > 1 / fireRate);}// 如何实例化一个子弹 交给子类来具体实现protected abstract void CreateBullet();}
}

基类Firearms中的所有成员都是public或者protected的。至于射速限制,还是交给基类来做,在我们的世界里,枪械都是有射速限制的。

新素材的导入:

  • 子弹抛壳

使用粒子效果实现的抛壳效果已经有了,换了新的枪焰粒子效果。我看之前用的素材是基于脚本的抛壳来着?粒子的具体制作过程嘛?没有,我直接用的别人的。本项目无任何商业用途。

新动画:

  • 开火动画

这个有点意思

在动画机里面没有进入方式,一开始我还在想Up是不是搞错了(这个项目是照着别人的视频做的),后来发现我在脚本里,直接Animator.Play(“Fire”)就能直接播放动画了,播放完之后自动转换。看来我真是学傻了,想着一定要从某个状态转换过来啊。
这个故事告诉我们,动画机中的动画可以跳过动画机参数直接用脚本播放,以前一直没注意。

        // 执行攻击public void DoAttack(){// 如果弹匣里有弹药if (currentAmmoInMag <= 0) return;// 并且能够继续射击if (!isAllowShooting()) return;// 弹药减一currentAmmoInMag -= 1;// 播放开火动画GunAnimator.Play("Fire", 0, 0);// 发射子弹Shooting();// 重置上一次发射时间lastFireTime = Time.time;}
  • 换弹动画

换弹动画有两种,一种是枪膛里还有子弹(ReloadLeft),另一种是枪膛里已经没有子弹了,子弹打空了(ReloadOutOfAmmo)。
设置一个新的layer,layer的weight越高动作越受到这个层的影响,用两个Trigger类型参数来控制动画变换,动画机如下。

注意,这个Idle在这里只是一个空的动画节点,设计目的是为了在换弹之后能继续执行其他layer的动画(目前Reload层的权重一直是1)。

这两个换弹动画都要打上Tag,方便之后在脚本中找到。


BUG以及缺陷:

UP讲的这个完全没搞懂

子弹抛壳的粒子在玩家转动时会跟着一起转,很诡异。

已经解决了,改一改粒子组件的这个参数


值得注意的:

gunStateInfo要记得赋值,而且一定要在每帧赋值,才能得到当前的动画状态。

gunStateInfo = gunAnimator.GetCurrentAnimatorStateInfo(1);

行了,就这样了,肝不动了。今天这个换弹算是写出自己的特色了哈哈。


开火射击的初步实现(2)——Unity随手记(2021.1.26)相关推荐

  1. 开火射击的初步实现(1)——Unity随手记(2021.1.25)

    文章目录 今天实现的内容: 武器的初步实现: 武器接口 枪基类 突击步枪类 BUG以及缺陷: 值得注意的: 今天实现的内容: 武器的初步实现: 武器接口 定义了武器的基本功能,也就是执行攻击.不同的武 ...

  2. 黑魂复刻游戏的玩家输入模块——Unity随手记(2021.3.14)

    文章目录 前言 今天实现的内容: 按键封装 获取输入及输入信号优化 输入的渐变 模块的软开关 处理输入 BUG以及缺陷: 值得注意的: 前言 好久不见,在接下来的Unity随手记里,我会学着B站上的视 ...

  3. 黑魂复刻游戏的玩家控制器(基础移动,动画实现及优化)——Unity随手记(2021.3.15)

    文章目录 今天实现的内容: 动画机设计理念 动画机的运用及模型旋转 玩家角色的位移 爬坡测试 跑步 旋转的优化 跑步动画的优化 BUG以及缺陷: 值得注意的: 今天实现的内容: 动画机设计理念 要我说 ...

  4. 对VR的初步了解及unity的简单操作

    VR发展史 VR :虚拟现实 虚拟现实技术是一种可以创建和体验虚拟世界的计算机仿真系统,它利用计算机生成一这种虚拟环境,是一种多元信息融合的,交互式的三位同代实景和实体行为的系统仿真使用户沉浸到该环境 ...

  5. fps射击HTML网页游戏,关于Unity中FPS第一人称射击类游戏制作(专题十)

    当前Unity最新版本5.6.3f1,我使用的是5.5.1f1 FPS第一人称射击类游戏实例 场景搭建 1.创建Unity项目工程和文件目录,保存场景 2.导入人物模型和子弹碎片的资源包charact ...

  6. Egret3D初步笔记二 (Unity导出场景使用)

    一 Scene 根据上一节的继续.在导入unity4.7.1_Egret3D_Dll.unitypackage后. 在Unity中双击打开Assets/Egret3D/Example下的Example ...

  7. 枪械切换(1)——Unity随手记(2021.2.16)

    文章目录 今天实现的内容: 脚本结构的优化 切换武器逻辑的初步实现 切换武器的GameObject实现 BUG以及缺陷: 值得注意的: 今天实现的内容: 脚本结构的优化 为接下来的切换枪械做铺垫,脚本 ...

  8. 子弹散射——Unity随手记(2021.2.4)

    文章目录 今天实现的内容: 子弹散射逻辑 BUG以及缺陷: 值得注意的: 今天实现的内容: 子弹散射逻辑 子弹散射就相对后坐力简单多了.在CreateBullet的时候给予子弹一个随机大小的角度偏量. ...

  9. 武器后坐力——Unity随手记(2021.2.2)

    文章目录 今天实现的内容: 武器后坐力逻辑 准心归位功能--2021年2月3日更新 BUG以及缺陷: 值得注意的: 今天实现的内容: 武器后坐力逻辑 不废话直接上脚本,这是up自己的办法,up的办法有 ...

最新文章

  1. 库克说AI也能创造很多就业岗位,苹果加码教育布局
  2. HTML-CSS-JS-PHP的顺序及相关网址(转)
  3. 专家首次释疑“接触传播”:手污染到病毒后,再揉眼睛可能会感染
  4. 内存分配的几个函数的简单对比分析
  5. 在ASP.NET中实现Url Rewriting
  6. 电脑格式化后需要重装系统吗_电脑经常重装系统对电脑有影响吗
  7. 一道关于比赛胜负的Sql查询题目
  8. linux系统学习第一天
  9. 小熊的人生回忆(六)
  10. java tcp聊天程序_java实现基于Tcp的socket聊天程序
  11. Windows下的MySQL安装、MySQL-Front安装
  12. DTCMS插件的制作实例电子资源管理(二)Admin后台页面编写
  13. 华为交换机导入配置_华为交换机配置教程 华为核心交换机配置
  14. 步进电机基础(5.9)-步进电机的驱动与控制-三相步进电机的驱动电路
  15. java 刽子手游戏_Java刽子手游戏重绘()无法正常工作
  16. mysql排列组合实现_排列组合的实现
  17. *.dfm resource kept;file *.dfm resource discarded
  18. java+mysql基于JSP0801报刊订阅系统(java,web)
  19. 求:生意参谋 data 破解,最好是用php
  20. 回首过去 展望未来

热门文章

  1. 数据库-Mysql使用学习笔记(命令行及图形化界面)
  2. 虚假ip链接服务器_使用代理服务器进行虚假相信
  3. RFC2544丢包率测试——信而泰网络测试仪实操
  4. 易基因|DNA甲基化方法全解析:方法发展、技术应用、优缺点
  5. iOS开发-App应用崩溃卡顿分析
  6. 数据导入 - Kafka 结合Doris Routine load 任务导入
  7. 如何在Ubuntu上安装微信(有效)
  8. PowerMILL Robot 机器人编程
  9. 汽车转向的机理——滑移角、转向力、转向过度、转向不足、轮胎选择
  10. 每日一笑--蛛丝马迹