今天实现的内容:
枪械的瞄准动画层
采用了和基本移动层类似的方法,射击动画由脚本直接播放。
瞄准状态的进入和退出由一个bool参数Aim来控制。
Idle和Reload层的Idle在形式上和功能上是一样的。形式上都是一个空节点,功能是为了在当前动画权重为1并且没有播放本层其他动画时依然能够播放其他层的动画。
权重记得设置为1。之前的Reload层也可以设置为1。
枪械瞄准的逻辑实现
由于每种枪都可能有不同的瞄准逻辑(比如不同的瞄具放大倍率啥的),我们在基类Firearms中设计了抽象方法Aim,让子类来具体实现瞄准逻辑。
// 瞄准 每种枪的瞄准会有不同(也许吧) 放大会有不同(机瞄,二倍镜,三倍镜) 需要派生类来具体实现
protected abstract void Aim();
通过修改摄像机的FOV来实现瞄准放大的效果。eyeCamera是摄像机的引用,originFOV用于存储摄像机原本的FOV值。
// 摄像机 用于瞄准时修改FOV
public Camera eyeCamera;
// 摄像机FOV的原数值
protected float originFOV;
Aim方法逻辑很简单,由于大头都被动画实现了,所以我们要做的就是修改动画参数。
// 瞄准
protected override void Aim()
{
gunAnimator.SetBool("Aim", m_isAiming);
}
重点在这里,Update方法中的瞄准逻辑,我们专门做了一个bool值m_isAiming来判断当前是否处于瞄准状态。由于Aim只是修改动画参数,所以我们要实现的逻辑是 m_isAiming要能反映当前是否处于瞄准状态。按住瞄准键就能保持瞄准状态,松开瞄准键退出瞄准。
// 瞄准 按住就会瞄准
if (Input.GetMouseButtonDown(1))
{
m_isAiming = true;
Aim();
}
// 松开按键退出瞄准
if (Input.GetMouseButtonUp(1))
{
m_isAiming = false;
Aim();
}
在Shooting方法中修改一下动画播放代码,由于两个层的开火动画节点都叫"Fire",所以动画名称不用改,只需要根据是否正在瞄准修改layer就行。
// 播放开火动画 注意我们会根据是否处于瞄准播放不同层的Fire
gunAnimator.Play("Fire", m_isAiming ? 2 : 0, 0);
接下来定义一个协程方法DoAim来实现瞄准要做的事情。在目前DoAim只用来实现瞄准时画面“放大”的效果。通过调整摄像机FOV值来实现。
// 目前主要处理瞄准时视野放大功能
private IEnumerator DoAim()
{
float temp_currentFOV = 0;
while (true)
{
yield return null;
eyeCamera.fieldOfView =
Mathf.SmoothDamp(eyeCamera.fieldOfView,
m_isAiming ? m_targetFOV : originFOV,
ref temp_currentFOV,
Time.deltaTime * 10f);
if(Mathf.Abs(eyeCamera.fieldOfView - m_targetFOV) < 0.0001f)
{
eyeCamera.fieldOfView = m_targetFOV;
yield break;
}
}
}
接下来再按照UP的方式来进行防协程冲突的代码编写,以下代码实现在Aim中。
// 开启DoAim协程
if (doAimCoroutine == null) // 说明协程还没有打开
{
doAimCoroutine = DoAim();
StartCoroutine(doAimCoroutine);
}
else // 不为空说明协程已经打开了
{
// 首先停下之前的协程
StopCoroutine(doAimCoroutine);
doAimCoroutine = null;
// 赋值以后再执行起来
doAimCoroutine = DoAim();
StartCoroutine(doAimCoroutine);
}
BUG以及缺陷:
各个层由于权重都是1,所以会出现很多动画冲突和BUG,比如瞄准时换弹并不能看到换弹但是换弹逻辑是在执行的,我觉得要么为每个层再单独做每个功能的动画(就像COD16瞄准时也能换弹,而且有单独的换弹动画那样),要么就好好掰扯一下各个层的权重之间的协调。
如果在BaseLayer状态下开枪的同时按下瞄准键会出现BUG,之后在瞄准开枪时动画会在Aim Layer的Fire和Base Layer的idle(Fire?)之间快速切换。十分鬼畜。
目前这个BUG已经解决了,应该是要播放开火动画的同时又要播放举枪动画所引起的BUG。方法就是在要举枪时不要再播放开火动画就行了。又添加了一些参数和代码。
// ------------------------------Shooting--------------------------------------
// 是否正在举枪 为了规避BUG 举枪动画播放时不能播放Fire动画
if (!m_isAimingIn)
{
// 播放开火动画 注意我们会根据是否处于瞄准播放不同层的Fire
gunAnimator.Play("Fire", m_isAiming ? 2 : 0, 0);
}
// ------------------------------Update---------------------------------------
// 瞄准 按住就会瞄准
if (Input.GetMouseButtonDown(1))
{
m_isAiming = true;
m_isAimingIn = true;
m_startAimInTime = Time.time;
Aim();
}
if (Input.GetMouseButtonUp(1))// 松开按键退出瞄准
{
m_isAiming = false;
Aim();
}
if(!(Time.time - m_startAimInTime < 0.3f)) //是否正在举枪 举枪时无法开枪
{
m_isAimingIn = false;
}
值得注意的:
关于Mathf.SmoothDamp,这个方法的smoothTime也就是缓冲时间的值越大,current到target的数值的变化速度就越慢。
不同倍率瞄具的数据保存可以通过ScriptableObject来实现。