オールインワンのマイコンボードM5StickC Plus(下図)を使って、前回から作り始めた後出しジャンケンゲームを完成させて、実際に遊んでみたいと思います。
ゲームを盛り上げるのに必要な音楽演奏は、M5StickC Plus用に移植したplay_mml()関数を使います(詳しくは以下記事を参照)。
「#25-2 M5StickC Plusでジュークボックス」
play_mml()関数をゲームに使う場合、音楽演奏中にも、プレイヤからの入力を受け付けたり、TFTディスプレイの表示を更新したり、並列処理が必要となります。つまり、演奏が終了するまで処理をブロックしてしまう、現在のplay_mml()関数では使い難いのです。
「#25-2 M5StickC Plusでジュークボックス」の記事にて予言しておいた様に、M5StickC Plusで稼働しているFreeRTOSのAPIでサブスレッドを起動して、並列処理させましょう。そうすることで、音楽演奏処理と、その他のゲーム処理を並列処理することができます。
音楽演奏用サブスレッド目標仕様:
もし、FreeRTOSが利用できない環境だったら、自分で割り込みなどを駆使しながら、並列処理を実装しなければならない所でした。
前節で起動したサブスレッドは音楽演奏だけを専門に引き受ける専用スレッドなので、指示された通りの音楽演奏だけを実行します。
メインプロセスは、以下手順でBGM演奏をサブスレッドに対して指示します。
BGM演奏手順:
bgm_rdy変数およびbgm_mmlポインタの値と、演奏の状態を以下図および表にまとめます。
状態変数 | 演奏状態 | ||
bgm_rdy | bgm_mml | ||
false | ≠NULL | PLAY | サブスレッドがbgm_mmlを演奏中。 |
=NULL | STOP_REQ |
メインプロセスが現在の演奏中断を要求。 サブスレッドが演奏中断するのを待っている。 |
|
true | ≠NULL | PLAY_REQ |
メインプロセスが新しいMMLの演奏開始を要求。 サブスレッドの演奏開始を待っている。 |
=NULL | IDLE |
サブスレッドは音楽を演奏していない。 メインプロセスからの演奏開始要求を待っている。 |
※メインプロセスとサブスレッドは非同期に並列処理しているため、全ての状態が観測できる保証はありません。上表4状態の内、どの状態が観測されても誤動作(ハングアップ等)しない様にプログラムしてください。
状態変数:
上のBGM演奏手順に従って、引数として渡したMML文字列を演奏する関数を起こして使います。
setup()関数内で、サブスレッドを起動する初期化play_bgm_init()関数を実行したら、後は、play_bgm()関数で普通にMMLを演奏することができるインターフェイスです。
なお、サブスレッドbgm_play_mml()関数では、M5StickC Plusの内蔵パッシブブザー(M5.Beepクラス)を占有リソースとして使っていますので、bgm_play_mml()関数以外の場所でパッシブブザーを使用してはいけません。パッシブブザーが共有リソースとなってしまうので、セマフォなどを使った排他制御が必要になってしまいます。
今後、音楽演奏したくなる度に、play_mml()関数の定義をコピー&ペーストするのは面倒なので、play_mml()関数および、play_bgm()関数をM5cp-PSライブラリへ収録しました。以下のリンクを辿って、最新版のライブラリに更新してください。
M5cp-PSライブラリ
それほど難しくないはずですが、追加収録したplay_bgm()関数の使い方に関しては、次節でリンクする後出しジャンケンゲームプログラムを参照してください。
以下に、完成したプログラム(一式)をリンクしておきます。M5StickC Plus開発環境の立ち上げは、こちらを参照してください。
完成プログラム: | m5cp_rps_game.ino |
ビットマップ定義(グー): | rps_rock.h |
ビットマップ定義(チョキ): | rps_scis.h |
ビットマップ定義(パー): | rps_papr.h |
ビットマップ定義(サムダウン): | rps_down.h |
すべてのヘッダファイルは、プログラム本体(m5cp_rps_game.ino)と同じディレクトリに配置してください。
※コンパイルには、M5cp-PSライブラリが必要となります。リンクを辿ってインストールしておいてください。
プログラム先頭付近のマクロで、TFTディスプレイの表示を英語版に切り換えることが可能です。必要に応じて、以下のマクロを有効化してコンパイル&書き込んでください。
//#define UI_SEL_ENG // u/i: select English
コンパイル&書き込むと、シリアルモニタから以下のコマンドを受け付けています。
シリアルモニタコマンド仕様:
全て、デバッグやプログラム動作調整用のコマンドなので、後出しジャンケンゲームとしてはシリアルモニタ無しで自立できます。
前節で完成したプログラムをM5StickC Plusにコンパイル&書き込むと準備完了です。下図は、ゲームプレイ中の様子です。
メニュー表示(上図左側)には、無駄に(?)カラー表示を使って見ました。ゲームなので、単色のメニューより、カラフルな方が楽しげな印象になります。カラー表示に使ったのは、tft_kprint()関数に備わった、制御コードによる描画色指定機能です。
まずは、シリアルモニタからコマンドを叩いて、確認作業を進めます。
実験サンプル:【 】内はシリアルモニタからの送信データ
【gyr,1】
OK: gyr ON
DBG: Gyro=(-136, 25, -18) [LSB]
DBG: Gyro=(-147, 33, -19) [LSB]
DBG: Gyro=(-152, 29, -15) [LSB]
DBG: Gyro=(-150, 27, -17) [LSB]
DBG: Gyro=(-165, 34, -20) [LSB]
DBG: Gyro=(-168, 31, -15) [LSB]
DBG: Gyro=(-155, 33, -19) [LSB]
DBG: Gyro=(-158, 29, -22) [LSB]
:
500ms毎にジャイロセンサを読み出して、シリアルモニタに表示しました。M5StickC Plus(に搭載されたジャイロセンサMPU6886)は、一切回転させていないので、読み出した非ゼロ値は、MPU6886のオフセットです。前回説明した様に、オフセット値をキャンセルして、ジェスチャを判定する必要があります。
オフセット値には個体差が有るので、機体毎に異なる値となります。上の値は、筆者手持ちM5StickC Plusでの計測値です。
なお、オフセットはプログラムで自動的にキャンセルしたので、確認したオフセット値自体は無視します。
続いて、【gyr,1】の状態でジャイロセンサの読み出し値を表示しながら、M5StickC Plusを振り回し、ジェスチャの判定閾値を決めました。
const int rps_GYRO_THSH=1024; // rps: gyro threshold for gesture
この値で、入力時のジェスチャの大きさが決まります。
一応、当方の感覚で、良さそうな値に設定してあるので、変更しなくても良いはずです。操作に違和感がある場合は、各自調整して適切な値に設定して見てください。
※ジャイロセンサのレンジ設定は2,000[°/s]レンジなので、rps_GYRO_THSH[LSB]×2,000[°/s]÷32,768LSBで回転させた場合に、検出します。
気が済んだら、【gyr,0】で表示を停止します。
以上、シリアルモニタで確認すべき項目は終了したので、今度は独立動作で実際にゲーム🎮を遊んでみましょう。
実験サンプル:
では、実際に遊んでいる様子を、以下動画でご確認ください。
自分で実際にゲームをプレイして見て判った事があります。
上の様な出題は他の出題に比べて簡単なため、高得点を取り易い状態でした。限られた3回戦の合計点で競うため、ランダムに簡単な出題が連続すると容易に高得点を取得できてしまい、ちょっと不公平が過ぎると感じました。調整が必要だと判りました。
テストプレイって、重要なんですね。
上にまとめた様な事は、どんなに頭で考えていても中々思いつきませんが、実際に遊んでみると、すぐに気が付きます。
市販ゲームでも、「これ、ちゃんとテストプレイしたのか?本当に面白いと思ってこんなバランスにしてるのか?」って、思う時があります。テストプレイは、「ポーズとして、とりあえず、こなしておく」のではなく、ちゃんとテストプレイヤの意見を取り込んで、ゲームバランス調整に活かさなくては、なりません。
その場合、テストプレイヤ側の責任も重大です。Yesマンにならず、客観的かつ率直な意見を上げて、改定を促せる様でないとダメです。当然ベンダ側も、開発日程ありきではなく、本当に必要な改定を実施した上でリリースすべきです。
そう言う大変さ、難しさの一端は、理解できたような気がします。
任天堂スイッチなんかも、そうなのでしょうが、ジェスチャ(体)を使ったゲームって言うのは面白いのですよね〜。後出しジャンケンゲームのテストプレイヤは、当方一人だけなので、調整しきれていない部分は、皆様がプログラムを調整・修正してお楽しみください。