今回は主人公が銃を構えた状態で照準を合わせる事が出来るようにします。
今回の機能は以前作成した機能に処理を追加する形で作成していますので、このスクリプトは何?と思ったら以下の記事で今までの機能作成の流れを確認出来ます。
前回の記事で銃を構えて、銃からレーザーポインタが表示され(厳密には銃からじゃなくレーザーポインタからレーザーですが・・・)、弾が当たったかどうか判定するところまで作成しました。
しかし一度構えてしまうと構えた状態から動けず照準を敵に合わせるという事が出来ませんでした。
今回は照準をブレさす(日本語がおかしい?)機能も追加します。
↑のような感じに仕上がりました。
バイオハザードとはちょっと違いますが、これはこれで面白く出来そうな気がします。(^^)/
銃を構えた状態で上半身を動かす為の設定
照準を合わすには銃を構えた状態で↑↓←→のキーを押した時に上半身だけを一定方向に向かせるようにします。
まずは主人公のアニメーターコントローラーを変更します。
Base Layerの歯車をクリックし、IK Passにチェックを入れます。
こうするとOnAnimatorIKというイベントが発生するようになるので、そのイベントを受け取り実行するメソッドを主人公操作スクリプトのCharacterScript内に記述していきます。
1 2 3 4 | void OnAnimatorIK(layerIndex : int) { } |
とします。
この特別なメソッドは、例えばキャラクターの手の位置を他のオブジェクトの位置に合わせたい時などに使用します。
これに関しては
を参考にしてください。
体の一部(ボーン)を動かすスクリプト
体の一部を動かすスクリプトを作成していきます。
CharacterScriptスクリプトにボーンの角度を変更する処理を追記していきます。
フィールド宣言部
まずはフィールド宣言部です。
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 | // SpineボーンのX軸の変更 [SerializeField] private float spineXAxis = 0f; // SpineボーンのZ軸の変更の限度値 [SerializeField] private float spineXAxisLimit = 60f; // SpineボーンのZ軸の変更値 [SerializeField] private float spineZAxis = 0f; // SpineボーンのZ軸の変更の限度値 [SerializeField] private float spineZAxisLimit = 30f; // 銃を構えた時のSpineボーンのZ軸の角度 [SerializeField] private float spineInitZRot; // 銃を構えた時のSpineボーンのX軸の角度 [SerializeField] private float spineInitXRot; //回転する角度のスピード [SerializeField] private float rotateSpeed = 45f; // 手ブレを使用するかどうか [SerializeField] private bool useHandBlur; // 手ブレ度合い [SerializeField] private float handBlurDegree = 0.05f; // 構え終わった時に値をセットしたらOn private bool isHoldGun; // 銃を構えた時の右肩の角度 private Vector3 rightShoulderAngle = new Vector3(341.4749f, 59.78393f, 182.4079f); // 銃を構えた時の左型の角度 private Vector3 leftShoulderAngle = new Vector3(16.17319f, 302.896f, 184.6721f); |
キーボードの上下左右のキーを押した時にSpine(脊椎)のボーンのX軸とZ軸を元に回転させますので、その回転値を宣言します。
無限に回転が出来ると人間ではなくなるので、それぞれ変化出来る限界角度を指定しておきます。
また銃を構えた時のSpineにあたるボーンのX軸とZ軸の角度を調べておき、その角度を初期角度として保持しておきます(アニメーションによって角度は変わります)。
後で銃を構え終わった時に角度を取得して設定しますが、念の為rightShoulderAngleとleftShouderAngleに手動で初期角度を設定します。
調べる方法はアニメーターコントローラーのIdleに銃を構えるアニメーションを設定し、銃を構え終わった時の主人公の右肩と左肩のTransformのRotationの値を調べます。
今回キャラクターにはEthanを使い、SpineにあたるボーンはSpine1となっていますが、キャラクターに取り付けたボーンによってはX、Y、Z軸の向きが違う場合があるので、キャラクターによってX、ZではなくXとYを使う可能性もあるので注意してください。
rotateSpeedは1秒間に回転する角度を指定します。
useHandBlurは手ブレを使用するかどうかで、handBlurDegreeは手ブレの度合いを設定します。
isHoldGunは銃を構え終わった時にtrueにします。
OnAnimatorIKメソッド
OnAnimatorIKメソッドの中身は以下のようになります。
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | // IK処理 void OnAnimatorIK() { // 銃を構え終わっている時 if (state == MyState.WaitShot && isHoldGun) { // 最終的なZ軸の回転角度 float lastSpineRotZ; // 最終的なX軸の回転角度 float lastSpineRotX; var inputHorizontal = Input.GetAxis("Horizontal"); var inputVertical = Input.GetAxis("Vertical"); // 左右の移動キーが押されていれば胸のボーンの角度値を計算 if (inputHorizontal != 0f || inputVertical != 0f) { if (inputHorizontal > 0f) { spineXAxis -= rotateSpeed * Time.deltaTime; } if (inputHorizontal < 0f) { spineXAxis += rotateSpeed * Time.deltaTime; } if (inputVertical > 0f) { spineZAxis -= rotateSpeed * Time.deltaTime; } if (inputVertical < 0f) { spineZAxis += rotateSpeed * Time.deltaTime; } // リミットを超えていたらリミット値を設定する spineXAxis = Mathf.Clamp(spineXAxis, -spineXAxisLimit, spineXAxisLimit); spineZAxis = Mathf.Clamp(spineZAxis, -spineZAxisLimit, spineZAxisLimit); } // 360度以内に収める lastSpineRotX = Mathf.Repeat(spineInitXRot + spineXAxis, 360f); lastSpineRotZ = Mathf.Repeat(spineInitZRot + spineZAxis, 360f); // 胸のボーンの角度を変更する animator.SetBoneLocalRotation(HumanBodyBones.Spine, Quaternion.Euler(lastSpineRotX, animator.GetBoneTransform(HumanBodyBones.Spine).localEulerAngles.y, lastSpineRotZ)); // 手ブレをOnにしていれば肩のボーンを揺らす if (useHandBlur) { var randomValue = new Vector3(Random.Range(-handBlurDegree, handBlurDegree), Random.Range(-handBlurDegree, handBlurDegree), Random.Range(-handBlurDegree, handBlurDegree)); animator.SetBoneLocalRotation(HumanBodyBones.RightShoulder, Quaternion.Euler(rightShoulderAngle + randomValue)); animator.SetBoneLocalRotation(HumanBodyBones.LeftShoulder, Quaternion.Euler(leftShoulderAngle + randomValue)); } } // 銃を構えるのをやめたらIKを設定しない if (!Input.GetButton("Fire2")) { spineXAxis = 0; spineZAxis = 0; isHoldGun = false; } } |
銃を構える状態で銃を構え終わっている時に↑↓←→のキーが押されている間はspineXAxisとspineZAxisの値を変化させます。
それぞれの限界角度を超えないようにMathf.Clampを使って数値を最低値と最大値の間に制限します。
角度の変更値に初期角度を足してボーンの角度を計算しますが、360度を越えては困るのでMathf.Repeatを使って0~360以内に納めます。
1 2 3 | animator.SetBoneLocalRotation(HumanBodyBones.Spine, Quaternion.Euler(lastSpineRotX, animator.GetBoneTransform(HumanBodyBones.Spine).localEulerAngles.y, lastSpineRotZ)); |
でHumanBodyBones.Spineに設定されている骨の部分の角度をさきほど指定した値に設定します。
Y軸の角度は変更しないのでそのままの角度を設定します。
キャラクターによっては↑のキーを押したら右に傾くといった事もあります。
Boneの設定の仕方やBoneの向いている角度が問題だと思われます。
なので角度指定の場所でZに指定していたyをYの所に指定したりして、調整します。
例えば以下のように設定する可能性もあります。
1 2 3 | animator.SetBoneLocalRotation(HumanBodyBones.Spine, Quaternion.Euler(lastSpineRotZ, lastSpineRotX, 0); |
↑はあくまでも例です。
1 2 3 4 5 6 7 8 | // 銃を構えるのをやめたらIKを設定しない if (!Input.GetButton("Fire2")) { spineXAxis = 0; spineZAxis = 0; isHoldGun = false; } |
Input.GetButton(“Fire2”)を押していない時は角度の増減に利用したspineXAxis、spineZAxisは0にしておきます。
でないと構えが終わった後もキャラクターの上半身の角度が変わったまま歩いたりしてしまう為です。
Input.GetButton(“Fire2”)を押していない時という条件が入っているのは、押して上半身の角度を変えている時に敵から攻撃を受けた際に角度がリセットされるのを防ぐためです。
銃を構え終わった時に実行するメソッド
銃を構え終わった時に実行するメソッドを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // 銃を構え終わった時に実行するメソッド public void InitializeShot() { isHoldGun = true; // 角度等を設定 if (!hasAngleData) { spineInitXRot = animator.GetBoneTransform(HumanBodyBones.Spine).localEulerAngles.x; spineInitZRot = animator.GetBoneTransform(HumanBodyBones.Spine).localEulerAngles.z; rightShoulderAngle = animator.GetBoneTransform(HumanBodyBones.RightShoulder).localEulerAngles; leftShoulderAngle = animator.GetBoneTransform(HumanBodyBones.LeftShoulder).localEulerAngles; hasAngleData = true; } } |
InitializeShotは銃を構え終わった時に実行するメソッドです。
銃を構え終わった時の角度が設定されていなければそれぞれの角度を保持します。
InitializeShotメソッドを呼び出すためにアニメーションクリップの最後にReadyShotというイベントを作ります。
アニメーションイベントに関しては
を参考にしてください。
アニメーションイベントを受け取っているProcessCharaAnimEventというスクリプト内(名前はそれぞれ違うと思いますが)でキャラクター操作スクリプトのInitializeShotを呼び出すようにします。
1 2 3 4 5 | public void ReadyShot() { characterScript.InitializeShot(); } |
主人公操作スクリプトのInitializeShotメソッドを呼び出します。
これで銃を構えるアニメーションで銃を構えたらReadyShotというアニメーションイベントが発生し、それをProcessCharaAnimEventで受け取ってキャラクター操作スクリプトCharacterScriptのInitializeShotメソッドが呼ばれます。
その時に『銃を構え終わった』というisHoldGunをtrueにし、角度を保持しています。
上半身が動くか確認する
主人公に設定したCharacterScriptスクリプトのインスペクタで角度の設定をして、確認してみてください。
上のように上半身の角度の変更が出来るようになりました。
これでかなりガンシューティングっぽくなってきました。
終わりに
これで構え終わってから上半身の角度の変更が出来るようになりました。
ここまで来たらほぼバイオハザードですね(違)
(;一_一)
次回は敵キャラの当たり判定が大雑把なのでもう少し厳密にしてみようかと思います。