【Unity 6 初学者向け】Input Systemでキャラクターを動かそう!WASD移動とSpaceキーでジャンプ

広告

今回は、Unity 6でキャラクターをキーボード操作できるようにするチュートリアルです。

教科書の173〜177ページ「6-3 キャラクター操作のためのスクリプトを書こう」をもとに、初めて学習する人でも理解しやすいように、操作手順とプログラムの意味を一つずつ確認していきます。

教科書と基本的な仕組みは同じですが、Unity 6での操作方法に合わせ、いくつかの手順を整理しています。

1. 今回作るもの

完成すると、次のような操作ができるようになります。

キーキャラクターの動作
W奥へ進む
S手前へ進む
A左へ進む
D右へ進む
Spaceジャンプする

キャラクターは移動する方向に向きを変え、地面に着地すると再びジャンプできるようになります。

今回学ぶ3つの仕組み

キャラクター操作には、主に3つの仕組みを使用します。

  • Input System:キーボードやゲームパッドなどの入力を受け取ります。例えば、Wキーが押されたことを検出します。
  • Player Input:Input Systemで定義した操作を、ゲームオブジェクトと結び付けるコンポーネントです。
  • Character Controller:キャラクターを移動させたり、壁や地面との衝突を処理したりするコンポーネントです。

今回は、この3つをC#スクリプトと組み合わせます。キーボードの入力が、キャラクターの動きになるまでの全体の流れは次の図のとおりです。

2. 作業前の準備

2-1. シーンを開く

教科書のサンプルプロジェクトを使用する場合は、MainSceneを開いてください。今回は、キャラクターとしてQuery-Chan-SDを使用します。Hierarchyウィンドウから、このオブジェクトを探してください。以降、このオブジェクトに必要なコンポーネントを追加していきます。

なお、自分のUnityプロジェクトで試す場合は、Capsuleなどの3Dオブジェクトでも構いません。ただし、床にはColliderが必要です。

2-2. Input Systemを確認する

Unity 6では、Input Systemが標準のプロジェクト設定で利用できる場合が多くなっています。まず、インストールされているか確認しましょう。

  1. UnityのメニューからWindowを開く。
  2. Package Managerを選択する。
  3. パッケージ一覧でInput Systemを探す。
  4. インストールされているか確認する。

インストールされていない場合は、Package Managerから追加してください。Input Systemを使用するための設定変更を求められた場合は、案内に従って有効化します。

ここで大切なのは、Input Systemはキーボード操作を受け取るための仕組みだということです。これだけではキャラクターは動きません。

3. Player Inputを追加する

3-1. コンポーネントを追加

HierarchyウィンドウでQuery-Chan-SDを選択し、Inspectorウィンドウで次の操作を行います。

  1. Add Componentをクリックする。
  2. 検索欄にPlayer Inputと入力する。
  3. 表示されたPlayer Inputを選択する。

これでPlayer Inputコンポーネントが追加されます。

3-2. Actionsを確認する

Player InputにはActionsという項目があります。これは、キャラクターがどのような操作を受け付けるかを定義するための設定です。今回必要なのは次の2つです。

Action役割
Moveキャラクターの移動
Jumpキャラクターのジャンプ

Unity 6では、InputSystem_Actionsというアセットが、Player InputのActionsに最初から設定されています。今回は、このアセットをそのまま使います。自分で作る必要はありません。

Actions欄のInputSystem_Actionsをダブルクリックすると、Input Actionsウィンドウが開きます。上の2つのActionが入っているか、ここで確認してください。

補足:Actions欄が空(None)の場合
Unity 6より前のバージョンなどでActions欄が空の場合は、Player InputのCreate Actionsから作成できます。作成したアセットの名前は、InputSystem_Actionsでなくても構いません。ただし、その場合はMoveとJumpのActionを、自分で追加する必要があります。

3-3. Action Mapを確認する

Input Actionsウィンドウを開くと、複数の設定が表示されます。標準のInputSystem_Actionsには、MoveとJump以外のActionも入っています。ここでは、今回使う部分だけを取り出して、次の3つの階層を覚えてください。

Input Actions
    │
    └─ Player(Action Map)
          │
          ├─ Move(Action)
          │    ├─ W
          │    ├─ A
          │    ├─ S
          │    └─ D
          │
          └─ Jump(Action)
               └─ Space

※ 実際には、MoveやJumpにはゲームパッドなど、ほかの入力も割り当てられている場合があります。上の図では、今回使うキーボードの入力だけを示しています。

