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

今回も下記動画を参考にしました。

youtu.be


まず、Shiftボタン押下でプレイヤーを走れるようにします。
はじめにInput Actionsを下のように設定。

そして、スクリプトを追加しました。

public class InputManager : MonoBehaviour
{
    //フィールド
    [Header("Button Inputs")]
    public bool runInput;

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

}
public class AnimatorManager : MonoBehaviour
{

 public void HandleAnimatorValues(float horizontalMovememt, float verticalMovement ,bool isRunning)
    {
        if (isRunning && snappedVertical > 0) //we don't want to be able to run backwards, or run whilst moving backwards 
        {
            snappedVertical = 2;

        }
    }
}

次に、mixamoから下記のRunアニメーションを取得しました。
アニメーション:Pistol Run

そして、Player_ConrollerのブレンドツリーにRunを設定します。

一番下がRunの部分です。PosY=2にしています。

次に、クイックターンをできるようにします。
Qボタン押下でクイックターンするようにinput Actionに[QuickTurn]を設定。

また、クイックターン時のアニメーションを設定します。
mixamoより下記アニメーションを取得。
アニメーション:Quick 180 Turn
(ライフル所持時のアニメーションですが、他に良いものが無かったので、こちらを使用。)

Animation Controller(Player_Controller)でLayersを追加(OverrideLayer)し、ここに取得したアニメーションを追加します。
なお、アニメーション名は後述するスクリプトに合わせて"Quick 180 Turn"→"QuickTurn"に変更。

そして、スクリプトを追加します。
ダッシュクイックターンが出来ました。
youtu.be

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

public class PlayerManager : MonoBehaviour
{
    PlayerCamera playerCamera;
    InputManager inputManager;
    PlayerLocomotionManager playerLocomotionManager;
    Animator animator; //EP3追加

    //EP3追加------------
    [Header("player Flags")]
   // public bool disableRootMotion;
    public bool isPreformingAction;
    public bool isPreformingQuickTurn;
    //-------------------

    private void Awake()
    {
        playerCamera=FindObjectOfType<PlayerCamera>();
        inputManager = GetComponent<InputManager>();
        playerLocomotionManager = GetComponent<PlayerLocomotionManager>();
        animator = GetComponent<Animator>(); //EP3追加
    }

    private void Update()
    {
        inputManager.HandleAllInputs();

     //   disableRootMotion = animator.GetBool("disableRootMotion"); //EP3追加
        isPreformingAction = animator.GetBool("isPreformingAction"); //EP3追加
        isPreformingQuickTurn = animator.GetBool("isPreformingQuickTurn"); //EP3追加
    }

    private void FixedUpdate()
    { 
        if (!isPreformingAction) //EP3追加
        {
            playerLocomotionManager.HandleAllLocomotion();
            inputManager.Move();
            inputManager.Turning();
        }
    }

