最近有許多朋友問到我一個問題,如何實作像戰地,使命召喚那樣逼真的射擊體驗。首先我們要知道,在沒有動畫師的幫助下,要想實作人為模拟的後坐力,精準度偏移等等其實并不是什麼難事,今天我們就來用較簡短的代碼來實作一個模拟的槍械後坐力。
首先是我們需要用到的工具類代碼:
public class Spring3
{
public Vector3 value = Vector2.zero;
private Vector3 dampValue = Vector2.zero;
private float damp = 1;
private float frequence = 1;
public void Clear ()
{
value = Vector2.zero;
dampValue = Vector2.zero;
}
public Spring3 (float damp, float frequence)
{
this.damp = damp;
this.frequence = frequence;
}
public void Update (float deltaTime, Vector3 target)
{
value -= dampValue * deltaTime * frequence;
dampValue = Vector3.Lerp (dampValue, value - target, deltaTime * damp);
}
}
public class SpringEulerAngle
{
public Vector3 value = Vector2.zero;
private Vector3 dampValue = Vector2.zero;
private float damp;
private float frequence = 1;
public SpringEulerAngle (float damp, float frequence)
{
this.damp = damp;
this.frequence = frequence;
}
public void Clear ()
{
value = Vector2.zero;
dampValue = Vector2.zero;
}
public void Update (float deltaTime, Vector3 target)
{
value -= dampValue * deltaTime * frequence;
dampValue = eulerLerp (dampValue, value - target, deltaTime * damp);
}
public static Vector3 eulerLerp(Vector3 left, Vector3 right, float t){
Vector3 ret;
ret.x = Mathf.LerpAngle (left.x, right.x, t);
ret.y = Mathf.LerpAngle (left.y, right.y, t);
ret.z = Mathf.LerpAngle (left.z, right.z, t);
return ret;
}
}
這是我們在進行模拟時最常用的一個算法,我稱之為彈簧算法(實在起不出好名字了嘿嘿),算法非常簡單,我們隻需要把自己的value輸入進去,作為希望插值的目标,然後調用Update函數就可以讓value以彈性姿勢像target靠攏,需要注意的是damp和frequency這兩個浮點數的用處,可以看到damp越高插值速率越高,越快的趨近于目标點,而frequency越高彈簧震動的越快,我們在之後的模拟中會用到這兩個值的。
通過使用彈簧算法,我們可以很輕松的實作一個讓擡起的槍口歸位的動作,那麼我們怎麼實作一套完整的後坐力弧線呢。要知道我們在開槍的時候(去過射擊場的朋友應該知道),槍口是呈現一個類似抛物線的運動,在扣動扳機瞬間槍口迅速上擡,随即在胳膊與胸口的壓力下槍的上擡幅度慢慢降低并開始下降,降到開槍前的點,是以我們需要在代碼中模拟這一系列動作。
這時有同學就會問了,直接把錄影機或者槍的transform.eulerAngles綁到上邊代碼中的value不就行了麼,這期教程不就可以結束了麼,如果這樣想我隻能說這位同學有些失了智,有靈性的同學應該已經發現了問題,單純通過一個彈簧,隻能模拟槍口歸位,但是槍口上擡這個過程就隻能強行進行角度偏移,就沒法實作我們說過的抛物線效果。是以我們應該用另一個插值來模拟一整套後坐力。将以下腳本挂載到槍或錄影機上,即可觸發後坐力。
明白了道理以後,就非常容易實作了,我們這裡寫一段非常簡單的代碼,通過點選滑鼠左鍵觸發,給目标加一段随機的角度,并且實作一個抛物線性質的偏移,注意,因為後坐力控制屬于實體運動,是以應該放在FixedUpdate中執行,FixedUpdate與Update的執行關系這裡就不贅述了。
public class Recoil : MonoBehaviour {
SpringEulerAngle recoilTarget;
public float damp = 20;
public float frequence = 15;
void Awake(){
recoilTarget = new SpringEulerAngle (damp, frequence);
}
// Update is called once per frame
void FixedUpdate () {
recoilTarget.Update (Time.deltaTime, Vector3.zero);
transform.rotation = Quaternion.Lerp (transform.rotation, Quaternion.Euler (recoilTarget.value), 0.75f); //這裡使用一個較大的插值數字以實作槍口的迅速擡升
}
void Update(){
if (Input.GetMouseButtonDown (0)) {
recoilTarget.value = new Vector3 (Random.Range (-20, 0), Random.Range (-3, 3), 0); //這裡做一個随機偏移來模拟後坐力。
}
}
}
将這段控制後坐力的代碼儲存為Recoil.cs并且挂載到錄影機或者模型上,運作點選左鍵就可以看出效果,隻不過要想實作自己理想的後坐力,需要通過不停的調參來獲得令自己滿意,令策劃滿意的效果。