はじめに
今回は、モニタボタンを使ってタートルを直接操作するプログラムを紹介します。
前回の最後に予想したとおり、前回のプログラムよりかなり短いですよ今回は。
機能的にも前回のプログラムの一部分を使っているだけなので、前回よりも理解しやすいプログラムだと思います。
ただしそれだけだとつまらないので、少しだけ高度な機能を付け加えてみました。
この機能を覚えるととても便利なのでがんばって覚えましょう。
「モニタボタンを使ったタートル操作プログラム」の仕様
- タートルとAdvanced Monitorの設置
- タートル(タートルの種類は問わない)を設置後、そのタートルの上に金色のAdvancedMonitorを設置する
- 真上にあるモニタはコントローラーです
- モニタの表示領域を9分割して、計9つのボタンを表示する
- ボタンを右クリックして押すことによって、真下のタートルを操作できる
- ただし、タートルがモニタから離れると操作できなくなるので、移動系の操作は実装しない。そのため燃料系の操作も除外。
- たとえば、次のような操作ができます。
- 目の前にあるブロックを破壊してゲット、左向いて、選択スロットを16に切り替えて、スロット16のブロックを設置。とか
- 敵対モブがやってくるのを待ち受けてタートルの正面にきたらAtkボタン連打して攻撃、あるいは日光消毒などで目の前に落ちたアイテムをSuckボタンでゲット。などなど。
- このプログラムは、設定の自由度を意識しました
- プログラムのConfig部分で、「ボタンの名前」とボタンを押すことで「実行するコマンド」を自由に書き換えることができる。
- たとえば、「ボタン6はturtle.suckUp()」にしたい! という場合は、Config部分を書き換えるだけでおk。
このプログラムで学べること
- モニタボタンの表示やボタンイベントの作り方
- 前回説明したので、説明を省略しています。
- 「eval(文字列)」関数の作成 ←今回の目玉機能
- 「eval("turtle.select(1)")」のように、与えた文字列を評価(関数として実行)する関数を定義します。
- 他のプログラム言語、特にスクリプト系(sh、ruby、perl、phpなどなど)のプログラムを組んだことある人ならば、eval関数の有用性はわかりますよね。
プログラムの解説
いつも同様、グローバル変数を押さえた上で、メイン部分を説明し、それから重要な部分に焦点を当てて説明しましょう。
グローバル変数(プログラムの設定パラメータ)
今回もプログラム使用者が自由に書き換えられるConfig部分にしかグローバル変数を使っていません。
- L7: 「CtrlMonSide」コントローラとなるモニタをタートルのどこにおくか
- L8-2: 計9つのボタンの設定。設定項目は、「ボタンの名前」と、そのボタンを押すことによって「実際に実行するコマンド」の2つです。
メイン部分の解説(プログラムの全体像)
前回に比べると、シンプルでしょ?
- L93-96: コントロールモニタの初期化をしています。
- L98-100: Configで設定したボタンパラメータとコントロールモニタを引数に取ることで、ボタン(Table)の作成と、モニタへのボタン表示を行っています。
- 102-111: メインループです。終了条件は特に指定していません。ループ中の具体的な動作は以下のとおり。
- L104: 自家製関数であるpullPushButtonEvent()を使うことで、"push_button" イベントを拾っています。この説明は前回行いました。
- L105: 拾ったイベント名が "push_button" であるときに、以下を実行します。(このIFはなくても動きますがw なんとなく)
- L106: どのボタンが押されたのかを、ターミナル画面に表示します。
- L107: Configで設定されたボタンコマンドを実行します。eval()は自家製の関数です。
今日のポイント「評価関数 eval() 」について
押すボタンによってコマンドを実行する部分は、L107の「eval(コマンド文字列)」だけで実現しているのですよ。実質的に、Cで言うswitch構文を1行で実現しているわけですね。すばらしい(自画自賛)*1。
このeval()はL27-30で定義していますが、実際にはL29だけが重要です。
もうね、慣用表現だと思って覚えちゃってください。最後につける()は忘れないように!
理屈がもっと知りたいという方のための説明は以下のとおり。
- loadstring(文字列)によって関数そのものを作り出します。そして関数の末尾に「()」をつけることによってその関数を実行します。これについては次回、例を挙げながらもう少し詳しく説明しましょう。そもそもLuaにおける関数とは……のように、そもそも論の話をしなくてはいけないのでw
- 「loadstring(コマンド文字列)()」と書いても動きます。しかしエラー処理のため、assert()をクッションとしてはさみこむのがお作法のようです。
- また、assert()の代わりに「pcall」「xpcall」*2を使う方法もあります。エラー処理を厳密に行うならこちらが適しているのですが、めんどうですしおすし。
- なおこの実装方法は、Lua5.1リファレンスにあった以下の部分をそのまま使っています。
loadstring (string [, chunkname])
load と同じであるが、 指定した文字列からチャンクを読み込む。
与えられた文字列をロードして実行するには、以下の慣用句を使う。
assert(loadstring(s))()
プログラム
このプログラムの応用について
RedNet APIについて説明しつつ、ラジコンプログラムへと発展させるか、
あるいは、ボタンを押して実行コマンドを記憶させ、後でまとめて実行させるような、ボタンプログラミング式タートル操作プログラムへと発展させるか。
迷うわー。どちらかを作ってくれる人いないかなー?(チラッチラッ