【バイオハザード風のゲームを作ってみよう。その6】

今回も下記動画を参考にやっていきましょう。

youtu.be

今回は照準時のズームインや体の向き等の動作を実装していきます。
[動画の流れ]
・PlayerManagersスクリプトとアニメータ(Player_Control)にparameterの"isAiming"を追加。

public class PlayerManager : MonoBehaviour
{
   (省略)
    public bool isAiming; //EP6追加

    private void Update()
    {
        (省略)
        isAiming = animator.GetBool("isAiming"); //EP6追加
    }
}

・プレイ中にマウス右クリックで反応するようにinputSystemに下記アクションを追加。

・InputManagerスクリプトでアクション"Aim"を登録し、右クリック時にアニメータ(Player_Control)のparameterの"isAiming"をtrueにします。

public class InputManager : MonoBehaviour
{
 (省略)
 [Header("Button Inputs")]
    public bool aimingInput; //EP6追加

 private void OnEnable() //OnEnableはオブジェクトが有効になったときに呼び出されます.
    {
        if(playerControls == null)
        {
          (省略)
            playerControls.PlayerActions.Aim.performed += i => aimingInput = true; //EP6追加
            playerControls.PlayerActions.Aim.canceled += i => aimingInput = false; //EP6追加
        }

        playerControls.Enable();
    }
 private void HandAimimgInput()
    {
        //移動中はaimは出来ない。
        if(verticalMovementInput!=0 || horizontalMovementInput != 0)
        {
            aimingInput = false;
            animator.SetBool("isAiming", false);
            return;
        }

        //マウスを右クリックした場合
        if (aimingInput)
        {
            animator.SetBool("isAiming", true);
        }
        else
        {
            animator.SetBool("isAiming", false);
        }
    }

}

・ PlayerLocomotionManagerスクリプトで下記追加。

public class PlayerLocomotionManager : MonoBehaviour
{
 (省略)
 private void HandleRotaion()
    {
        //Aiming中,マウスに合わせてプレイヤーが回転する
        if (playerManager.isAiming) //EP6追加
        {
            targetRotation = Quaternion.Euler(0, playerCamera.eulerAngles.y, 0); //EP6追加
            playerRotation = Quaternion.Slerp(transform.rotation,targetRotation, rotationSpeed * Time.deltaTime); //EP6追加
            transform.rotation = playerRotation; //EP6追加

        }
        else //EP6追加
        {
            //変数targetRotationにプレイヤーカメラのRotationを格納。
            targetRotation = Quaternion.Euler(0, playerCamera.eulerAngles.y, 0);

            //変数playerRotationにプレイヤー位置~変数targetRotationの値を格納。
            playerRotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);

            //プレイヤーが移動中にマウスを動かした場合、プレイヤーはマウスの動きに合わせて回転する(カメラの向きに回転する)。
            if (inputManager.verticalMovementInput != 0 || inputManager.horizontalMovementInput != 0)
            {
                transform.rotation = playerRotation;
            }
        }
    }
}

・照準時に右肩越しにズームインする際のターゲットとなる空オブジェクト(CameraAimigPosition)をプレイヤー右肩下に作成。

・カメラのズームインを PlayerCameraスクリプトに追加。ここで、照準時に適当な画面になるように上記のオブジェクトCameraAimigPositionの位置を調整。

public class PlayerCamera : MonoBehaviour
{
 (省略)
    [Header("Camera Follow Targrts")] //EP6追加
    public Transform aimedCameraPosition; //Follow this position while we are aiming

 void FollowPlayer()
    {
        //Aim中はプレイヤー肩越の視点にする
        if (playerManager.isAiming) //EP6追加
        {
            targetPosition = Vector3.SmoothDamp(transform.position, aimedCameraPosition.transform.position, ref cameraFollowVelocity, cameraSmoothTime * Time.deltaTime); //EP6追加
           // transform.position =new Vector3( targetPosition.x, targetPosition.y-1.5f, targetPosition.z);                                                                                                                                                              //transform.position = targetPosition; //EP6追加
            transform.position = targetPosition; //EP6追加
        }
        else
        {
            //SmoothDamp:目的地に向かって時間の経過とともに徐々にベクトルを変化させる。目的地は2番目の変数。
            targetPosition = Vector3.SmoothDamp(transform.position, player.transform.position, ref cameraFollowVelocity, cameraSmoothTime * Time.deltaTime);
            transform.position = targetPosition;
        }
    }

・ズームインの際、カメラが上下にも大きく動く為、これを抑えるためにPlayerCameraスクリプト内のメソッドRotateCameraを修正。

