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

今天实现的内容:

武器后坐力逻辑

不废话直接上脚本,这是up自己的办法,up的办法有点不直观,但是也能实现。Up的想法大概是用过曲线来控制插值的第三个参数,从而再控制后坐力的运用。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FPCameraLook : MonoBehaviour
{
    
    
    // 摄像机Transform
    public Transform cameraTransform;
    // 鼠标灵敏度
    public float mouseSentivity;
    // 俯仰角
    public Vector2 maxMinAngle;

    // 后坐力曲线
    public AnimationCurve recoilCurve;
    // 后坐力范围
    public Vector2 recoilRange;
    // 用于后坐力效果的减弱
    public float recoilFadeoutTime = 0.3f;
    // 当前后坐力曲线时间
    [HideInInspector]
    public float currentRecoilTime;

    // 用于存储输入
    private Vector3 m_mouseInputValue;

    // 当前帧的后坐力大小
    private Vector2 currentRecoil;



    //private void Start()
    //{
    
    
    //    currentRecoil = recoilRange; // 测试用
    //}

    void Update()
    {
    
    
        float mouseX = Input.GetAxis("Mouse X");
        float mouseY = Input.GetAxis("Mouse Y");
        m_mouseInputValue.y += mouseX * mouseSentivity;
        m_mouseInputValue.x -= mouseY * mouseSentivity;

        // 限制垂直旋转的角度(以符合现实情况)
        m_mouseInputValue.x = Mathf.Clamp(m_mouseInputValue.x, maxMinAngle.x, maxMinAngle.y);

        // 计算后坐力
        CalculateRecoilOffset();
        Debug.Log(currentRecoil);
        m_mouseInputValue.y += currentRecoil.x;
        m_mouseInputValue.x -= currentRecoil.y;

        // 水平旋转整个GameObject
        this.transform.rotation = Quaternion.Euler(0, m_mouseInputValue.y, 0);
        // 垂直只旋转摄像机
        cameraTransform.localRotation = Quaternion.Euler(m_mouseInputValue.x, 0, 0);
    }

    // 用于计算后坐力导致的偏移
    private void CalculateRecoilOffset()
    {
    
    
        // 累计开火时间
        currentRecoilTime += Time.deltaTime;
        // 运用recoilFadeoutTime
        float temp_recoilFraction = currentRecoilTime/recoilFadeoutTime;
        // 得到当前帧时间上的后坐力曲线上的值
        float temp_recoilValue = recoilCurve.Evaluate(temp_recoilFraction);
        // 使用插值得到渐变后坐力
        currentRecoil = Vector2.Lerp(Vector2.zero, currentRecoil, temp_recoilValue);
    }

    public void RecoilTest()
    {
    
    
        currentRecoil += recoilRange;
        currentRecoilTime = 0; //将开枪时间设置为0
    }
}

准心归位功能——2021年2月3日更新