Action Mapは操作をまとめたグループです。例えば、ゲーム中の操作をPlayer、メニュー画面の操作をUIとして分けることができます。今回はPlayerを使用します。

4. Moveアクションを設定する

4-1. Moveとは

Moveはキャラクターの移動方向を表すActionです。例えば、Wキーを押した場合は前方向に進みます。移動には前後と左右の2方向が必要です。そのため、MoveではVector2というデータ型を使用します。

Vector2 moveValue;

Vector2は2つの数値をまとめて扱う型です。

値意味
x左右方向
y前後方向

キーの入力を数値にすると、次のようになります。

キーxy
W01
S0-1
A-10
D10
入力なし00

例えば、WキーとDキーを同時に押した場合、斜め右前への移動になります。

4-2. Moveの設定を確認する

Input ActionsウィンドウでMoveを選択し、次のようになっているか確認してください。標準のInputSystem_Actionsでは、最初からこの設定になっています。

項目設定
Action NameMove
Action TypeValue
Control TypeVector2
Binding2D Vector
UpW
DownS
LeftA
RightD

設定を変更する必要はありません。WASDのほかに、矢印キーやゲームパッドが割り当てられている場合もあります。今回のスクリプトは、どの入力でもVector2の値として受け取るので、そのまま動きます。

4-3. 2D Vectorとは

2D Vectorは、複数のボタン入力を1つのVector2に変換する仕組みです。例えば、WキーとDキーが押されると、右前方向の入力が作られます。

ただし、2D Vectorの設定によっては、斜め方向の値が正規化されます。今回のスクリプトでは、斜め移動だけが速くならないようにする処理も入れます。

5. Jumpアクションを設定する

続いて、Spaceキーによるジャンプを確認します。Unity 6では、標準のInputSystem_ActionsにJumpアクションが最初から設定されています。Input ActionsウィンドウでJumpを選択し、次のようになっているか確認してください。

項目設定
Action NameJump
Action TypeButton
BindingSpace

ゲームパッドのボタンなど、Space以外の入力も割り当てられている場合があります。

補足:Jumpが存在しない場合
Unity 6より前のバージョンなどでJumpが存在しない場合は、新しくActionを作成します。Input ActionsウィンドウでAuto-Saveにチェックを入れ、Jumpを追加して、Spaceキーを割り当ててください。ゲームパッドのButton Southも割り当てる場合は、それぞれGamepad、Keyboard&Mouseのチェックを忘れないようにしましょう。

MoveとJumpでは、扱う入力の種類が異なります。Moveでは、どちらの方向に移動するかが必要です。一方、Jumpでは、基本的にボタンが押されたかどうかを知ることができれば十分です。そのため、JumpではButtonを使用します。

今回は、ジャンプボタンを押した瞬間を検出するために、次のメソッドを使用します。

_jump.WasPressedThisFrame()

このメソッドは、今回のフレームでジャンプボタンが押されたかを調べます。

ここまでのMoveとJumpの設定は、Input Actionsエディターでは次のように表示されます。

6. Player Inputの設定を確認する

Input Actionsの設定が完了したら、Inspectorに戻ってPlayer Inputの設定を確認してください。

項目設定
ActionsInputSystem_Actions(標準で設定済み)
Default MapPlayer
BehaviorSend Messagesなど(今回はイベントを使用しない)

今回のプログラムでは、Player InputからInputActionを直接取得します。そのため、Invoke Unity Eventsでメソッドを登録する必要はありません。

BehaviorをSend Messagesにした場合、スクリプトにOnMoveなどのメソッドがあれば、入力時に自動で呼び出されます。今回はこれらのメソッドを使わず、スクリプトから入力の状態を直接読み取るため、動作には影響しません。

Player Inputには、入力をスクリプトへ通知する複数の方法があります。今回のようにスクリプトから入力状態を読み取る方法と、入力イベントによってメソッドを呼び出す方法は、区別して理解しておきましょう。

なお、ソフトライムの記事「Unity 6でキー入力による移動を実装する ― Input Systemの基本」では、Player Inputを使わず、InputActionをスクリプトから直接読み取る書き方を紹介しています。1人用のゲームであれば、そちらの方法でも同じように移動できます。

Unity公式ドキュメントでも、Player InputがAction Mapの有効化などを管理する仕組みが説明されています。Unity Documentation:Player Inputコンポーネント

7. PlayerControllerスクリプトを作成する

ここからC#プログラムを作成します。Projectウィンドウで、スクリプトを保存するフォルダーを開きます。教科書のサンプルプロジェクトでは、次の場所です。

