今回は処理を止めない実装方法の解説です。
--
いきなりここに飛んで来ちゃった人は、よろしければ下記からご覧ください。
C言語基礎講座インデックス


  • 2種類の実装方法
C言語の標準ライブラリでキー入力を行うと、リアルタイムキー入力はほぼ絶望的です。そのため、どうしてもリアルタイムキー入力を行いたければ、機種依存コードを記述することになります。Windows であれば…

#include <windows.h> if(GetAsyncKeyState('A')) { printf("Aキーが押されている\n"); }

こんな処理になります。ものすごく簡単ですね。ところで、conio.h には _kbhit() なる関数が用意されています。これを使っても出来そうです。

#include <conio.h> if (_kbhit() && tolower(_getch()) == 'a') { printf("Aキーが押されている\n"); }
さて、上記の記述で同じことが出来ているように見えますが、実は微妙に実行結果が異なります。GetAsyncKeyState では A ボタンが押されている間 0 以外が返ってきています。_kbhit() はなにかキーがキーボードバッファにあればという意味になります。そのため、GetAsyncKeyState はリアルタイムキー入力と言えますが、_kbhit() を利用した例では、キーが押された最初の一回と、その後のキーリピート毎のタイミングでのみ入力が入るという動作になります。

アクションゲームのリアルタイム入力を求めるのであれば GetAsyncKeyState が適切です。_kbhit() && _getch() の使い方としては、どんな入力があったのかを処理を止めずに待ちたい場合に有効となります。GetAsyncKeyState はこのキーが押されていればという判定に対して、_kbhit() && _getch() ではどんなキーが押されているかという判定です。

例えば、名前をリアルタイム入力させようと思ったら _kbhit() && _getch() であれば…

#include <conio.h> char name[8+1] = { 0 }; int max = _countof(name); int pos = 0; while (true) { if (_kbhit()) { int key = _getch(); if (key == 0x0D) break; if (key >= ' ' && pos < max - 1){ name[pos++] = key; } } }

このように比較的短く記述できます。これと同じ事を GetAsyncKeyState で記述しようと思ったら大変なのは理解できるのではないでしょうか。
※ 毎日ジメジメしてて気持ち悪いですよね。こちらを使って強制的に除湿しては如何でしょうか。使い方にもよりますが、かなりの勢いで水が溜まっていきます。耐久度だけ少し心配ですね…

  • アニメーション
ここまで苦労してキー入力で処理を止めないようにする一番の理由は何でしょうか。それはアニメーションです。画面に変化をさせようと思ったら、常に処理を動かし続ける必要があります。キー入力待ちなんてされてしまったら、処理が止まってしまいますのでアニメーションの実現は不可能なのです。※ 並列動作させるなどでキー待ちしてもアニメを実現させる方法はあります。

先の名前入力のプログラムにアニメを付けてみます。

char name[8 + 1] = { 0 }; int max = _countof(name); char prt[] = { '*', 'x', '+', '-'}; int pos = 0, ani = 0; while (true) { printf(LOCATE, 1, 1); printf("%s%c", name, prt[ani++ % 4]); Sleep(100); // 0.1secウエイト if (_kbhit()) { int key = _getch(); if (key == 0x0D) break; if (key >= ' ' && pos < max - 1){ name[pos++] = key; } } }

ウエイトを入れたのは、この程度の処理だと速すぎてアニメが止まって見えてしまうためです。また、_kbhit() && _getch() はキーボードバッファを確認する動作ですので、ウエイト中にキー入力があっても取りこぼすことがありません。


  • アクションゲームっぽいなにか
GetAsyncKeyState は特定のキーが押されているかどうかを調査する関数です。アクションゲームでは、例えばカーソルキーの上下左右で、自機の移動を行うなど、入力を監視するキーが固定されていますので、GetAsyncKeyState で実装しやすくなっています。テストで実装してみます。

char player[] = { '+', 'X' }; int ani = 0, posX = 1, posY = 1; while (true) { printf(LOCATE, posY, posX); printf("@"); if (GetAsyncKeyState(VK_LEFT)) --posX; if (GetAsyncKeyState(VK_RIGHT)) ++posX; if (GetAsyncKeyState(VK_UP)) --posY; if (GetAsyncKeyState(VK_DOWN)) ++posY; if (posX < 1) posX = 1; if (posY < 1) posY = 1; printf(LOCATE, posY, posX); printf("%c", player[ani++ % 2]); Sleep(50); // 0.05secウエイト }

最初に @ を書いてからもう一度 + または X を書いているのは、軌跡は常に @ にしたいなと考えたためです。座標に関してはマイナス方向だけは今回は制限するようにしました。動作させるとカーソルキー操作で以下のような表示を作ることが出来ます。
動きがあるゲームみたいな表現
この制限がない状態で、マイナス方向に動かすと何が起きるかは、実際に試してみると良いかと思います。少しだけ大変なことになります(苦笑
※こちらは大型です。洗濯物を干すのに使えるタイプです。