【メタルギアソリッド風ゲームを作りたい(UE4)。その16】

今回は巡回AIの追加を行います。
youtu.be

・前回作成したPath_PointのActor内のtag:Path_Aを設定します。

また、AIキャラクターにもActor内のtag:Path_Aを設定します。

・新たに巡回ルートにPathPointを配置します。

・Path_PointのActor内のtag:Path_Bを設定します。
また、AIキャラクターを追加し、Actor内のtag:Path_Bを設定します。

・BPのBTTask_Pathに下図のノードを追加します。

今回は以上です。

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

今回はスプリントアニメーションを実装します。
youtu.be

・スプリントアニメーションを用意します。私はmixamoからダウンロードしました。
RigをHumanoidに変更します。Animationを下記のように設定します。

・スプリントアニメーションをBlend Treeに設定します。

・下記スクリプトを追加します。

public class InputHandler : MonoBehaviour //+EP1 playerにアタッチ
    {
        public float rollInputTimer; //+EP5 ローリングボタンを押下している時間を格納
        public bool sprintFlag; //+EP5 スプリントフラグがOnの場合、true

        //+EP4 ローリングをONした場合の設定
        private void HandleRollInput(float delta)
        {
            //ローリングボタン押下でb_input=true
            b_Input = inputActions.PlayerActions.Roll.phase == UnityEngine.InputSystem.InputActionPhase.Started;

            //ローリングボタンを押下中
            if (b_Input)
            {
                //+EP5
                rollInputTimer += delta;
                sprintFlag = true;
            }
            else //+EP5
            {
                //ローリングボタンを下記条件でON/OFFした場合、ローリングを行う
                if(rollInputTimer>0 && rollInputTimer < 0.5f)
                {
                    sprintFlag = false;
                    rollFlag = true;
                }

                rollInputTimer = 0;
            }
        }
   }
 public class PlayerLocomotion : MonoBehaviour //+EP1 playerにアタッチ
    {
     float sprintSpeed = 7; //+EP5 スプリントスピード

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

            isSprinting = inputHandler.b_Input; //+EP5

            inputHandler.TickInput(delta);
            HandleMovement(delta);
            HandleRollingAndSprinting(delta); //+EP4   
        }

        //+EP4 プレイヤーの移動を制御
        public void HandleMovement(float delta)
        {
            //+EP5 ローリング中の場合、以降なにもしない
            if (inputHandler.rollFlag)
                return;

            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();

            moveDirection.y = 0; //+EP2

            float speed = movementSpeed;

            //+EP5 スプリントフラグがONになった場合の設定
            if (inputHandler.sprintFlag)
            {
                speed = sprintSpeed;
                isSprinting = true;
                moveDirection *= speed;
            }
            else
            {
                moveDirection *= speed;
            }

            //ProjectOnPlane:斜面に沿ったベクトルを計算
            Vector3 projectedVelocity = Vector3.ProjectOnPlane(moveDirection, normalVector);

            //velocityによりプレイヤー移動
            rigidbody.velocity = projectedVelocity;

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

            if (animatorHandler.canRotate)
            {
                HandleRotaion(delta);
            }
        }
    }
public class AnimatorHandler : MonoBehaviour //+EP1 player直下のPaladinにアタッチ
{
  public void UpdateAnimatorValues(float verticalMovement,float horizontalMovement,bool isSprinting)
  {
       Vertical
    
      Horizontal

     //+EP5 スプリントフラグがONになった場合の設定
            if (isSprinting)
            {
                v = 2;
                h = horizontalMovement;
            }
            // anim.SetFloat(id, 値,値が変化する幅 , 1フレームの時間幅);
            anim.SetFloat(vertical, v, 0.1f, Time.deltaTime);
            anim.SetFloat(horizontal, h, 0.1f, Time.deltaTime);    
  }
}
public class PlayerManager : MonoBehaviour //+EP4 playerにアタッチ
{
        void Update()
        {
            //変数isInteractingを取得
            inputHandler.isInteracting = anim.GetBool("isInteracting");

            inputHandler.rollFlag = false;

            inputHandler.sprintFlag = false; //+EP5
        }
}

今回は以上です。

【メタルギアソリッド風ゲームを作りたい(UE4)。その15】

今回は敵キャラクターAIの実装で、決まったルート巡回するように設定します。
youtu.be

・キャラクターのアニメーションをベースに敵キャラのアニメーションを作ります。

・BP(BP_EnemyAI)を作成し、メッシュと武器を配置します。
また、武器が特定の位置になるように下記BPを追加します。

・敵AIの実装をしていきます。AIControllerやビヘイビアツリー、ブラックボードを使用しますが、詳細は下記参照。
docs.unrealengine.com

・AIControllerを作成し、下記BPを追加します。

・ビヘイビアツリーとブラックボードを作ります。ブラックボードにキーを追加します。

・巡回ルートの設定をします。ActorクラスのBPを作成し、巡回ルートに配置します。

・TaskクラスのBPを作成します。巡回ポイントのロケーションをブラックボードに設定しています。

・ビヘイビアツリーを下図のように設定します。上記のタスクで設定したポイントに向けてMove Toノードでキャラクター移動させます。
その後、1秒間待機し、次のポイントに向けて移動します。

