サイトアイコン GAMEWORKS LAB

C言語で簡単なキー入力システム(キーバインド付き)

入力装置

ゲームを作るには基本的に何らかの入力装置を使ってユーザーの行動を反映させる必要があります。
入力装置とは下記のようなものです。

・キーボード
・ゲーム機等のコントローラー
・スマートフォン等のタッチパネル
・音声認識の装置
・画像認識の装置

他にも色々とあるかと思いますが、簡単にはこんな感じになります。

そんな中で個人でゲームを作る際に簡単で身近なのがキーボードになるかと思います。
今回はキーボードを使った入力システムの実装について紹介していきます。

 

Windowsでキーボードの入力判定

Windows環境ではGetKeyboardStateの関数で簡単にキーボードの入力状態が取得できます。
この記事ではこれを使って入力判定を実装していきます。

先ずはGetKeyboardStateの使い方です。

// キーステートの保持用
BYTE keyboardState[256];
// キーステートの更新
void UpdateKeyState()
{
	// キーボードの状態取得
	GetKeyboardState(keyboardState);
}

// 指定されたキーが押されているか?
bool IsKeyPress(int keycode)
{
	// 入力判定
	if( keyboardState[keycode] & 0x80 )
	{
		return true;
	}
	return false;
}


GetKeyboardStateは256個のキーの入力状態を取得できます。
取得した際に入力されているキーは最上位ビットが立ちます。

これだけで入力されているかの判定は出来てしまいます。

作ろうと思えば上記の入力処理でゲームを作くることも可能です。
ただゲームを作る際には他にもあると便利な機能がいくつかあります。

  1. キーが入力された瞬間の取得
  2. キーが離された瞬間の取得
  3. キーバインド機能

これぐらいがあれば最低限はゲームを作っていけるかと思います。

ではこの3つも含めて満たせるシステムを実際に作りながら見ていきましょう。

 

キー入力システムの実装

キー入力は闇雲に使用するキーを増やすと管理がややこしくなります。
それにユーザーにも不親切な作りになってしまいます。

ただ入力キーが固定だと、後で入力するキー配置を変えたい場合やキーコンフィグ機能等のオプション機能の実装が難しくなります。

なので先ずはキーバインドをする機能を想定した準備をします。

enum EKeyBind
{
	Option,	// Option
	A,	// A
	B,	// B

	Up,	// ↑
	Down,	// ↓
	Left,	// ←
	Right,	// →

	KeyBindMax,
};


ゲーム内でキー入力を取得するときはこのEKeyBindの値を使っていきます。
今回は単純な入力のみですが、必要な入力するに合わせて拡張してください。

次に入力システムの必要な処理を定義していきます。

// 入力システムの初期化
void InitializeInput(void);
// 入力システムの解放
void FinalizeInput(void);
// 入力状況の更新
void UpdateInput(void);

// キーバインドの設定
void BindKey(EKeyBind key, int keyCode);


初期化と解放、更新、そしてキーバインド用の関数を準備します。

次のページでは実装を見ていきましょう。

先ず実装に必要なキーバインドやキー入力の状態を保持する構造体を用意します。

// キー情報用構造体
struct KeyInfo
{
	int keyBind[KeyBindMax];	// キーバインド情報
	int keyState;			// キーステート
	int keyStateOld;		// 前回のキーステート
};
// キー情報を実体を定義
static KeyInfo	s_keyState;


キー入力の状態はここに全てが格納されていきます。

次に初期化処理です。

// 入力システムの初期化
void InitializeInput(void)
{
	for( int i=0; i < KeyBindMax; i++ )
	{
		s_keyState.keyBind[i] = -1;
	}
	s_keyState.keyState = 0;
	s_keyState.keyStateOld = 0;
}

// 入力システムの解放
void FinalizeInput(void)
{
	InitializeInput();
}


特に特殊な機能は使わないので両方とも構造体の内容を初期化するだけです。
ここでkeyBindに-1を入れていますが、-1はバインドが未設定として扱います。

次にキーバインドです。

