2024年03月23日
【ツール制作】MMLコンパイラ(通常版) 第2回
MMLコンパイラ(通常版) 第2回です。
さて、「MMLコンパイラ(通常版)」で「T」(テンポ)コマンドが扱えるようにします。
MSXに限らず「音楽演奏ルーチン」を制作する時、必ず問題になる「テンポずれ」。
過去に制作した「MMLコンパイラ(高機能版)」でも相当悩みましたので、よく覚えています。
今更ながら、テンポは1分間に4分音符を何回鳴らすかの設定です。
1分間の走査線割込み発生回数(60分の1秒ごとに発生するので、1分間で3,600回。)をテンポで割った時に小数点以下が発生します。
しかし当然割り算ですので、割り切れない場合が存在します。
この時の割り切れない端数が「テンポずれ」の原因です。
具体的に説明しますと…、
小数点以下の処理は、小数点以下だけを足していき1を超えたところで音長に1加える等で、音長を調整します。
例えば「T40」の16分音符の音長は「22.5」となります。
それを2回鳴らす際、小数点以下は切り捨てられるので調整しないと「22」・「22」となります。
そこで、別途小数点以下をワークで足していき(「0.5」・「0.5+0.5」)、1を超えた2音目の音長に「1」を足して、「22」・「23」とします。
ただ、割り算の商がBASICで扱える小数点以下の桁(確か15桁)を超えるテンポが指定された場合に、加算しても小数点以下15桁目以降がおかしくなります。
例えば「T41」の32分音符の音長は「21.951219512195・・・」と割り切れない音長となり、2回加算しても正確な値にはなりません。
これが「テンポずれ」となります。
この「テンポずれ」を、何らかの方法で調整しなければなりません。
「MuSICA」をはじめ、各種音楽演奏ルーチンは、当然なんらかの処理を行っています。
例えば1分間の総音長を音符側で算出し、1分演奏したらその時残っている端数と総音長で調整する。など、方法はいくつか考えられます。
しかし、元々私は「T」コマンドがなぜあんなに細かく設定できるのか不思議でした。
1テンポの違いを聴き比べずに感じ取れる人がどれだけいるのだろう…。
…と、いう事で「MMLコンパイラ(通常版)」では、テンポずれが発生しないテンポ(64分音符が小数点以下15桁までに割り切れるテンポ)に強制的に調整します。
例えば、「T41」と指定したら、一番近いテンポずれしない「T40」にデータ上強制的に変更します。
過去に制作した「MMLコンパイラ(高機能版)」もこの方式でした。
高機能版は、コンパイラもマシン語でした。
参考までに、マシン語での小数点以下の扱いは下図になります。
バイト数を増やせば増やすほど、より「1」に近づける事が出来ます。
「MMLコンパイラ(高機能版)」では、少数点以下の計算は確か3バイトでやっていたかと記憶しています。
2024年03月22日
祝! 一周年!!
2024年03月21日
【ツール制作】MMLコンパイラ(通常版) 第1回
MMLコンパイラ(通常版) 第1回です。
「ゲーム制作」カテゴリで、2作品作成してきましたが、意外と時間かかっているのが「BGM」の作曲。
電子ピアノで作曲し、MMLに起こし「簡易MMLコンパイラ Ver.2.0」に登録してコンパイルしているのですが、
・3チャンネル纏めてコンパイルできない。(チャンネル毎に登録してコンパイルしなければいけない。)
・BGMの確認がリアルタイムにできない。(コンパイルしたデータをゲームに登録しないと演奏できないため、ちゃんとコンパイルできたかの確認に手間がかかる。)
など、かなり不便です。
このツールを使って3〜4曲も作曲するとなると、そりゃ時間かかるわ…。
…と、言う事で、「MMLコンパイラ(通常版) Ver.1.0」を制作します。
簡易版に比べ強化される機能は、
・3チャンネル纏めてコンパイル可能。
・テンポ「T」の指定が可能。
・コンパイルし終わると、演奏が開始。
・付点「.」が最大2つから最大4つに増加。
・ボリューム「V」の指定が可能。(併せて「)」、「(」でボリューム増減。)
・エンベロープ波形「S」、周期「M」の指定が可能。
・ノイズ周波数「Z」の指定が可能。
など、大幅な機能アップです。
「通常版」なので、せめてMSX標準MMLくらいはサポートしなきゃと言う事で、この機能に決めました。(「N」コマンドは過去に使ったことがないので不要と考え不採用。)
ただ、やはりコンパイラ部がBASICなので遅いと思われます。
「高機能版」を制作することがあるなら、「MuSICA」のようにエディタ部を用意し、コンパイラもマシン語で制作し、コンパイルから演奏まで高速処理できるようにしたいですね。
では、制作に取り掛かっていきます。
2024年03月20日
次回作選考
さて、そろそろ次回作について考えたいと思います。
「ロールプレイングゲーム」、「アドベンチャーゲーム」と、比較的腰据えてやるタイプのゲームが続きました。
一度、「暇つぶしにちょっとやってみようか」と思えるゲームを作りたいなと思いました。
と、なると「面クリア型のアクションゲーム」が一番近いのかな?
まぁ、簡単な「パズルゲーム」でもいいけど、「パズルゲーム」を手軽にプレイするっていう感覚が私にはない。
さて、「面クリア型のアクションゲーム」や「パズルゲーム」の制作をする時に一番悩むもの。
それは、「ゲームシステム」です。
「ロールプレイングゲーム」や「アドベンチャーゲーム」はプログラミングレベルとしては高いのですが、ゲームシステムとしては過去に「ほぼ完成形」となる作品がでており、ほぼ確立したものが存在します。
ストーリーに重きが置かれ、ゲームシステムがほぼ同じでも、別のゲームと認識されています。
私も「ゲームシステム」は既存のものを踏襲したので一切悩まずに、ストーリーやプログラミングに集中出来ました。
しかし、「面クリア型のアクションゲーム」や「パズルゲーム」は、「ゲームシステム」がそのゲームそのものであり、唯一無二のシステムとなります。
言い換えれば、ゲームシステムに重きが置かれ、キャラクタやストーリーを変えても、そのゲームシステムを使うと、「xxxの真似じゃん」となります。
「パックマン」や「マッピー」、「ディグダグ」などのアクションゲーム、「倉庫番」、「ロードランナー」などのパズルゲームを見れば分かりますが、そのゲーム独自のシステムで、逆にシステムを見ればゲームタイトルが思い出されます。
しかしいかんせん、既に無数の「面クリア型のアクションゲーム」や「パズルゲーム」が制作公開されており、今から全く新しいゲームシステムを作り出せと言うのは困難です。
と、なるとやはり既存のシステムをどうしても真似る形となるのは仕方ないのかな…。
最近「面クリア型のアクションゲーム」が出なくなったのも、こういったことが背景にあるのかも。(著作権など。)
まぁ、もうちょっと考えてみます。
2024年03月19日
過去製作ゲーム公開について
さて、私の過去製作作品の公開について「MuSICA」を利用している事で難しくなりました。(2024年03月02日記事参照「「MuSICA」の著作権」)
まさしく、次作の「TETRIS-SPECIAL」から「MuSICA」を利用しています。
公開を止めようかとも思いましたが、公開予定の作品がまだ9作品も残っている…。(2024年01月26日記事参照「公開状況」)
問題は「MuSICA」だけなので、BGMさえ何とかすれば解決します。
そこで、取れる対策としては、
@ BGMなし版を公開する
A 「MuSICA」に代わるツールと差し替える
B 自作「BGM演奏ルーチン」と差し替える
以上の3点くらいでしょうか…。
@は、論外。
Aは、良いツールが揃っていますが、やはりハンドアセンブルな私としては、ゲームプログラムのアドレスに影響のある選択はしたくない。(と、言うか「.BIN」のソースがないので、改変は不可能。)
それにやはり高機能すぎて容量が大きい。(これは「MuSICA」も同じでしたが…。)
となると、残るはBになります。
まぁ、今ちょうど「ゲーム制作」一息ついているところなので、いいタイミングではあります。
それに、ゲームに利用したBGMの元MMLは、「MuSICA」ディスクに残っているので、FM音源複数音の中から2音選択する形で流用できます。
…と、言う訳で自作の「BGM演奏ルーチン」に差し替える事にします。
ただ、現状の「簡易MMLコンパイラ」と「BGM演奏ルーチン」では、生産性が非常に悪い…。
せめて、MSX標準MMLは全て使えるようにしたいのと、コンパイルを1チャンネル毎ではなく全チャンネル纏めてコンパイルでき、そのBGMをすぐに確認できるようにしたいですね。
そこで「簡易MMLコンパイラ」と「BGM演奏ルーチン」のバージョンアップに取り掛かります。
これで「簡易版」から「通常版」となります。
この制作は、「ゲーム制作」カテゴリの一つとして扱う事にします。(ツールとして扱うので、新作連番は無し。)
因みに高機能版は、「MuSICA」のようにエディタ画面付きで「@」コマンドで各チャンネル毎にエンベロープ周期が設定でき音色の制作ができました。
ただ、この高機能版、制作した記憶と実際使って作ったBGMは残っているのですが、プログラム本体(フロッピーディスク1枚だった…。)が見付かりませんでした。(2023年03月24日記事参照「ディスクの中が見れた…」)
こちらもいずれ再制作したいですね。
2024年03月18日
Laboratoryテーマ8「32枚を超えてSPRITEを表示させたい(非走査線割込)」
Laboratoryテーマ8「32枚を超えてSPRITEを表示させたい(非走査線割込)」です。
MSX1のSPRITEは非常に便利ですが、32枚までしか使えません。
ゲームによっては32枚じゃ足らない事もしばしば…。
そこで32枚を超えて表示させる方法を検討します。
実は技術的には走査線割込みを使って表示させることは可能です。
HRAさんが公開されてますので、ご覧ください。
私の方法は走査線割込みは使いません。(結果的には使いますが…。)
横スクロール系のゲームを作るなら必須となる前回のテーマ、Laboratoryテーマ7「水平ライン4枚(MSX2は8枚)を超えてSPRITEを表示させたい」。
その拡張で可能です。
組み込む必要のあるロジックを、少しの拡張するだけで32枚を超えて表示させる機能が追加できるので、メモリ的なメリットも大きいと思います。
要は簡単。
仮想スプライトアトリビュートテーブルを64キャラクタ分用意します。(&HC900~&HC9FF)
シャッフルする際は前回同様、32/64キャラクタ分シャッフルし表示します。
あとは素数を加算し、32以上になったら32引いていたところを、64以上になったら64引くに変更するだけです。
そう、この表示される32キャラクタは64キャラクタの中から素数入替法によって選ばれますので、何回かの表示の間に必ず64キャラクタ全て表示されます。
結果はこちら。
点滅していて見辛いですが、64キャラクタ表示されています。(横16キャラクタ×縦4キャラ=64キャラクタ)
キャラクタ表示数をカウントし、32キャラを超えたらこちらのロジックに切り替えるだけで済みます。
(切り替えないと32キャラクタ以下でも点滅してしまいます。)
走査線割込みが技術的に難しいと言う方はどうぞ。
サンプルプログラムです。
「OVER32.BAS」【ダウンロード】
BASIC部です。
2024年03月17日
Laboratoryテーマ7「水平ライン4枚(MSX2は8枚)を超えてSPRITEを表示させたい」そのB
Laboratoryテーマ7「水平ライン4枚(MSX2は8枚)を超えてSPRITEを表示させたい」そのBです。
前回、実際に点滅する仕組みを作りましたが、ゴミが表示されるため、その解消法を検討します。
これも例えば、表スプライトアトリビュートテーブル(&H1B00)表示中に、裏スプライトアトリビュートテーブル(&H1C00)を書き換え、裏の書換えが終わったところで、表裏のスプライトアトリビュートテーブルのアドレスを書き換える(ページングと呼ばれる手法。)など、いくつかの方法があります。
しかし、シャッフルしながらVRAMへ転送をかけていると、タイミング取りの「NOP」の余り時間が無駄になり積み重なるとそれなりの無駄時間になります。
そこで、私のやり方は仮想スプライトアトリビュートテーブル(&HC900)とは別に、シャッフル用の仮想スプライトアトリビュートテーブル(&HC980)をもう一個用意します。
仮想スプライトアトリビュートテーブルからシャッフル用仮想スプライトアトリビュートテーブルに、メモリ上でシャッフル処理を行い、シャッフルした結果をニーモニックでいうところの「OTIR」命令(連続転送)でVRAM上のスプライトアトリビュートテーブルに一括転送します。
VRAMを扱うのはこの一括転送時のみ、シャッフル処理もメモリ間で行うため、早いです。
ただ、128バイト余分にメモリを消費します。
メモリに余裕があるゲームを作るならこちらがお奨めです。
メモリがない時は、上記ページングでやるしかないかな?
これで綺麗に表示されるようになりました。
このロジックを走査線割込みの中に組み込めば、普通にゲームを作るだけでSPRITEが水平方向に5枚以上並べば勝手に点滅してくれます。
1/60毎に毎回128バイトVRAMへ転送しているとは思えない速度ですよね。
走査線割り込みを使用したサンプルプログラムです。
「SPRITE.BAS」【ダウンロード】
戦闘機を動かす部分、スペースバーで全機止まります。
BASIC部です。
これで、横スクロールアクションも作れるようになりました。
2024年03月16日
Laboratoryテーマ7「水平ライン4枚(MSX2は8枚)を超えてSPRITEを表示させたい」そのA
Laboratoryテーマ7「水平ライン4枚(MSX2は8枚)を超えてSPRITEを表示させたい」そのAです。
前回、素数入替法の説明をしましたので、具体的にプログラムを組んでいきます。
このSPRITE面の入替は常時行っています。
よって、処理が早くないとゲーム全体の速度に影響します。
そこで、処理の遅い「BIOS」は使いません。
ニーモニックでいうところの「OUT」命令などを使います。
HRAさんのページに「スプライトが5枚以上並んだときに点滅表示で擬似的に5枚以上見えるようにする方法」の具体的なロジックの説明がありましたので(流石です。)、それを基に組んでみました。
まず、メモリ上に仮想スプライトアトリビュートテーブルを用意します。
この仮想スプライトアトリビュートテーブルから素数入替法に則り、VRAM上のスプライトアトリビュートテーブルへ1面につき4バイトずつシャッフルしながら32面分転送していきます。
こちらがその結果。
点滅はしていますが、それ以外のところにもなんだかゴミみたいなものが表示されてる…。
これはVDPの表示期間中にVRAM上のスプライトアトリビュートテーブルを書き換えると、意図しない中途半端な状態で表示が実行されてしまうためです。
では、次回は解消方法を検討します。
2024年03月15日
Laboratoryテーマ7「水平ライン4枚(MSX2は8枚)を超えてSPRITEを表示させたい」その@
「3Dダンジョンロールプレイングゲーム」完成後に、Laboratoryテーマ7「水平ライン4枚(MSX2は8枚)を超えてSPRITEを表示させたい」をする予定だったのですが、余りにも時間空け過ぎてすっかり忘れてました。
Laboratoryテーマ7「水平ライン4枚(MSX2は8枚)を超えてSPRITEを表示させたい」です。
MSX1で横スクロールのシューティングゲームやアクションゲームを作る際には、必須の機能です。
MSX1のSPRITE機能は非常に便利ですが、1枚につき1色であったり、水平方向に4枚までしか表示できないなど、制約が多いです。
特に問題なのは、後者「水平方向に4枚までしか表示できない」の方で、シューティングゲームの敵の弾とか見えなくなってはゲームになりません。
SPRITEはFONTを表示するテキスト面の前に、32枚分の表示面があり前から0面〜31面となります。
水平方向に5枚以上並んだ時は、面番号が小さい面が優先し表示されます。
(SPRITE面11、2、4、14、25、8と6枚並んだ時は、11、2、4、8が表示される。)
そこで、水平方向に5枚以上並んだ時に点滅させて疑似的に5枚以上表示させる方法がよく見られます。
全く見えなくなるよりは、点滅して見難くても見えた方が良いですよね。
やり方はルールに則って、SPRITEの表示面を入替えるだけです。
その方法で有名なのが「素数入替法」。
あるキャラクタを表示する時、どの表示面に表示するかの決定に、素数を利用する方法です。
0面から開始して、例えば17を足して32以上になったら32引くようにしていくと、32面目で元の数字に戻ります。(下表の「表示素数」)
面白いですね。
これを利用すると、例えば自機を表示する時、初回は0面に表示、次は17面、次は2面...となり、優先順位が高くなったり低くなったりで点滅すると言う訳です。
(キャラクタ側の面は+1でも良いのですが、こちらも別の素数でやるとバラけていい感じに点滅します。)
この方法の良いところは、何と言っても加算だけで表示面を決められること。
ただでさえメモリ→VRAM間の転送は遅いのに、その転送準備に時間をかけると、処理速度がどんどん遅くなってしまいます。
シャッフルするのに乱数や乗除算を使うと、毎回32回ループすることを考えるとマシン語とは言え負荷が大きいです。
因みに、この方法は1例です。
拙作「PAC-MAN」はこれとは別の方法で点滅させています。(確か4面単位で後ろにシフトする方法だったと思います。(記憶が薄い。))
自分の好きな方法を使うといいです。
では、次回は具体的に組んでみます。
2024年03月14日
Laboratoryテーマ28「マシン語でフロッピーディスクを制御したい」そのA
今回は、Laboratoryテーマ28「マシン語でフロッピーディスクを制御したい」そのAです。
マシン語で、フロッピーディスクからメモリへの読込と、メモリからフロッピーディスクへの書出を行うサンプルプログラムです。
「DISK.BAS」【ダウンロード】
読み込み時は、前回説明した「FCB」にドライブ番号と、ファイル名、拡張子をセットし「&H0F」でシステムコール(&HF37D)。
フロッピーディスク内にあるファイルから「FCB」にファイル情報が読み込まれます。
ファイルの読み込み先アドレスをセットし、「&H1A」でシステムコール。
「FCB」に読み込みに必要な情報をセットし、「&H27」でシステムコール。
書き出し時は、「FCB」にドライブ番号と、ファイル名、拡張子をセットし「&H16」でシステムコール。
フロッピーディスクに新たなファイルが作成されます。(強制上書)
メモリの書き出し元アドレスをセットし、「&H1A」でシステムコール。
「FCB」に書き出しに必要な情報をセットし、「&H26」でシステムコール。
書き出し時は、最後に「&H10」でシステムコールし、ファイルを閉じます。
簡単です。
BASIC部です。