Scrolling Background
Scrolling the background is an easy task. However, for a SMOOTH slow scrolling background: scaled integers1 will be used.
⚠️ Scaled Integers1 are a way to provide smooth “sub-pixel” movement. They are slightly more difficult to understand & implement than implementing a counter, but they provide smoother motion.
Initializing the Background
At the start of the gameplay game state we called the initialize background function. This function shows the star field background, and resets our background scroll variables:
Just like with our title screen graphic, because our text font tiles are at the beginning of VRAM: we offset the tilemap values by 52
INCLUDE "src/main/utils/hardware.inc"
INCLUDE "src/main/utils/macros/text-macros.inc"
SECTION "BackgroundVariables", WRAM0
mBackgroundScroll:: dw
SECTION "GameplayBackgroundSection", ROM0
starFieldMap: INCBIN "src/generated/backgrounds/star-field.tilemap"
starFieldMapEnd:
starFieldTileData: INCBIN "src/generated/backgrounds/star-field.2bpp"
starFieldTileDataEnd:
InitializeBackground::
; Copy the tile data
ld de, starFieldTileData ; de contains the address where data will be copied from;
ld hl, $9340 ; hl contains the address where data will be copied to;
ld bc, starFieldTileDataEnd - starFieldTileData ; bc contains how many bytes we have to copy.
call CopyDEintoMemoryAtHL
; Copy the tilemap
ld de, starFieldMap
ld hl, $9800
ld bc, starFieldMapEnd - starFieldMap
call CopyDEintoMemoryAtHL_With52Offset
xor a
ld [mBackgroundScroll], a
ld [mBackgroundScroll+1], a
ret
To scroll the background in a gameboy game, we simply need to gradually change the SCX
or SCX
registers. Our code is a tiny bit more complicated because of scaled integer usage. Our background’s scroll position is stored in a 16-bit integer called mBackgroundScroll
. We’l increase that 16-bit integer by a set amount.
; This is called during gameplay state on every frame
UpdateBackground::
; Increase our scaled integer by 5
; Get our true (non-scaled) value, and save it for later usage in bc
ld a, [mBackgroundScroll]
add a, 5
ld b, a
ld [mBackgroundScroll], a
ld a, [mBackgroundScroll+1]
adc 0
ld c, a
ld [mBackgroundScroll+1], a
We won’t directly draw the background using this value. De-scaling a scaled integer simulates having a (more precise and useful for smooth movement) floating-point number. The value we draw our background at will be the de-scaled version of that 16-bit integer. To get that non-scaled version, we’ll simply shift all of it’s bit rightward 4 places. The final result will saved for when we update our background’s y position.
; Descale our scaled integer
; shift bits to the right 4 spaces
srl c
rr b
srl c
rr b
srl c
rr b
srl c
rr b
; Use the de-scaled low byte as the backgrounds position
ld a, b
ld [rSCY], a
ret