Unity 6でキー入力による移動を実装する ― Input Systemの基本

広告

はじめに

Unityでプレイヤーをキーボード操作によって移動させる場合、以前は次のようなコードをよく使用していました。

float x = Input.GetAxis("Horizontal");
float y = Input.GetAxis("Vertical");

あるいは、

if (Input.GetKey(KeyCode.LeftArrow))
{
    // 左へ移動
}

という書き方もよく使われていました。

しかし、現在のUnityでは、これらの古いInput Managerではなく、Input Systemを使う方法が基本となっています。

Unity 6の公式マニュアルでも、Input System Packageは新しく柔軟性の高い入力システムとして紹介されており、ほとんどのプロジェクトで推奨されています。従来のInput Managerはレガシー機能として扱われています。

今回は、

キーボード
   ↓
Input Action(Move)
   ↓
PlayerController
   ↓
GameObjectを移動

という流れで、キャラクターを上下左右に動かしてみます。

Unity 6では、Move(移動)などの基本的なInput Actionが、あらかじめ用意されています。今回はそれを使うため、自分でInput Actionsを作る必要はありません。自分で作る方法は、記事の途中にある折りたたみの中で説明します。


今回作るもの

キーボードの

W
A S D

または矢印キーを押すことで、GameObjectを上下左右に移動できるようにします。

たとえば、

       W
        ↑
A ← Player → D
        ↓
        S

という操作です。

今回重要なのは、

「Wキーが押されたか」をプログラムから直接調べるのではない

という点です。

Input Systemでは、

Wキー
Sキー
Aキー
Dキー
   ↓
Moveという入力
   ↓
Vector2

として扱います。


Input Systemとは

Input Systemは、Unityの新しい入力システムです。

キーボードだけでなく、

  • ゲームパッド
  • マウス
  • タッチパネル
  • ジョイスティック
  • VR / XRコントローラー

など、さまざまな入力デバイスを統一的に扱えます。

Unity 6ではInput Systemが推奨されており、Input Systemパッケージのリリース版が提供されています。

Input Systemの大きな特徴は、

「ゲーム上の操作」と「実際のキー」を分離できること

です。

たとえば、

Move

という操作を作り、そこへ

キーボード WASD
キーボード 矢印キー
ゲームパッド 左スティック

などを登録できます。

つまりプログラム側では、

どのキーが押されたか

ではなく、

Moveという操作が行われた

と考えます。

Input Actionは、このように「操作の意味」と物理的な入力デバイスを分離するための仕組みです。


Input Systemを準備する

Unityのバージョンや作成したプロジェクトによっては、Input Systemがすでに利用できる状態になっている場合があります。

Package Managerを開き、「In Project」の一覧にInput Systemがあれば準備完了です。

Input Systemが入っていない場合

利用できない場合は、

Window
  ↓
Package Manager

を開き、

Input System

をインストールします。

インストール後に、新しい入力システムを有効にするか確認するダイアログが表示された場合は、「Yes」を選んでUnityを再起動します。

また、Edit → Project Settings → Player にある Active Input Handling が、Input System Package (New) または Both になっていることも確認しておきましょう。


Playerオブジェクトを作成する

まず、移動させるGameObjectを用意します。

2Dであれば、例えば

Hierarchy
  ↓
右クリック
  ↓
2D Object
  ↓
Sprites
  ↓
Square

などで作成できます。

名前を

Player

としておきましょう。


既定のInput Actionsを確認する

次に、Input Actionsを確認します。

Unity 6では、Move(移動)やJump(ジャンプ)など、よく使う操作があらかじめ登録されたInput Actionsが用意されています。

メニューから、

Edit
↓
Project Settings
↓
Input System Package

を開きます。

Project-wide Actions の欄に、

InputSystem_Actions

が設定されていれば、そのまま使えます。

作成用のボタンが表示されている場合は、それを押すと既定のInput Actionsが作成され、Projectウィンドウに InputSystem_Actions が追加されます。

InputSystem_Actions は、テンプレートによって、Assets 直下ではなく Assets → Settings フォルダの中にあります。見つからないときは、Projectウィンドウの検索欄に InputSystem_Actions と入力して探してください。

Project-wide Actionsに設定したInput Actionsは、プログラムから InputSystem.actions で直接使えます。また、最初から有効になっているため、有効化する処理も必要ありません。

中身は、次のようにActionがまとめられています。

InputSystem_Actions
├─ Player(Action Map)
│   ├─ Move
│   ├─ Look
│   ├─ Attack
│   ├─ Interact
│   ├─ Crouch
│   ├─ Jump
│   ├─ Previous
│   ├─ Next
│   └─ Sprint
└─ UI(Action Map)

このように、関連する操作をまとめたグループをAction Mapと呼びます。今回使うのは、Playerの中にあるMoveです。


Moveの中身を確認する

