Skip to content

第01回 Input Systemとキャラクター操作 ​

次回: 第02回 カメラ操作とコンポーネント分割。

この授業の目的 ​

  • ゲームの機能を段階的に実装しながら C#の用法と設計の基本を実践的に学ぶ
  • 個人でゲームを完成させる過程を通じて ゲーム制作の経験と全体観を養う
  • ソフトウェア部品の責務を理解し 保守と拡張を考慮した設計を身につける

制作するゲーム ​

Unity Scene上に床とPlayerを配置した完成イメージUnity Scene上に床とPlayerを配置した完成イメージ

  • 床は3DのPlaneまたはMesh
  • カメラは斜め上からフィールドを見下ろす俯瞰カメラ
  • プレイヤー、敵、アイテム等は3Dオブジェクト
  • 移動は水平面(X-Z平面)に限定する
  • ジャンプ、複雑な段差は授業内では扱わないが 実装は自由とする

全10回の流れ ​

  1. Input Systemとキャラクター操作
  2. カメラ操作とコンポーネント分割
  3. 攻撃入力と攻撃モーション
  4. 衝突判定とダメージ
  5. Enemyの探索・追跡・攻撃
  6. Enemyの状態制御とゲームフィール
  7. アイテムとオブジェクトプール
  8. GameManagerとゲーム状態
  9. UIとシーン遷移
  10. ビルドと公開

今回の授業内容 ​

  • Input Systemで移動入力を受け取る
  • 入力値から移動方向を作る
  • X-Z平面でキャラクターを移動させる
  • キャラクターを移動方向へ振り向かせる
  • 移動速度と回転速度をInspectorで調整できるようにする

今回の配布コード ​

この回で使う完成コードは、以下から個別に確認できます。

ファイル内容
Assets/Scripts/PlayerController.cs入力を読み取り、移動と振り向きを行う

入力をアクションとして扱う ​

今回はInput Systemの Move アクションへ、WASD、方向キー、ゲームパッドの左スティックを割り当てます。アクションは「移動」のような操作の名前です。異なる入力方法を同じアクションへまとめることで、移動処理は使用中のキーやデバイスを個別に調べず、移動方向と入力の強さを受け取れます。

例えばWキーと左スティックの上方向をどちらも Move に割り当てると、移動処理はどちらからでも前方向の入力を受け取れます。次に、その対応をInput Actionsアセットへ設定します。

Input Actionsアセットを設定する ​

Input SystemではInput Actionsアセットで入力の種類や割り当てを定義し、スクリプトからはActionを通じて入力値を読み取ります。

Input Actionsの設定手順 ​

今回は Move Actionで移動入力を扱います。Move Actionは、移動方向と入力の強さを表す Vector2 として読み取ります。実際の移動量は、この入力に速度と経過時間を掛けて求めます。

Project SettingsでInput System Packageを有効にした画面Project SettingsでInput System Packageを有効にした画面

  1. Input Actions アセットを作成します
  2. Player Action Mapに Move Actionを追加します
  3. Action Type を Value、Control Type を Vector2 にします
  4. WASD、方向キー、左スティックを割り当てます

Input ActionsからC#クラスを生成する ​

Input ActionsでGenerate C# Classを有効にしたInspectorInput ActionsでGenerate C# Classを有効にしたInspector

Generate C# Class を有効にすると、Actionへ型安全にアクセスするクラスが生成されます。
設定変更の際にコンパイルが発生しますが、TypoやAction名の変更に強く、コード補完も効き便利です。

この教材ではクラスを生成して利用します。
クラス名は InputSystem_Actions で統一しています。

生成されたクラスから入力値を読む ​

Awake で InputActions を作成し、OnEnable OnDisable でAction Mapを有効化/無効化します。

有効化されたAction MapのActionは ReadValue<T>() で値を読み取ることができます。
ここでは ReadValue<Vector2>() で移動入力を読み取っています。

