今天实现的内容:
武器后坐力逻辑
不废话直接上脚本,这是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,插值就不再进行了,所以每次的后坐力作用也就只有一秒。而且曲线是逐渐变小的,一次开火的后坐力效果就会很平滑。