InputSystem_Actions をダブルクリックして開き、Player → Move を選んでみましょう。

Moveは、次のように設定されています。

Action Type : Value
Control Type : Vector2

なぜVector2なのでしょうか。

上下左右の移動には、

X方向
Y方向

という2つの値が必要だからです。

例えば、

右 → ( 1,  0)
左 → (-1,  0)
上 → ( 0,  1)
下 → ( 0, -1)

となります。

Moveには、キーやスティックを結び付ける「Binding」が登録されています。キーボードのBindingは、「WASD」という名前のComposite(上下左右の4方向をまとめるBinding)に入っていて、次のように登録されています。

Up     → W / Up Arrow
Down   → S / Down Arrow
Left   → A / Left Arrow
Right  → D / Right Arrow

このように、1つの方向に、WASDのキーと矢印キーの2つが割り当てられています。

これによって、

W → (0, 1)
S → (0,-1)
A → (-1,0)
D → (1, 0)

というVector2を取得できます。矢印キーでも同じ値になります。Bindingの一覧には、このほかに、ゲームパッドの左スティックなども並んでいます。


PlayerControllerを作る

次にC#スクリプトを作成します。

Playerを選択して、InspectorのAdd Componentをクリックします。

検索欄に、

PlayerController

と入力します。

検索結果の一番下に、

New script

と表示されるので、それを選びます。

名前が PlayerController になっていることを確認して作成すると、スクリプトが作成され、そのままPlayerにアタッチされます。

スクリプトの作り方は、ほかにもあります。この記事では、迷わないようにこの方法だけで進めます。

作成されたスクリプトは、Projectウィンドウでダブルクリックすると開けます。中身を、次のように書き換えます。

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    [SerializeField]
    private float speed = 5.0f;

    private InputAction moveAction;

    private void Start()
    {
        moveAction = InputSystem.actions.FindAction("Player/Move");
    }

    private void Update()
    {
        Vector2 moveInput = moveAction.ReadValue<Vector2>();

        Vector3 move = new Vector3(
            moveInput.x,
            moveInput.y,
            0
        );

        transform.position += move * speed * Time.deltaTime;
    }
}

ここからコードを詳しく見ていきます。


InputSystemを利用する

using UnityEngine.InputSystem;

Input Systemのクラスを使用するために必要です。

今回使用する、

InputAction

や、

InputSystem

もInput Systemに含まれています。


移動速度を用意する

[SerializeField]
private float speed = 5.0f;

プレイヤーの移動速度です。

SerializeFieldを付けているため、privateでありながらInspectorから値を変更できます。

例えば、

Speed
5

のように表示されます。


Move Actionを取得する

private InputAction moveAction;

ここには、Move Actionを保存します。

InputActionは、MoveやJumpのような「操作」を表すクラスです。

private void Start()
{
    moveAction = InputSystem.actions.FindAction("Player/Move");
}

Start() は、ゲームを開始したときに1回だけ呼ばれるメソッドです。

InputSystem.actions は、Project-wide Actionsに設定した InputSystem_Actions を指します。

