【ダークソウル風ゲームを作りたい(Unity)。その1】

今回からダークソウル風ゲームを作っていきたいと思います。

今回はプレイヤーの移動を実装します。
youtu.be

・Planeを設置し、キャラクターを配置します。
私はmixamoからキャラクターをダウンロードしました。

www.mixamo.com

・Input systemをPackageManagerでインストールします。

・Input Actionsを作成(PlayerControls)を作成し、下記のように設定しました。

RightStick[GamePad]はProcessorにStickDeadZone(デッドゾーン:特定範囲の入力値を補正すること)を設定します。
詳細は下記参照。
nekojara.city

・プレイヤーにCapsuleColliderとRigidbodyをアタッチします。

・プレイヤーに下記スクリプトを追加します。

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


namespace SG
{
    public class InputHandler : MonoBehaviour //+EP1 playerにアタッチ
    {
        public float horizontal;
        public float vertical;
        public float moveAmount;
        public float mouseX;
        public float mouseY;

        PlayerControls inputActions;

        Vector2 movementInput;
        Vector2 cameraInput;

        public void OnEnable() //OnEnableはオブジェクトが有効になったときに呼び出されます.
        {
            if (inputActions == null)
            {
                inputActions = new PlayerControls();
                 //アクション実行中(performed)に、+=でデリゲート登録されたメソッドが実行される。
                inputActions.PlayerMovement.Movement.performed += inputActions => movementInput = inputActions.ReadValue<Vector2>();
                inputActions.PlayerMovement.Camera.performed += i => cameraInput = i.ReadValue<Vector2>();
            }
            //インプットアクションの有効化
            inputActions.Enable();
        }

        private void OnDisable()
        {
            //インプットアクションの無効化
            inputActions.Disable();
        }

        public void TickInput(float delta)
        {
            MoveInput(delta);
        }

