M5Stack で「あみだくじ対戦ゲーム」を作った話


  • 社内の有志で定期的に行っているハッカソンで
    「あみだくじ対戦ゲーム」を作りました。
    • コードは GitHub にありますので、
      よろしければ遊んでみてください。

Prologue

  • 後輩の某氏が
    「ねんがんの M5Stack を てにいれたぞー」
    というので、死蔵しているウチの M5Stack Fire があるし、
    一緒に対戦ゲームを作ろうということで、開催の運びとなりました。

仕様 draft 投稿

  • Slack に投稿した当時の内容がこちらです
準備と実装:
1. N 台の m5stack を用意する
2. 何らかの方法で相互無線通信できるようにする
3. 何らかの方法であみだくじと弾丸を描画できるようにする

遊び方:
1. 弾丸を撃つ側と撃たれる側に分かれる
2. 撃つ側は3つのボタンの好きなボタンを押す
3. 押されたボタンが撃たれる側に届く(無線通信)
4. 撃たれる側はあみだくじと弾丸の描画を開始
 あみだくじを辿り終わるまでに正解のボタンを押せたらガード成功。
  攻守チェンジ。
5. 以下同文

ポイント:
* 後輩ンゴは「Arduino 開発環境」で、@andromedroid は
  「MicroPython 開発環境」で上記の仕様を満たす実装を行う。
* 難易度設定はそれぞれの開発者に委ねられるが、ゲームとして
  面白いレベルを目指す(あみだの複雑さ、弾丸の到達速度など)。

以上
  • 最終仕様としては、M5Stack は全て「撃たれる方」となり、
    「撃つ方」は MQTT Broker たる Raspberry Pi0 が担うことになりました。

  • channel への投稿なので、我々はこれをやりますと
    全員を前に宣言した形です。

しかし、新人さんも来るハッカソンで
「がんばったけど開発が終わりませんでした」
というブサマな姿を晒すわけにはいかないという強烈な虚栄心が働き、
フライングで作業を開始することになります。

フライング作業 〜 最初のプロトタイプ

  • まず、開発環境には UIFlow を選びました。

    • 途中でバージョンが上がったりして、最終的には
      v1.3.2 上で動くコードになりました。
  • 普段はゲーム屋でも GUI 屋でもないので、
    アニメーションとかどうしたら良いかもよく分からず、
    UIFlow にある demo examples から参考になるものを探し、
    バッテリーアニメーションの demo をベースにスタートしました。

  • 最初の AMIDA (2019/05/23)

  • 雰囲気は伝わるでしょうか。
    これを作って、ちょっとできそうな気がしてきました。

通信処理 (MQTT) の動作確認

  • 初めは BLE で P2P 通信をしようと考えたのですが、
    UIFlow で BLE を扱うライブラリは無さそうでした。

  • そこで、お互いの環境で利用可能な MQTT を使うことにして、
    Pi0 + mosquitto で Broker を立て、そこから publish される
    "missile" topic を subscribe する形にしました。

  • 通信のモジュールは開発でも苦しむことが多いので、
    hello_mqtt.py で簡単に仕様把握と動作確認をしたところ、
    思いの外あっけなく動きました。

  • pi0 + mosquitto broker

  • M5Stack の hello_mqtt.py

from m5stack import *
from m5ui import *
from uiflow import *
from m5mqtt import M5mqtt

setScreenColor( 0x222222)

m5mqtt = M5mqtt( '', '192.168.0.255', 1883, '', '', 300)

def fun_light_( topic_data) :
  # global params
  rgb.setColorAll( 0xff0000)
# pass

m5mqtt.subscribe( str( 'light'), fun_light_)

def btnA_wasPressed():
  # global params
  m5mqtt.publish( str( 'light'),str( 'A'))
# pass

btnA.wasPressed( btnA_wasPressed)

m5mqtt.start()
  • 唯一面倒なのが、UIFlow で MQTT 機能を利用する場合には
    オンライン実行ができず、ダウンロードでの実行しかできない点で、
    いちいち更新のためにアプリを切り替える必要があります。

Amida クラスの作成

  • その後、後輩の某氏が立派な AMIDA trace アニメーションと
    防御正否判定の実装まで終えるフライングをかましてきたために、
    またまた焦燥感に追われることになりました。

  • とりま追いつくしか無いので、この時点でゲームの
    進行を管理する、以下のようなクラスを作ることにしました。

    1. "missile" topic を受信したら instance 生成
    2. random で AMIDA の horizontal line を 10 行分生成
      • 0: なし
      • 1: 左側
      • 2: 右側
    3. missile が AMIDA trace を開始
      • 先の random 生成結果に従い line trace する
    4. shield の button を callback で受け取る
    5. missile が着弾、防御正否判定し effect
  • 実行動画 (2019/06/12)

  • アニメーションのお作法が良くないのか、
    Circle の描画が崩れてパック○ンみたいになります。
    • まあええか!
    • 通った後に道も無くなるけど、パッ○マンらしくてええわ!

当日

  • いざ、尋常に!

    • Oculus Quest を体験させてもらったりして穏やかに時間が経過
    • 後輩の某氏に「MQTT の動作を確認したいのですが」と言われ、
      慌てて Pi0 のセットアップを行う。
    • 当日のトラブルあるあるで、Wi-Fi ルーターのキャパオーバー
      と思われる DHCP の IP 割り当てが来なかったりが発生。
  • そんな感じで、とりあえずリリースしました。

    • 完成版コード
    • 当日は MQTT Broker から定期的に飛来する missile を
      subscribe して遊ぶようにしていましたが、
      こちらのコードでは、単品で遊べるように main() 内で
      定期的に missile を飛ばすようにしました。
    • 時々 AMIDA の道を踏み外すバグがあったりして、
      なかなか楽しく遊んでもらうことができました。

本日は以上です