 //カメラをマウスに合わせて上下、左右に回転させる
    private void RotateCamera()
    {
        //Aim中は,上下回転をしないようにする。
        if (playerManager.isAiming) //EP6追加
        {
            //Aimではない場合のRotationがcameraPivot.localRotationに設定される為、リセットする。
            cameraPivot.localRotation = Quaternion.Euler(0, 0, 0); //Aimではない場合のRotationが

            //変数lookAmountVertical(unity内のy軸方向の回転量を格納)にunity内のy軸方向のマウス入力(inputManager.horizontalcameraInput)を格納。
            lookAmountVertical = lookAmountVertical + (inputManager.horizontalcameraInput);
            // Debug.Log("h:" + inputManager.horizontalcameraInput);

            // lookAmountHorizontal(unity内のx軸方向の回転量を格納)にunity内のx軸方向のマウス入力(inputManager.verticalCameraInput)を格納。
            lookAmountHorizontal = lookAmountHorizontal - (inputManager.verticalCameraInput);
            //  Debug.Log("v:" + inputManager.verticalCameraInput);

            // lookAmountHorizontal((unity内のx軸方向の回転量)を範囲内(minimumPivotAngle~maximumPivotAngle)の値に制限。
            //Mathf.Clamp(int value, int min, int max):min と max の範囲に値を制限し、その値を返す
            lookAmountHorizontal = Mathf.Clamp(lookAmountHorizontal, minimumPivotAngle, maximumPivotAngle);

            //unity内のy軸方向の回転量を設定
            //cameraRotationの初期化
            cameraRotation = Vector3.zero;

            cameraRotation.y = lookAmountVertical;

            //public static Quaternion Euler (Vector3 euler): z 軸を中心に z 度、x 軸を中心に x 度、y 軸を中心に y 度回転する回転を返す。
            targetRotation = Quaternion.Euler(cameraRotation);

            //public static Quaternion Slerp (Quaternion a, Quaternion b, float t): a と b の間を t で球状に補間します。パラメーター t は、[0, 1] の範囲です。
            targetRotation = Quaternion.Slerp(transform.rotation, targetRotation, aimedCameraSmothTime);

            //自身(player Cameraオブジェクト)のRotationをtargetRotation値に設定。
            transform.rotation = targetRotation;

            // unity内のx軸方向の回転量を設定
            cameraRotation = Vector3.zero;
            cameraRotation.x = lookAmountHorizontal;
            targetRotation = Quaternion.Euler(cameraRotation);
            targetRotation = Quaternion.Slerp(cameraPivot.localRotation, targetRotation, aimedCameraSmothTime);
            cameraObject.transform.localRotation = targetRotation;
        }
        else
        {
            cameraObject.transform.localRotation = Quaternion.Euler(0, 0, 0); //EP6追加

            //変数lookAmountVertical(unity内のy軸方向の回転量を格納)にunity内のy軸方向のマウス入力(inputManager.horizontalcameraInput)を格納。
            lookAmountVertical = lookAmountVertical + (inputManager.horizontalcameraInput);
            // Debug.Log("h:" + inputManager.horizontalcameraInput);

            // lookAmountHorizontal(unity内のx軸方向の回転量を格納)にunity内のx軸方向のマウス入力(inputManager.verticalCameraInput)を格納。
            lookAmountHorizontal = lookAmountHorizontal - (inputManager.verticalCameraInput);
            //  Debug.Log("v:" + inputManager.verticalCameraInput);

            // lookAmountHorizontal((unity内のx軸方向の回転量)を範囲内(minimumPivotAngle~maximumPivotAngle)の値に制限。
            //Mathf.Clamp(int value, int min, int max):min と max の範囲に値を制限し、その値を返す
            lookAmountHorizontal = Mathf.Clamp(lookAmountHorizontal, minimumPivotAngle, maximumPivotAngle);

            //unity内のy軸方向の回転量を設定
            //cameraRotationの初期化
            cameraRotation = Vector3.zero;

            cameraRotation.y = lookAmountVertical;

            //public static Quaternion Euler (Vector3 euler): z 軸を中心に z 度、x 軸を中心に x 度、y 軸を中心に y 度回転する回転を返す。
            targetRotation = Quaternion.Euler(cameraRotation);

            //public static Quaternion Slerp (Quaternion a, Quaternion b, float t): a と b の間を t で球状に補間します。パラメーター t は、[0, 1] の範囲です。
            targetRotation = Quaternion.Slerp(transform.rotation, targetRotation, cameraSmoothTime);

            //自身(player Cameraオブジェクト)のRotationをtargetRotation値に設定。
            transform.rotation = targetRotation;

            //クイックターンが実行された場合、カメラも180度回転
            if (inputManager.quickTurnInput) //EP3追加
            {

                inputManager.quickTurnInput = false;
                lookAmountVertical = player.transform.eulerAngles.y + 180;
                cameraRotation.y = cameraRotation.y + 180;
                transform.rotation = targetRotation;
                //IN FUTURE, ADD AMOOTH TRANSITION
            }

            // unity内のx軸方向の回転量を設定
            cameraRotation = Vector3.zero;
            cameraRotation.x = lookAmountHorizontal;
            targetRotation = Quaternion.Euler(cameraRotation);
            targetRotation = Quaternion.Slerp(cameraPivot.localRotation, targetRotation, cameraSmoothTime);
            cameraPivot.localRotation = targetRotation;
        }
    }

・照準時にプレイヤーの体も照準ポイントに向くように、設定していきます。まずは、RigLayer_Hand_IkオブジェクトをコピーしてRigLayer_Body_Aimingと名前を変更してRigLayer_Hand_Ikの上に移動。次に、下記のように直下に3つの空オブジェクトを作成。

・プレイヤーのコンポーネントRig Layersに下記のように先ほど作成したRigLayer_Body_Aimingを追加。ここで、RigLayersの処理は上から下へ行われる為、最後に手の処理が行われるような順番にする。

Aiming_Spine_01オブジェクトにMulti-Aim Constraintコンポネントをアタッチ。このコンポーネントで今回設定するのは下記三か所。
 ・Constrained Object:ターゲット(SorceObject)の向きに動かしたいオブジェクトを設定。
 ・Aim Axis:オブジェクト前方軸を設定するよう。
 ・SorceObject:ターゲットオブジェクトを設定。

詳細は下記参照。
docs.unity3d.com

実際に下記のように設定。

