Unity 6|Input SystemのSprintでダッシュを実装!Shiftで移動速度を変える【前編】

広告

前回の記事では、WASDで移動し、Spaceキーでジャンプする仕組みを作りました。今回は、Left Shiftを押している間だけ速く移動できるようにします。

前編の目標は「速度が切り替わること」です。見た目の歩行・走行アニメーションは後編で追加します。まずはカプセルのままでも確認できます。

1. 今回の完成イメージ

操作結果
WASDだけ通常速度3で移動
WASD+Left Shiftダッシュ速度6で移動
Shiftを離す通常速度に戻る
Shiftだけ移動しない
Space前回と同じジャンプ

ここで3と6は、1秒あたりに移動を試みるUnityの距離単位です。障害物に当たると実際の移動距離は小さくなります。

2. 前回のシーンを準備する

前回作成したプレイヤーに、Character Controller、Player Input、PlayerControllerが付いていることを確認します。地面にはColliderが必要です。Character Controllerの寸法やCenterはモデルに合わせた前回の設定を引き継いでください。

Player Inputは次の設定を使います。

項目設定
Actions今回確認するInputSystem_Actions
Default MapPlayer
BehaviorInvoke Unity Events(Eventsへの登録なし)

前回のコードと同様に、入力は毎フレーム直接読み取ります。今回は通知先のないSend Messagesによる不要なメッセージエラーを避けるため、BehaviorをInvoke Unity Eventsにそろえます。OnMoveやOnSprintの登録は不要です。既存のイベント登録がある場合は、この教材用の複製シーンで外してください。

Project SettingsのInput System Packageを開き、Update Modeは「Process Events In Dynamic Update」を選びます。これは、入力の処理を毎フレームのUpdateのタイミングで行う設定です。本教材のコードも入力をUpdateで読むので、タイミングがそろいます。Active Input Handlingは、Input Systemを使用できる設定にしてください。

3. SprintとLeft Shiftを確認する

ProjectからInputSystem_Actionsを開き、Action MapsでPlayerを選びます。Sprintを展開し、Left Shiftのバインディングを選びましょう。

添付の実画面では、Sprintの下にLeft Shift [Keyboard]があり、右側でKeyboard&Mouseにチェックが入っています。Left Stick Press [Gamepad]も見えます。これはこのアセットでの設定であり、すべてのUnityプロジェクトに同じSprintがあるとは限りません。

Sprintの行そのものを選択し、Action TypeがButtonになっていることも確認します。添付画像はバインディングを選択した画面なので、Action Typeまでは写っていません。今回の設定では、Action側・Binding側のInteractionsとも空にします。「押している間だけ速くする」ためにHold Interactionを追加する必要はありません。

Sprintがない場合は、PlayerにSprintという名前のActionを追加し、Action TypeをButton、BindingのPathを<Keyboard>/leftShiftにします。既存のKeyboard&Mouseスキームを使う場合は、そのチェックも付けて保存してください。

Sprintは「ダッシュしたい」という入力の名前です。作っただけでキャラクターが速くなるわけではありません。次にC#で速度を切り替えます。

4. 追加する処理を先に見る

入力を取得する変数を1つ増やします。

private InputAction _sprint;

Startで、Player Inputが使っているAction MapからSprintを取得します。

_sprint = actionMap.FindAction("Sprint");

Updateでは、押しているかどうかを毎フレーム確認します。

bool isSprinting = _sprint.IsPressed();
float currentSpeed = isSprinting ? sprintSpeed : moveSpeed;

IsPressed()は押されている間trueになります。? :は条件によって値を選ぶ書き方です。trueならsprintSpeed、falseならmoveSpeedを選びます。

ジャンプのWasPressedThisFrame()は「今回のフレームで押した瞬間」を調べます。これをダッシュに使うと、押し続ける操作を表せません。用途に合わせて使い分けましょう。

5. PlayerController.csの完成コード

既存のPlayerController.csの中身を、次のコードに置き換えます。同じ名前のクラスやスクリプトを追加しないでください。moveSpeedとjumpPowerという既存フィールド名を残しているため、Inspectorで設定済みの値を引き継げます。Sprint Speedには6を設定します。

