WinFormsのKeyDown/KeyUpからUnity Input Systemへ ― キー入力の考え方はどう変わる?

広告

WinFormsでゲームを作るとき、私たちは「キーが押された」をイベントで受け取り、プレイヤーを動かしました。Unity 6のInput Systemでも、入力を受け取って動かす流れは変わりません。ただし、受け取るものが「キー」から「操作(Move)」に変わります。

この記事では、WinFormsで学んだことがUnityのどこにつながるのかを整理します。

この記事の位置づけ

学習の流れの中で、この記事は次の位置にあります。

  1. 落ち物キャッチゲームKeyDown で直接動かす
  2. キー長押し検知:入力状態を保存し、Timer_Tick で動かす
  3. この記事:この考え方がUnityにどうつながるかを整理する
  4. Unity 6でキー入力による移動を実装するMoveUpdate() で読んで動かす

1. WinFormsでの2つの段階

段階1:KeyDownですぐ動かす

落ち物キャッチゲームでは、KeyDown の中で直接プレイヤーを動かしました(画面端のチェックは省略)。

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    int moveSpeed = 20;

    if (e.KeyCode == Keys.Left)
        pictureBoxPlayer.Left -= moveSpeed;
    else if (e.KeyCode == Keys.Right)
        pictureBoxPlayer.Left += moveSpeed;
}
キーが押された → KeyDown → その場で移動

分かりやすい反面、キーを押しっぱなしにすると、最初の1回のあとに間があき、その後に連続で KeyDown が発生します(キーリピート)。動きがぎこちなくなります。

段階2:押下状態を保存し、タイマーで動かす

キー長押し検知の記事では、この問題を次の構造で解決しました。

KeyDown / KeyUp → 押下状態を保存(isLeftPressed など)
Timer_Tick(約16ms)→ 状態を確認して移動
// KeyDown / KeyUp から呼ばれる
private void SetKeyState(Keys key, bool isPressed)
{
    switch (key)
    {
        case Keys.Left:  isLeftPressed = isPressed;  break;
        case Keys.Right: isRightPressed = isPressed; break;
    }
}

// 約16msごとに呼ばれる
private void UpdatePaddlePosition()
{
    if (isLeftPressed)  paddleX -= PaddleSpeed;
    if (isRightPressed) paddleX += PaddleSpeed;
}

(記事のコードから、画面端の制限と Invalidate() を省いた抜粋です。)

2. 実は、Unityへの橋はここにある

WinFormsでキー長押しに対応すると、構造はこうなります。

キーが押された/離された
         ↓
現在の入力状態を保存
         ↓
一定周期で状態を確認
         ↓
プレイヤーを移動

Unityでも、基本的な考え方は同じです。

キーやゲームパッド
         ↓
Move という入力状態
         ↓
Update() で現在の値を確認
         ↓
プレイヤーを移動

WinFormsで学んだ「入力」と「移動処理」を分ける考え方は、そのままUnityにつながっています。 この状態管理の考え方を理解していると、Unityの入力処理にもスムーズにつながります。

3. Unityで同じことをすると

Unity 6では、次の流れになります(詳しくはUnity 6でキー入力による移動を実装するを参照)。

WASD / 矢印キー / ゲームパッド
         ↓
InputSystem_Actions の Player / Move
         ↓
FindAction("Player/Move") で取得
         ↓
Update() で ReadValue<Vector2>()
         ↓
transform.position を変更
private void Update()
{
    Vector2 moveInput = moveAction.ReadValue<Vector2>();
    Vector3 move = new Vector3(moveInput.x, moveInput.y, 0);
    transform.position += move * speed * Time.deltaTime;
}

4. 対応表

ここまでの対応を1枚の図にまとめると、次のようになります。

WinFormsのKeyDown/KeyUpと押下状態の保存、Timer_Tickでの移動を、UnityのInput Action「Move」、ReadValue<Vector2>()、Update()での移動と左右に並べて対応づけた比較図