cs
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    private InputSystem_Actions _inputActions;

    private void Awake()
    {
        _inputActions = new InputSystem_Actions();
    }

    private void OnEnable()
    {
        _inputActions.Player.Enable();
    }

    private void OnDisable()
    {
        _inputActions.Player.Disable();
    }

    private void Update()
    {
        var moveInput =
            _inputActions.Player.Move.ReadValue<Vector2>();

        // 省略

2D入力を3Dの移動方向に変換する ​

cs
var direction = new Vector3(
    moveInput.x,
    0f,
    moveInput.y
);

// 斜め移動が速くなりすぎないようにする
direction = Vector3.ClampMagnitude(direction, 1f);

Input Systemの移動入力は二次元の Vector2 ですが 3D 空間の移動方向は三次元の Vector3 で扱います。
ここでは入力の x をワールドの x に、入力の y をワールドの z に適用します。

入力値は-1.0~1.0の範囲で正規化されています。
そのまま移動に適用すると斜め入力時の移動の速度が √2倍(約1.414倍)になってしまいますので、長さを最大1に制限します。

設定値をInspectorで調整可能に ​

cs
public class PlayerController : MonoBehaviour
{
    [SerializeField] private float moveSpeed = 5f;
    [SerializeField] private float rotationSpeed = 720f;

プレイ感に関わる数値は何度も調整することになります。こうした値はコードを書き換えずに変更できるようにするのがゲーム制作の基本です。
変数の定義時に SerializeField を付けると Inspector から値を設定できるようになります。

INFO

アクセス修飾子を public にしても Inspector から設定できるようになりますが、それでは変数が外部公開されてしまいます。
変数のアクセス修飾子は、特に理由がなければ private を基本とし、必要に応じて SerializeField で Inspector から設定できるようにすることをおすすめします。

プレイヤーをワールド座標基準で移動させる ​

cs
public class PlayerController : MonoBehaviour
{
    [SerializeField] private float moveSpeed = 5f;

    private InputSystem_Actions _inputActions;

    // Awake / OnEnable / OnDisable は省略

    private void Update()
    {
        var moveInput =
            _inputActions.Player.Move.ReadValue<Vector2>();

        var direction = new Vector3(moveInput.x, 0f, moveInput.y);
        direction = Vector3.ClampMagnitude(direction, 1f);

        transform.position +=
            direction * (moveSpeed * Time.deltaTime);
    }
}

Time.deltaTime はフレーム内の経過時間です。移動速度に Time.deltaTime を掛ければ、1フレームあたりの移動量を求めることができます。
まずはプレイヤーを X-Z 平面で動かしてみましょう。

入力があるときだけ移動方向に振り向く ​

cs
if (direction != Vector3.zero)
{
    var targetRotation =
        Quaternion.LookRotation(direction);

    transform.rotation = Quaternion.RotateTowards(
        transform.rotation,
        targetRotation,
        rotationSpeed * Time.deltaTime);
}

Quaternion.LookRotation で、最終的な回転を表す Quaternion を作った後、Quaternion.RotateTowards で現在の回転から目標の回転へ、指定の速度で回転させます。rotationSpeed は1秒あたりに回転できる角度の速さ(度/秒)です。

これでプレイヤーが入力方向に指定の速度で振り向くようになります。

完成した PlayerController ​

cs
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    [SerializeField] private float moveSpeed = 5f;
    [SerializeField] private float rotationSpeed = 720f;

    private InputSystem_Actions _inputActions;

    private void Awake()
    {
        _inputActions = new InputSystem_Actions();
    }

    private void OnEnable()
    {
        _inputActions.Player.Enable();
    }

    private void OnDisable()
    {
        _inputActions.Player.Disable();
    }
    private void Update()
    {
        var moveInput =
            _inputActions.Player.Move.ReadValue<Vector2>();

        var direction =
            new Vector3(moveInput.x, 0f, moveInput.y);
        direction = Vector3.ClampMagnitude(direction, 1f);

        transform.position +=
            direction * (moveSpeed * Time.deltaTime);

        if (direction == Vector3.zero)
            return;

        var targetRotation =
            Quaternion.LookRotation(direction);

        transform.rotation = Quaternion.RotateTowards(
            transform.rotation,
            targetRotation,
            rotationSpeed * Time.deltaTime);
    }
}

実習:プレイヤーを操作できるようにする ​

  1. Planeを配置する
  2. CapsuleをPlayerとして配置する
  3. Input Actionsアセットを作成する
  4. Move Actionを設定する
  5. C#コード生成を有効にして InputSystem_Actions を生成する
  6. PlayerController をPlayerに追加する
  7. WASDまたは左スティックで 移動を確認する
  8. 移動方向へ振り向くことを確認する

動作しない場合のチェックリスト ​

  • Move Actionの型が Vector2 になっているか
  • InputSystem_Actions.cs が生成されているか
  • OnEnable で Player Action Mapを有効化しているか
  • moveSpeed が0になっていないか
  • rotationSpeed が0になっていないか
  • direction の値がゼロのままになっていないか

完成条件 ​

  • WASDまたはコントローラーの左スティックで移動できる
  • 斜め移動が他の入力に比べて速くなっていない
  • プレイヤーが移動方向へ振り向く
  • Move ActionをInput Systemで設定している
  • InputSystem_Actions のC#コード生成を有効にしている
  • moveSpeed と rotationSpeed をInspectorで調整できる

まとめ ​

  • 入力は Input Action から受け取る
  • 移動方向は入力値から作る
  • 移動量は速度と時間から作る
  • 回転は移動方向から作る