【MSX】アドベンチャーゲーム制作記
初版 2025/07/13
改訂
1. はじめに
MSX1の画像変換ツールを試してみたところ、当時では考えもしなかったレベルでの画面表示ができたことに驚いた。
以前から、1980年代のコマンド入力式アドベンチャーゲームを作ってみたかったのだが、この手法を取り入れればグラフィックの問題も解決すると考え、試しに小規模なものを作ってみることにした。
このドキュメントは、その作成にあたり検討した内容を記載している。
なお、クロスコンパイラとしてz88dkを使用する前提となっている。z88dkは様々な初期処理やサポートを行うライブラリが含まれており、他のコンパイラを使用する場合はここに記載されていない部分で考慮が必要になることがあるので、注意すること。
2. 基本方針
まず、全体の設計として、以下の方針とした。
- 画面はTEXT1モード+グラフィックモード(SCREEN 1.5)を使用する これは、ターゲットをMSX1とするためと、グラフィックの高速性を両立させるためである。1
- ブロック1~2(画面上2/3)に画像、ブロック3(画面下1/3)にメッセージテキストという画面構成とする
MSXのSCREEN 2のVRAMは3つのブロックに分かれており、各ブロックで同じキャラクターコードに対し別々のパターンを定義できる。
この特性を利用し、画面上部はPCG定義によるグラフィックを、画面下部は文字を表示することとした。 - ROMカートリッジ形式(.rom)で作成する ROM形式はデータアクセスが高速であることがメリットなので、プレイ時のストレスが少ないと考え、この形式とした。2
- AIを活用する アドベンチャーゲームで一番の問題は、各シーンに表示される画像をどう作るかだが、自分で作成すると非常に時間がかかる上にクオリティが上げられないことがすぐに予測できたため、新しい試みとしてAIを活用することとした。 また、一部の製作補助ツールについても、AIで作成した。
3. 画像データ
(1) 元画像の準備
アドベンチャーゲームといえば、まずはグラフィックである。
このグラフィックは前述のとおり、MSXで直接作るのではなく、PCで作成した画像を元にデータを変換する手法を取った。
絵そのものは、自分で描くと労力の割によい画像が作れないので、基本方針にもある通り、AIを使用した。
chatGPT、grok、gemini、copilotを使い、いい感じにできてきたものを採用したが、画のトーンや色合いがバラバラなのと、MSX1の色に変換した時にメリハリがつかない淡い色合いになることが多かったため、画像に対し手動で色合いや明るさ、コントラストを補正している。
プロンプトだけでは意図したものができないこともあったが、その場合は手書きでラフ画像を作成し、それを元にリアル化を依頼することで生成した。
手書きのラフ画像は本当にラフで良く、ある程度イメージがあれば、この手法を取った方が効率的と考える。
以下、未使用画像だが、サンプルとして掲載する。
ラフ画:
生成結果:
なお、現状のAIは「同じ絵で一部だけ変化した画像」を作るのが苦手で、必ず指定箇所以外もどこか変化してしまう。
そのような画像を作る場合は、自分で直接編集した方が速いだろう。ある程度荒くても、次の変換で細かい常法は失われるため、あまり丁寧に加工しなくても良い。
(2) MSX SCREEN2形式への変換
MSXで表示するためのデータを作成する。
これについては、以下手順とした。
- 画像変換(Image converter) for MSX/MSX2/MSX2+で画像を変換、データを作成
- データをMSXpenで表示
- VRAMのブロック1と2のパターンジェネレートテーブル・カラーテーブルをBSAVEでディスクイメージに保存
- ディスクイメージをエクスポートし、PCに保存
- PCからディスクイメージ操作ツール3で上記2.で作成したバイナリファイルをPCに保存
3.で保存するデータについてだが、SCREEN 2のVRAMは、以下の構成となっている。
- パターンジェネレータテーブル
- ブロック1:
0x0000
~0x07FF
- ブロック2:
0x0800
~0x0FFF
- ブロック3:
0x1000
~0x17FF
- ブロック1:
- カラーテーブル
- ブロック1:
0x2000
~0x27FF
- ブロック2:
0x2800
~0x2FFF
- ブロック3:
0x3000
~0x37FF
- ブロック1:
今回は、各テーブルのブロック1~2のデータを使用したいので、保存するアドレスは以下となる。
- パターンジェネレータテーブル:
0x0000
~0x0FFF
(0x1000
バイト) - カラーテーブル:
0x2000
~0x2FFF
(0x1000
バイト)
この情報を基に、1.で変換時に生成されたBASICプログラムに対し、以下80~90行目を追加し、データを取得した。
10 SCREEN 2
20 CLS
30 'VDP(9)=VDP(9) OR &H22:REM NO USE TRANSPARENT COLOR & SPRITE OFF
40 'VDP(10)=VDP(10) OR &H80:REM Y=212
50 BLOAD "NAME.SC2",S
60 'COLOR=RESTORE
70 IF INKEY$="" THEN 70
80 BSAVE "SC00_PTN.BIN", &H0000, &H0FFF, S
90 BSAVE "SC00_COL.BIN", &H2000, &H2FFF, S
100 END
(3) プログラムで表示する
次に、プログラムで表示する。
上記5.でPCに保存したバイナリファイルは、z88dkではアセンブリソースに以下のように書くことで、コンパイル時にファイルの内容がそのまま取り込まれる。
なお、Cプログラムからアセンブリソースのラベルを参照する場合、ラベル名の先頭に「_」を付与しておく必要があるルールがある点に注意する。
PUBLIC _PTNTBL, _COLTBL
_PTNTBL:
INCBIN "../../resources/SC00_PTN.bin"
_COLTBL:
INCBIN "../../resources/SC00_COL.bin"
表示は、上記で取り込んだデータをVRAMへブロック転送するだけだ。
Cプログラムからアセンブリソースのラベルへの参照は、先頭の「_」は付けずに指定する。
#include <msx.h>
#include <msx/gfx.h>
extern uint8_t PTNTBL[];
extern uint8_t COLTBL[];
void main()
{
// set screen 1.5
set_color(15, 1, 1);
set_mangled_mode();
// vram write
vdp_vwrite(PTNTBL, 0x0000, 0x1000);
vdp_vwrite(COLTBL, 0x2000, 0x1000);
while (0 == 0){}
}
この方法で表示したのが、冒頭の画像となる。
しかし、非圧縮のベタデータであるため、サイズが8KB(パターンテーブル4KB+カラーテーブル4KB)とMSXとしれは大きなサイズとなり、32KB ROM では2~3画面分のデータしか入れられない計算になる。
これではゲームにならないので、対策を練った。
(4) 画像データの縮小
まずは、基本的な対策として、表示サイズを小さくすることにした。 解像度の低いMSXでは、あまり小さくすると判別しにくい画像になるが、色々試した結果、19×14キャラクター(152×112ドット)、元画像の約60%の大きさが妥当と判断とした。
まず、元画像(1,024x768)を60%縮小する。
この時の画素数は、後の手順で余分なデータは切り捨てられるので、だいたいで構わない。
後の処理がしやすいよう、縮小した画像を左上に配置した1,024x768の画像を作成し、MSXの画像へ変換、データを取得する。
生成したデータから不要部分(周囲の黒い部分)を削除する。
これについては、まずデータの並びを理解する必要がある。
データは、横8ドットを1バイト、それが8バイト並んで1キャラクター(下図太枠)を構成する。
※先頭アドレスからのオフセットを記載
画面は3ブロックに分割されており、1ブロックあたり8行となる。
今回は14行分の画像にするため、2ブロック分の処理を行う必要がある。
-
ブロック1:先頭から19キャラクタ=152バイト(19×8)を保存、以後は切り捨てる。これを8行分繰り返す
-
ブロック2:ブロック1と同様の処理を6行分繰り返す
これを行うPythonのツールは、AIに仕様を伝えて生成してもらった。
また、ツールで処理しやすいように、MSXpenでデータ保存する際も、各ブロックごとに行うようにBASICプログラムを変更した。
以上で、キャラクターコード0x00
から連続したコードにパターンが設定されるようになったので、あとはパターンネームテーブルの表示したい位置にキャラクターコードを書き込んでおけば良い(非表示の部分は未定義のキャラクターコードで埋めておく)。
この縮小の対応の結果、以下のデータサイズとなった。
- 縮小前:8.00KB(パターンジェネレータテーブル4.00KB、カラーテーブル4.00KB)
- 縮小後:4.15KB(パターンジェネレータテーブル2.07KB、カラーテーブル2.07KB)
机上の計算よりもサイズが若干小さくなったが、期待通りとなった。
しかし、これでもまだデータサイズが大きいため、追加の対応としてデータ圧縮を行うこととした。
(5) データ圧縮
圧縮・展開プログラムは、既に色々なものがあるため、自作はせずに既存のものを使用することにした。
中でも、ライセンスの問題がなく、容易に組み込めそうなものとして、Pletterを選択した。
展開処理のソースはZ80アセンブリで書かれているが、sjasm用に書かれていたため、z88dk(z80asm)用に一部ソース修正を行っている4。
データ圧縮後のデータ量は以下で、非圧縮時に比べ平均70%の削減効果となった。
- 圧縮前:4.15KB(パターンジェネレータテーブル2.07KB、カラーテーブル2.07KB)
- 圧縮後:2.19KB(パターンジェネレータテーブル1.49KB、カラーテーブル0.70KB)
4. メッセージ
(1) フォントパターン
フォントは、美咲フォントを参考に、nMSXTiles5で打ち込んだ。
登録したのは記号、数字、英大文字、ひらがな、カタカナとしたが、後述の多言語対応の際に小文字も追加している。
初期処理で一度定義できれば良いため、データは画像と同様に圧縮した。
なお、未定義のキャラクターがまだあるので、使用頻度の高い漢字を定義すれば漢字表示も可能である。
(2) メッセージデータ
まず、PC上のデータをMSXの文字コードに変換する必要があるため、Pythonで変換ツールを作成した。
これもAIで作成してもらったが、MSXのキャラクターコードを理解してもらえなかったため、変換テーブルは自前で作成している。
# MSXの文字コードマッピング
convert_map = {
' ' : 0x20, '!' : 0x21, '\"': 0x22, '#' : 0x23, '$' : 0x24,
'%' : 0x25, '&' : 0x26, '\'': 0x27, '(' : 0x28, ')' : 0x29,
'*' : 0x2A, '+' : 0x2B, ',' : 0x2C, '-' : 0x2D, '.' : 0x2E,
'/' : 0x2F,
'0' : 0x30, '1' : 0x31, '2' : 0x32, '3' : 0x33, '4' : 0x34,
'5' : 0x35, '6' : 0x36, '7' : 0x37, '8' : 0x38, '9' : 0x39,
':' : 0x3A, ';' : 0x3B, '<' : 0x3C, '=' : 0x3D, '>' : 0x3E,
'?' : 0x3F, '@' : 0x40,
'A' : 0x41, 'B' : 0x42, 'C' : 0x43, 'D' : 0x44, 'E' : 0x45,
'F' : 0x46, 'G' : 0x47, 'H' : 0x48, 'I' : 0x49, 'J' : 0x4A,
'K' : 0x4B, 'L' : 0x4C, 'M' : 0x4D, 'N' : 0x4E, 'O' : 0x4F,
'P' : 0x50, 'Q' : 0x51, 'R' : 0x52, 'S' : 0x53, 'T' : 0x54,
'U' : 0x55, 'V' : 0x56, 'W' : 0x57, 'X' : 0x58, 'Y' : 0x59,
'Z' : 0x5A,
'[' : 0x5B, '\\': 0x5C, ']' : 0x5D, '^' : 0x5E, '_' : 0x5F,
'`' : 0x60,
'a' : 0x61, 'b' : 0x62, 'c' : 0x63, 'd' : 0x64, 'e' : 0x65,
'f' : 0x66, 'g' : 0x67, 'h' : 0x68, 'i' : 0x69, 'j' : 0x6A,
'k' : 0x6B, 'l' : 0x6C, 'm' : 0x6D, 'n' : 0x6E, 'o' : 0x6F,
'p' : 0x70, 'q' : 0x71, 'r' : 0x72, 's' : 0x73, 't' : 0x74,
'u' : 0x75, 'v' : 0x76, 'w' : 0x77, 'x' : 0x78, 'y' : 0x79,
'z' : 0x7A,
'を': 0x86,
'ぁ': 0x87, 'ぃ': 0x88, 'ぅ': 0x89, 'ぇ': 0x8A, 'ぉ': 0x8B,
'ゃ': 0x8C, 'ゅ': 0x8D, 'ょ': 0x8E, 'っ': 0x8F,
'あ': 0x91, 'い': 0x92, 'う': 0x93, 'え': 0x94, 'お': 0x95,
'か': 0x96, 'き': 0x97, 'く': 0x98, 'け': 0x99, 'こ': 0x9A,
'さ': 0x9B, 'し': 0x9C, 'す': 0x9D, 'せ': 0x9E, 'そ': 0x9F,
'。': 0xA1, '「': 0xA2, '」': 0xA3, '、': 0xA4, '・': 0xA5,
'ヲ': 0xA6,
'ァ': 0xA7, 'ィ': 0xA8, 'ゥ': 0xA9, 'ェ': 0xAA, 'ォ': 0xAB,
'ャ': 0xAC, 'ュ': 0xAD, 'ョ': 0xAE, 'ッ': 0xAF,
'ー': 0xB0,
'ア': 0xB1, 'イ': 0xB2, 'ウ': 0xB3, 'エ': 0xB4, 'オ': 0xB5,
'カ': 0xB6, 'キ': 0xB7, 'ク': 0xB8, 'ケ': 0xB9, 'コ': 0xBa,
'サ': 0xBB, 'シ': 0xBC, 'ス': 0xBD, 'セ': 0xBE, 'ソ': 0xBF,
'タ': 0xC0, 'チ': 0xC1, 'ツ': 0xC2, 'テ': 0xC3, 'ト': 0xC4,
'ナ': 0xC5, 'ニ': 0xC6, 'ヌ': 0xC7, 'ネ': 0xC8, 'ノ': 0xC9,
'ハ': 0xCA, 'ヒ': 0xCB, 'フ': 0xCC, 'ヘ': 0xCD, 'ホ': 0xCE,
'マ': 0xCF, 'ミ': 0xD0, 'ム': 0xD1, 'メ': 0xD2, 'モ': 0xD3,
'ヤ': 0xD4, 'ユ': 0xD5, 'ヨ': 0xD6,
'ラ': 0xD7, 'リ': 0xD8, 'ル': 0xD9, 'レ': 0xDA, 'ロ': 0xDB,
'ワ': 0xDC, 'ン': 0xDD,
'゙': 0xDE, '゚': 0xDF,
'た': 0xE0, 'ち': 0xE1, 'つ': 0xE2, 'て': 0xE3, 'と': 0xE4,
'な': 0xE5, 'に': 0xE6, 'ぬ': 0xE7, 'ね': 0xE8, 'の': 0xE9,
'は': 0xEA, 'ひ': 0xEB, 'ふ': 0xEC, 'へ': 0xED, 'ほ': 0xEE,
'ま': 0xEF, 'み': 0xF0, 'む': 0xF1, 'め': 0xF2, 'も': 0xF3,
'や': 0xF4, 'ゆ': 0xF5, 'よ': 0xF6,
'ら': 0xF7, 'り': 0xF8, 'る': 0xF9, 'れ': 0xFA, 'ろ': 0xFB,
'わ': 0xFC, 'ん': 0xFD,
}
メッセージデータは、当初はCソースでメッセージIDの名前を付けた配列の形で定義していた。
しかし、多言語対応(後述)に伴い言語を切り替える必要が発生したため、メッセージIDの下に各言語のデータを定義するようにした。
具体的には、アセンブリソースの形でメッセージIDのラベルには各言語データへの参照を定義、その後に各言語用のメッセージデータを記述した。
メッセージ表示処理にはメッセージIDのアドレスが渡され、そこから判定された言語に合わせたアドレスを取得し、メッセージデータを参照する。
以下、データの一部抜粋を掲載する。
_MESSAGE01000:
dw _MESSAGE01000_J
dw _MESSAGE01000_E
_MESSAGE01000_J:
db 0x97, 0x96, 0xDE, 0xE2, 0x98, 0xE4, 0xA4, 0x91, 0xE5, 0xE0, 0xEA, 0xA4, 0xE1, 0x92, 0x9B, 0xE5, 0xED, 0xF4, 0x28, 0x52, 0x4F, 0x4F, 0x4D, 0x29, 0xE6, 0x92, 0xE0, 0xA1, 0x00
_MESSAGE01000_E:
db 0x54, 0x68, 0x65, 0x20, 0x6E, 0x65, 0x78, 0x74, 0x20, 0x74, 0x68, 0x69, 0x6E, 0x67, 0x20, 0x79, 0x6F, 0x75, 0x20, 0x6B, 0x6E, 0x6F, 0x77, 0x2C, 0x20, 0x79, 0x6F, 0x75, 0x0A
db 0x61, 0x72, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x61, 0x20, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x20, 0x72, 0x6F, 0x6F, 0x6D, 0x2E, 0x00
(3) シナリオデータ
シナリオデータの構造は、後述のプログラムと同時にAIに設計してもらった。
大きくシーンデータと選択肢データの構成になっており、どちらもCの構造体として定義している。
シーンデータは、最終的に以下の構造となった。
// シーン定義
typedef struct {
SceneId sceneId; // シーンID
uint16_t flag_to_check; // シーンの分岐に使うフラグ(0で分岐なし)
SceneId next_sceneId_if_unset; // フラグが未設定のときのシーンID
SceneId next_sceneId_if_set; // フラグが設定済のときのシーンID
uint8_t graphic_bank; // グラフィックデータのバンク
uint8_t *graphic_ptn0; // グラフィックデータ(パターンジェネレータテーブル PAGE0)
uint8_t *graphic_ptn1; // グラフィックデータ(パターンジェネレータテーブル PAGE1)
uint8_t *graphic_col0; // グラフィックデータ(カラーテーブル PAGE0)
uint8_t *graphic_col1; // グラフィックデータ(カラーテーブル PAGE1)
uint8_t *message; // シーンの最初に表示するメッセージ
uint8_t *choices;
} Scene;
なお、配列1要素に1画面が対応しているため、例えば単純なシーン分岐だけを行う場合、分岐先のシーンを別要素として定義する必要がある。
メモリ的に無駄があるが、まずはシンプルに現在の形としている。
選択肢データは、各画面で受け付けるコマンドとそれに対するメッセージ、及び変化させるフラグの情報や遷移先のシーン名を持たせている。
// 選択肢定義
typedef struct {
uint16_t required_flag; // この選択肢が表示・実行できる条件(0で条件なし)
const char *commands[5]; // プレイヤーの入力コマンド(1選択肢につき最大5コマンド)
uint16_t flag_to_check; // メッセージの分岐に使うフラグ(0で分岐なし)
uint8_t *message_if_unset; // フラグが未設定のときの表示メッセージ
uint16_t set_flag_if_unset; // 実行後にセットするフラグ(フラグ未設定時)
SceneId next_sceneId_if_unset; // 次のシーン名(空で遷移なし)
uint8_t *message_if_set; // フラグが設定済のときの表示メッセージ
uint16_t set_flag_if_set; // 実行後にセットするフラグ(フラグ設定時)
SceneId next_sceneId_if_set; // 次のシーン名(空で遷移なし)
} Choice;
コマンドは「言葉探し」になりすぎないよう、意味が同じような複数のコマンドを最大5つ定義できるように考慮した。
特に辞書化などは行わずにベタで定義しているため、ソースコードを見るとすぐに受け付けるコマンドがわかってしまう。
6. プログラム
(1) メイン処理
ゲームに速度性がそれほど求められないこと、テキストやシナリオのデータや判定の扱いやすさから、メインロジックはCで作ることとした。
個人的にはこれまでアドベンチャーゲームを作成した経験がなかったため、AIに基本形を作成してもらった。
アプローチとしては、まずLinuxコンソールアプリの形式でコマンド入力式のテキストアドベンチャーゲームの形を作り、そこからフラグ管理、複数コマンドの対応、と仕様を追加していった。
一通り動作を確認し、これをベースとしてMSX向けの画像表示やメッセージ表示など、細かい修正と拡張は手で行っていった。
(2) サブルーチン
サブルーチンは入出力関連の処理が多く、プログラムの書きやすさ、プログラムサイズの削減目的から、アセンブリで記述した。6
作成した主な処理は以下となる。
-
メッセージ表示
与えられたポインタ(アドレス)のメッセージデータを終端まで表示する処理。
日本語の濁点・半濁点をそのまま横並びで表示すると文字間隔が空いて読みにくくなるため、対象文字の上部に表示するよう考慮を入れている。
また、メッセージデータに制御コマンドを含めることで、キー入力待ちやサウンド再生を同時に行えるようにしている。 -
コマンド入力
コマンド入力を行う処理。
特に特殊な制御は不要のため、BIOSのCHGET
をそのまま使用し、カーソルキーなどの入力制御のみを行った。
なお、C-BIOSではキーリピート値が1固定で設定されているため、非常に入力しにくくなっている。 -
キー入力待ち処理
PUSH SPACE
のメッセージを表示し、スペースキーの入力を待つ処理。
この処理は、複数ページにまたがるメッセージ表示時にメッセージ表示処理から呼ばれる他、ヘルプメッセージ表示時にも呼ばれる。 -
メッセージエリアクリア処理
画面の下1/3を消去する処理。
この処理も、キー入力待ち処理と同様、メッセージ表示処理以外にも利用される。 -
ABURI GAMESロゴ表示
これは、当初は無かったが、言語選択を可能にするために最後に追加した。
ゲーム起動直後、パターンジェネレータテーブルの定義などを行う前に表示するため、16x16スプライトを2倍の大きさで表示している。
この2倍角でのスプライト表示は、制御するBIOSが用意されていなかったため、VDPを直接操作している。(コードコメントのSI
が1ビット目、MAG
が0ビット目となる)
; set sprite mode
di
ld a, (WRVDP)
inc a
ld c, a ; set control register#1(vdp write) port
ld a, 0b01100011 ; SI=1(16x16), MAG=1(double size)
nop
out (c), a
nop
ld a, 0b10000001 ; mode register #1
nop
out (c), a
nop
ei
7. その他
(1) メガROM対応について
もともと小規模なものを、と作成していたが、さすがに32KB ROMでは容量が足りず、想定したシナリオの半分までしか入れられなかった。
そのため、開発途中でメガロムを使用することとした。
メガロムの仕組みとしては、特定のアドレスにバンクの値を書き込むことで、ROMに搭載されたバンクコントローラが対応したアドレスへのアクセスを切り替えてくれるというもの。
幸い、z88dkはメガロムフォーマットでの.romファイル出力をサポートしており、容易に対応できた。
今回は、扱うデータサイズが大きいことから、ASCII16K形式を採用した。
まず、バンクに配置したいセクションに「BANK_nn」を記述する。
こうすることで、その後定義したデータはそのバンクに配置される。
SECTION BANK_01
; TITLE
_TITLE_PTN_BLK0:
INCBIN "../../resources/bank01/title_ptn_blk0.plet5"
_TITLE_PTN_BLK1:
INCBIN "../../resources/bank01/title_ptn_blk1.plet5"
_TITLE_COL_BLK0:
INCBIN "../../resources/bank01/title_col_blk0.plet5"
_TITLE_COL_BLK1:
INCBIN "../../resources/bank01/title_col_blk1.plet5"
:
:
バンクの切り替えは、特定のアドレスにバンク番号を書き込むことで行う。
これには、以下のサブルーチンを作成した。
(アセンブリからも利用できるよう、呼び出し用のラベルは分けて定義している)
; ============================================================
; void switch_bank(uint8_t bank_no) __naked;
;
; enter : uint8_t/a switch bank no
; exit : void
; ============================================================
_switch_bank:
di
ld hl, 2
add hl, sp
ld a, (hl) ; get arg value
switch_bank:
ld hl, 0x7000 ; ASCII16 Mapper control port(0x8000~0x8fff)
ld (hl), a
ei
ret
実際に切り替える際は、上記のswitch_bank
をバンク番号を与えて呼び出すだけである。
// データを展開し表示する
switch_bank(scene->graphic_bank);
unpack(scene->graphic_ptn0, temp);
vdp_vwrite(temp, VRAM_PTN_GENR_TBL1, 0x04c0);
unpack(scene->graphic_ptn1, temp);
vdp_vwrite(temp, VRAM_PTN_GENR_TBL2, 0x0390);
なお、今回のプログラムでのメモリ利用イメージは、以下となっている。
(2) 多言語対応について
メガロムに対応したことで容量に余裕ができたのと、英字フォントもあるので、せっかくならばと英語に対応することにした。
仕組みとしては、起動時にBIOSの地域コード(0x002b
、0x002c
)を読み、いずれもゼロなら日本、以外は英語7として判定、結果をワークに保存。
メッセージデータの表示制御については前述の「4. メッセージ」で記載しているため、そちら参照。
なお、日本語では改行時に2行送っていたが、英語では見にくくなるため1行だけ送るよう、制御を追加した。
また、海外のユーザーでも日本のMSXを使用している方がいるとのことで、本体のバージョンだけで判定するのではなく、起動した時にSELECTキーを押すことで英語モードに変更できるようにもした。
(3) ヘルプメッセージの追加
コマンド入力式の操作になれていないユーザーに対し、簡単なヘルプメッセージを表示するようにした。
これについては、簡単な処理であるため詳細な説明は割愛するが、全シーンを通して共通で表示するため、コマンド判定時に通常のコマンドとは別に固定でHELP
の文字判定と、ヘルプメッセージのみを表示する特殊処理を追加して対応している。
8. 総括
今回のアドベンチャーゲームでは、ROM媒体で作成することで瞬間的な表示が可能になり、またメガロムフォーマットにすることである程度大きな規模のものが作れることがわかった。
ゲームシステム的には現状では古臭く単調であるため、今の時代に合わせて追加要素を検討、また改善する余地がある。(海外ユーザーは、BGMが欲しい、持ち物を見たい、コマンド入力が手間、などの意見があった)
この結果をベースに引き続き、本格的な規模のものを作成したいと思う。
プログラム的な観点では、z88dkでC言語の学習がてら作り始めたが、画面や音といったハードウェアに近い処理はアセンブリで書いた方がプログラムサイズ的な効率が良く、結局半分以上の処理をアセンブリで書いてしまった。
ただ、バイナリサイズがだいぶ削減できたので、8ビット機においては、C言語では全体の制御に関するコードを、アセンブリでは細かいサブルーチンなどのコードを書いた方が効率が良いかもしれない。
まだC言語に慣れてだけかも知れないので、この辺りは試行錯誤してみたい。