独習 Unity アプリ開発

独習でスマフォ向けアプリ開発を勉強中

Unity で8番出口風ゲームを作る!#3

Unityでゲームを作る

Unity で8番出口風ゲームを作る!

 Unity Version 2022.3.16

 

TPS風のプレーヤーコントロールとカメラ


Unity では、TPS やFPS 用のキャラクターを制御するために、CharacterController と呼ばれるコンポーネントが準備されている。今回は、このコンポーネントを使ってプレーヤーオブジェクトを作成する。

 

まず空のGameObject を作成して"Player" に名前を変更する。そのオブジェクトにCharacterController と PlayerInput をアタッチする。8番出口ではプレーヤーの体は全く表示されない(透明)のでレンダラー系のコンポネントをアタッチする必要はない。

プレーヤーオブジェクト

CharacterController にはCollider の機能も含まれている。Collider の形はCapsule になっている。Collider系で設定するパラメータは身長(Height)と胴回り(Radius)、体の中心(Center)の3点。今回は、身長 1.7 m、胴回り 0.25 m、体の中心(0, 0.85, 0)とした。

あと、TPS視点を実現するため、MainCamera をPlayer オブジェクトの子オブジェクトにして、頭の位置に移動しておく。

カメラ


PlayerInput は、マウスとキー入力イベントを取得するためにアタッチする。Input Actionの定義は、以下の4つを追加する。

  • Look
    • 視野移動のアクション。マウスのポジション位置をDelta(前回フレームからの差分)で取得する。
  • Move
    • WASD キーによる前後左右の移動のアクション。Vector2 型で入力値を取得する。
  • Dash
    • Dash 移動のアクション。Shift キーを押しながらWASD キーを押した場合はDash 移動する。
  • Lock
    • Lock Action はWebGL 対策で、WebGLだとマウスポジション入力がゲーム画面内でしか有効でないのと、フルスクリーン表示でもゲーム画面の端まで行くとそれ以上イベントが発生しなくなってしまう問題があり、それを回避するためにWebGL ビルドの場合だけ、視野移動にLock Action(マウス左ボタン)を組み合わせることにした。

Input Action

 

設定したInput Action にそれぞれコールバック関数を設定する。

Player Input コールバック設定

 

PlayerAction.cs

OnLookEvent コールバックでマウスポジションを取得して視野の移動を行う。マウスの上下移動はカメラを上下に回転させるが、マウスの左右移動は、プレーヤーオブジェクトを左右回転させることで視野を移動させる。(Cameraはプレーヤーオブジェクトの子オブジェクトになっているため)

OnMoveEvent コールバックではWASDキーによる前後左右入力をVector2 型で取得して、プレーヤーの移動を行う。このメソッド内では移動方向を計算するだけで、実際の移動処理はUpdate メソッドからコールされるDoMove メソッドにて行う。

OnDashEvent コールバックはShiftキーイベントの取得を行う。このメソッドでは単にDash中であるかどうかを判定するためのフラグのオンオフだけを行い、DoMove メソッドで実際の移動速度計算に反映させる。

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.InputSystem;
  5.  
  6. public class PlayerActions : MonoBehaviour
  7. {
  8.   [SerializeField] private CharacterController Controller;
  9.   [SerializeField] private Camera MainCamera;
  10.   private const float WALK_SPEED = 1.8f; // 1.8 [m/s]
  11.   private const float DASH_SPEED = 1.8f * 3f; // 5.4 [m/s]
  12.   private const float INVERT = -1f;
  13.   private const float ROTATION_RATIO = 0.1f;
  14.   private Vector3 _direction;
  15.   private Vector3 _keyInput;
  16.   private float _cameraAngle = 0f;
  17.   private bool _isMoving = false;
  18.   private bool _isDash = false;
  19.   private bool _isLocked = false;
  20.  
  21.   public void OnLockEvent(InputAction.CallbackContext context)
  22.   {
  23.     if (context.phase == InputActionPhase.Started)
  24.     {
  25.       _isLocked = true;
  26.     }
  27.     else if (context.phase == InputActionPhase.Canceled)
  28.     {
  29.       _isLocked = false;
  30.     }
  31.   }
  32.  
  33.   public void OnLookEvent(InputAction.CallbackContext context)
  34.   {
  35.     #if UNITY_WEBGL
  36.       if (!_isLocked) return;
  37.     #endif
  38.  
  39.     if (context.phase == InputActionPhase.Performed)
  40.     {
  41.       Vector2 delta = context.ReadValue<Vector2>();
  42.       _cameraAngle += delta.y * INVERT * ROTATION_RATIO;
  43.       _cameraAngle = Mathf.Clamp(_cameraAngle, -89f, 89f);
  44.       MainCamera.transform.localEulerAngles = new Vector3(_cameraAngle, 0f, 0f);
  45.  
  46.       this.transform.Rotate(new Vector3(0f, delta.x * ROTATION_RATIO, 0f), Space.Self);
  47.  
  48.       if (_isMoving)
  49.       {
  50.         _direction = this.transform.TransformVector(_keyInput);
  51.       }
  52.     }
  53.   }
  54.  
  55.   public void OnMoveEvent(InputAction.CallbackContext context)
  56.   {
  57.     if (context.phase == InputActionPhase.Started)
  58.     {
  59.       _isMoving = true;
  60.       Vector2 input = context.ReadValue<Vector2>();
  61.       _keyInput = new Vector3(input.x, 0, input.y);
  62.       _direction = this.transform.TransformVector(_keyInput);
  63.     }
  64.     else if (context.phase == InputActionPhase.Performed)
  65.     {
  66.       Vector2 input = context.ReadValue<Vector2>();
  67.       _keyInput = new Vector3(input.x, 0, input.y);
  68.       _direction = this.transform.TransformVector(_keyInput);
  69.     }
  70.     else if (context.phase == InputActionPhase.Canceled)
  71.     {
  72.       _isMoving = false;
  73.       _direction = Vector2.zero;
  74.     }
  75.   }
  76.  
  77.   public void OnDashEvent(InputAction.CallbackContext context)
  78.   {
  79.     if (context.phase == InputActionPhase.Started)
  80.     {
  81.       _isDash = true;
  82.     }
  83.     else if (context.phase == InputActionPhase.Canceled)
  84.     {
  85.       _isDash = false;
  86.     }
  87.   }
  88.  
  89.   private void DoMove(float deltaTime)
  90.   {
  91.     var speed = (_isDash)? DASH_SPEED : WALK_SPEED;
  92.     Controller.Move(_direction.normalized * deltaTime * speed);
  93.   }
  94.  
  95.   // Update is called once per frame
  96.   void Update()
  97.   {
  98.     DoMove(Time.deltaTime);
  99.   }
  100. }
  101.  

 

 

前回作成した通路Aを配置して、プレーヤーの移動のテストを行った結果がこちら。

www.youtube.com

 

 

次の記事


from20150817.hatenablog.com

 

 


目次に戻る