https://gbdev.io/gb-asm-tutorial/part2/collision.html
Collision
Ball:
dw `00033000
dw `00322300
dw `03222230
dw `03222230
dw `00322300
dw `00033000
dw `00000000
dw `00000000
BallEnd:
; Copy the ball tile
ld de, Ball
ld hl, $8010
ld bc, BallEnd - Ball
call Memcopy
ボールのタイルを定義します。
; Now initialize the ball sprite
ld a, 100 + 16
ld [hli], a
ld a, 32 + 8
ld [hli], a
ld a, 1
ld [hli], a
ld a, 0
ld [hli], a
ボールのオブジェクトも設定。
SECTION "Ball Data", WRAM0
wBallMomentumX: db
wBallMomentumY: db
ボールのX座標の増分、Y座標の増分のワークエリアです。
; Now initialize the ball sprite
ld a, 100 + 16
ld [hli], a
ld a, 32 + 8
ld [hli], a
ld a, 1
ld [hli], a
ld a, 0
ld [hli], a
; The ball starts out going up and to the right
ld a, 1
ld [wBallMomentumX], a
ld a, -1
ld [wBallMomentumY], a
ボールの座標と増分の初期化ですね。ボールの座標はOAMをそのまま利用します。
Main:
ld a, [rLY]
cp 144
jp nc, Main
WaitVBlank2:
ld a, [rLY]
cp 144
jp c, WaitVBlank2
; Add the ball's momentum to its position in OAM.
ld a, [wBallMomentumX]
ld b, a
ld a, [_OAMRAM + 5]
add a, b
ld [_OAMRAM + 5], a
ld a, [wBallMomentumY]
ld b, a
ld a, [_OAMRAM + 4]
add a, b
ld [_OAMRAM + 4], a
メインルーチンで、OAMにある座標をよみとって、増分を足し込んでいます。負の数を足していますが、これは
アセンブラが-1を2の補数($FF)にしてくれています。2の補数なんて、
まったく忘れていました。情報処理の資格を勉強したときでてきたような。
; Convert a pixel position to a tilemap address
; hl = $9800 + X + Y * 32
; @param b: X
; @param c: Y
; @return hl: tile address
GetTileByPixel:
; First, we need to divide by 8 to convert a pixel position to a tile position.
; After this we want to multiply the Y position by 32.
; These operations effectively cancel out so we only need to mask the Y value.
ld a, c
and a, %11111000
ld l, a
ld h, 0
; Now we have the position * 8 in hl
add hl, hl ; position * 16
add hl, hl ; position * 32
; Convert the X position to an offset.
ld a, b
srl a ; a / 2
srl a ; a / 4
srl a ; a / 8
; Add the two offsets together.
add a, l
ld l, a
adc a, h
sub a, l
ld h, a
; Add the offset to the tilemap's base address, and we are done!
ld bc, $9800
add hl, bc
ret
ボールの座標からタイルマップのアドレスを求める関数です。
x: ボールのX座標を8で割った商
y: ボールのY座標を8で割った商
としたとき、
hl = $9800 + x + y * 32
ですね。Yをタイルの横の個数である32倍して(スクロールがあるので32個あるらしい)Xを足せば、$9800 からいくずれたかを求めることができます。
y*32をもとめるのに、Y座標を8の倍数に切り捨ててからadd hl, hl を2回くりかえして4倍にしてhlに格納されます。
xをもとめるのに、X座標を右シフト3回して求めています。これがaに入ります。このaをhlに足し込みたいのですが、
aにlを足したものをlにいれて、adcでaにhとcフラグを足し込んでから、lをひくことで、hが求まります。これでできたhlに$9800を足してできあがあり。
これは難しいです(笑)adcとsubを使うのはよくあるテクニックなのでしょうか。
ld c, a
ld b, 0
add hl, bc
ld bc, $9800
add hl, bc
ためしにこちらのコードに変えたところ動作しているようなので、うまくいってるかな。
; @param a: tile ID
; @return z: set if a is a wall.
IsWallTile:
cp a, $00
ret z
cp a, $01
ret z
cp a, $02
ret z
cp a, $04
ret z
cp a, $05
ret z
cp a, $06
ret z
cp a, $07
ret
これはタイルが壁かどうかの判定です。switch文ですね。ret z
はゼロフラグがたっていたらreturn; です
あとは、座標からバウンドするかどうかを計算して、増分を変更するというコードでしたので、割愛します(笑)
オブジェクト同士の当たり判定は、座標から計算するしかないんですね。
Bricks
https://gbdev.io/gb-asm-tutorial/part2/bricks.html
こちらは、ボールがレンガにぶつかったとき、タイルマップのタイルIDを変えてなくすというコードでした。
チュートリアル PartII 完走!これからなにをやるか
これでチュートリアル PartIIはおしまいです。PartIIIは画面遷移などがあるようですが、こちらはさらっと読むだけにとどめておいて、Qiitaでなぞるのはやめておきます(面倒になってきた(笑))
チュートリアルを読む前よりも、なにができるのかが少しずつわかってきました。
そろそろなにかつくりたくなってきました(笑)ビットの計算のところが少し自分には難しいですが
少しずつ解決していけば、なんとかなりそう。ChatGPT先生もいますし。
なにをつくるかですが、いくつか考えてみました。
- 内藤さんMSX処女作のカーレース(のようなゲーム)
- 内藤さんがドラクエ3の空き容量で入れていた連射判定ゲーム
- ただひたすらたまを避けるだけのゲーム
連射判定ゲームが一番簡単そうでしょうか?もう少し検討したいと思います!
あとサウンドもやってみたい!