久しぶりのFPSを作ってみようの記事です。
と言っても、ブログのコメント欄に頂いた質問に返信した内容を編集しただけですが・・・・(^_^;)
今回やる事は、銃を撃った時の反動をスクリプトから作っていきます。
以前の記事ではキャラクターが銃を撃った後のアニメーションとして、銃の反動を表現していました。
もちろんこれでも実現出来ますが、スクリプトから反動の処理を行うと銃をハンドガンからマシンガンに変えた時などにもそのスクリプトですぐに表現出来るところです。
アニメーションで作成すると銃ごとに反動のアニメーションを細かく作成する事が出来ますが、手間がだいぶかかります。
スクリプトの場合はシンプルですが柔軟性があります。
スクリプトで反動を表現する時に必要な事、不必要な事
不必要になる事
スクリプトから銃の反動を表現すると、Animatorに作っていた銃を撃った時のアニメーションの遷移を作成する必要がなくなります。
今まで作成していた銃を撃った時の遷移は↑の赤い四角の部分です。
ですが、反動の部分ではなく引き金を引く時の指の細かいアニメーションも設定している場合は残したままの方がいいと思います。
銃を構えているWaitShot状態から反動を加えたアニメーションを再生するShot状態へと遷移させて戻る部分なのでShot状態はいらなくなります。
それに伴って、銃を撃った時のスクリプトでアニメーションパラメータのShotを操作していた部分もいらなくなります。
この後作成する、カメラを動かす、設定したボーンを動かす場合は残す必要があります。
必要になる事
FPSをつくってみようカテゴリの記事では銃の反動をアニメーションで作成していました。
なのでスクリプトから反動を表現する事を念頭に置いていなかった為、MyCharaスクリプト(キャラクター操作スクリプト)とMain Cameraに設定したスクリプトで個別にダミーのCameraPositionを変更しています(現在は修正済み)。
両方でダミーカメラ位置を操作している為、反動がうまく適用できません。
スクリプトで反動の処理を作成する
スクリプトで反動の処理を作っていきたいと思います。
スクリプトから反動を表現するにはカメラだけを動かすか、キャラクターのボーンの角度を動かす事で、銃を撃った時の反動を表現します。
カメラだけを動かす、単にボーンをランダムに動かすやり方と、キャラクターの腰のボーンを一定角度まで回転させてから元に戻す方法の3通りの方法をやってみたいと思います。
カメラだけをランダムに動かす
まずはカメラだけをランダムに動かす方法です。
このやり方を行う場合はアニメーションパラメータのShotの処理を残しておく必要があります。
この方法はFPS視点のカメラの位置を動かす事で反動を表現しますが、キャラクター自体の体は動かしません。
カメラのダミー位置CameraPositionに反動スクリプトWaveCameraを作成し取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | using UnityEngine; using System.Collections; public class WaveCamera : MonoBehaviour { private Animator animator; private Vector3 defaultPos; [SerializeField] float wave; // Use this for initialization void Start () { animator = transform.root.GetComponent <Animator> (); defaultPos = transform.localPosition; } // Update is called once per frame void Update () { if (animator.GetBool ("Shot")) { transform.localPosition = new Vector3 (transform.localPosition.x + Random.Range (-wave, wave), transform.localPosition.y + Random.Range(-wave, wave), transform.localPosition.z); } else { transform.localPosition = defaultPos; } } } |
銃を撃っている時はアニメーションパラメータのShotをオンにしているので、その間はランダム値を使ってCameraPositionのローカル位置を動かします。
Shotがオフになったら元に戻します。
アニメーションパラメータのShotを使って処理の判断をしている為、Shotを残すか時間計測で判断する必要があります。
WaveCameraに設定をして確認してみます。
↑のようにWaveには非常に小さい値を入れます。
↑のような感じになりました。
特定のボーンをランダムに動かす
次は特定のボーンに取り付けて、そのボーンを小刻みに動かす方法です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | using UnityEngine; using System.Collections; public class WaveBone : MonoBehaviour { private Animator animator; [SerializeField] float wave = 0.2f; [SerializeField] float waveTime = 0.2f; private float nowTime; // Use this for initialization void Start () { animator = transform.root.GetComponent <Animator> (); nowTime = 0f; } void Update () { nowTime += Time.deltaTime; } void LateUpdate () { if (animator.GetBool ("Shot") && nowTime <= waveTime) { transform.localRotation = Quaternion.Euler (transform.localEulerAngles.x + Random.Range (-wave, wave), transform.localEulerAngles.y + Random.Range(-wave, wave), transform.localEulerAngles.z); } else if(!animator.GetBool("Shot")){ nowTime = 0f; } } } |
このスクリプトは動かしたいボーンに取り付けます。
スクリプトの内容はWaveCameraとほとんど同じです。
今回はEthanの右ひじと左ひじ(交互に見て♪)にスクリプトを設定し、動かして見たいと思います。
EthanRightForeArmの位置は、
↑の辺りです。
このボーンをスクリプトから動かします。
WaveBoneの設定値は以下のようにします。
それでは実行してみましょう。
↑のようになりました。
右ひじと左ひじは個別に動かしているので、同じ動きにしたい場合はスクリプトを書き換える必要があります。
スクリプトをキャラクター自身等に取り付け、右ひじと左ひじのTransformをpublic等で宣言し、取得出来るようにします。
後はランダム値を作り、右ひじ、左ひじのそれぞれの位置をそのランダム値で動かすと同じ動きになります。
ボーンを特定の角度まで動かしてから元に戻す
最後に、胸のボーンを特定の角度まで動かしてから元に戻して反動を表現する方法です。
MyCharaスクリプトに処理を追加していきます。
まずはフィールド宣言です。
1 2 3 4 5 6 7 8 9 | private bool recoilFlag = false; // 銃の反動が作用しているかどうか private bool recoilUp = false; // 反動で銃が上に移動しているかどうか private Quaternion defaultRot; // 反動が起こる前の角度 [SerializeField] private float maxRecoil = 1f; [SerializeField] private float recoilSpeed = 100f; // 反動から元の位置に戻るスピード [SerializeField] private float undoSpeed = 100f; |
maxRecoilは反動で動かす角度を指定します。
recoilSpeedは上向きに移動するスピード、undoSpeedは元の角度に戻るスピードを指定します。
次に銃を撃つキーを押した時の処理に追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | if (waitShot && Input.GetButtonDown ("Fire1") && !animator.GetCurrentAnimatorStateInfo (animator.GetLayerIndex ("UpperBody")).IsName ("Shot") && !animator.IsInTransition (animator.GetLayerIndex ("UpperBody"))) { shot.Judge (); animator.SetBool ("Shot", true); if (!recoilFlag) { recoilFlag = true; recoilUp = true; defaultRot = cPos.localRotation; } // 打つたびに元の位置から開始する場合はこの処理が必要 // cPos.localRotation = defaultRot; } |
銃を撃った時にrecoilFlagがオフ(反動処理をしていない)の時にrecoilFlagをオン、recoilUpをオン、defaultRotに現在のダミーカメラCameraPositionの角度を入れておきます。
recoilUpは反動で跳ね上がっている状態の時で、オフの時は元の位置に戻る状態の時です。
次に実際に反動処理を行う部分です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | void LateUpdate() { if (recoilFlag) { // 反動で跳ね上がる if(recoilUp) { // 角度が制限値を超えていなければ角度変更 if (Quaternion.Angle (cPos.localRotation, defaultRot) < maxRecoil) { cPos.localRotation = Quaternion.RotateTowards (cPos.localRotation, Quaternion.Euler (cPos.localEulerAngles.x - maxRecoil, 0f, 0f), Time.deltaTime * recoilSpeed); } else { recoilUp = false; } // 元の位置に戻す } else { cPos.localRotation = Quaternion.RotateTowards (cPos.localRotation, defaultRot, Time.deltaTime * undoSpeed); // 角度がデフォルトの位置に戻ったら反動の終了と初期化 if(cPos.localRotation == defaultRot) { cPos.localRotation = defaultRot; recoilFlag = false; } } } else { // キャラクターの向きを変える transform.rotation = Quaternion.Euler (transform.eulerAngles.x, Mathf.Repeat(transform.eulerAngles.y + yRotate, 360f), transform.eulerAngles.z); // カメラの角度を変える float rot = Mathf.Repeat (cPos.localEulerAngles.x + xRotate, 360f); if(cameraRotateLimit > Quaternion.Angle(initCameraRot, Quaternion.Euler(rot, 0, 0))) { cPos.localRotation= Quaternion.Euler (rot, cPos.localEulerAngles.y, cPos.localEulerAngles.z); } } // 腰のボーンの角度をカメラの向きにする spine.rotation = Quaternion.Euler (spine.eulerAngles.x, spine.eulerAngles.y, spine.eulerAngles.z + cPos.localEulerAngles.x); // 即座にカメラ位置と角度を反映させる Camera.main.transform.position = cPos.transform.position; Camera.main.transform.localRotation = cPos.localRotation; } |
角度の変更はLateUpdateメソッド内に記述します。
recoilFlagがオンの時は反動が起きている状態で、その後recoilUpの状態を見て銃が跳ね上がっている時か、元に戻っている時かを判断し、ダミーカメラの角度を変更しています。
跳ね上がっている時はダミーカメラの角度と元の角度が限度角度内であればダミーカメラの角度を限度角度まで変更します。
限界角度までいったら、recoilUpをfalseにし、ダミーカメラの角度を元の角度に戻していきます。
ダミーカメラの角度が元の角度に戻ったらrecoilFlagをfalseにして、反動処理の終了とします。
cPos.localRotationとdefaultRotを==で比較していますが、反動ではX軸の角度だけを変更しているのでMathf.Approximatelyを使ってそれぞれのX軸の角度を比較し、判定した方が正しい結果が得られるかもしれません。
それではMyCharaスクリプトの反動のパラメータを設定し確認してみましょう。
↑のような感じのパラメータを設定します。
↑のようになりました。
反動のパラメータによって動きが変わるので、反動が大きいバズーカ等はmaxRecoilを大きくすると良さそうですね。
動画で銃を撃った時に反動が起きていない時がありますね・・・、動画を作ったのは少し前の事なのでどうだったか忘れてしまいました・・・(^_^;)
今、確認してみたところ問題は出ていませんが、何かあれば処理に修正を入れるかもしれません(入れないかもしれません)。