制作途中のプログラムを公開
いま作ってるゲームのプログラムを公開してみます。
ぜんぜん途中ですが、今までの記事で説明したことを詰め込んでいます。
とりあえずソースコードはこちら。
https://github.com/sailorman-msx/games/tree/main/src/sample023
Page#0を有効活用してます
PCGデータやスプライトパターン、マップデータについてはPage#0に詰め込んでいます。ソースコードだとこんな感じ。
;----------------------------------------------------------
; 初期処理(お約束コード)
; 当プログラムでは以下のようなページ構成で
; ROMを作成する
; Page#0 : PCG、スプライト作成処理
; Page#1 : プログラムエリア
; Page#2 : プログラムエリア
; Page#3 : 変数領域/サウンド領域/ワークエリア(F380H以降)
; 変数領域とサウンド領域はC000H-DE3FHまでとする
;-----------------------------------------------------------
; プログラムの開始位置アドレスは0x0000
org $0000
;-------------------------------------------
; PCG, スプライトの定義処理
; Page#0に記述する
;-------------------------------------------
include "pcg_graphic2.asm"
include "sprite_define.asm"
include "data_pcg.asm"
include "data_sprite.asm"
include "map.asm"
;-------------------------------------------
; Page#0の終端に余りがある場合は
; 0x00で埋める
;-------------------------------------------
defs $4000 - $, $00
Header:
; MSX の ROM ヘッダ (16 bytes)
; プログラムの先頭位置は0x4010
defb 'A', 'B', $10, $40, $00, $00, $00, $00
defb $00, $00, $00, $00, $00, $00, $00, $00
0000Hからアセンブルされたコードが埋め込まれるように
org $0000
と宣言しています。そして、0000H内にコードが生成されるようにincludeで各種処理を並べて、Header(ここが4000Hになる)としています。
Page#0の処理を呼ぶときは以下のような感じです。
;-------------------------------------------
; PCGの定義
;-------------------------------------------
; ひらがなフォントを作成する
ld a, 2
ld (WK_VALUE08), a
ld hl, CreateCharacterPattern
ld (PAGE0_FUNC), hl
call ChangePage0Call
変数PAGE0_FUNCに呼び出し先のアドレスをセットしてChangePage0Callを呼び出すと、Page#0がROMのPage#0に切り替わり処理が呼び出されたあと再度Page#0が基本スロットのPage#0(BIOS領域)になって返ってきます。
これは過去記事で説明したとおりですね。
この方式を使うことで48KBのROMカートリッジまでは作成できることになります。やったー!
ちょっと変わったスプライト処理:16x24ドットで動的パターン変更
今回のゲームでは横16x縦24ドットのスプライトを使っています。
見た目がだいぶ変わります。
また、自キャラのスプライトはあらかじめPage#0に格納しているデータをRAMに展開し、プレイヤーが向きを変えたりするたびにそのパターンデータを動的に読み直してスプライトパターンを作成しなおしています。
;--------------------------------------------
; SUB-ROUTINE: CreateSpritePattern
; スプライトパターンと
; 仮想スプライトアトリビュートテーブルを作成する
;--------------------------------------------
CreateSpritePattern:
;-------------------------------------------
; VRAM領域の初期化(ゼロクリア)
;-------------------------------------------
ld (hl), a
ld hl, $1B00 ;
ld bc, $0500 ; 1B00H-1FFFHまでのアドレスを0で埋める
xor a
call WRTVRMFIL
ld (hl), a
ld hl, $3800 ;
ld bc, $0800 ; 3800H-3EFFHまでのアドレスを0で埋める
xor a
call WRTVRMFIL
;--------------------------------------------
; スプライトパターンデータの固定データを
; RAMのスプライトパターンデータテーブルに
; 転送する
;--------------------------------------------
ld hl, SPRPTN
ld de, WK_SPRITE_PTNTBL
ld bc, 2048
call MemCpy
WK_SPRITE_PTNTBLというのがRAM上のスプライトパターンのアドレスです。CreateSpritePatternといいながら実際にはPage#0のデータをRAMに展開しているだけになっています。んで、使いどころはこんな感じで。
(H.TIMIハンドラの処理内:interval.asm)
; プレイヤーの向きに合わせて
; スプライトパターンを再定義する
call VBLANK_sprredefine
ld a, (WK_SPRREDRAW_FINE)
or a
jp z, SkipPutSprite
call PutSprite
xor a
ld (WK_SPRREDRAW_FINE), a
H.TIMIの処理内、つまり1/60秒ごとにスプライトパターンを再定義しています。実際の定義処理は、vb_sprredifine.asm でやってます。確認してみてください。スプライト再定義時には以下のようにスプライト再表示フラグ(WK_SPRREDRAW_FINE)をクリアしてから再定義(RAMからVRAMへの転送)を行っています。これをやらないとVRAMへの転送中でVBLANKが発生してテアリングが発生し、中途半端な状態でスプライトが表示されてしまうためです。
; 特定した先頭アドレスから128バイト分を
; VRAMのスプライトパターンテーブル#0-#15に転送する
; スプライト再描画フラグをOFFにする
xor a
ld (WK_SPRREDRAW_FINE), a
ld de, $3800
ld bc, 128
call WRTVRMSERIAL
この「スプライトデータを都度定義しなおす」という作戦で便利だなと思ったのは、スプライトパターンパターン番号#0-#15だけが自キャラが使うパターン番号として制限できるため、あとのパターン番号は他のスプライトに使えるっていう点です。いろんなスプライト表現が可能になる気がします。
今回はサイドビュー形式
ということで、今回のゲームでは自キャラがいろんな動きをします。
・左右に移動
・連続移動で加速
・上ボタンでジャンプ(加速時は大ジャンプ)
・足元に穴が空いていたら落下
・ハシゴがあったら上下ボタンでハシゴ昇降
一見、よくある動きですが、この動きを実現させるまでエライ大変でした・・
特に【落下】です。
落下が難しかった・・。
落下させるときにX座標をアジャストしています。こうしないと落ちたあとで半キャラずれて変な表示になったりします。
ジャンプ処理はジャンプ時のY値のオフセット値をテーブルに持たせて上にあがって〜、下におちる〜を実現しています。
下に落ち切ったあとで足元になにもなければそのまま落下していきます。
player.asm と keyinput.asm、それと collision.asm に全てが詰まっています。
ORG 0000Hから使うのでZ88DKのかきぶりもちょっと変えてる
z8makeというスクリプトをみてください。
z88dk-disでディスアセンブルの結果を出力しているのですが、ORGが0000になっているので若干いままでとは修正しています。
スランプに突入ちゅう・・
なお、この状態になったのが4月後半くらいで、その後の進展がほとんどありません・・スランプに突入してしまいました・・
まあ、ゆっくり牛歩で進めていこうと思っております(汗)
今回のソースコードでわからない点がありましたら筆者までお問い合わせください。
ではまた!ノシ
セーラー服が似合うおじさんです。猫好き、酒好き、ガジェット好き、楽しいことならなんでも好き。そんな「好き」をつらつらと書き留めていきます。