// キーバインドの設定
void BindKey(EKeyBind key, int keycode)
{
	s_keyState.keyBind[key] = keycode;
}


単純ですね。

EKeyBindで指定された場所に入力に使用したいキーコードを設定します。
これでEKeyBindとkeycodeが関連付けられました。

次に更新処理です。

// 入力状況の更新
void UpdateInput(void)
{
	BYTE keyboardState[256];
	// キーボード状態の取得
	GetKeyboardState(keyboardState);

	int keyState = 0;
	for( int i=0; i < KeyBindMax; i++ )
	{
		// キーがバインドされていなければスルーする
		int key = s_keyState.keyBind[i];
		if( key == -1 ) continue;

		// 入力されている場合はキーのビットを立てる
		if( keyboardState[key] & 0x80 )
		{
			keyState |= (1 << i);
		}
	}
	// ステートの更新
	s_keyState.keyStateOld = s_keyState.keyState;
	s_keyState.keyState = keyState;
}


キーボードの入力状態を取得した後にキーバインドの設定に合わせてステートを設定していっています。

最後は前回と今回のステートを更新しています。

ここではint型のkeyStateに対してbitフラグで管理をしています。
※int型は32bitなので最大32個のキーしか扱えません。

これでキー入力の判定に必要な処理は実装が出来ました。

最後にキー判定です。

// キーが押されているか?
bool IsKeyPress(EKeyBind key);
// キーが押されたか?
bool IsKeyPush(EKeyBind key);
// キーが離されたか?
bool IsKeyReleases(EKeyBind key);


キー判定の処理はこの3つとなります。

実装もかなり単純になっています。

// キーが押されているか?
bool IsKeyPress(EKeyBind key)
{
	int bit = (1 << key);
	// 単純に現在のステートから判定
	int state = s_keyState.keyState;
	return (state & bit)? true: false;
}

// キーが押されたか?
bool IsKeyPush(EKeyBind key)
{
	int bit = (1 << key);
	// 今回と前回とのステートの差(1 & ~0)で判定
	int state = s_keyState.keyState & ~s_keyState.keyStateOld;
	return (state & bit)? true: false;
}

// キーが離されたか?
bool IsKeyReleases(EKeyBind key)
{
	int bit = (1 << key);
	// 前回と今回のステートの差(1 & ~0)で判定
	int state = s_keyState.keyStateOld & ~s_keyState.keyState;
	return (state & bit)? true: false;
}


全てがビット演算だけで判定できてしまいます。

では最後に使い方の説明をしていきます。

このキー入力システムは簡単に使うことができます。

先ず最初に初期化と解放の処理をメインループの前後などで呼び出します。
そしてメインループ内で更新処理を呼び出します。

// 入力システムの初期化
InitializeInput();

// メインループ
while( !IsQuitMessage() )
{
	if( !UpdateWindowMessage() )
	{
		UpdateInput();

		// ゲーム処理
	}
}
// 入力システムの解放
FinalizeInput();


これでキー入力システムが動作する環境は整いました。

次にキーバインドの設定を行います。

// キーバインドの設定
BindKey(EKeyBind::Up,    'W');
BindKey(EKeyBind::Down,  'S');
BindKey(EKeyBind::Left,  'A');
BindKey(EKeyBind::Right, 'D');


キーバインドは入力システムが初期化した後であれば何処で設定しても問題ありません。
基本的にはゲームの初期化時がしっくりくるかなと思います。

今回は上下左右にWSADをバインドしています。

最後に実際に入力を判定して処理します。

if( IsKeyPress(EKeyBind::Up) )
{
	// 上に歩く
}
else if( IsKeyPress(EKeyBind::Down) )
{
	// 下に歩く
}
if( IsKeyPress(EKeyBind::Left) )
{
	// 左に歩く
}
else if( IsKeyPress(EKeyBind::Right) )
{
	// 右に歩く
}


例えば移動処理なんかはこうやって実装していく感じになるかと思います。

簡単ですよね!
ゲームを作る上でキー配置はかなり重要なポイントなので自由に変更して対応できるように考えた実装をしていきましょう。

モバイルバージョンを終了