ボタン入力の応用

ボタン入力を取得すること自体はとても簡単です。ところが実際のゲームに使うとなると、気をつけなくてはいけないことや、テクニックがいくつかあります。たとえば以下のソースコードはやってはいけない例の1つです。これにはチャタリングの問題が絡んでいます。

int main()
{
	for(;;)
	{
		u16 key = *(volatile u16*)0x4000130;
 		TRACE("KEY: %x\n", key);
	}
}

チャタリングとは

信号線はデジタルでも、ボタンを押す強さそのものはぴよこアナローグです。したがって、ボタンを押している/押していないの間の「中間の状態」が必ず存在することになります。その「中間の状態」がどういう波形になるかというと、細かい話を抜きにしておおざっぱに書けばこうです。

 OFF ───┐┌┐┌┐┌┐┌┐┌┐
 ON     └┘└┘└┘└┘└┘└───

人間の指は、ボタンの状態を瞬間的に「ON/OFF」に出来るほど強靭なものではないため、ボタンを押したごく一瞬にも「中間の状態」が発生してしまいます。ウェイト処理を入れずに連続してキーセンスを読んだ場合(大抵はμsec~nsec単位)、この波形を読む事により誤動作を起こすわけです。この現象がチャタリングです。そこでVBLANK期間中(1/60秒間隔)に1回だけ呼び出すことで、その分キーセンスを読みに行く間隔も遅くしチャタリングも解決しています。つまり「押した、押さない」という入力は最大30回までの認識になるということです。

int main()
{
	IrqInit();

	for(;;)
	{
		VBlankIntrWait();

		u16 key = *(volatile u16*)0x4000130;
		TRACE("KEY: %x\n", key);
	}
}

また「メインループの初めにボタン状態を取得しておき、以後のキー入力チェック処理(自機移動など)では、その取得した値を参照する」ようにします。理由は、「必要になったそのつど入出力を読みに行っていたのでは、連続で読むような構造の場合に、短い間隔で読んでいる事と同じ」になってしまうためです。キーセンスは「必要最低限の間隔で一気に」読む事が重要です。

さらなる改良

さらにゲームに特化した場合、モジュール化を進めるのが得策です。以下に自分の使っているライブラリを表します。

実行画面

それぞれのボタンを押したときの特徴です。

Key.cnt現在、押されているボタンを格納する。
Key.trg押された時だけのボタンを格納する。押しつづけているボタンは格納しない。
Key.off離された時だけのボタンを格納する。もう一度「離されたボタン」を押さない限り保持する。
Key.rep十字キーのみ対応。押されているボタンをウェイトを挟んで格納する。

出典元

履歴


トップ   一覧 検索 最終更新   ヘルプ   最終更新のRSS