今回は以上です。

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

今回はローリングを実装します。
youtu.be

・ローリングのアニメーションを準備します。私はmixamoからダウンロードしました。
RigのAnimationTypeをHumanoidに変更。
Animationを下図に設定。

・AnimatorにRollingステートを追加しMotionに用意したアニメーションを設定します。また、bool型のパラメータ:isInteractingを追加します。

・PlayerControls(Input Action)でローリングのインプット設定します。キーボードの場合、LeftShiftに設定しました。

・下記スクリプトを追加します。

  public class InputHandler : MonoBehaviour //+EP1 playerにアタッチ
    {
        public bool b_Input; //+EP4
        public bool rollFlag; //+EP4
        public bool isInteracting; //+EP4

 public void TickInput(float delta)
        {
            MoveInput(delta);
            HandleRollInput(delta); //+EP4
        }

 //+EP4 ローリングをONした場合の設定
        private void HandleRollInput(float delta)
        {
            //ローリングボタン押下でb_input=true
            b_Input = inputActions.PlayerActions.Roll.phase == UnityEngine.InputSystem.InputActionPhase.Started;

            if (b_Input)
            {
                rollFlag = true;
            }
        }
}
 public class PlayerLocomotion : MonoBehaviour //+EP1 playerにアタッチ
    {
 public void Update()
        {
            //Time.deltaTime:前回のフレームからの経過時間
            float delta = Time.deltaTime;

            inputHandler.TickInput(delta);
            HandleMovement(delta);
            HandleRollingAndSprinting(delta); //+EP4
        }

 //+EP4 ローリングアニメーションを実行
        public void HandleRollingAndSprinting(float delta)
        {
            //プレイヤーがローリング中はローリングボタンを押しても何もしない
            if (animatorHandler.anim.GetBool("isInteracting"))
                return;

            //ローリングボタンを押した場合
            if (inputHandler.rollFlag)
            {
                moveDirection = cameraObject.forward * inputHandler.vertical;
                // moveDirection:カメラに対するプレイヤーの前後方向のベクトル
                //cameraObject.forward:カメラの正面の向きのベクトル。
                //カメラz軸向き(青色)をワールド座標系で表現された長さが1の単位ベクトル
                //inputHandler.vertical:プレイヤーの前後の向き。前+1,後ろー1

                moveDirection += cameraObject.right * inputHandler.horizontal;
                // moveDirection:カメラに対するプレイヤーの方向のベクトル
                //cameraObject.right:カメラの右向きのベクトル。
                //カメラx軸向き(赤色)をワールド座標系で表現された長さが1の単位ベクトル
                //inputHandler.horizontal:プレイヤーの左右の向き。右+1,左ー1

                //移動中(WASDが押された場合)
                if (inputHandler.moveAmount > 0)
                {
                    //ローリングアニメーションを実行
                    animatorHandler.PlayTargetAnimation("Rolling", true);
                    moveDirection.y = 0;

                 //   Quaternion rollRotation = Quaternion.LookRotation(moveDirection);
                    //LookRotation (Vector3 forward, Vector3 upwards= Vector3.up):指定された forward と upwards 方向に回転

                //    myTransform.rotation = rollRotation;
                }
                else
                {
                    // animatorHandler.PlayTargetAnimation("BackStep", true);
                }
            }
        }

    }
 public class AnimatorHandler : MonoBehaviour //+EP1 player直下のPaladinにアタッチ
    {
 //+EP4 キャラクターの動く速度と、アニメーションが同期してない場合に、フットスライディング(足滑り)が起こることを防ぐために、
        //キャラクターの動く速度を調整
        private void OnAnimatorMove()
        {
            //ローリング中ではない場合、関数を抜ける
            if (inputHandler.isInteracting == false)
                return;

            float delta = Time.deltaTime;

            playerLocomotion.rigidbody.drag = 0;
            //Drag (抗力) は、オブジェクトを減速するために使われます。 抗力が高いほどオブジェクトはより減速します。

            //アニメーションの1フレーム分の位置の差分
            Vector3 deltaPosition = anim.deltaPosition;

            deltaPosition.y = 0;

            //アニメーションの1フレーム分の速度
            Vector3 velocity = deltaPosition / delta;

            //アニメーションによるプレイヤー
           playerLocomotion.rigidbody.velocity = velocity;
        }
    }
 public class PlayerManager : MonoBehaviour //+EP4 playerにアタッチ
    {
        InputHandler inputHandler;
        Animator anim;

        void Start()
        {
            inputHandler = GetComponent<InputHandler>();
            anim = GetComponentInChildren<Animator>();
        }

        void Update()
        {
            //変数isInteractingを取得
            inputHandler.isInteracting = anim.GetBool("isInteracting");

            inputHandler.rollFlag = false;
        }
    }


・AnimatorのRollingノードに下記スクリプトをアタッチします。

public class ResetIsInteracting : StateMachineBehaviour
{
 //+EP4
    // OnStateExit is called when a transition ends and the state machine finishes evaluating this state
    override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        animator.SetBool("isInteracting", false);
    }
}

今回は以上です。