Game Playerは、DOOMのゲーム実行機能です。Chocolate-Doom を移植したものになっていますが、Nucleo版と違いSTM32H7B3I-DKではSDRAMが16MBもあるので、ソースの改変は最小限に抑えることができます。画面への出力と音楽/サウンド再生、そしてDualSenseとの通信というような入出力を用意してやれば、比較的簡単に動かすことができます。
元々のDOOMは、320x200ドットの画面を想定していますが、使用しているLCDの解像度は480x272ドットです。320x200の画面をそのまま表示することはできますが、それでは画面が小さくて見ずらいので 480x272に拡大表示しています。DOOMのカラー表示はパレットを用いたL8フォーマットですので、bi-linerのようなアルゴリズムを使って近傍の画素との色の平均をとって補間するというわけにもいきません。上下左右方向で前の画素と同じIndexを繰り返すことで拡大するしかありませんが、画像がいびつになることもなく、それらしく拡大できました。
SDRAMの容量が十分にあるので、LVGLのGUIを動かしたままで、DOOMをFreeRTOSのタスクのひとつとして実行させ、それぞれにLTDCのLayer2とLayer1を描画するフレームバッファとして割り当てることにしました。そして、この2レイヤを用いてGUI操作によるチート機能用意しました。
具体的には、ゲーム実行中に画面の中央部分をタッチすると代表的なチートコードのLVGLボタンが表示され、ボタンを押すだけでコードの入力ができます。
これだけでは、他のコードを入力できないので、 画面を少し長押しタッチするとキーボード入力もできるようにしてあります。
上のレイヤであるLVGL画面の背景を透明にしてやることで、下側のDOOM画面が透けて見えるわけですが、透過のためにフレームバッファをARGB8888の32bitにすると消費メモリも増えるし、描画時間も長くなってしまいます。LTDCではARGB1555がサポートされていますが、LVGLではサポートできません。そこで、LVGLのレイヤのフレームバッファにはRGB565を使うこととし、クロマ・キーを使ってDOOM実行時には緑背景でLVGL画面を描画することにしています。
具体的には、次の手順でボタンの表示を行っています。
1. ゲーム実行中は、LVGLでLayer2にボタンを描画しておきますが、LTDCの操作によりこのレイヤは非表示にしておきます。これにより、見えないけれども、画面上にボタンが用意されて、タッチによる検出が可能になります。
2 . タッチを検出したら、チートコードのボタンを表示して、Layer2の表示を許可します。緑背景の部分はクロマキーと一致するため透明扱いとなり、下のレイヤであるDOOMの画面が表示されます。
3. ボタンが押されたら、1に戻るとともに、選択されたキーコードをDOOMに送ります。
チート機能により体力や弾丸の回復が簡単に行えるうえに、好きなレベルステージに移動できるのですが、ゲームを続けるだけの根気の無いわたしはまだ一度もゲームをクリヤしたことがありません。
LVGLを使うことのもうひとつの利点は、ENDOOM画面の表示です。この画面はゲームの終了時に表示されるテキスト画面ですが、300x200 L8フォーマットのDOOM画面のレイヤでは表示することができません。そこで、ゲームの終了時にはLVGLのレイヤにこの画面を表示して、DOOMの画面レイヤを非表示にしています。
元々のchocolate-doomに含まれるtextscreen用のフォントは480x272ドットのLCDで使うにちょっと向いていなかったので、large.png を縮小して6x11ドットのグリフサイズのフォントを生成して使っているのですが、ちょっと字が潰れて読みにくいのが残念なところです。
ゲームの際中には音楽も再生されますが、これはSDファイルにあるFLACファイルを再生しているため、頻繁にSDカードへのアクセスが発生します。一応、ゲームの途中でのsave/load機能や、スクリーンショット機能も動くのですが、ゲーム音楽の再生とSDカードへのアクセスが競合してしまうので、どうしても音が途切れてしまう問題が発生してしまうのが、ちょっと残念なところです。