        //キーボードやマウスの入力値を取得
        public void MoveInput(float delta)
        {
            //Actions:Movementのx方向入力値(キーボードA(-1) or D(+1))を変数horizontalに代入
            horizontal = movementInput.x;
            //Actions:Movementのy方向入力値(キーボードW(+1) or S(-1))を変数Verticalに代入
            vertical = movementInput.y;
           //Clamp値(0~1)を変数moveAmountに代入。WASDの押下により1が代入される。
            moveAmount = Mathf.Clamp01(Mathf.Abs(horizontal) + Mathf.Abs(vertical));

            //Actions:Cameraの入力値(マウスの場合、前フレーム位置からの差分)を変数mouseに代入
            //mouseXはx方向値(画面横方向のデルタ値)を代入。
            //mouseYはy方向値(画面縦方向のデルタ値)を代入。
            mouseX = cameraInput.x;
            mouseY = cameraInput.y;
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace SG
{
    public class PlayerLocomotion : MonoBehaviour //+EP1 playerにアタッチ
    {
        Transform cameraObject;
        InputHandler inputHandler;
        Vector3 moveDirection;

        [HideInInspector]
        public Transform myTransform;
        [HideInInspector]
        public AnimatorHandler animatorHandler;

        public new Rigidbody rigidbody;
        public GameObject normalCamera;

        [Header("Stats")]
        [SerializeField]
        float movementSpeed = 5; //移動スピード
        [SerializeField]
        float rotationSpeed = 10; //歩行スピード

        private void Start()
        {
            rigidbody = GetComponent<Rigidbody>();
            inputHandler = GetComponent<InputHandler>();
            animatorHandler = GetComponentInChildren<AnimatorHandler>();
            //MainCameraのtransformを取得
            cameraObject = Camera.main.transform;
            myTransform = transform;
            animatorHandler.initialize();
        }

        public void Update()
        {
            //Time.deltaTime:前回のフレームからの経過時間
            float delta = Time.deltaTime;

            inputHandler.TickInput(delta);

            moveDirection = cameraObject.forward * inputHandler.vertical;
            //cameraObject.forward:world spaceに対するプレイヤーの前方位置(0.0, 0.0, -1.0)
            //inputHandler.vertical:キーボードW(+1) or S(-1)
            //moveDirection:キーボードW(0.0, 0.0, -1.0) or S(0.0, 0.0, 1.0)

            moveDirection += cameraObject.right * inputHandler.horizontal;
            //cameraObject.right:world spaceに対するプレイヤーの右位置(-1.0, 0.0, 0.0)
            //inputHandler.horizontal:キーボードA(-1) or D(+1)
            //cameraObject.right * inputHandler.horizontal:キーボードA(1.0, 0.0, 0.0) or D(-1.0, 0.0, 0.0)
            // moveDirection:キーボードW,A押下の場合(1.0, 0.0, -1.0) 

            //正規化:ベクトルの向きは変えずに大きさを1にする
            moveDirection.Normalize();

            float speed = movementSpeed;
            moveDirection *= speed;

            //ProjectOnPlane:斜面に沿ったベクトルを計算
            Vector3 projectedVelocity = Vector3.ProjectOnPlane(moveDirection, normalVector);
           
            //velocityによりプレイヤー移動
            rigidbody.velocity = projectedVelocity;

            //animatorの変数Verticalと変数Horizontalの値を設定
            animatorHandler.UpdateAnimatorValues(inputHandler.moveAmount, 0);

            if (animatorHandler.canRotate)
            {
                HandleRotaion(delta);
            }
        }

        #region Movement
        Vector3 normalVector;
        Vector3 targetPosition;

        //プレイヤーの回転方向を制御
        private void HandleRotaion(float delta)
        {
            Vector3 targetDir = Vector3.zero;
            float moveOverride = inputHandler.moveAmount;

            targetDir = cameraObject.forward * inputHandler.vertical;

            targetDir += cameraObject.right*inputHandler.horizontal;

            //正規化されたときベクトルは同じ方向は維持したままで長さが 1.0 のものが作成されます。
            targetDir.Normalize();
            targetDir.y = 0;

            //WASDを押してない場合
            if (targetDir == Vector3.zero)
                targetDir = myTransform.forward;

            float rs = rotationSpeed;

            Quaternion tr = Quaternion.LookRotation(targetDir); //指定された forward(targetDir) と upwards 方向に回転します。

            //public static Quaternion Slerp (Quaternion a, Quaternion b, float t): a と b の間を t で球状に補間します。
            //パラメーター t は、[0, 1] の範囲です。
            //回転がゆるやかになる。
              Quaternion targetRotation = Quaternion.Slerp(myTransform.rotation, tr, rs * delta);

              myTransform.rotation = targetRotation;
        }

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


namespace SG
{
    public class AnimatorHandler : MonoBehaviour //+EP1 player直下のPaladinにアタッチ
    {
        public Animator anim;
        int vertical;
        int horizontal;
        public bool canRotate;

        public void initialize()
        {
            anim = GetComponent<Animator>();
            //animatorからstring名の値を取得
            vertical = Animator.StringToHash("Vertical");
            horizontal = Animator.StringToHash("Horizontal");
        }

        public void UpdateAnimatorValues(float verticalMovement,float horizontalMovement)
        {
            #region Vertical
            float v = 0;

            //引数により変数vを設定
            if(verticalMovement>0 && verticalMovement < 0.55f)
            {
                v = 0.5f;
            }
            else if (verticalMovement > 0.55f)
            {
                v = 1;
            }
            else if (verticalMovement < 0 && verticalMovement > -0.55f)
            {
                v = -0.5f;
            }
            else if (verticalMovement < -0.55f)
            {
                v = -1;
            }
            else
            {
                v = 0;
            }

            #endregion

            #region

            float h = 0;

            if(horizontalMovement>0 && horizontalMovement < 0.55f)
            {
                h = 0.5f;
            }
            else if (horizontalMovement > 0.55f)
            {
                h = 1;
            }
            else if (horizontalMovement < 0 && horizontalMovement > -0.55f)
            {
                h = -0.5f;
            }
            else if (horizontalMovement < -0.55f)
            {
                h = -1;
            }
            else
            {
                h = 0;
            }

            #endregion

            // anim.SetFloat(id, 値,値が変化する幅 , 1フレームの時間幅);
            anim.SetFloat(vertical, v, 0.1f, Time.deltaTime);
            anim.SetFloat(horizontal, h, 0.1f, Time.deltaTime);    
        }

        public void CanRotate()
        {
            canRotate = true;
        }

        public void StopRotation()
        {
            canRotate = false;
        }
    }
}


・Animatorコントローラーを作成し、BlendTree(Locomotion)を作ります。また、パラメータverticalとhorizontalを追加します。

・BlendTree(Locomotion)でIdle、walking、Runアニメーションを設定します。

アニメーションはmixamoからダウンロードしました。
アニメーションの設定です(Idle,Walking,Run全て同じ設定)。
Rig設定をHumanoidに変更。
LoopTimeとLoopPoseにチェック。
【Root Transform Rotation】
Bake Into Poseにチェック:チェックを入れるとオブジェクトの向きが、アニメ―ションによる回転に左右されなくなります。
Based UponをOriginalにする:Idle時にBody Orientation設定だと斜め前を見ているがBased UponをOriginalに設定すると正面を向くようになった為、idle以外にも適用。

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

今回は以上です。