【発展・希望者向け】WinFormsでキー入力と押しっぱなしを扱う
この記事は 授業の範囲外 です。興味のある人・余力のある人が読む発展用の内容です。 無理に読む必要はありません。
- 1. 前提
- 2. はじめに
- 3. 今日作るもの
- 4. キーリピートでは不向きな理由
- 5. ソリューションとプロジェクトを作る
- 6. フォームに配置するコントロール
- 7. フォームの設定(重要)
- 8. 手順1:キー押下状態のフラグを追加する
- 9. 手順2:KeyDown と KeyUp イベントを登録する
- 10. 手順3:KeyDown でフラグを true にする
- 11. 手順4:KeyUp でフラグを false にする
- 12. 手順5:Timer の Tick で移動処理を書く
- 13. 手順6:Form1 の Load で Timer を開始する
- 14. 完成コード
- 15. プログラムの流れ
- 16. 重要ポイント
- 17. 発展アイデア
前提
- Timer の使い方が分かる
- 変数・イベントの基本が分かる
- 電話帳課題を理解しているとスムーズです
はじめに
これまでの学習では、
- ボタンをクリックする
- クリックイベントが呼ばれる
という形で入力を受け取ってきました。
しかし、ゲームなどではキーボードを押しっぱなしでキャラを動かしたいことがあります。
今回は、矢印キーでキャラがスムーズに動くプログラムを作ります。押しっぱなしの検出方法を学びます。
今日作るもの
矢印キーを押すと、画面上の■が上下左右に動きます。
- 押している間、ずっと動き続ける
- 離すと止まる
- 複数のキーを同時押しすると斜めに動く(例:↑と→で右上)
キーリピートでは不向きな理由
KeyDown イベントは、キーを押し続けるとリピートで何度も発生します。しかしゲームには向きません。
| 問題 | 説明 |
|---|---|
| 初回遅延 | 押し始めてから最初のリピートまで、数百ミリ秒の待ち時間がある |
| 一定間隔 | リピート間隔が OS 設定に依存し、ゲームのフレームレートと合わない |
| 区別しづらい | 1回だけ押したのか、押しっぱなしなのか、イベントだけでは判断しづらい |
そのため、KeyDown + KeyUp + フラグ + Timer のパターンを使います。
ソリューションとプロジェクトを作る
Visual Studio で新しいプロジェクトを作ります。
- テンプレート: Windows Forms アプリ (.NET Framework または .NET 6/8/10)
- ソリューション名: KeyInputSample
- プロジェクト名: KeyMove
作成すると Form1 が表示されます。
フォームに配置するコントロール
フォームに次の2つを配置します。
| コントロール | 名前 |
|---|---|
| Label | labelPlayer |
| Timer | timer1 |
- labelPlayer:Text を「■」に、Font を大きく(例:24pt)に、AutoSize を false にして適度なサイズに
- timer1:Interval を 30(約30ミリ秒ごとに更新 = 滑らかに動く)
- Timer は画面には表示されません。フォーム下のコンポーネントトレイに配置されます
フォームの設定(重要)
フォームがキー入力を受け取るため、次の設定をします。
- Form1 をクリックして選択
- プロパティウィンドウで KeyPreview を true に変更
KeyPreview = true にすると、フォーム上のどのコントロールにフォーカスがあっても、フォームが先にキーイベントを受け取るようになります。
手順1:キー押下状態のフラグを追加する
Form1.cs のクラス内に、次のフィールドを追加します。
bool keyUpPressed = false;
bool keyDownPressed = false;
bool keyLeftPressed = false;
bool keyRightPressed = false;
手順2:KeyDown と KeyUp イベントを登録する
- Form1 をクリックして選択
- プロパティウィンドウの ⚡ 雷マーク(イベント) をクリック
- KeyDown をダブルクリック →
Form1_KeyDownが作成される - KeyUp をダブルクリック →
Form1_KeyUpが作成される
手順3:KeyDown でフラグを true にする
Form1_KeyDown に次のコードを書きます。
switch (e.KeyCode)
{
case Keys.Up: keyUpPressed = true; break;
case Keys.Down: keyDownPressed = true; break;
case Keys.Left: keyLeftPressed = true; break;
case Keys.Right: keyRightPressed = true; break;
}
手順4:KeyUp でフラグを false にする
Form1_KeyUp に次のコードを書きます。
switch (e.KeyCode)
{
case Keys.Up: keyUpPressed = false; break;
case Keys.Down: keyDownPressed = false; break;
case Keys.Left: keyLeftPressed = false; break;
case Keys.Right: keyRightPressed = false; break;
}
手順5:Timer の Tick で移動処理を書く
Timer をダブルクリックして timer1_Tick を作成し、次のコードを書きます。
int speed = 5;
if (keyUpPressed) labelPlayer.Top -= speed;
if (keyDownPressed) labelPlayer.Top += speed;
if (keyLeftPressed) labelPlayer.Left -= speed;
if (keyRightPressed) labelPlayer.Left += speed;
手順6:Form1 の Load で Timer を開始する
フォームをダブルクリックして Form1_Load を作成し、次のコードを書きます。
timer1.Start();
完成コード
using System;
using System.Windows.Forms;
namespace KeyMove
{
public partial class Form1 : Form
{
bool keyUpPressed = false;
bool keyDownPressed = false;
bool keyLeftPressed = false;
bool keyRightPressed = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Start();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up: keyUpPressed = true; break;
case Keys.Down: keyDownPressed = true; break;
case Keys.Left: keyLeftPressed = true; break;
case Keys.Right: keyRightPressed = true; break;
}
}
private void Form1_KeyUp(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Up: keyUpPressed = false; break;
case Keys.Down: keyDownPressed = false; break;
case Keys.Left: keyLeftPressed = false; break;
case Keys.Right: keyRightPressed = false; break;
}
}
private void timer1_Tick(object sender, EventArgs e)
{
int speed = 5;
if (keyUpPressed) labelPlayer.Top -= speed;
if (keyDownPressed) labelPlayer.Top += speed;
if (keyLeftPressed) labelPlayer.Left -= speed;
if (keyRightPressed) labelPlayer.Left += speed;
}
}
}
プログラムの流れ
キーを押す
↓
KeyDown が呼ばれる → フラグを true
↓
Timer の Tick が一定間隔で呼ばれる
↓
フラグが true の間、毎回移動処理
↓
キーを離す
↓
KeyUp が呼ばれる → フラグを false
↓
移動停止
重要ポイント
押しっぱなしの検出は「KeyDown + KeyUp + フラグ + Timer」
- KeyDown:押した瞬間にフラグを true
- KeyUp:離した瞬間にフラグを false
- Timer:フラグが true の間、毎フレーム移動処理
キーリピートに頼らず、自分でフラグを管理することで、ゲーム向きのスムーズな動きになります。
- KeyPreview = true を忘れない(フォームがキーを受け取るため)
- 複数キー同時押しも、フラグを分けておけば自然に対応できる
補足:初回の遅延について
KeyDown から次の Timer Tick まで、最大で Interval 分(例:30ms)の遅延があり得ます。体感しにくいことが多いですが、気になる場合は Interval を 16 にすると滑らかになります。KeyDown の時点で移動処理を 1 回だけ呼べば、初回の遅延はほぼなくなります。
発展アイデア
キー入力が使えるようになると、次のようなものも作れます。
- 座標と物理の記事と組み合わせて、重力付きのジャンプゲーム
- 当たり判定を入れて、障害物を避けるゲーム
- サウンドの記事と組み合わせて、移動時に効果音を鳴らす




ディスカッション
コメント一覧
まだ、コメントがありません