using UnityEngine;
using UnityEngine.InputSystem;

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

    // Shiftを押している間の移動速度
    [SerializeField] private float sprintSpeed = 6f;

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

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

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

    // 移動速度
    private Vector3 _moveVelocity;

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

    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;
        }

        // Default Mapの有効化はPlayerInputに任せる

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

        if (_move == null || _jump == null || _sprint == null)
        {
            Debug.LogError(
                "Move・Jump・Sprintアクションを確認してください。"
            );
            enabled = false;
        }
    }

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

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

        // 押している間だけダッシュ速度にする
        bool isSprinting = _sprint.IsPressed();
        float currentSpeed = isSprinting ? sprintSpeed : moveSpeed;

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

        _moveVelocity.x =
            direction.x * currentSpeed;

        _moveVelocity.z =
            direction.y * currentSpeed;

        // 移動している方向に向きを変える
        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
        );
    }
}

6. 速度以外の処理を残す理由

ClampMagnitudeは斜め入力の長さが1を超えないようにします。WとDを同時に押した場合も、まっすぐ進む場合より速くなりません。スティックを小さく倒した入力は、その強さを残します。

変更するのはXとZの速度だけです。Yにはジャンプや落下の速度が入っているので、Sprintの値を掛けません。Shiftを押してもジャンプ力や重力は変わりません。

CharacterController.Move()に渡すのは速度ではなく、そのフレームの移動量です。そのため速度にTime.deltaTimeを掛けています。Moveは重力を自動適用しないので、前回の重力処理も残しています。

前回と同様、接地中の下向き速度は-2に戻し、ジャンプ時はjumpPowerで上書きします。この教材では空中でも水平ダッシュが可能です。空中操作を制限する処理は追加していません。

7. 動作を確認しよう

再生し、Gameビューをクリックしてから次の順番で試してください。

  1. Wで進み、Shiftを押すと速くなる。
  2. Wを押したままShiftを離すと、すぐ通常速度に戻る。
  3. 移動キーを離すと、Shiftを押していても停止する。
  4. W+Dでも、Wだけの場合と同じ上限速度になる。
  5. 通常移動中とダッシュ中にSpaceでジャンプでき、着地後も操作できる。

これは、皆さんが自分で試して確かめるための手順です。本稿のコードは公式資料と既存のコードを読み合わせて作成していますが、筆者のUnity Editorでのコンパイルと実際の動作確認はまだ行っていません。動かない場合は、設定と入力した名前の綴りを順に見直してください。

8. うまく動かないとき

症状確認する点
Actionが見つからない大文字小文字を含めMove・Jump・Sprint、Default MapのPlayer、Actionsの参照先
Shiftで変化しないLeft ShiftのPath、スキーム、Sprint SpeedとMove Speedが異なるか
入力が届かないPlayer Inputが有効か、Gameビューにフォーカスがあるか
2倍以上に動く古い移動スクリプトや別の移動処理が同時に動いていないか

補足:イベント方式(CallbackContext)との違い

教科書などで、OnSprint(InputAction.CallbackContext context)のように、入力があったときに呼ばれるメソッドで受け取る方法を見た人向けの補足です。難しければ、読み飛ばして構いません。この方法は、Player InputのBehaviorをInvoke Unity Eventsにして、SprintのイベントにメソッドをInspectorで登録します。なお、Send Messagesで値を受け取る場合の引数はInputValueで、CallbackContextとは別の型です。

CallbackContextは、メソッドが呼ばれている間だけ使えます。後で使うために変数へ保存せず、その場でReadValueAsButton()などを使ってボタンの状態を読み取ります。また、キーを離したときのcanceledの処理も書いて、ダッシュの状態をfalseに戻す必要があります。この記事は前回と同じく、毎フレーム直接入力を読む方式なので、これらのコールバックを追加する必要はありません。

次は歩き・走りの見た目を切り替えよう

これで、入力に応じて移動速度を選ぶ仕組みができました。後編「Animatorの1D Blend TreeでIdle・Walk・Runを切り替える」では、この入力を使ってアニメーション用のSpeedを決めます。

参考資料

Input SystemはEditorとは別のパッケージです。上記の入力関連資料は1.14.2を参照しています。添付画面だけでは導入済みのパッケージ版は特定できないため、Package Managerで自分の版も確認してください。

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

広告