Assets/1-kinokoBattle/Scripts

フォルダー内の何もない場所を右クリックし、Create → MonoBehaviour Scriptを選択します。スクリプト名はPlayerControllerとしてください。作成されたスクリプトをダブルクリックして開きます。

8. プログラム全体

まず、今回使用するプログラムを示します。教科書のプログラムを基本にしつつ、地面に接しているときの処理と、入力が取得できなかった場合の確認を追加しています。

using UnityEngine;
using UnityEngine.InputSystem;

[RequireComponent(typeof(CharacterController))]
[RequireComponent(typeof(PlayerInput))]
public class PlayerController : MonoBehaviour
{
    // キャラクターの移動速度
    [SerializeField]
    private float moveSpeed = 3f;

    // ジャンプ時の上方向の速度
    [SerializeField]
    private float jumpPower = 3f;

    // コンポーネント
    private CharacterController _characterController;

    // キャラクター自身のTransform
    private Transform _transform;

    // 移動速度
    private Vector3 _moveVelocity;

    // Input Action
    private InputAction _move;
    private InputAction _jump;

    private void Start()
    {
        // Character Controllerを取得
        _characterController =
            GetComponent<CharacterController>();

        // Transformを取得
        _transform = transform;

        // Player Inputを取得
        PlayerInput input =
            GetComponent<PlayerInput>();

        // Player Action Mapを取得
        InputActionMap actionMap =
            input.currentActionMap;

        if (actionMap == null)
        {
            Debug.LogError(
                "Player InputのDefault Mapを確認してください。"
            );
            enabled = false;
            return;
        }

        // Action Mapを有効化
        actionMap.Enable();

        // MoveとJumpを取得
        _move = actionMap.FindAction("Move");
        _jump = actionMap.FindAction("Jump");

        if (_move == null || _jump == null)
        {
            Debug.LogError(
                "MoveまたはJumpアクションが見つかりません。"
            );
            enabled = false;
        }
    }

    private void Update()
    {
        // 地面に接しているか確認
        bool isGrounded =
            _characterController.isGrounded;

        // 移動方向を取得
        Vector2 moveValue =
            _move.ReadValue<Vector2>();

        // 左右・前後の移動速度を設定
        Vector2 direction =
            Vector2.ClampMagnitude(moveValue, 1f);

        _moveVelocity.x =
            direction.x * moveSpeed;

        _moveVelocity.z =
            direction.y * moveSpeed;

        // 移動している方向に向きを変える
        Vector3 horizontalDirection =
            new Vector3(
                _moveVelocity.x,
                0f,
                _moveVelocity.z
            );

        if (horizontalDirection.sqrMagnitude > 0.001f)
        {
            _transform.LookAt(
                _transform.position +
                horizontalDirection
            );
        }

        // 地面にいる場合
        if (isGrounded)
        {
            // 下方向の速度を小さく保つ
            if (_moveVelocity.y < 0f)
            {
                _moveVelocity.y = -2f;
            }

            // ジャンプボタンが押されたら
            if (_jump.WasPressedThisFrame())
            {
                _moveVelocity.y = jumpPower;
            }
        }
        else
        {
            // 空中では重力で下方向に加速
            _moveVelocity.y +=
                Physics.gravity.y * Time.deltaTime;
        }

        // キャラクターを移動
        _characterController.Move(
            _moveVelocity * Time.deltaTime
        );
    }
}

このプログラムでは、移動方向をワールド座標として扱います。キャラクターモデルによっては、見た目の正面方向が異なることがあります。その場合は、モデルの向きを調整する必要があります。

また、Character ControllerのMove()は重力を自動適用しないため、重力による速度の変化をスクリプトで計算しています。Unity Documentation:CharacterController.Move

9. プログラムを少しずつ理解しよう

プログラム全体を一度に理解する必要はありません。ここでは、重要な部分を分解して確認します。

9-1. usingとは

using UnityEngine;
using UnityEngine.InputSystem;

usingは、名前空間を指定するための記述です。UnityEngineには、ゲームオブジェクトやTransformなど、Unityで使用する基本的な機能が含まれています。UnityEngine.InputSystemには、Input Systemに関するクラスが含まれています。これがないと、今回使用するInputActionなどを短い名前で記述できません。

9-2. RequireComponentとは

[RequireComponent(typeof(CharacterController))]
[RequireComponent(typeof(PlayerInput))]

これは、このスクリプトが必要とするコンポーネントを指定するものです。例えば、PlayerControllerをゲームオブジェクトに追加した際にCharacter Controllerがなければ、Unityが追加してくれます。

