ユーザ用ツール

文書の過去の版を表示しています。


フルーツフィールド for M5 Stack / M5 Core2

概要

フルーツフィールドの勝手移植第二段。 M5 Stack に BLEキーボードをつないだら、何か、アクション性のあるものも作ってみたくなりました。 でないと、キーボードにオートリピートをつける動機づけができないので。

そんなわけで、アクション性、というほどのものではないが、パズルは程よい題材だということで、フルーツフィールドを移植することに。

移植の方針

PC-6001mkII版に使った、PC-8001版のキャラクターをベースにしたものを今回も流用することに。 ただし、PC-6001mkII版は、PC-6001mkIIのVRAMに合わせたデータ構造にしてしまったので、これを逆に汎用の形に戻して、改めてRGB332 1)にすることに。

ハイハイスクールアドベンチャー経験から、スプライトが使えるといってばかばか使っているとあっという間にメモリを使い切ってしまうので最初から8bppで、しかも、8×8のキャラクターを16×16に引き伸ばして表示する方向でさらにメモリをケチることに。

音は、本格的なサウンドドライバーを作ったりしないで、単純に割り込みの中で、M5.Speaker.tone() を使って適当に鳴らすだけにします。 この割り込みを使うのも今回のテーマの一つです。

あとは、BLEキーボードハンドラに、オートリピートを持たせること。

とりあえず、コンストラクションモードは省いて、したがって、コンストラクションデータの保存と読込もとりあえずは省いていくことにします。

以下、実装にあたってのあれこれを書き綴ります。

画面

M5 Stack/M5 Core2 は 320×240 の 16bppの画面を持っているので、160×100 の PC-8001 由来のゲームを移植するのには不足はありません。

方針のところにも書いたように、キャラクターは8×8でM5GFX のスプライトとして保持します。 描画するときに、いずれかの段階で16×16にストレッチすることにします。 ストレッチのコストはありますが、メモリをけちるのはこの環境では大切です。

キャラクタを16×16で表示することにすると、ゲームマップは 16×10キャラクター構成なので、256×160 pixels の画面領域を占有します。

一応、M5 ATOM + 外部液晶(240×240)も視野に入れているので、拡大縮小は必須でもあります。

なお、タイトルロゴは、PC-8001版からの流用ではなく、Google Geminiに作らせたものを使っています。

割込

音

キーボード回り

デモ

スクリーンショット

M5Stackにせよ、なんにせよ、スクリーンショットを撮る機能は搭載されていないので、スクリーンショットが欲しかったら、それをプログラムに組み込むしかない。

めんどくさいファイル形式はコードがデカくなるだけなので、BMP形式で出力するものとする。 めんどくさいからパレットも省略して2)、M5.Displayの表示している16bppの画像をがさっと書き出す。 大したことないだろうと思ったら、ちょっとだけめんどくさかった。

ヘッダを作れ

BMP形式のファイルは、ファイルヘッダ+情報ヘッダ+パレット情報+画像データという構成になっている。 なので、ヘッダをつけてやればBMP形式ファイルの出来上がりである。 そう。キモはヘッダなのだ。

ヘッダの構成は次のようになっているらしい。

ファイルヘッダ

オフセットサイズ名称内容
+0x00002bfTypeファイルタイプ 'BM' (0x4d42)
+0x00024bfSize画像サイズ in bytes
+0x00062bfReserved1予約領域(0)
+0x00082bfReserved2予約領域(0)
+0x000a4bfOffBitsファイルの先頭から画像データまでのオフセット in bytes パレットがなければ 14 + 40)

情報ヘッダ

オフセットサイズ名称内容
+0x000e4biSize情報ヘッダサイズ 40 (OS/2だと違うらしい)
+0x00124biWidth画像幅 in pixels
+0x00164bcHeight画像高 in pixels … 正数の場合画像データは下から上に向かって、負数なら上から下向かって並ぶ
+0x001a2bcPlanesプレーン数 (1)
+0x001c2bcBitCount画素当たりのデータサイズ in bits
+0x001e4biCompression圧縮形式 無圧縮なら 0
+0x00224biSizeImage画像サイズ in bytes / bfSizeと同じ
+0x00264biXPixPerMeterX方向の画像密度 2インチモニタのM5Stackでは200dpiで7874になる(はず)
+0x002a4biYPixPerMeterY方向の画像密度 上に同じ
+0x002e4biClrUsed格納されているパレット数(0)
+0x00324biClrImportant重要なパレットのインデックス(0)

定義はシンプルだし、大してめんどくさくなさそうだけれど、いくつか注意しないといけないポイントがある。

エンディアン

ESP32は Little Endianである。 なんで、'BM'という文字コードを格納しようとして、bfType = ('B' « 8) | 'M'; とかすると、'MB'の順に格納されてしまう。 0x424d としても同じこと。

定義にそって、uint16_t bfType とするより、uint8_t bfType[2] として、'B', 'M'の順に格納する方がいいかもしれない。

アラインメント

昔からそうだけれど、CPUがメモリーにアクセスするとき、アラインメントに沿っていないとアクセスが遅くなったり、場合によっては、取り出しが分割されたりするので、気の利いたコンパイラだと、構造体の定義は、いい感じにアラインメントされるように処理される。

すると、ヘッダのデータが狙った場所に出てこなくなる。

なので、構造体を使ってヘッダーを作成するなら、ヘッダーを詰めて配置するようにコンパイラに指示しなければならない。 attribute3) は詰めて配置しろという指示になる。 大体が、組込みプロセッサであるESP32の場合、デフォルトがこっちでいいんじゃないかという気もするんだが。

    struct __attribute__((__packed__)) BMP_File_Header {
        uint16_t bfType;
        uint32_t bfSize;
        uint16_t bfReserved1, bfReserved2;
        uint32_t bfOffBits;
    };

    struct __attribute__((__packed__)) BMP_Info_Header {
        uint32_t biSize;
        uint32_t biWidth;
        uint32_t biHeight;
        uint16_t biPlanes;
        uint16_t biBitCount;
        uint32_t biCompression;
        uint32_t biSizeImage;
        uint32_t biXPixPerMeter;
        uint32_t biYPixPerMeter;
        uint32_t biClrUsed;
        uint32_t biClrImportant;
    };

なんで、こんなことを書いているかというと、まさにこれがゆえに、作成されたBMPファイルは「サポートされない形式」っていわれてしまったのだから。

1)
8bitカラー
2)
多分これはちょっとまずいかもしれないけれど
3)
packed

This website uses cookies. By using the website, you agree with storing cookies on your computer. Also, you acknowledge that you have read and understand our Privacy Policy. If you do not agree, please leave the website.

More information