 ・Constrained Object:プレイヤーのSpine01を設定。
 ・Aim Axis:Z軸を設定。

 ・SorceObject:後述するカメラオブジェクト直下に作成したAiming Targetオブジェクトを設定。

・Camera Object直下にターゲットとなるAimingTargetを作成。PositionZ:10に設定。

Aiming_Spine_02、Aiming_HeadについてもMulti-Aim Constraintコンポネントをアタッチし、同様に設定。

・照準時のカメラ速度を早くする。

public class PlayerCamera : MonoBehaviour
{
 (省略)
 public float aimedCameraSmothTime = 3f; //EP6追加

private void RotateCamera()
    {
        if (playerManager.isAiming) 
        {
          (省略)
    targetRotation = Quaternion.Slerp(transform.rotation, targetRotation, aimedCameraSmothTime);
   (省略)
     targetRotation = Quaternion.Slerp(cameraPivot.localRotation, targetRotation, aimedCameraSmothTime);
        }
 }
}

・今のままだと照準時以外でもプレイヤーの体がターゲットに向くようになっている。また、照準時も上下に体が動きすぎるので、スクリプトによって調整する。

public class AnimatorManager : MonoBehaviour
{
 [Header("Aiming Constraints")] //EP6追加
    public MultiAimConstraint spine01; //These constraints turn the character toward aiming target
    public MultiAimConstraint spine02;
    public MultiAimConstraint head;

 (省略)
 //while aiming our character will turn towards the center of the screen
    public void UpdateAimConstraints() //EP6追加
    {
        if (playerManager.isAiming)
        {
            spine01.weight = 0.3f;
            spine02.weight = 0.3f;
            head.weight = 0.7f;
        }
        else
        {
            spine01.weight = 0f;
            spine02.weight = 0f;
            head.weight = 0f;
        }
    }
}
public class InputManager : MonoBehaviour
{
 (省略)
 private void HandAimimgInput()
    {
        //移動中はaimは出来ない。
        if(verticalMovementInput!=0 || horizontalMovementInput != 0)
      
        if (aimingInput)
        {
            animator.SetBool("isAiming", true);
            playerUIManager.crosshair.SetActive(true);
        }
        else
        {
            animator.SetBool("isAiming", false);
            playerUIManager.crosshair.SetActive(false);
        }

        animatorManager.UpdateAimConstraints(); //今回追加
    }
}

・照準ポイントに十字線を表示させる。ヒエラルキービューでImageを作成し、十字線を設定。

設定した画像。

・照準時に十字線が表示されるようにスクリプトを追加。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerUIManager : MonoBehaviour //EP6追加 Canvasにアタッチ
{
    [Header("Crosshair")]
    public GameObject crosshair;
}
public class InputManager : MonoBehaviour
{
PlayerUIManager playerUIManager; //EP6追加
(省略)
 private void Awake()
    {
       (省略)
        playerUIManager = FindObjectOfType<PlayerUIManager>(); //EP6追加
    }
(省略)
 private void HandAimimgInput()
    {
        //移動中はaimは出来ない。
        if(verticalMovementInput!=0 || horizontalMovementInput != 0)
        {
            aimingInput = false;
            animator.SetBool("isAiming", false);
            playerUIManager.crosshair.SetActive(false); //今回追加
            return;
        }

        //マウスを右クリックした場合
        if (aimingInput)
        {
            animator.SetBool("isAiming", true);
            playerUIManager.crosshair.SetActive(true); //今回追加
        }
        else
        {
            animator.SetBool("isAiming", false);
            playerUIManager.crosshair.SetActive(false); //今回追加
        }

        animatorManager.UpdateAimConstraints();
    }
}

・mixamoからダウンロードしたpistol IdleアニメーションのRig設定をHumanoidに変更すると、少し回転し、斜め前を見ているが、Animation設定のRoot Transform RotationのBased UponをOriginalに設定すると正面を向くようになった。

[Based Upon : Body Orientation]

[Based Upon : Original]

アニメーション設定については下記参照。
xr-hub.com

プレイするとこんな感じ。
youtu.be