2011年7月7日木曜日

ドリトルの壁反射をカスタマイズする方法

ドリトルの「ピンポンゲーム」は定番のネタ。教科書の「Activity 6」(34〜38ページ)にも載っている。

移動するタートルや図形の衝突したときに、入射角と反射角が等しいように反射させることで、この手のゲームは成立する。

教科書では、V2.2に対応して、例えばカメ太がタイマーで移動する場合、

カメ太:衝突=タートル:跳ね返る。

と定義する、と書いてある(36ページ)。カメ太の衝突のはずなのに、わざわざその親(プロトタイプ)を指定してメソッド定義する理由はなぜか、気になりませんか。

というわけで、「タートル:」を外して、継承されているはずの「跳ね返る」メソッドを直接実行するようにしてみる。ひとまず、カメ太と壁を描いて、カメ太を直進させてみる。まだ衝突は定義しない。
カメ太=タートル!作る ペンなし。
大工さん=タートル!作る 30 線の太さ (青)線の色。
大工さん!ペンなし 350 300 位置 ペンあり -90 向き 600 歩く 図形を作る。
大工さん!消える。
タイマー!作る 0.01 間隔 5 時間「カメ太!5 歩く」実行。

当然ながら、なにごともなかったかのようにすり抜けていく。

で、衝突の定義を追加。
カメ太=タートル!作る ペンなし。
大工さん=タートル!作る 30 線の太さ (青)線の色。
大工さん!ペンなし 350 300 位置 ペンあり -90 向き 600 歩く 図形を作る。
大工さん!消える。
カメ太:衝突=跳ね返る。
タイマー!作る 0.01 間隔 2 時間「カメ太!5 歩く」実行。
なんと、衝突が発生しない!そこで、メソッド定義に変えてみる。
カメ太=タートル!作る ペンなし。
大工さん=タートル!作る 30 線の太さ (青)線の色。
大工さん!ペンなし 350 300 位置 ペンあり -90 向き 600 歩く 図形を作る。
大工さん!消える。
カメ太:衝突=「!跳ね返る」。
タイマー!作る 0.01 間隔 2 時間「カメ太!5 歩く」実行。 

するとなんと素敵なことに、衝突した瞬間、「"_active"オブジェクトが作られていません("なら")」というエラーが発生する!

普通の人はここで戸惑って「ごめんなさい」と謝るところだけれど、こちらは「なぜタートルのメソッドでなければならないのか」を疑っているので、「これはしめた!」と思うところ。

「なら」というわけで、条件文に使われている変数名だろうとあたりをつけて、ローカル変数(というか引数)を指定してやる。ただし、衝突の引数は、衝突相手のオブジェクトなので、_activeは第二引数にして、以下のように書いてみる。
カメ太=タートル!作る ペンなし。
大工さん=タートル!作る 30 線の太さ (青)線の色。
大工さん!ペンなし 350 300 位置 ペンあり -90 向き 600 歩く 図形を作る。
大工さん!消える。
カメ太:衝突=「|相手 _active|!跳ね返る」。
タイマー!作る 0.01 間隔 2 時間「カメ太!5 歩く」実行。

ナイス!「"_hittarget"オブジェクトが作られていません("向き?")」というエラーメッセージ。名前からしてこれは衝突相手を意図したものということで、第一引数名をこれに変更。

見事に反射する。というわけで、この「_hittarget」と「_active」という、いかにも内部変数な名前が、「跳ね返る」メソッドの引数として使われていて、子ども側でそれに合わせてやらないと、未定義オブジェクトに対するメッセージ送信となってエラーが発生するということがわかる。

ちなみにdolittle.jarをunzipで展開してdolittle.iniファイルを眺めると、高麗大の青木さんががんばって改良した「タートル:跳ね返る」メソッド定義が、まさにこの2つの引数をとるように書かれているのがわかる。青木さんありがとう。

これで、ドリトルの「跳ね返る」メソッドを使いながらオリジナルの衝突メソッドを定義することができるようになった。

例えば壁に反射するときには、「カキン!」という効果音を入れたくなるもの。というか、動きを見ていれば、音が脳内で鳴り響く。そこで、効果音を追加してみる。
カメ太=タートル!作る ペンなし。
大工さん=タートル!作る 30 線の太さ (青)線の色。
大工さん!ペンなし 350 300 位置 ペンあり -90 向き 600 歩く 図形を作る。
大工さん!消える。
壁反射音=メロディ!作る『↑ど16↑ど16』追加
             (楽器!『ウッドブロック』作る)設定。
