Scroll & Camera

The scroll and camera system is implemented in C.

Scroll System Overview

Scroll Boundaries

Incremental Tile Loading

Key Functions

FunctionDescriptionNotes
scroll_load_row(x, y) Load a single tile row NONBANKED. Not declared in scroll.h — needs manual declaration:
void scroll_load_row(UBYTE x, UBYTE y);
scroll_load_col(x, y) Load a single tile column NONBANKED
scroll_repaint() Full screen repaint (loads all visible rows/columns) Called during scene loading

Scroll Offsets

Camera System

Camera Settings

camera_settings bitmask:

FlagValueDescription
CAMERA_UNLOCKED 0x00 Camera follows player
CAMERA_LOCK_X_FLAG 0x01 Lock horizontal position
CAMERA_LOCK_Y_FLAG 0x02 Lock vertical position

Camera Parameters

VariableTypeDescription
camera_offset_x, camera_offset_y BYTE Offset from player center
camera_deadzone_x, camera_deadzone_y BYTE How far player can move before camera follows
camera_clamp_x, camera_clamp_y UINT16 Boundary clamping values

Camera Update

Parallax System

parallax_rows[3] Struct

FieldDescription
scxHorizontal scroll value for this layer
next_yScanline where next layer begins
shiftScroll speed divisor (bit shift)
start_tileFirst tile row of this layer
tile_heightHeight of this layer in tile rows
shadow_scxBuffered SCX for VBlank timing

Parallax Example

// 3-layer parallax: sky (slow), mountains (medium), ground (fast)
parallax_rows[0].shift = 2;    // sky scrolls at 1/4 speed
parallax_rows[0].next_y = 48;  // sky is 48 scanlines
parallax_rows[1].shift = 1;    // mountains at 1/2 speed
parallax_rows[1].next_y = 96;  // mountains end at scanline 96
parallax_rows[2].shift = 0;    // ground at full speed
parallax_rows[2].next_y = 144; // rest of screen

GBVM Camera Commands

CommandDescription
VM_CAMERA_MOVE_TOMove camera to position
VM_CAMERA_SET_POSInstantly set camera position
VM_CAMERA_LOCKLock camera to current position
VM_CAMERA_UNLOCKResume following player
VM_CAMERA_SET_FOLLOWSet follow parameters (offset, deadzone)

Common Patterns

Custom Scene with Locked Camera

void my_scene_init(void) BANKED {
    camera_settings = CAMERA_LOCK_X_FLAG | CAMERA_LOCK_Y_FLAG;
    camera_x = PX_TO_SUBPX(80);  // center of screen
    camera_y = PX_TO_SUBPX(72);
}

Pre-loading Tile Rows

// Pre-load rows that aren't visible yet
for (UINT8 y = visible_end; y < visible_end + 3; y++) {
    scroll_load_row(0, y);
}
Sprite OAM Timing

draw_scroll_y at the start of state_update still holds the previous frame's value, matching the sprite OAM that was DMA'd. Buffer it for your ISR before scroll_update overwrites it.

Parallax Conflicts

Custom ISR plugins must clear parallax_rows and set scene_LCD_type = LCD_simple to prevent parallax ISR conflicts.