ただし、必要なコンポーネントの存在を保証するための仕組みであり、ColliderのサイズやInput Actionの設定まで自動的に正しくなるわけではありません。

9-3. SerializeFieldとは

[SerializeField]
private float moveSpeed = 3f;

通常、private変数はInspectorに表示されません。しかし、[SerializeField]を付けると、Inspectorから値を変更できるようになります。今回は次の2つを調整できます。

変数初期値役割
moveSpeed3移動速度
jumpPower3ジャンプ時の上方向の速度

例えば、moveSpeedを6にすると、キャラクターの移動速度は約2倍になります。なお、jumpPowerはジャンプの高さそのものではありません。ジャンプ開始時の上向きの速度です。

9-4. GetComponentとは

_characterController =
    GetComponent<CharacterController>();

GetComponent<T>()は、ゲームオブジェクトに付いているコンポーネントを取得するメソッドです。今回はCharacter Controllerを取得しています。取得したコンポーネントを変数に保存することで、以降の処理で利用できます。

_characterController.isGrounded

これは、キャラクターが地面に接しているかを確認するためのプロパティです。

9-5. InputActionを取得する

PlayerInput input =
    GetComponent<PlayerInput>();

まず、Player Inputコンポーネントを取得します。続いて、Default Mapで指定されているAction Mapを取得します。

InputActionMap actionMap =
    input.currentActionMap;

そして、MoveとJumpのアクションを取得します。

_move = actionMap.FindAction("Move");
_jump = actionMap.FindAction("Jump");

ここで重要なのは、"Move"と"Jump"がInput Actionsエディターで設定した名前であることです。大文字と小文字も含め、名前を一致させる必要があります。

教科書と同じく、取得したAction MapはEnable()で有効にしています。これで、Action Map内のMoveとJumpの入力を読み取れる状態になります。

9-6. ReadValueとは

Vector2 moveValue =
    _move.ReadValue<Vector2>();

これは、Moveアクションの現在の値を取得する処理です。例えば、Wキーを押すと、通常は x = 0、y = 1 という値になります。Aキーなら x = -1、y = 0 となります。この値を使用してキャラクターの移動方向を決めます。

9-7. 移動速度を計算する

_moveVelocity.x =
    direction.x * moveSpeed;

_moveVelocity.z =
    direction.y * moveSpeed;

ここは初学者が混乱しやすい部分です。Input Systemから取得したVector2では、xが左右、yが前後を表します。一方、Unityの3D空間では通常、X軸が左右、Y軸が上下、Z軸が前後です。そのため、Input Systemのyを3D空間のzに割り当てています。

Input System             Unityの3D空間

x(左右) ───────────→ X(左右)

y(前後) ───────────→ Z(前後)

ここは今回の重要なポイントです。次の図で、入力の値と3D空間での移動方向の対応を確認してください。

9-8. LookAtとは

_transform.LookAt(
    _transform.position +
    horizontalDirection
);

これは、キャラクターを移動方向へ向ける処理です。例えば、右方向に移動している場合は、キャラクターが右を向くようになります。ただし、入力がない状態でLookAtを実行すると、現在位置とほぼ同じ位置を向こうとしてしまいます。そのため、今回のプログラムでは移動方向が存在するときだけLookAtを実行しています。

9-9. ジャンプ処理

if (_jump.WasPressedThisFrame())
{
    _moveVelocity.y = jumpPower;
}

Spaceキーが押された瞬間に、上方向の速度を設定します。今回はjumpPower = 3なので、Y方向の初速度が3になります。その結果、キャラクターが上へ移動し始めます。ただし、これだけでは自然なジャンプになりません。上昇したキャラクターを下へ戻すために、重力が必要です。

9-10. 重力処理

_moveVelocity.y +=
    Physics.gravity.y * Time.deltaTime;

これは、重力によって縦方向の速度を変化させる処理です。Physics.gravity.yは、通常は約-9.81です。負の値なので、時間が経過するほど下方向の速度が大きくなります。その結果、次のような動きになります。

  1. ジャンプ直後は上昇する。
  2. 重力によって上昇速度が減少する。
  3. 一番高い位置で上昇速度が0になる。
  4. その後、落下する。

9-11. Time.deltaTimeとは

Time.deltaTime

これは、直前のフレームから今回のフレームまでに経過した時間です。例えば、1秒間に60回Updateが実行される場合、約0.0167秒になります。移動速度にdeltaTimeを掛けることで、フレームレートの違いによる移動距離の差を小さくできます。詳しくは、Time.deltaTimeの解説記事で復習してください。