FindAction(“Player/Move") で、Action Mapの「Player」にある「Move」を探します。「Action Map名/Action名」の形で指定します。

毎フレーム探すのは無駄なので、最初に1回だけ探して、moveActionへ保存しておきます。


入力値を読み取る

Vector2 moveInput = moveAction.ReadValue<Vector2>();

ここがInput Systemで重要な部分です。

Update() の中で、Move Actionの現在の値を毎フレーム読み取ります。

例えば、

Dキー

Vector2
(1, 0)

となります。何も押していないときは、(0, 0) です。


Vector2からVector3を作る

UnityのTransformの座標はVector3です。

そのため、

Vector3 move = new Vector3(
    moveInput.x,
    moveInput.y,
    0
);

としてVector3へ変換しています。

2Dゲームなので、

Z = 0

としています。


プレイヤーを移動する

transform.position += move * speed * Time.deltaTime;

現在位置に移動量を加えています。

考え方としては、

現在位置
+
移動方向
×
速度
×
経過時間

です。


なぜTime.deltaTimeを掛けるのか

次のように、

transform.position += move * speed;

だけにすると、Updateが呼び出される回数によって移動速度が変わってしまいます。

例えば、

60 FPS
120 FPS

ではUpdateの実行回数が異なります。

そこで、

Time.deltaTime

を掛けます。

move * speed * Time.deltaTime

とすることで、おおむね

1秒あたりspeedだけ移動

という考え方にできます。


動かしてみる

Playerに PlayerController がアタッチされていることを確認して、ゲームを再生します。

W A S D キーでPlayerが上下左右に動けば成功です。

今回は、Player Inputコンポーネントを追加していません。Project-wide Actionsを使うと、PlayerInputがなくてもMoveの値を読み取れます。

動かない場合は、次の2点を確認してください。

1つ目は、Edit → Project Settings → Input System Package で、Project-wide Actions に InputSystem_Actions が設定されているかです。

2つ目は、Edit → Project Settings → Player の Active Input Handling が、Input System Package (New) または Both になっているかです。


入力から移動までの流れ

ここまでの処理を整理すると、次のようになります。

Wキーを押す
↓
Input System
↓
Move Action
↓
Vector2 (0, 1)
↓
ReadValue<Vector2>()
↓
moveInput
↓
Update()
↓
transform.position
↓
Playerが移動

ここが今回もっとも重要なポイントです。


矢印キーやゲームパッドでも動かしてみる

既定のMoveには、WASDだけでなく、矢印キーやゲームパッドの左スティックも登録されています。

ゲームを再生して、矢印キーでもPlayerを動かしてみましょう。ゲームパッドを接続している場合は、左スティックでも動きます。

プログラムは、まったく変更していません。

Input Systemの利点は、プログラムを変更せずに入力方法を追加できることです。MoveにBindingを追加するだけで、同じPlayerControllerをそのまま使えます。

これがInput Actionを使う大きな利点です。


Input Actionsを自分で作る方法(詳細)

ここまでは、Unity 6に用意されている既定のInput Actionsを使ってきました。

ここでは、自分専用のInput Actionsを作成し、PlayerInputコンポーネントとOnMoveメソッドで入力を受け取る方法を説明します。Input Systemの仕組みを一から確認したい場合や、既定のInput Actionsを使わない場合に役立ちます。

PlayerInput方式の流れを図にすると、次のようになります。

WASDキーの入力が、Input Action(Move)、PlayerInput、OnMove()を通ってPlayerの移動につながるまでの流れを、5つのステップで示した図
PlayerInput+OnMoveで受け取る詳しい手順(クリックで開く)

PlayerInputを追加する

Playerを選択してInspectorから、

Add Component

をクリックします。

検索欄に、

Player Input

と入力して、

Player Input

コンポーネントを追加します。

PlayerInput は、1人のプレイヤーと、そのプレイヤーが使用するInput Actionを関連付けるためのコンポーネントです。Input Actionの有効化やコールバックの設定も補助してくれます。


Input Actionsを作成する

Player Inputコンポーネントには、

Create Actions...

というボタンがあります。

これをクリックすると、

Input Actions

を保存できます。

例えば、

PlayerControls

という名前にします。

作成するとProjectウィンドウに、

PlayerControls.inputactions

が作成されます。


Action Mapとは

Input Actionsを開くと、

Action Maps

という項目があります。

Action Mapは、

関連する操作をまとめるグループ

だと考えると分かりやすいでしょう。

例えば、

Player
 ├─ Move
 ├─ Jump
 └─ Attack

のようにまとめられます。

今回は、

Player

というAction Mapの中に、

Move

を作ります。


Move Actionを作る

Actionを追加して、

Move

という名前にします。

設定は次のようにします。

Action Type : Value
Control Type : Vector2

なぜVector2なのでしょうか。

上下左右の移動には、

X方向
Y方向

という2つの値が必要だからです。

例えば、

右 → ( 1,  0)
左 → (-1,  0)
上 → ( 0,  1)
下 → ( 0, -1)

となります。


WASDを登録する

Move ActionへBindingを追加します。

通常のBindingではなく、

Add Up/Down/Left/Right Composite

を使用します。

これは、

2D Vector Composite

として扱われます。

次のように設定します。

Up     → W
Down   → S
Left   → A
Right  → D

これによって、

W → (0, 1)
S → (0,-1)
A → (-1,0)
D → (1, 0)

というVector2を取得できます。


PlayerControllerを作る(OnMove方式)

次にC#スクリプトを作成します。

名前は、

PlayerController

とします。

Playerへアタッチしてください。

まず、次のように記述します。

using UnityEngine;
using UnityEngine.InputSystem;

public class PlayerController : MonoBehaviour
{
    [SerializeField]
    private float speed = 5.0f;

    private Vector2 moveInput;

    public void OnMove(InputAction.CallbackContext context)
    {
        moveInput = context.ReadValue<Vector2>();
    }

    private void Update()
    {
        Vector3 move = new Vector3(
            moveInput.x,
            moveInput.y,
            0
        );

        transform.position += move * speed * Time.deltaTime;
    }
}

using宣言、移動速度、Vector3への変換、Time.deltaTimeについては、前の説明と同じです。ここでは、PlayerInput方式で異なる部分だけを見ていきます。


入力値を保存する

private Vector2 moveInput;

ここには、現在の移動方向を保存します。

例えばDキーを押している場合、

(1, 0)

Wキーなら、

(0, 1)

が入ります。


OnMoveメソッド

public void OnMove(InputAction.CallbackContext context)
{
    moveInput = context.ReadValue<Vector2>();
}

ここがInput Systemで重要な部分です。

Move Actionが発生すると、このメソッドが呼ばれます。

contextには、そのときの入力情報が渡されます。

そして、

context.ReadValue<Vector2>()

によってMove Actionの値を取得します。

例えば、

Dキー

Vector2
(1, 0)

となります。


PlayerInputとOnMoveを接続する

Player Inputコンポーネントの、

Behavior

を確認します。

授業でInput Systemの仕組みを見せるのであれば、

Invoke Unity Events

を使う方法が分かりやすいでしょう。

Player InputにActionごとのイベントが表示されるため、

Move

へPlayerオブジェクトを登録します。

そして関数として、

PlayerController
    ↓
OnMove

を指定します。

公式のInput Systemガイドでも、PlayerInputとInput Actionを使い、InputAction.CallbackContextを受け取るメソッドへ接続する方法が紹介されています。


入力から移動までの流れ(PlayerInput方式)

ここまでの処理を整理すると、次のようになります。

Wキーを押す
      ↓
Input System
      ↓
Move Action
      ↓
Vector2 (0, 1)
      ↓
PlayerInput
      ↓
OnMove()
      ↓
moveInput
      ↓
Update()
      ↓
transform.position
      ↓
Playerが移動

ここが今回もっとも重要なポイントです。


矢印キーにも対応する

Input Systemの利点は、プログラムを変更せずに入力方法を追加できることです。

Moveへもう1つ、

2D Vector Composite

を追加します。

例えば、

Up     → Up Arrow
Down   → Down Arrow
Left   → Left Arrow
Right  → Right Arrow

と設定します。

すると、

WASD

でも、

矢印キー

でも同じPlayerControllerを使用できます。

コードは変更する必要がありません。


ゲームパッドにも対応できる

さらにMoveへ、

Gamepad
  ↓
Left Stick

を登録すれば、ゲームパッドにも対応できます。

プログラム側は、

moveInput = context.ReadValue<Vector2>();

のままです。

つまり、

キーボード

WASD
   ↓
      Move
   ↓
PlayerController

ゲームパッド

Left Stick
   ↑

という構造になります。

これがInput Actionを使う大きな利点です。


Keyboard.currentという方法もある

Input Systemでは、次のような書き方もできます。

if (Keyboard.current.leftArrowKey.isPressed)
{
    transform.position += Vector3.left
        * speed
        * Time.deltaTime;
}

これは古い、

Input.GetKey()

とは異なり、新しいInput SystemのAPIです。

簡単な動作確認では便利です。

ただし、この方法では、

左矢印キー

という具体的なデバイス入力とプログラムが直接結び付きます。

Input Actionsなら、

Left Arrow
A
Gamepad Left Stick

などを、

Move

という1つの意味にまとめられます。

そのため、ゲームを作る場合はInput Actionsを使う設計の方が拡張しやすくなります。


従来のInput Managerとの違い

以前よく使用されたコードは、

Input.GetAxis("Horizontal");

や、

Input.GetKey(KeyCode.Space);

でした。

Input Systemでは考え方が変わります。

従来

キー
 ↓
プログラム


Input System

キー
 ↓
Binding
 ↓
Input Action
 ↓
プログラム

間に、

Input Action

が入るのが大きな違いです。


なぜInput Systemを使うのか

少し複雑に見えるかもしれません。

単純にキーを1つ読むだけなら、

Input.GetKey()

の方が簡単に見えます。

しかしゲームが大きくなると、

WASD
矢印キー
ゲームパッド
キーコンフィグ
複数プレイヤー

などへの対応が必要になることがあります。

Input Systemでは、

Move
Jump
Attack

のように「ゲーム上の操作」を中心に設計できます。

そのため、

入力デバイス

と、

ゲームの処理

を分離しやすくなります。


まとめ

Unity 6でキー入力を扱う場合は、Input Systemを使う方法を基本として考えるとよいでしょう。

今回の重要なポイントは、

Input System
Input Actions
InputSystem_Actions
FindAction
Vector2
Time.deltaTime

です。

特に覚えておきたい考え方は、

キーを直接調べる

のではなく、

Moveという操作を受け取る

ということです。

例えば、

W
↑
        ┌─────────┐
A ←     │  Move   │ → Vector2 → PlayerController
        └─────────┘
↓
S

という構造です。

この仕組みを理解すると、後から

矢印キー
ゲームパッド
別のキー配置

を追加しても、PlayerControllerの移動処理をほとんど変更せずに対応できます。

これが、現在のUnityでInput Systemを使う大きなメリットです。

なお、PlayerInputとOnMoveを使う方法や、自分でInput Actionsを作成する方法は、記事の途中にある折りたたみの中にまとめています。

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

広告

C#,Unity,Unity6

Posted by hidepon