Minecraftとタートルと僕

PCゲームMinecraftのMOD「ComputerCraft」の情報を集めたニッチなブログです。

sleep実装からイベントを学ぶ(3)―Keyイベントを使いこなす

keyイベントを使いこなす

前回予告した通り、矢印キーを使った操作についてサンプルプログラムを示しましょう。

画面出力についての注意

この手の画面描画系プログラムでは、画面ちらつき防止のためにダブルバッファリングという手法を使うのが一般的です。
しかし今回のプログラムでは、一度に1個のキャラクタしか画面表示しないこと、そして何よりわかりやすさのために、ダブルバッファリングは使わずにそのまま直接画面出力しています。
ダブルバッファリングについては、Paintutils APIと一緒に、そのうち説明したいですね*1

サンプルプログラムの仕様

  • f:id:hevohevo:20131224215522p:plain:w400
  • まず、ターミナル画面の左上に「@」というキャラクタを表示します。
  • キーボードのカーソルキー(矢印キー)でこのキャラクタを上下左右に動かすことができます。
  • キャラクタが画面の端にきたらそれ以上その方向に移動することはできません。
  • 「q」キーを押すとプログラムを終了します。

サンプルプログラム

http://pastebin.com/Z4i6EsX1

使っているAPIについての解説

Term API

ターミナル画面(CCコンピュータやタートルを右クリックして表示される黒い画面)について様々な表示や操作を行うためのAPIです。

  • 横方向がX軸、縦方向がY軸です。画面の一番左上の座標が(1,1)となります。右下に行くほど値が増えます。
  • term.setCursorPos(X座標,Y座標) : 指定した座標にカーソル(描画位置)を移動します。
  • term.write(文字): 現在のカーソル位置に指定した文字を出力します。print関数とは異なり、改行はしません。出力後のカーソル位置は出力した文字により自動的に移動します。
  • term.clear(): ターミナル画面の表示をすべて消します。
  • term.getSize(): ターミナル画面のサイズを調べ、最も右下の座標を返します(2つの値を返します)。たとえば「15」「10」のような2つの値を返した場合、このターミナル画面は(1,1)~(15,10)の座標が使えることがわかります。

プログラムの解説

まずメイン部分から見ていきましょう。

  • L23)ターミナル画面のサイズを調べ、MAX_X、MAX_Yにそれぞれ代入しています。
  • L25)最初に画面を一度全消去します。
  • L26)moveSymbol(X座標,Y座標)。指定した座標にキャラクタ(今回は「@」)を書き込むよう定義した関数です。詳しくは後述します。ここではまず最初に「@」を画面左上に表示しています。
  • L28-50)while true doで永久ループさせています。なお、L49でsleep(0)と最短のスリープを入れていますが、この数字を大きくすることでキー入力に対する反応を鈍くすることができます。キャラクターの動きが早すぎると感じたら調整してください。
    • このプログラムでは、pullEvent()があるためsleep()は必要ありません。この理由については、そのうち別の記事で解説したいと思います(2013/12/30追記)。
  • L29)今回はkeyイベントしか拾いません。keyイベントは1つ目の返値がイベント名、2つ目がキーコードの数値です。それぞれ変数「ename」「keycode」に代入しています。
  • L31)まずイベント名が"key"であることをIF文で確かめています。
    • そのうえで、上下左右の矢印キー操作に対応する座標にキャラクタを書き込んでいます。
    • また、キーコードは数値なのでキーコード対応表を見るのが面倒です。そのため、Keys APIを使うことで、見やすくしています。たとえば「keys.up」は「↑」キーに対応するキーコードを返します。
    • なお、pullEvent("key")とフィルタをかけているので、pullEventはKeyイベントしか拾いません。そのため、L31のIF文は省略しても正常に動きます。
  • 画面端にキャラクタが移動した際に、それ以上はみ出さないようにIFの条件式を工夫しています。
    • L32)変数xとyに現在のカーソル位置の座標を代入しておいて
    • L34)「if keycode == keys.up and y > 1 then」の意味は、「↑」キーの入力があり、なおかつ現在のカーソルのY座標が1よりも大きいならば、です。つまり、上に移動する空間が残っているときに移動します。

次に、定義したサブ関数を見ましょう。2つありますが、まずmoveSymbol()を先に見ましょう。

  • L9)指定した座標にカーソルを移動します。
  • L10)そして「@」を書き込みます。
  • ここで注意したいのが、書き込みによってカーソル位置が@の後ろに移動してしまうことです。このままでは問題なので、再度setCursorPos()することで、カーソルを元の位置に引き戻しています。
    • たとえば、座標(1,1)でterm.write("@")すると、座標(2,1)にカーソル位置が移動します。

なお、このプログラムを実行するとわかるのですが、「@」を移動させると、元の位置の「@」は消えないので移動の軌跡が永久に残ることになります。画面が「@」だらけになりますね。
もう一つのサブ関数moveSymbol2()は、移動前に現在位置を空白で上書きすることで表示を消し、その後に目標座標への出力を行っています。プログラム中でmoveSymbolの部分をすべてmoveSymbol2に書き換えると、移動の軌跡を残さない仕様になります。

メニュー選択画面の実装

「プログラムを起動するとメニューが出てきて、矢印キーで選択肢を選び、スペースキーで確定」
このような動作をするプログラムをCC関連動画でたまに見かけます。
この動作は、今回のサンプルプログラムを理解できたなら比較的簡単に実装できますので挑戦してみましょう。
この実装はあなたへの課題です。解答例は後日提示するのでがんばってみましょう。

課題

以下のような仕様のプログラムを作成せよ。

  • 以下のようなメニュー画面を表示する。
  • 矢印キーで「>」を上下に移動することができる
  • スペースキーでその選択肢を確定する。
    • 「EASY」「NORMAL」「DIFFICULT」を選んだなら画面をすべて消去し、選んだ選択肢(例「EASY」)を画面左上に表示してからプログラムを終了する。
    • 「EXIT」を選んだならそのままプログラムを終了する。
 > EASY
   NORMAL
   DIFFICULT
   EXIT

*1:そういえばちょっと前に「CCモニターでBad apple!!」という動画が話題になりましたね。あれの原理的なものが理解できるパラパラアニメプログラムを解説するとネタとして面白いかな? ソースは公開されてないので想像での解説になるけど。