9-12. CharacterController.Moveとは

最後にキャラクターを実際に移動させます。

_characterController.Move(
    _moveVelocity * Time.deltaTime
);

_moveVelocityには移動速度が入っています。これにTime.deltaTimeを掛けることで、このフレームで移動する距離を求めています。Character ControllerのMoveは、その距離だけキャラクターを動かそうとします。壁などに衝突した場合は、Colliderによる制約を受けます。

10. Unityで動かしてみよう

スクリプトが完成したら、Unityに戻ってください。

10-1. スクリプトを追加する

HierarchyからQuery-Chan-SDを選択し、InspectorのAdd ComponentからPlayerControllerを追加します。RequireComponentの指定によって、必要なコンポーネントが自動的に追加される場合があります。最終的に、次の3つが存在することを確認します。

Query-Chan-SD
    │
    ├─ Player Input
    │
    ├─ Character Controller
    │
    └─ Player Controller

10-2. Character Controllerのサイズを調整する

教科書の設定値は次のとおりです。

項目値
Center Y0.57
Radius0.25
Height1

この値は、教科書のQuery-Chan-SDに合わせた設定です。別のモデルを使用する場合は、モデルの大きさに合わせて調整してください。特に、Character Controllerのカプセルが地面に埋まっていないか確認します。

10-3. ゲームを実行する

Unity上部の再生ボタンを押します。Gameビューをクリックしてから、次の操作を試してください。

操作確認すること
W前に進む
S後ろに進む
A左に進む
D右に進む
Spaceジャンプする
Space連打空中で何度もジャンプしない
移動中にキーを離す移動が止まる

すべて動作すれば完成です。

11. うまく動かない場合

11-1. キーを押しても動かない

まず、Player Inputを確認します。特に以下が重要です。

  • ActionsにInput Actionsアセットが設定されているか。
  • Default MapがPlayerになっているか。
  • MoveというActionが存在するか。
  • MoveのControl TypeがVector2になっているか。
  • Gameビューにキーボードのフォーカスがあるか。

11-2. ジャンプできない

Jumpアクションの設定を確認してください。特に、Spaceキーが正しく割り当てられているか確認します。また、Character Controllerが地面に接していない場合、今回のプログラムではジャンプできません。isGroundedがtrueになる状態が必要です。

11-3. キャラクターが地面をすり抜ける

床のオブジェクトにColliderが設定されているか確認してください。例えば、Cubeを床に使用した場合はBox Colliderが必要です。

11-4. キャラクターが横向きになる

モデルの正面方向とUnityのZ軸の向きが一致していない可能性があります。今回のLookAtは、ローカルのZ軸正方向を移動方向に向ける処理です。モデルの向きが違う場合は、モデルを子オブジェクトにして向きを調整する方法があります。

12. 練習問題

プログラムを完成させたら、次の課題に挑戦してください。

練習①:移動速度を変更する
InspectorからMove Speedを3から6へ変更してください。キャラクターの動きはどう変わりましたか?

練習②:ジャンプの強さを変更する
Jump Powerを3から5へ変更してください。ジャンプの高さはどう変わりましたか?

練習③:コードの意味を考える
次のコードを削除すると、キャラクターはどうなるでしょうか。実際に試す前に、結果を予想してみましょう。

_moveVelocity.y +=
    Physics.gravity.y * Time.deltaTime;

練習④:移動方向を確認する
次の行を書き加えて、Consoleで入力値を確認してください。W、A、S、Dそれぞれのキーを押し、表示される数値を確認しましょう。

Debug.Log(moveValue);

確認が終わったら、Consoleに大量のメッセージが表示されないよう、この行は削除するかコメントアウトしてください。

13. まとめ

今回は、Input Systemを使用してキャラクターを操作する方法を学びました。重要なのは、キーボードから入力を受け取って、実際にキャラクターが動くまでの流れを理解することです。

キーボード
    ↓
Input System
    ↓
Player Input
    ↓
Input Action
    ↓
PlayerController.cs
    ↓
CharacterController.Move()
    ↓
キャラクターが移動する

この一連の流れが理解できれば、ゲームパッドへの対応や、別の操作方法の追加にも応用できます。また、今回使用したCharacterController.Move()は、Rigidbodyによる物理移動とは異なる仕組みです。今後は、この違いについても学習していきましょう。

Unity公式資料

訪問数 4 回, 今日の訪問数 4回

広告