    private void LateUpdate() //毎フレーム毎、様々計算が終わった後に呼ばれる。
    {
        playerCamera.HandleAllCameraMovement();
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerCamera : MonoBehaviour
{
    public InputManager inputManager;

    public Transform cameraPivot;
    public Camera cameraObject;
    public GameObject player;

    Vector3 cameraFollowVelocity = Vector3.zero;
    Vector3 targetPosition;
    Vector3 cameraRotation;
    Quaternion targetRotation;

    [Header("Camera Speeds")] //Inspectorにヘッダーを追加
    public float cameraSmoothTime=0.2f;

    float lookAmountVertical;
    float lookAmountHorizontal;
    float maximumPivotAngle = 15;
    float minimumPivotAngle = -15;


    public void HandleAllCameraMovement()
    {
        FollowPlayer();
        RotateCamera();
    }

    //カメラをプレイヤーに追尾させる
    void FollowPlayer()
    {
        //SmoothDamp:目的地に向かって時間の経過とともに徐々にベクトルを変化させる。目的地は2番目の変数。
        targetPosition = Vector3.SmoothDamp(transform.position, player.transform.position, ref cameraFollowVelocity, cameraSmoothTime * Time.deltaTime);
        transform.position = targetPosition;
    }

    //カメラをマウスに合わせて上下、左右に回転させる
    private void RotateCamera()
    {
        //変数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;
        
    }

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

public class AnimatorManager : MonoBehaviour
{
    
    Animator animator;
    PlayerManager playerManager; //EP3追加
    PlayerLocomotionManager playerLocomotionManager; //EP3追加

    float snappedHorizontal;
    float snappedVertical;

    public void Awake()
    {
        animator = GetComponent<Animator>();
        playerManager = GetComponent<PlayerManager>(); //EP3追加
        playerLocomotionManager = GetComponent<PlayerLocomotionManager>(); //EP3追加
    }

    //クイックターンのアニメーションを実行
    public void PlayAnimationWithOutRootMotion(string targetAnimation, bool isPreformingAction) //EP3追加
    {
        animator.SetBool("isPreformingAction", isPreformingAction);
        animator.SetBool("disableRootMotion", true);
       // animator.applyRootMotion = false;
        //Animation.CrossFade:time 秒に渡り他のアニメーションをフェードアウトさせながら animation という名のアニメーションをフェードインさせる
        animator.CrossFade(targetAnimation, 0.2f);
    }

    //移動アニメーション
    public void HandleAnimatorValues(float horizontalMovememt, float verticalMovement ,bool isRunning)
    {
        if (horizontalMovememt > 0)
        {
            snappedHorizontal = 1;
        }
        else if (horizontalMovememt < 0)
        {
            snappedHorizontal = -1;
        }
        else
        {
            snappedHorizontal = 0;
        }


        if (verticalMovement > 0)
        {
            snappedVertical = 1;
        }
        else if (verticalMovement < 0)
        {
            snappedVertical = -1;
        }
        else
        {
            snappedVertical = 0;
        }

        if (isRunning && snappedVertical > 0) //we don't want to be able to run backwards, or run whilst moving backwards 
        {
            snappedVertical = 2;
        }

        animator.SetFloat("Horizontal", snappedHorizontal, 0.1f, Time.deltaTime);
        animator.SetFloat("Vertical", snappedVertical, 0.1f, Time.deltaTime);

    }

    //EP3追加
    private void OnAnimatorMove()
    {
        /*
        if (playerManager.disableRootMotion)
            return;
        */

        Vector3 animatorDeltaposition = animator.deltaPosition;
        animatorDeltaposition.y = 0;

        Vector3 velocity = animatorDeltaposition / Time.deltaTime;
        playerLocomotionManager.playerRigidbody.drag = 0;
        playerLocomotionManager.playerRigidbody.velocity = velocity;
        transform.rotation *= animator.deltaRotation;
    }

}

>|cs|
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//テスト用
using UnityEngine.InputSystem;

public class InputManager : MonoBehaviour
{
    PlayerControls playerControls;
    AnimatorManager animatorManager;
    Animator animator; //EP3追加
    PlayerManager playerManager; //EP3追加

    [Header("Player Movement")]
    public float verticalMovementInput;
    public float horizontalMovementInput;
    private Vector2 movementInput;

    [Header("Camera Rotaion")]
    public float verticalCameraInput;
    public float horizontalcameraInput;
    private Vector2 CameraInput;

    [Header("Move")]
    public float speed = 3f; //歩行スピード
    public float rotateSpeed = 1f; //体の向きのスピード
    Vector3 direction;

    [Header("Button Inputs")]
    public bool runInput;

    public bool quickTurnInput; //EP3追加

    private void Awake()
    {
        animatorManager = GetComponent<AnimatorManager>();
        animator = GetComponent<Animator>(); //EP3追加
        playerManager = GetComponent<PlayerManager>(); //EP3追加
    }

    private void OnEnable() //OnEnableはオブジェクトが有効になったときに呼び出されます.
    {
        if(playerControls == null)
        {
            playerControls = new PlayerControls();

            //アクション実行中コールバック
            //~.performedにiを登録.
             playerControls.PlayerMovement.Movement.performed += i => movementInput = i.ReadValue<Vector2>();

            //テスト用
           // playerControls.PlayerMovement.Movement.performed += Value_V2;

            playerControls.PlayerMovement.Camera.performed += i => CameraInput = i.ReadValue<Vector2>();

            playerControls.PlayerMovement.Run.performed += i => runInput = true;
            playerControls.PlayerMovement.Run.canceled += i => runInput = false;
            //InputActionのQuickTurnに設定されたボタンが押下された場合、quickTurnInput = trueとする。
            playerControls.PlayerMovement.QuickTurn.performed += i => quickTurnInput = true; //EP3追加
        }

        playerControls.Enable();
    }

    /*
    //テスト用
    private void Value_V2(InputAction.CallbackContext obj )
    {
        movementInput = obj.ReadValue<Vector2>();
       // Debug.Log("y: " + movementInput.y);
    }
    */

    private void OnDisable()
    {
        playerControls.Disable();
    }

    public void HandleAllInputs()
    {
        HandMovementInput();
        HandCameraInput();
        HandleQuickTurnInput(); //EP3追加

        // Debug.Log("y: " + movementInput.y);
    }

    private void HandMovementInput()
    {
        horizontalMovementInput = movementInput.x;
        verticalMovementInput = movementInput.y;
        animatorManager.HandleAnimatorValues(horizontalMovementInput, verticalMovementInput, runInput);
    }

    private void HandCameraInput()
    {
        horizontalcameraInput = CameraInput.x;
        verticalCameraInput = CameraInput.y;
    }

    public void Move()
    {
      //   Debug.Log("x: " + horizontalMovementInput);
      //   Debug.Log("y: " + verticalMovementInput);
        MoveInput(verticalMovementInput);
    }

    // 上下キーが押されたら前進/後進
    void MoveInput(float v)
    {
        if (v != 0)
        {
            direction = transform.TransformDirection(new Vector3(0.0f, 0.0f, v)); //ローカル空間からワールド空間へ x、y、z を変換します。
            direction *= speed;
            direction *= Time.deltaTime;
            // direction /= 2f; //歩行スピード調整
            transform.position += direction;
        }
      
    }

    public void Turning()
    {
        TurningInput(horizontalMovementInput);
    }

    //左右キーが押されたら回転
    void TurningInput(float h)
    {
        if (h != 0)
        {
            //左右に回転させるにはRotateメソッドを使用。
            //y軸の引数に左右キーが押下された時に得られる1or - 1の値にspeedとdeleteTimeをかけて引数として使用しました。
            transform.Rotate(0, 100 * Time.deltaTime * rotateSpeed * h, 0);
            
        }
    }

    //
    private void HandleQuickTurnInput() //EP3追加
    {
        //クイックターン実行中はQボタンの押下を無効にする。
        if (playerManager.isPreformingAction)
        {
            return;
        }

        if (quickTurnInput) //Qボタンが押された場合
        {
            animator.SetBool("isPreformingQuickTurn", true);
            animatorManager.PlayAnimationWithOutRootMotion("QuickTurn" ,true);
        }
    }

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

public class PlayerLocomotionManager : MonoBehaviour
{
    InputManager inputManager;

    public Rigidbody playerRigidbody; //EP3追加
    PlayerManager playerManager; //EP3追加

    [Header("Camera Transform")]
    public Transform playerCamera;

    [Header("Movement Speed")]
    public float rotationSpeed = 3.5f;
    public float quickTurnSpeed = 8; //EP3追加

    [Header("Rotaion Variables")]
    Quaternion targetRotation; //The place we want to rotate
    Quaternion playerRotation; //The place we are rotating now, constantly changing

    private void Awake()
    {
        playerRigidbody = GetComponent<Rigidbody>(); //EP3追加
        inputManager = GetComponent<InputManager>();
        playerManager=GetComponent<PlayerManager>(); //EP3追加
    }

    public void HandleAllLocomotion()
    {
        HandleRotaion();

    }

    private void HandleRotaion()
    {
        //変数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;
        }

        //EP3追加。アニメーションで回転する為、以下不要。
        //クイックターン時にプレイヤーを現在位置から~変数targetRotationの値まで回転する。
        /*
        if (playerManager.isPreformingQuickTurn)
        {
            playerRotation = Quaternion.Slerp(transform.rotation, targetRotation, quickTurnSpeed * Time.deltaTime);
            transform.rotation = playerRotation;
        }
        */
        
        
        
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ResetAnimatiorBool : StateMachineBehaviour //EP3追加
{
    /*
    [Header("disableRootMotion")]
    public string disableRootMotion = "disableRootMotion";
    public bool disableRootMotionStatus = false;
    */

    [Header("Is Preforming Action Bool")]
    public string isPreformingAction = "isPreformingAction";
    public bool isPreformingActionStatus = false;

    [Header("isPreformingQuickTurn")]
    public string isPreformingQuickTurn = "isPreformingQuickTurn";
    public bool isPreformingQuickTurnStatus = false;

    // OnStateEnter is called when a transition starts and the state machine starts to evaluate this state
    override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
      //  animator.SetBool(disableRootMotion, disableRootMotionStatus);
        animator.SetBool(isPreformingAction, isPreformingActionStatus);
        animator.SetBool(isPreformingQuickTurn, isPreformingQuickTurnStatus);
    }

}