文字で整理すると、次の表になります。

考え方WinFormsUnity
入力の受け取りKeyDown / KeyUpInput Actionの Move
入力の中身Keys.Left などVector2
入力状態の扱いisLeftPressed などに保存ReadValue<Vector2>() で現在値を取得
定期更新Timer_TickUpdate()
移動座標を一定量変更方向 × speed × Time.deltaTime

「一定のタイミングで、現在の入力状態を確認して移動する」という役割で見ると、WinFormsの Timer_Tick とUnityの Update() はよく似ています。ただし、Timer_Tick は指定した間隔で発生し、Update() は描画フレームごとに呼ばれる、という違いがあります。この違いは6章で扱います。

5. 違い① ― 「キー」ではなく「操作」を受け取る

WinFormsでは、プログラムが具体的なキーを判定します。

case Keys.Left: isLeftPressed = isPressed; break;

Unityでは、キーの種類は見ません。受け取るのは「Moveの値」です。

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

「左キーが押された」ではなく、「Moveの値が (-1, 0) になった」と考えます。

6. 違い② ― 動かし方

WinFormsの例では、約16msごとに PaddleSpeed(5)ずつ動かしました。1回あたりの移動量が固定です。

Unityの Update() は、実行環境によって呼ばれる間隔が変わります。そこで経過時間 Time.deltaTime を掛けます。

移動量 = 方向 × speed × Time.deltaTime

これで「1秒あたり speed だけ動く」という考え方になり、フレームレートが違っても速さが揃います。

7. なぜ「操作」に分けるのか

WinFormsで、矢印キーに加えてA/Dキーでも動かしたくなったとします。

case Keys.Left:
case Keys.A:
    isLeftPressed = isPressed;
    break;
case Keys.Right:
case Keys.D:
    isRightPressed = isPressed;
    break;

キーが増えるたびにC#を書き換えます。ゲームパッドは標準のキーイベントにはなく、別の仕組みが必要です。

Unityでは、Moveに割り当て(Binding)を追加するだけで済みます。PlayerController は変更しません。

矢印キー   ┐
WASD      ├→ Move → PlayerController(そのまま)
左スティック ┘

これが、間にInput Actionを挟む理由です。

補足:コールバックで受け取る方法もあります

Unityには、入力が変化したときにメソッド(OnMove)が呼ばれる方法もあります。ただし本編では、WinFormsの「一定周期で状態を確認する」考え方から移行しやすい Update() + ReadValue<Vector2>() を使っています。コールバック方式は、本編の記事の折りたたみ部分で紹介しています。

8. まとめ

  • WinFormsでは、直接動かす方法から、状態を保存して Timer_Tick で動かす方法へ進んだ
  • Unityでも「入力状態を確認して Update() で動かす」という考え方は同じ
  • 違いは、受け取るものが「具体的なキー」から「Moveという操作の値」になること
  • 移動量は、固定値から Time.deltaTime を使った速度ベースへ変わる
  • 操作に分けることで、キーやゲームパッドを追加してもコードを変えずに済む

次は、実際にInput Systemで動かす記事に進みましょう。

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

発展:画面の外に出ないようにするには?

WinFormsの例では、Math.MaxMath.Min でパドルが画面外に出ないようにしていました。Unity本編では、まずInput Systemの理解に集中するため、この処理を入れていません。Unityではどのように移動範囲を制限すればよいでしょうか。余裕があれば考えてみてください。

確認問題

  1. WinFormsの Timer_Tick と、Unityの Update() が似ている点は何ですか。違う点も1つ挙げてください。
  2. Keys.LeftMove の違いを、「何を受け取るか」の観点で説明してください。
  3. Moveにゲームパッドの左スティックを追加するとき、PlayerController は変更が必要ですか。理由も答えてください。
訪問数 7 回, 今日の訪問数 7回

広告