言ってしまったからには、やるしかない!
第43回JAET 和歌山大会の展示会場で知ったのですが、アンドール株式会社が開発・販売している「ぶろっくめーかー」
http://www.andor.co.jp/products/blockmaker/index.html
という、立方体の組み合わせで、簡単に3Dモデルをデザインできる機能と、命令ブロックによるプログラミングで3Dモデルを画面上で自由に動かうことができる機能を持つソフトウェアがあることを知りました。
プログラミングエディタ部分の見た目は、Blocklyそのものでしたので、スモウルビーとの親近感があり、展示ブースで説明されている方と少し話しをしました。
実は、まだ導入実績が少ないということでしたので、おせっかいな話しではありますが、「Scratch 2のHTTP拡張を使えば、Scratch 2でも制御できるのではないでしょうかね。その実装は簡単なはずですよ。Scratchで制御できるならば、使いたい人はたくさんいるはずです!」なんて言ってしまいました。言ったからには実現するしかないですよね!
ということで、
Python で軽量サーバを作って Scratch 2 のHTTP拡張を待ち受けてみる
https://qiita.com/memakura/items/fb48d7f6fb6b4b88b5bb
を参考にして、プロトタイプを作成します。
ぶろっくめーかー独自の命令ブロックは、4ジャンル、15種類あるので、各ジャンルから1種類をピックアップして、次の4種類の命令ブロックを実現することにします。
- <1>秒間、前に<1>進みます
- <1>秒間、X方向に<1>進みます
- <1>秒間、左右に<90>度回転します
- <1>秒間、X軸を中心に<90>度回転します
(余談: あらためて命令ブロックのラベルを見て思ったのは、命令っぽくない言い回しなので、Scratchを参考にして「進む」「回す」といった表現のほうがいいような気がします。そのほうが命令っぽい感じですよね。阿部先生が翻訳されたものですしね。)
s2eの作成
まずは、独自の命令ブロックを作成します。具体的には、以下のような内容のファイルを用意します。ファイル名は、「ぶろっくめーかー.s2e」とします。
{
"extensionName": "Block Macker", // ここには日本語は使えません
"extensionPort": 12345, // 本体側の待受ポート
"blockSpecs": [
[" ", "%n 秒間、前に %n 進みます", "move", 1, 1],
[" ", "%n 秒間、X方向に %n 進みます", "move_x", 1, 1],
[" ", "%n 秒間、左右に %n 度回転します", "turn_lr", 1, 90],
[" ", "%n 秒間、X軸を中心に %n 度回転します", "turn_x", 1, 90],
]
}
ファイルのフォーマットは、
https://wiki.scratch.mit.edu/w/images/ExtensionsDoc.HTTP-9-11.pdf
に書いてあるとおりです。英語ですが、プログラム例が多くて読みやすいです。
s2eの読み込み
ファイルを作成したら、Scratch 2で読み込みます。
Scratch 2のオフラインエディタ
https://scratch.mit.edu/download
を起動して、 シフトキーを押しながら 「ファイル」メニューをクリックすると、一番下に「実験的なHTTP拡張を読み込む」というメニューが表示されます。
実は、Scratch 2は、シフトキーを押しながら各メニューをクリックすると隠しメニューが表示される、というね、ワクワクした作りになっているようです。
「実験的なHTTP拡張を読み込む」メニューを選択して、先程作成した s2e ファイルを読み込むと、これだけで、「その他」カテゴリに独自の命令ブロックが追加されます。
APIサーバの実装
独自の命令ブロックを追加すると、Scratch 2から、手元のコンピュータ(具体的なIPアドレスは127.0.0.1
)の12345ポートへ、HTTP通信を行うようになります。
独自の命令ブロックを実行するときは、命令ブロックの種類やパラメータをパスに埋め込んでHTTP通信を行います。
例えば、「<1>秒間、左右に<90>度回転します」の場合は、次のパスを指定して、HTTP通信を行います。
http://127.0.0.1:12345/turn_lr/1/90
「turn_lr」「1」「90」は、それぞれ、命令、秒間、角度に対応しています。
このようなHTTP通信を受け取って、適切に処理する HTTP サーバ(ここでは、APIサーバと呼びます)を実現すれば、独自の命令ブロックを処理することができます。
あと、APIサーバにはもうひとつ /poll
というパスへのHTTP通信を受け取って空(""
)の応答を返す、という処理も実装する必要があります。このHTTP通信は1秒間に最大で30回行われますので、それなりの負荷です。これはセンサーを実現したりするために必要なのですが、今回はセンサーは実現しないため、説明を省略します。
以上の処理を行うAPIサーバを実装します。ここでは、筆者の都合でプログラミング言語Rubyで実装します。
require "webrick"
server = WEBrick::HTTPServer.new(
DocumentRoot: "./",
BindAddress: "127.0.0.1",
Port: 12345
)
server.mount_proc(
"/poll",
->(request, response) {
response.body = ""
}
)
server.mount_proc(
"/move",
->(request, response) {
match = %r"/move/(\d+)/(\d+)".match(request.path)
wait = match[1].to_f
step = match[2].to_f
p([wait, step]) # ここでは受け取った情報を画面に表示しているだけだが、適切な処理を行う
sleep(wait)
}
)
server.mount_proc(
"/move_x",
->(request, response) {
match = %r"/move_x/(\d+)/(\d+)".match(request.path)
wait = match[1].to_f
step = match[2].to_f
p([wait, step]) # ここでは受け取った情報を画面に表示しているだけだが、適切な処理を行う
sleep(wait)
}
)
server.mount_proc(
"/turn_lr",
->(request, response) {
match = %r"/turn_lr/(\d+)/(\d+)".match(request.path)
wait = match[1].to_f
degree = match[2].to_f
p([wait, degree]) # ここでは受け取った情報を画面に表示しているだけだが、適切な処理を行う
sleep(wait)
}
)
server.mount_proc(
"/turn_x",
->(request, response) {
match = %r"/turn_x/(\d+)/(\d+)".match(request.path)
wait = match[1].to_f
degree = match[2].to_f
p([wait, degree]) # ここでは受け取った情報を画面に表示しているだけだが、適切な処理を行う
sleep(wait)
}
)
Signal.trap(:INT) do
server.shutdown
end
server.start
Macの場合、macOSにRubyが含まれていますので、Rubyをインストールする必要はありません。
Windowsの場合は、RubyInstaller for Windows
https://rubyinstaller.org/
から、最新のRubyのインストーラをダウンロードしてインストールしてください。
Rubyを用意できたら、Windowsの場合はコマンドプロンプトで、Macの場合はターミナルで、
ruby block_maker_api_server.rb
として、APIサーバを起動します。
あとは、Scratch 2を操作して任意のスクリプトを作成します。
イ〜ジョウ!