更新于2021年2月3日,在UP做法的基础上添加了准星归位效果。准星归位我采用的方法为再拿一个Vector2来专门记录鼠标的输入累计,也就是说如果没有后坐力那么鼠标应该在哪里,这样在开火结束后,通过这个新的Vector2上记录的鼠标输入累计量的值就可以将准心拉回去。
注意在开火时不要再记录mouse Y的值,因为玩家一般都会压枪,如果记录了,玩家又压枪,后坐力结束后会恢复过渡,mouse X一定要记录,不然在玩家边开火边旋转的情况下,射击结束后准心自己就转回去了,会让玩家抓狂。
还有就是,两个控制摄像机旋转的Vector2参数都别忘了Clamp。
代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FPCameraLook : MonoBehaviour
{
    
    
    // 摄像机Transform
    public Transform cameraTransform;
    // 鼠标灵敏度
    public float mouseSentivity;
    // 俯仰角
    public Vector2 maxMinAngle;

    // 后坐力曲线
    public AnimationCurve recoilCurve;
    // 后坐力范围
    public Vector2 recoilRange;
    // 用于后坐力效果的强度调整 1是原本的后坐力 值越小后坐力效果越小 
    public float recoilFadeoutTime = 0.3f;
    // 后坐力恢复时间
    public float recoilRecoverDampTime = 2f;

    // 用于存储鼠标输入的积累
    private Vector3 m_mouseInputValue;
    // 用于存储鼠标输入加上后坐力
    private Vector3 m_cameraRotationValue;
    // 当前帧的后坐力大小
    private Vector2 m_currentRecoil;
    // 用于表示后坐力曲线的当前时间
    private float m_currentRecoilTime;

    void Update()
    {
    
    
        // 获取输入
        float mouseX = Input.GetAxis("Mouse X");
        float mouseY = Input.GetAxis("Mouse Y");
        // 记录鼠标累计输入 用于记录开火时瞄准的原旋转量 后坐力复原时使用
        if(!Input.GetMouseButton(0)) // 开火时 不再记录mouse Y的信息 因为玩家会压枪
        {
    
    

            m_mouseInputValue.x -= mouseY * mouseSentivity;
            m_mouseInputValue.x = Mathf.Clamp(m_mouseInputValue.x, maxMinAngle.x, maxMinAngle.y);
        }
        m_mouseInputValue.y += mouseX * mouseSentivity;

        // 当前帧鼠标旋转量
        m_cameraRotationValue.y += mouseX * mouseSentivity;
        m_cameraRotationValue.x -= mouseY * mouseSentivity;

        // 计算后坐力
        CalculateRecoilOffset();
        // 后坐力造成的旋转量
        m_cameraRotationValue.y += m_currentRecoil.x;
        m_cameraRotationValue.x -= m_currentRecoil.y;

        // 限制垂直旋转的角度(以符合现实情况)
        m_cameraRotationValue.x = Mathf.Clamp(m_cameraRotationValue.x, maxMinAngle.x, maxMinAngle.y);

        // 水平旋转整个GameObject
        this.transform.rotation = Quaternion.Euler(0, m_cameraRotationValue.y, 0);
        // 垂直只旋转摄像机
        cameraTransform.localRotation = Quaternion.Euler(m_cameraRotationValue.x, 0, 0);
    }

    // 用于计算后坐力导致的偏移
    private void CalculateRecoilOffset()
    {
    
    
        // 累计开火后经过的时间 时间超过曲线定义的长度后就没有效果了
        m_currentRecoilTime += Time.deltaTime;
        // 运用recoilFadeoutTime 减小后坐力效果
        float temp_recoilFraction = m_currentRecoilTime / recoilFadeoutTime;
        // 得到当前帧时间上的后坐力曲线上的值
        float temp_recoilValue = recoilCurve.Evaluate(temp_recoilFraction);
        // 使用插值得到渐变后坐力
        m_currentRecoil = Vector2.Lerp(Vector2.zero, m_currentRecoil, temp_recoilValue);
    }

    // 使用后坐力 交给外部枪械脚本来使用
    public void DoRecoil()
    {
    
    
        m_currentRecoil += recoilRange;
        m_currentRecoilTime = 0; //将开枪时间设置为0
    }

    // 后坐力效果复原
    public void RecoverRecoil()
    {
    
    
        // 临时参数 用来给SmoothDamp传值
        Vector2 temp_currentVelocity = new Vector2();
        // 使用SmoothDamp平滑的回到瞄准的原点
        m_cameraRotationValue = Vector2.SmoothDamp(m_cameraRotationValue, m_mouseInputValue, 
            ref temp_currentVelocity, 
            recoilRecoverDampTime * Time.deltaTime);
    }
}

使用方法为,在AssaultRifle类的Update中,如果没有按开火键并且枪也没有子弹了。就将准心拉回去。

            // 开火
            if (Input.GetMouseButton(0) && currentAmmoInMag > 0)
            {
    
    
                DoAttack();
            }
            else
            {
    
    
                //if (Input.GetAxis("Mouse X") == 0 && Input.GetAxis("Mouse Y") == 0)
                cameraLook.RecoverRecoil();

            }

BUG以及缺陷:

现在的游戏在后坐力效果结束后准心会自动归位。比如说CSGO。这个估计得明天来弄了。
可以使用一些随机数。
让后坐力与枪械类相关,而不是与摄像机相关。至少将后坐力大小的决定值设计在枪械类中,让将来设计的不同武器能拥有不同后坐力。更好的办法是完全搬到枪械类中,这样连武器后坐力模式都可以自定。
准心归位功能依然不够好,进行上下拉枪时还是会将玩家的操作也归位了。


值得注意的:

感觉后坐力的算法应该会有很多,并且要根据实际需求自己设计。
曲线的作用是用来控制后坐力变化插值Lerp的,曲线只有一秒,一秒以后曲线值为0,插值就不再进行了,所以每次的后坐力作用也就只有一秒。而且曲线是逐渐变小的,一次开火的后坐力效果就会很平滑。


猜你喜欢

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