枪械切换(2)——Unity随手记(2021.2.17)

今天实现的内容:

加入放下武器的动画

之前的切换武器是旧枪直接消失,然后把新枪掏出来,现在在这之前多了一个将旧枪放下的动画。新动画能够让切换武器的过程显得更自然。
在这里插入图片描述
为两把枪都添加上这个动画,再打上标签。
在这里插入图片描述
设置新的parameter Trigger,用于触发动画。
在这里插入图片描述

运用放下武器的动画以及改善的切枪逻辑

参考之前的换弹,我们设计了一个协程来检查放下武器的动画是否播放完成,播放完成后再执行逻辑上的切枪,为了实现以上功能,还优化了代码逻辑。
以下是改善过后的WeaponManager。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Scripts.Weapon;

// 用于武器的控制 切换
public class WeaponManager : MonoBehaviour
{
    
    
    // 主武器
    public Firearms mainWeapon;
    // 副武器
    public Firearms secondaryWeapon;


    // 当前手上拿着的武器
    private Firearms m_currentWeapon;
    // FPCharacterControllerMovement的引用 用于传递Animator
    private FPCharacterControllerMovement m_controller;

    // 用于防止换枪方法的协程冲突
    private IEnumerator m_checkHolsterAnimationEndCoroutine;


    // 逻辑上的切换武器
    private void SwapWeapon()
    {
    
    
        // 以下操作为防止协程冲突
        if (m_checkHolsterAnimationEndCoroutine == null)
            m_checkHolsterAnimationEndCoroutine = CheckHolsterAnimationEnd(); // 当前没有正在执行协程
        else
            return; //当前正在执行协程,什么都不做
        StartCoroutine(m_checkHolsterAnimationEndCoroutine); // 如果当前
                                                             // 检查收枪动画是否播放完成
                                                             // 完成后该方法自动调用逻辑上的换枪方法

        // 播放收枪动画
        m_currentWeapon.gunAnimator.SetTrigger("Holster");
        // 现在正在切换武器
        m_currentWeapon.isSwapingWeapon = true;
    }

    // 检查换枪动画(收枪动画)是否完成
    private IEnumerator CheckHolsterAnimationEnd()
    {
    
    
        while(true)
        {
    
    
            // 一定要在每帧赋值 才能得到current state
            AnimatorStateInfo temp_animatorStateInfo = m_currentWeapon.gunAnimator.GetCurrentAnimatorStateInfo(0);
            if (temp_animatorStateInfo.IsTag("Holster")) //这个基本上肯定是true 因为我们先设置了播放换弹动画
            {
    
    
                if (temp_animatorStateInfo.normalizedTime >= 0.9f) //当换弹动画快要播完时
                {
    
    
                    // 逻辑切枪
                    SetupWeaponChange();
                    m_checkHolsterAnimationEndCoroutine = null; //协程执行完毕 将该IEnumerator清空
                    m_currentWeapon.isSwapingWeapon = false; //武器切换完成
                    yield break;
                }
            }
            yield return null;
        }
    }

    // 执行枪械切换时的逻辑
    private void SetupWeaponChange()
    {
    
    
        m_currentWeapon.gameObject.SetActive(false); //隐藏现在的武器
        m_currentWeapon = (m_currentWeapon == mainWeapon) ? secondaryWeapon : mainWeapon; //切换武器
        m_currentWeapon.gameObject.SetActive(true); //显示切换后的武器
        m_controller.SetupAnimator(m_currentWeapon.gunAnimator); //切换controller的Animator引用
    }

    private void Start()
    {
    
    
        if(m_currentWeapon == null)
        {
    
    
            Debug.Log("current weapon is null");
        }

        m_currentWeapon = mainWeapon;
        m_currentWeapon.gameObject.SetActive(true);
        secondaryWeapon.gameObject.SetActive(false);

        m_controller = GetComponent<FPCharacterControllerMovement>();
        m_controller.SetupAnimator(m_currentWeapon.gunAnimator);
    }

    private void Update()
    {
    
    
        // 如果当前没有武器 什么都不执行
        if (!m_currentWeapon) return;

        // 换弹
        if (Input.GetKeyDown(KeyCode.R))
        {
    
    
            m_currentWeapon.ReloadAmmo();
        }

        //按住扳机
        if (Input.GetMouseButton(0))
        {
    
    
            m_currentWeapon.HoldTrigger();
        }

        //松开扳机
        if(Input.GetMouseButtonUp(0))
        {
    
    
            m_currentWeapon.ReleaseTrigger();

        }

        // 瞄准 按下就会瞄准
        if (Input.GetMouseButtonDown(1))
        {
    
    
            m_currentWeapon.Aiming(true);
        }

        // 松开按键退出瞄准
        if (Input.GetMouseButtonUp(1))
        {
    
    

            m_currentWeapon.Aiming(false);
        }

        //当使用滚轮时 切换武器
        if (Input.GetAxis("Mouse ScrollWheel") != 0) 
        {
    
    
            SwapWeapon();
        }
        //当按下键盘1键时
        if (Input.GetKeyDown(KeyCode.Alpha1)) 
        {
    
    
            // 如果当前拿着主武器 什么都不做
            if (m_currentWeapon == mainWeapon) {
    
     }
            else
                SwapWeapon();
        }
        //当按下键盘2键时
        if (Input.GetKeyDown(KeyCode.Alpha2)) 
        {
    
    
            // 如果当前拿着副武器 什么都不做
            if (m_currentWeapon == secondaryWeapon) {
    
     }
            else
                SwapWeapon();
        }

    }
}