カメ太:衝突=「|_hittarget _active|!跳ね返る。壁反射音!演奏」。
タイマー!作る 0.01 間隔 2 時間「カメ太!5 歩く」実行。
成功するはず。お試しあれ。

で、効果音つきのスカッシュゲーム風に仕上げてみた。パドルの移動は、マウスカーソルの位置に合わせるようにしている。ご参考まで。

カメ太=タートル!作る ペンなし。

大工さん=タートル!作る ペンなし。
大工さん!30 線の太さ(青)線の色。
大工さん!-300 300 位置 0 向き ペンあり。

壁=大工さん!650 歩く 90 右回り 600 歩く 90 右回り 650 歩く 図形を作る。

パドル=大工さん!ペンなし -350 75 位置 ペンあり -90 向き
    25 線の太さ(ピンク)線の色 150 歩く 図形を作る。

大工さん!消える。

パドル:上下=「パドル!-350 ((マウス!縦の位置?) + 75) 位置」。

壁衝突音=メロディ!作る『↑ど16↑ど〜』追加(楽器!『ウッドブロック』作る)設定。
パドル音=メロディ!作る『↓ど16』追加(楽器!『ミュートギター』作る)設定。

カメ太:衝突=「|_hittarget _active|!跳ね返る。
       「(_hittarget == 壁)」!なら「壁衝突音!演奏」そうでなければ「パドル音!演奏」実行」。

速さ=5。

カメ太!((90!乱数) - 45)向き。

アウト音=メロディ!作る『↓↓↓み8み8』追加(楽器!『シンセベース1』作る)設定。

アウト=「アウト音!演奏。タイマー!作る 3 時間「」実行 待つ。
               カメ太!中心に戻る ((90!乱数) - 45) 向き」。

タイマー!作る 3 間隔 30 時間「速さ=(速さ)+ 1」実行。

タイマー!作る 0.01 間隔 30 時間
       「カメ太!(速さ) 歩く。パドル!上下。
       「(カメ太!横の位置?) < -400」!なら「ルート!アウト」実行。
       」実行。

なお、3秒ごとにカメ太の動きが速くなるようにしてみた。ゲーム時間は30秒で、そのまま終了するので、メッセージを出すなど改良していただけるとありがたい。

Scratchに距離センサをつないでみる

Scratch+Arduinoで次はなにをしようかと考えて、遠隔操縦のロボットカーでもやってみようかと考えてみた。通信はXBeeを使う予定。

ひとまず、ロボットカーの仕様を検討。定番で、障害物回避ロボットを考えて、障害物検知のために距離センサをサーボモータに載せ、首振りさせてやることにしてみた。

きょうはまず、距離センサの実験。

前回試した、port 42001の通信をProcessingあたりでXBeeに流しこむのがいいのだろうけれど、動作確認が主目的なので、きょうはScratchセンサーボードのArduinoコードを改造することにした。

横川さんと阿部さんのコードでは、168のArduinoに合わせて、足りないアナログ入力端子で余った「抵抗D」を、抵抗Cと同じにすることでScratchに合わせている。その余った端子をいただく魂胆。

距離センサは、赤外線を使うPSD測距センサが無難だしそのままArduinoのアナログ入力端子に接続できるので簡単ではあるのだけれど、ちょうど手元に使っていないParallax社の超音波距離センサモジュール「PING)))」があったので、試してみることにした。

PING)))をArduinoで使うコードは、本家のチュートリアルにあるので、そのままいただいた。

PING)))の仕様では、ソナーの発信を2μ秒以上行ってから、反射音が受信されるまでの時間をマイクロ秒単位で計測する。上のコードでは、5μ秒の発信をしてから、ArduinoのpulseIn()関数で受信までの時間を計測している。その結果を29で割ってさらに2で割ると、cm単位のリニアな距離になるそうだ。PING)))の仕様書を見ると、3mまで測定できるらしい。

というわけで、借りたコードをそのままチャンネルDに突っ込んだコードが以下のもの。

SensorBoardWithMotorAndPing.pde

まったくもって何の工夫もない。 PING)))は、D5に接続することにしてある。

Scratch側でセンサーボードを表示すると、Dの値がcm単位で出る。しかし、センサーボードは0〜100までの整数値しか扱えないので、最大1mまでの計測ということになる。

きょうはとりあえずここまで。

続く首振りのためのサーボモータはServo関数があるので、例えばモータ用のPWM端子であるD11をanalogWrite()からServo.write()に置き換えればそれで済んでしまうけれど、そこまでやると、もとのセンサーボードとの互換性の問題があるので、やっぱり外部通信でやるべきかなと思う。