ゲームボーイ RGBDS 開発メモ 第10回 チュートリアルを読んでいく (Collision, Bricks)

[https://gbdev.io/gb-asm-tutorial/part2/collision.html](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 を２回くりかえして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](https://gbdev.io/gb-asm-tutorial/part2/bricks.html)

こちらは、ボールがレンガにぶつかったとき、タイルマップのタイルIDを変えてなくすというコードでした。

### チュートリアル PartII 完走！これからなにをやるか

これでチュートリアル PartIIはおしまいです。PartIIIは画面遷移などがあるようですが、こちらはさらっと読むだけにとどめておいて、Qiitaでなぞるのはやめておきます(面倒になってきた（笑）)

チュートリアルを読む前よりも、なにができるのかが少しずつわかってきました。
そろそろなにかつくりたくなってきました（笑）ビットの計算のところが少し自分には難しいですが
少しずつ解決していけば、なんとかなりそう。ChatGPT先生もいますし。

なにをつくるかですが、いくつか考えてみました。

- [内藤さんMSX処女作のカーレース（のようなゲーム）](https://www.youtube.com/watch?v=qbjOtau2-SE)
- [内藤さんがドラクエ3の空き容量で入れていた連射判定ゲーム](https://www.youtube.com/watch?v=U9wEUj3j-Ms)
- ただひたすらたまを避けるだけのゲーム

連射判定ゲームが一番簡単そうでしょうか？もう少し検討したいと思います！
あとサウンドもやってみたい！