Ogaki Mini Maker Faire 2022出展記 (GUNPLA Shooting 実装編)

# はじめに
　[toio友の会](https://www.facebook.com/groups/toiotomo)の主催者の一人、田中愛章さんからOgaki Mini Maker Faire 2022に出ませんか？とお誘いをいただきました。岐阜県も大垣市も行ったことないから行ってみたいですー、参加しますーと軽い気持ちで了解しました。

https://www.iamas.ac.jp/ommf2022/

　さて何を出そうかなぁ。[スティックCチャン(StickC-chan) feat. toio core cube](https://protopedia.net/prototype/3238)とか、[モルカーチョコの空き箱](https://gourmet.watch.impress.co.jp/img/grw/docs/1418/862/html/sub7_o.jpg.html)をつかってなんか作るとかのカワイイ路線でいこうかなぁと思っていたのですが、OMMF2022のエントリーの文面を見て驚愕！

https://www.iamas.ac.jp/ommf2022/maker/robot-gamers-from-toio-tomo-group/

「ロボットゲームステーション with toio」「あの『ドム』を自由に動かしてバトルできる作品」とか書いてある。マジかー、[Sendai Micro Maker Faire 2022で出したtoio駆動型ドム](https://qiita.com/kenichih/items/56fb0fda975df811f46f)でゲームつくんないとダメなのかー、と急遽ゲームを仕立てることになりました。

## アイディアをまとめる
　ガンプラで「バトル」とかいうと「[ビルドファイターズ](http://gundam-bf.net/)」なのですが、[プラフスキー粒子](http://gundam-bf.net/2013/mechanics/)はまだ開発できていないので、別の方法をとる必要があります。
　動かすほうはtoio core cubeが使えるとして、弾丸やビーム、そして「ドム」なのでジェット噴射とかのエフェクトはつけたい。そういえば液晶モニタの上にマットを敷いて液晶モニタの映像を透過させて陣取りゲームみたいなのを作ってたのをどっかで見たなぁ、あれでいけるかなあと。(元ネタがGoogleってもみつからなかったのであとで見つけたらリンクを追記しておきます)
　見つけました。モリカトロンさんのこれですね。https://toio.io/blog/detail/20210729-1.html
　試してみると24インチモニタの表示面の縦幅(短辺)とA3の短辺がほぼ同じなので開発者マットおよび簡易マットがピッタリ合います。液晶モニタの表示を明るめに設定し、部屋を若干暗くすると十分に表示が透けて見えました。これならいけそうです。

　操作のほうに関しては、ここのところ「さわらずに楽しめる」というコンセプトで作品([MFT2020](https://qiita.com/kenichih/items/62dc46bbbf4051e73b8c)、[SendaiMMF2022](https://qiita.com/kenichih/items/56fb0fda975df811f46f))を作ってきましたが今回は「ゲーム」ということもあり、また新型コロナ感染対策としても、人が触ったものに触ったあとは「手を顔や口にもっていかない」「手をよく洗う」「触ったもののほうをアルコールなどで適宜消毒」などの対策方法がわかってきたこともあり、今回はがっつりゲームパッド(PS4のDualShock4)で操作してもらうことにしました。

# ゲーム制作

## 試作と表示テスト

　ハードウェア([toio駆動型ドム](https://qiita.com/kenichih/items/56fb0fda975df811f46f))はすでにあるので、ゲーム仕立てとするソフトウェアの開発がメインとなります。
　今回のゲーム制作のために使うのは毎度おなじみ？の[toio core cube用Pythonライブラリtomotoio改](https://qiita.com/kenichih/items/f78356107ba6428d73ec)と、[2Dゲーム作成用ライブラリのpygame](https://www.pygame.org/)です。
 Ubuntu20.04を入れたPCでゲームソフトウェアの作成とテスト、展示での本番運用はJetson Xavier NX Dev.kit(JetPack5.1 Ubuntu20.04)で動かします。
　Jetsonにしたのは本体がコンパクトで持ち運びしやすいのと、しっかりしたBluetoothアンテナを搭載していたからで、python3とbluepyとpygameが動く環境であればなんでもかまいません。Raspberry Piとかでも十分動作します。(実際、予備機としてRaspberry Pi 4BにRaspberry Pi OSを入れてpythonスクリプトを同じように動かせるように準備しました。)

　ゲーム内容は単純なシューティングゲームで、逃げ回るターゲットを狙ってバズーカ砲を当てるというものにしました。
　ゲームは大きく分けて２つのpythonスクリプトからなり、それぞれが別のプロセスとして動作し、２つの間をZMQのpub/sub通信で情報のやりとりをするようにしました。これはtoio core cubeの位置をnotifyしてくる頻度とpygameが画面を更新する頻度が違っているため、両方を一つのスクリプトで行おうとするといろいろめんどくさいことになるためです。(一つのスクリプトで動かすにはpythonのマルチスレッド機能とか使えばいいのですがまあ、手っ取り早く実装したかったので)

![ソフトウェア構成_2つのスクリプト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/cc01bf2e-b0ad-23a5-8bd7-f88607a10114.png)

　今回、domShootCtrl.pyではゲームパッドの入力を50msおきに検知、またtoio core cubeの位置情報のnotifyはデフォルトで10msおきですが、これを全部domShootView.pyに送ってしまうと多すぎて描画が追いつかないので少し間引きをおこなっています。domShootView.pyでの画面更新頻度は120fpsに設定していますが、実際ディスプレイにはそこまで速く更新されないのでちょっと過剰ではあります。どれくらいの頻度で処理するかはpythonスクリプトを動かす環境によって処理しきれるかどうかが違ってくるのでこのへんの値はうまく調整する必要があります。

### 仮の表示で作りはじめる
　最初はターゲットの位置表示として「いらすとや」からとってきたおもちゃロボットの画像を使ってました。画像の下側の短い線はドムの両足の間の位置を示していて、透明背景の真ん中に線を一本引いただけの画像を作ってそれを表示しています。
<img alt="開発初期段階画面" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/95aa2074-b381-a28c-0f20-fcb0730e6dab.jpeg" width=50% /><img alt="[GunplaShootingScreen0.png]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/00bb43c1-a178-e032-ef0e-9fd091cf6043.png" width=40% />

　toioのマット座標(305x215)と液晶モニタの画面座標(pygame座標 得点表示の余白部分など含めて1700x1050)の変換ですが、マット座標のほうが解像度が低いため、マット座標を何倍かして画面座標にあわせます。最初はざっくりx5でゲームを作っていき、最終的にきっちりあうように調整しました。今回はx4.97でほぼピッタリ一致するようになりました。
　また、マット座標では角度の回転方向が時計回り、pygameでは反時計回りなのでこれも合わせる必要がありました。

　液晶モニタの上に敷くマットですが、A3サイズのマットとしてSwitchScienceで買える[toio™開発用A3プレイマット [TMD01SS]](https://www.switch-science.com/products/6650)と、[toio core cubeの単体売り](https://www.switch-science.com/products/6300)に付属する簡易プレイマットがあります。どちらでも使えるのですが、[toio core cubeの単体売り](https://www.switch-science.com/products/6300)に付属する簡易プレイマットのほうが紙が薄く、液晶モニタの光をよく透しますのでそちらを採用。
　今回使った液晶モニタは[ViewSonic XG2401](https://www.viewsonic.com/jp/products/sheet/XG2401)で24インチ(正確には23.8インチ?)のものです。なるべく明るくコントラスト強めの設定にしないとうまく透けて見えません。また、部屋の明かりを若干暗めにしたほうがよく見えるようになります。

制作途中のもの

https://youtu.be/4rk5PHQtKWQ

## ブラッシュアップ

　ある程度ゲームとして動くようになったら見栄えをよくするために改良してゆきます。

### ビットマップ類の差し替え
　いらすとや、やニコニ・コモンズから使えそうなものを拾ってきます。

いらすとや、からとってきたもの
ジャイアント・バズの弾丸(元は「銀の弾丸」)
<img alt="![gin_dangan_silver_bullet.png]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/a4e78741-d2a8-7e71-3a7f-074bffa1ed15.png" width=20% />
ターゲットが撃ってくるビーム
<img alt="![beam_pink.png]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/48e5795a-7798-9205-ab3d-0c71cf6f70ba.png" width=50% />
ちょっと長すぎるので短く縮めてつかっています。
<img alt="![beam_pink_short.png]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/68a7313d-3b9c-cdc4-f0c4-2a71f1190dcd.png" width=30% />

ニコニ・コモンズからとってきたもの
<img alt="![jet1.png]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/91f03c63-91d3-744b-e3c8-38af3b9e5454.png" width=30% />　https://commons.nicovideo.jp/material/nc94293
もともとは数枚からなるアニメーションで噴射がゆらめくのですが、今回は１枚だけ切り出して表示、右足、左足のtoio core cubeの速度に応じて大きさを変えて描画しています。

ヒット時あるいは被弾時の爆発アニメーションは
以下の記事を参考にWindowsアプリ[発色弾](http://taillove.jp/mia/tools.html)で生成しました。

https://aidiary.hatenablog.com/entry/20081011/1281193049

発色弾で生成した爆発
![EXP1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/8f724b24-ee67-5ad1-2b7f-dc4369953f25.png)

### 効果音をつける
効果音も使えそうなフリー素材をとってきて利用させてもらいます。以下のページから爆発音、発射音、ゲームスタート、終了時のブザーなどに使えそうな音源をとってきました。

SprinGin'フリー音源
https://www.springin.org/sound-stock/

効果音ラボ
https://soundeffect-lab.info/sound/anime/

ザ・マッチメイカァズ
http://osabisi.sakura.ne.jp/m2/tm4/se_new.html

ビットマップを差し替えてジェット噴射を足した画面
<img alt="![GunplaShootingScreen1.png]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/a46d889a-b2f6-f8d0-c9a9-65cba809cb64.png" width=40% /><img alt="![GunplaShootingScreen2.png]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/c5b17032-16ea-a5c8-f139-8bc816f05fe4.png"  width=40% />


### その他追加したもの
- toio core cubeのモーター制御によるエフェクト
    - ドムのジャイアント・バズを発射したときにあたかも反動があるかのように見せるため、少し後ろに下がってすぐ元の位置にもどるような動作
    - ジャイアント・バズの弾がターゲットに当たったとき、ターゲットが回転してヒットしたことを示す
    - ドムがターゲットの打ってくるビームに当たると数回左右に振れて被弾を示す(いわゆる硬直による時間ロス)
- 場面切替およびゲーム時間のタイムリミット
    - スタート前画面(スコア加算なしの練習モード)、スタートボタン押したあとのReady表示とカウントダウンブザー、ゲーム中、ゲームオーバー表示、の切り替え
- スコアは遠くからあてた場合に高得点、極端に近い位置からだと0点に
- ハイスコア機能
- ゲームパッドのアナログスティック２本をつかって左右のtoio core cubeを直接コントロールするdual stick mode
    - 実装が簡単なので作ってはみましたが本番では全く使いませんでした。
    - ![うごかしかた.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/6c597343-ee51-578a-60b2-c1f6f446f064.png)


## わりきり
　展示の日まで迫ってきたことと、ゲーム難易度の調整のほうに時間をとったので以下の点については実装しないとわりきりました。
- toio core cubeとの接続が切れた場合のリカバリはしない、手動でスクリプトを再起動する
- スクリプトが異常終了した場合の自動再起動もしない、手動でスクリプトを再起動する
- ターゲットのtoio core cubeの上にのせるのはガンプラくんのアクリルキーホルダー
    - いろいろ検討はしたのですが、(プラモデル組んでる時間もなく) toio core cubeの天面に台座を両面テープで貼るだけですむのでこれにしました。
    - <img alt="[PXL_20221210_005232140_1.jpg]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/7ad8d204-8c94-7af1-a925-34ea20dc10b8.jpeg" width=66% /><img alt="[PXL_20221210_005239672_1.jpg]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/406c8207-fe52-96a2-ad9c-796119db17e4.jpeg" width=33% />
    - <img alt="![PXL_20221210_105348477_1.jpg]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/ebcde849-0203-8c47-c12d-6ada4f1b8492.jpeg" width=60% /> <img alt="![PXL_20221210_105109653_1.jpg]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/70ff029a-a528-61aa-d625-8d1dd663902c.jpeg" width=33% />
- 当たり判定調整はpygameのスプライトの大きさの四角領域のまま
    - これは普通のシューティングゲームでもそうですが、自機の見えているサイズそのままにきっちりに合わせると当たりすぎてしまうように感じるので、やや小さめに設定するのが定石です。ドムの足の外側よりだいぶ内側の四角い領域のスプライトビットマップをつくってそれをそのまま当たり判定領域としました。
- ターゲット(ガンプラくん)の移動は端のほうを直線状にうごくだけ
    - 右辺や左辺のほうにも移動してコの字に動くようにもしようかと思いましたがゲームとしてむつかしくなるのでやめました
- ターゲット(ガンプラくん)が撃ってくるビームはまっすぐ撃ってくるだけ
    - 斜め方向にも撃ってくるようにもできましたが、ドムを操作しての回避の難易度が高くなるのでやめました
- ドムがマットの外に飛び出ないようなリミットもなし
    - 液晶モニタのふちがいい感じにストッパーになるので不要でした
- (ドムに押しだされたりして)稀にターゲット(ガンプラくん)がマットの外に出ることがあるので、そのばあいは手でひろってターゲットをマットの上に置きなおしたら移動を再開するようにはした

# こんな感じに仕上がりました

<img alt="![PXL_20221126_042542480_1.jpg]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/f536b850-be54-301e-d618-1c4752e57bf1.jpeg" width=33% /><img alt="![PXL_20221126_042613536_1.jpg]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/d74226b4-750b-b25c-d9ed-94095fc2c6cd.jpeg" width=33% /><img alt="![PXL_20221126_042623909_1.jpg]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/93d8a42a-5e90-901c-507b-6d82683679ca.jpeg" width=33% />

<img alt="![PXL_20221126_042708906_1.jpg]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/fdc8a2ed-74ce-d405-4a2b-05eee8d98b20.jpeg" width=33% /><img alt="![PXL_20221126_043022345_1.jpg]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/52988b71-c34a-4f29-6cf5-eb532c98a516.jpeg" width=33% /><img alt="![PXL_20221126_052906050_1.jpg]" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/5407c481-0380-c289-ad7b-c074daa181cc.jpeg" width=33% />

https://youtu.be/CdT4rlL9Fxw

実はこの画像、ハイスコア機能をつける前に撮ったものなのでハイスコア表示がまだありませんw

[Ogaki Mini Maker Faire 2022出展記 (GUNPLA Shooting 展示編)](https://qiita.com/kenichih/items/6ec9b8114c94dfc4baed)に続く