可以看到SwapWeapon已经大变,原来的判断功能转移到了Update中进行,同时使用IEnumerator对象来判断协程是否正在进行,如果协程正在进行,则什么都不做。防止玩家能一直划滚轮就一直触发开始切枪。


BUG以及缺陷:

在切换武器时开枪,会导致武器从此无法切换,建议控制脚本使得切换武器时无法射击,或射击时打断切换武器的协程。我个人认为做成切枪时无法开火要好一些。
这是解决方案

        // 当前是否正在切换武器 为了防止BUG也为了游戏设定 正在切换武器时不能开枪
        // 该参数交由WeaponManager进行控制
        internal bool isSwapingWeapon;

在Firearms中添加这一个参数isSwapingWeapon,交由我们的WeaponManager来具体控制,在切枪开始时设置为true,切枪结束时设置为false。

        // 按下扳机 作为外部接口 定义当按住枪械扳机时会发生什么
        internal void HoldTrigger()
        {
    
    
            // 当枪里没子弹时 无法射击 或者也许可以发出一个音效?
            if (currentAmmoInMag <= 0)
            {
    
    
                isShooting = false;    
                return; 
            }
            // 正在切换武器时 不能开枪
            if (isSwapingWeapon)
                return;

            // 发动攻击
            isShooting = true;
            DoAttack();
        }

执行HoldTrigger,也就是扣下扳机时,再执行一次判断,如果isSwapingWeapon为true,说明当前正在切换武器,则不能开枪。


在换弹时换枪,会导致这把枪之后无法开枪。主要原因是因为换弹开始时,枪里的子弹被设置为0,但是如果再进行换枪就打断了换弹过程,也就导致枪里的子弹一直为0。
我的解决方案可能有点复杂。
首先,在Firearms中定义新成员currentAmmoInMagWhileReload ,该成员用于记录换弹时从弹匣中取出来多少子弹。

        // 记录换弹开始时弹匣中的子弹 如果换弹被打断 会派上用场
        protected int currentAmmoInMagWhileReload = 0;

在将currentAmmoInMag清零之前,先用currentAmmoInMagWhileReload记录下currentAmmoInMag。

                    // 记录此时弹匣中的子弹
                    currentAmmoInMagWhileReload = currentAmmoInMag;
                    // 将所有的子弹放到currentAmmoCarried
                    currentAmmoCarried += currentAmmoInMag;
                    // 将currentAmmoInMag设置为0 一是为了方便接下来的计算 二是为了让换弹时无法射击
                    currentAmmoInMag = 0;

接着,如果出现了换枪时换弹的情况,再将currentAmmoInMagWhileReload赋值给currentAmmoInMag,相当于把子弹重新“塞回去”。

        // 停止重新装填 当换弹时换枪会打断重新装填
        internal virtual void StopReloading()
        {
    
    
            // 将当前武器的m_isReloading设置为false 防止换弹协程中断导致的m_isReloading无法修改
            m_isReloading = false; // 这个方法没有解决问题
            // 停止换弹协程
            StopCoroutine(checkReloadAmmoAnimationEndCoroutine);
            checkReloadAmmoAnimationEndCoroutine = null;
            // 将弹匣重新放回枪 这是因为换弹时会将子弹清零
            currentAmmoInMag = currentAmmoInMagWhileReload;
        }

为了方便SwapWeapon的工作,专门做了方法对停止换弹的代码做封装。

// 当前是否正在换弹 正在换弹时切换武器 要停止换弹协程 并且修正换弹相关参数
        if(m_currentWeapon.IsReloading())
        {
    
    
            Debug.Log(111);
            m_currentWeapon.StopReloading();
        }

        // 播放收枪动画
        m_currentWeapon.gunAnimator.SetTrigger("Holster");

SwapWeapon在判断是否正在换弹以及停止换弹的工作就很简单。


这应该是最后一个缺陷了,换弹时切枪,不会播放收枪动画,原因是换弹动画没有停,就把收枪动画覆盖掉了。我觉得,要么手动停止换弹动画,要么将收枪动画单独放在最新的Animator Layer。我还是手动停止换弹动画算了。
在这里插入图片描述
制作新参数,StopReloading。记得手枪步枪都用上。

		// 停止换弹动画
		gunAnimator.SetTrigger("StopReloading");

紧接着,在需要停止换弹动画的地方写上上面那句话。需要停止换弹动画的地方,除了换弹时切枪,还有正常换弹成功时。
我的代码已经肉眼可见的臃肿了。


2021.2.25更新,新问题,瞄准时切枪会导致AimIn一直处于true的状态,导致切回来时开枪播放的是瞄准时的开枪动画。


值得注意的:

在SwapWeapon中,

        // 播放收枪动画
        m_currentWeapon.gunAnimator.SetTrigger("Holster");
        // 现在正在切换武器
        m_currentWeapon.isSwapingWeapon = true;

以上代码要安排到协程冲突判断之后执行,确保return发挥作用。


猜你喜欢

转载自blog.csdn.net/qq_37856544/article/details/113838959