Version 4.3.0 — Requires GB Studio ≥ 4.3.0
A GB Studio engine plugin that enables seamless screen-scrolling transitions between scenes, similar to the overworld navigation in The Legend of Zelda: Link's Awakening. When the player walks off the edge of a scene, the screen scrolls in that direction and loads the neighbouring scene without a fade. The player and camera glide smoothly across the boundary, and the game loop stays fully active throughout.
All supported scene types (Top-Down, Platformer, Adventure, Point & Click, SHMUP) work with the plugin. Three events are added to the Scene group: Set Neighbour Scene, Auto Connect Neighbour Scenes and Assign current scene scroll offset to Variable.
- Concepts
- Project Setup
- Size Limits and Restrictions
- Events Reference
- Engine Settings
- Memory Footprint
- Bank 0 (HOME) Usage
- Changelog
GB Studio normally resets the viewport and tilemap when changing scenes. This plugin sidesteps the reset by keeping the bkg_offset_x/bkg_offset_y accumulators alive across scene loads and by managing the camera and player positions manually during the transition. The net result is that the screen content slides continuously in one direction while the new scene's tiles load row-by-row or column-by-column into the off-screen portion of the VRAM background map.
The GB hardware background tilemap is 32×32 tiles but only 20×18 tiles are visible at once. The plugin exploits this by treating the map as a wrap-around ring buffer: when scrolling right, the new scene's column data is written into the left edge of the VRAM map (which is off-screen on the right side thanks to the SCX register), so no visual pop occurs.
The bkg_offset_x and bkg_offset_y fields accumulate the total tile displacement across all transitions. They are masked to 5 bits (& 31) to stay within the 32-tile VRAM map dimension.
All scenes that scroll into each other must share the same common tileset. Click the puzzle-piece icon on each scene in GB Studio and assign the same common tileset asset. This ensures tile indices are consistent across scene boundaries so that the visual join is seamless.
Scenes can be larger than the screen, but the dimension along the shared edge must match exactly between the two connecting scenes:
- A scene to the left/right of another must have the same height.
- A scene above/below another must have the same width.
In the On Init script of every scene that can scroll to a neighbour, add a Set Neighbour Scene event for each direction that has a neighbour. There is no need to place triggers on scene edges — the plugin detects boundary crossing automatically.
- Set the Scene to the neighbour scene in that direction.
- Set the Direction of scroll (Up, Down, Left, Right).
- Check Round position to nearest tile if using the Top-Down scene type so the player snaps cleanly to the tile grid after crossing.
Screen.Recording.2026-03-02.214222.mp4
Screen.Recording.2026-03-02.214343.mp4
Screen.Recording.2026-03-02.214528.mp4
Screen.Recording.2026-03-02.214643.mp4
If your game displays a fixed HUD on the overlay/window layer, the plugin needs to know its size so the scroll boundaries and camera calculations account for the reduced playfield area.
| HUD Position | Setting to adjust |
|---|---|
| Bottom-aligned HUD | Set Bottom margin to the HUD height in tiles. |
| Right-aligned HUD | Set Right margin to the HUD width in tiles. |
| Top-aligned HUD | Set Bottom margin to the HUD height in tiles and set Top scroll offset to the HUD height in pixels. |
The Bottom margin shrinks the effective scene height used for scroll boundary calculations. The Right margin shrinks the effective width. The Top scroll offset shifts the draw scroll Y downward in pixels so the background origin aligns below the top HUD.
Due to the ring-buffer nature of the VRAM tilemap, the usable scene dimensions are limited to 128 tiles wide and 128 tiles tall (half of the standard GB Studio maximum of 256×256). Exceeding this causes visual wrap-around corruption during transitions.
The dimension perpendicular to the scroll direction must be identical on both sides of the boundary. For example, a left-right scroll requires both scenes to have the same number of tile rows. Mismatched sizes produce an offset seam.
Both connecting scenes must use the same common tileset. Because no tileset reload happens during a scroll transition (the VRAM tile data stays unchanged), any tile in the new scene that is not present in the shared tileset will display incorrectly.
When a transition begins, every running script in the current scene is terminated — without clearing variables. Timers, input events and music events are reset too. The new scene's init scripts run once the scene has loaded.
The camera lock is cleared at the start of a transition and restored once both the camera and the player have reached their target positions. Normal camera following is suspended for the duration, so the transition owns the camera.
- Disable player sprite loading on scene scroll (enabled by default): prevents redundant VRAM writes if the player sprite is the same in both scenes.
- Disable tileset loading on scene scroll: prevents the tileset from being reloaded if both scenes share the same common tileset fully (saves time but must only be used when truly identical).
- Disable loading UI tileset on scene load: prevents the UI tileset reload on every scene load; useful if the UI is part of the common tileset.
All events are in the Scene group.
EVENT_SET_NEIGHBOUR_SCENE
Registers a scene as the neighbour in a given direction and enables boundary-crossing detection for the current scene. Must be called in the scene's On Init script. Can be called up to four times (once per direction) to register all neighbours.
| Field | Description |
|---|---|
| Scene | The scene to scroll to when the player exits in the chosen direction. |
| Direction of scroll | Up, Down, Left, or Right — the direction the screen will scroll when the boundary is crossed. |
| Round position to nearest tile | Snaps the player's position to the nearest tile grid after the transition completes. Recommended for Top-Down scenes to prevent sub-tile misalignment. |
EVENT_AUTO_CONNECT_NEIGHBOUR_SCENE
Automatically wires up Set Neighbour Scene calls for a whole group of scenes at compile time, based on how the scenes are laid out in the GB Studio editor. Place this event once, in the On Init script of a dedicated empty "compiler" scene — the event itself emits no runtime code where it is placed.
Important: scene scripts are compiled in project scene order, and this event can only inject into scenes that are compiled after the scene containing it. The scene holding the event must therefore be the first scene of the project (it is first when it is the first scene ever added; on an existing project, edit the scene's
.gbsresfile and set its"_index"lower than every other scene's, e.g.-1). For the same reason the hosting scene itself never receives auto-connections — use a scene that is not part of the connected map.
For every scene whose GBVM symbol starts with the given prefix (set the symbol per scene under the scene's settings), the event looks for other matching scenes whose edges touch it in the editor and injects a script at the start of that scene's On Init that registers each detected neighbour via set_neighbour_scene. Scene positions are compared in tiles (editor pixel position ÷ 8).
Because the scroll transition preserves the player's position along the shared edge with no offset correction, two scenes are only connected when their edges are exactly aligned: left/right neighbours must have the same top edge, up/down neighbours the same left edge. Scenes that merely overlap at an offset are skipped.
| Field | Description |
|---|---|
| Scene data symbol prefix | Only scenes whose GBVM symbol starts with this prefix are considered for connection. Leave empty to match every scene. |
| Loop Horizontally | Additionally connects scenes on the left-most map edge to aligned scenes on the right-most map edge (wrap-around world). |
| Loop Vertically | Additionally connects scenes on the top-most map edge to aligned scenes on the bottom-most map edge. |
| Round position to nearest tile | Applies the tile-snap flag to every generated connection. Recommended for Top-Down scenes. |
The usual plugin restrictions still apply to auto-connected scenes: shared common tileset, matching edge dimensions, and a maximum scene size of 128×128 tiles.
EVENT_GET_SCROLL_OFFSET
Reads the current accumulated background offset (bkg_offset_x, bkg_offset_y) and stores the values, masked to 0–31, into two variables. This is useful for scripts that need to compensate for the viewport shift when drawing to fixed screen positions (e.g. placing overlay elements that must align with world tiles).
| Field | Description |
|---|---|
| X Offset Variable | Destination variable for the horizontal tile offset (0–31). |
| Y Offset Variable | Destination variable for the vertical tile offset (0–31). |
These settings are found under Settings → Engine Fields → Screen Scroll.
| Setting | Type | Default | Description |
|---|---|---|---|
Right margin (scroll_right_margin) |
Slider (0–20 tiles) | 0 | Width in tiles reserved by a right-aligned HUD. Shrinks the effective horizontal scroll area. |
Bottom margin (scroll_bottom_margin) |
Slider (0–18 tiles) | 0 | Height in tiles reserved by a bottom- or top-aligned HUD. Shrinks the effective vertical scroll area. |
Top scroll offset (scroll_top_offset) |
Slider (0–144 px) | 0 | Pixel offset applied to the draw scroll Y each frame. Use to push the background origin below a top-aligned HUD. |
These values are in sub-pixels (256 sub-pixels = 1 tile = 8 px). They control how far into the new scene the player walks before the scroll animation ends and the camera re-locks.
| Setting | Default (subpx) | Description |
|---|---|---|
| Player transition right distance | 512 | Distance the player travels right after crossing the right edge. |
| Player transition left distance | 512 | Distance the player travels left after crossing the left edge. |
| Player transition top distance | 512 | Distance the player travels upward after crossing the top edge. |
| Player transition bottom distance | 512 | Distance the player travels downward after crossing the bottom edge. |
The threshold is compared against the player's position in sub-pixels. A transition triggers when the player's coordinate is less than the threshold (for top/left) or greater than scene_size − threshold (for bottom/right).
| Setting | Default (subpx) | Description |
|---|---|---|
| Player transition right threshold | 512 | Minimum distance from the right edge to trigger a right scroll. |
| Player transition left threshold | 0 | Position below which a left scroll triggers. |
| Player transition top threshold | 256 | Position above which an upward scroll triggers. |
| Player transition bottom threshold | 256 | Minimum distance from the bottom edge to trigger a downward scroll. |
| Setting | Default | Description |
|---|---|---|
| Disable player sprite loading on scene scroll | Enabled | Skips re-uploading the player sprite VRAM data on scroll transitions. Safe when the player sprite is unchanged between scenes. |
| Disable tileset loading on scene scroll | Disabled | Skips full tileset VRAM reload on scroll transitions. Only enable if both scenes use an identical common tileset. |
| Disable loading UI tileset on scene load | Disabled | Skips the UI tileset reload on every scene load. Enable if the UI tiles are baked into the common tileset. |
These are read-only engine fields accessible via Engine Field Value in scripts.
| Field | Description |
|---|---|
scene_transition_enabled |
Non-zero when at least one neighbour scene has been registered (i.e. after any Set Neighbour Scene call). |
is_transitioning_scene |
Non-zero and equal to the direction flag while a scroll is in progress (1=Up, 2=Right, 4=Down, 8=Left). Zero when idle. |
bkg_offset_x |
Accumulated horizontal tile offset of the viewport (0–31). Updated on every transition. |
bkg_offset_y |
Accumulated vertical tile offset of the viewport (0–31). Updated on every transition. |
Every setting here changes what gets compiled. Figures are what you get back by turning the setting off; rows marked off by default show what turning it on costs instead, and sliders show the cost per step. A dash means that budget does not move.
| Setting | Bank 0 | WRAM | Banked ROM |
|---|---|---|---|
| Disable player sprite loading on scene scroll | — | — | 16 B |
| Disable tileset loading on scene scroll (off by default — cost of turning it on) | — | — | +7 B |
| Disable loading ui tileset on scene load (off by default — cost of turning it on) | — | — | −8 B |
Turning off every on-by-default switch above frees 16 B of banked ROM — the full span between this plugin at its fullest and stripped to nothing. Treat it as a ceiling rather than a recipe: you keep whatever your game actually uses.
How these were measured
GB Studio 4.3.0-e1. This plugin's engine/src/**/*.c was compiled with the
toolchain and flags GB Studio itself uses (lcc -msm83:gb -Wf--max-allocs-per-node 3000 -DHUGE_TRACKER -DRUMBLE_ENABLE=0x08u) against a merged include tree, and the SDCC object
files' area records were read: _HOME is bank 0, _DATA/_INITIALIZED/_BSS are WRAM,
and _CODE*/_CONST/_LIT/_INITIALIZER are banked ROM.
Two caveats. Only this plugin's own engine sources are measured, so a setting that also changes a struct shared with stock engine files can move a few more bytes in files the plugin does not ship. And each setting is toggled on its own: a handful measure slightly negative because enabling their code lets the compiler drop a fallback path elsewhere, and settings that gate other settings only show their own contribution.
Measured against the stock GB Studio 4.3.0-e1 engine (per-file SDCC compile with GB Studio's build flags, default engine settings). Values are the plugin's delta versus the stock engine; DMG build, with CGB noted where it differs. ROM cost lands in banked ROM (GB Studio's autobanker spreads it across switchable banks); using the plugin's events additionally compiles a few bytes of GBVM script per call into your project's script banks.
| Cost | |
|---|---|
| WRAM | +46 bytes |
| ROM | +3,550 bytes (DMG) / +3,575 bytes (CGB) |
- WRAM: 46 bytes, mostly scene-transition scratch state.
- Engine WRAM headroom: the stock GB Studio 4.3.0 engine leaves about 854 bytes of WRAM free (usable engine WRAM is 7,776 bytes at 0xC0A0–0xDF00; the stock engine uses 6,922 bytes). With this plugin installed roughly 808 bytes remain. This figure does not depend on how many global variables your project defines: the script memory array has a fixed size of VM_HEAP_SIZE + (VM_MAX_CONTEXTS × VM_CONTEXT_STACK_SIZE) words — 768 + 16 × 64 = 1,792 words (3,584 bytes) with stock engine settings.
- SRAM: not used.
Bank 0 is the 16 KB non-switchable ROM bank that the GB Studio engine core, the interrupt handlers and the GBDK runtime all share. Banked ROM is cheap (add another bank), bank 0 is not, so it is usually the first thing a project runs out of.
| Bytes | |
|---|---|
| Bank 0 used by this plugin | -152 |
| Bank 0 free with this plugin installed | 1,603 of 16,384 (90% used) |
This plugin gives bank 0 space back. Its replacements for stock engine files compile smaller than the originals, freeing 152 bytes.
| Module | This plugin | Stock engine | Bank 0 cost |
|---|---|---|---|
actor.c |
669 | 871 | -202 |
collision.c |
431 | 401 | +30 |
scroll.c |
306 | 286 | +20 |
Modules that replace or patch a stock engine file only cost the difference: the stock version's bank 0 bytes were being spent anyway.
How this was measured
GB Studio 4.3.2, DMG target, default engine settings. Each module's bank 0
contribution is the A _HOME size record that SDCC writes into its .rel
object, summed over the engine sources this plugin provides. Stock sizes come
from building projects whose only plugin ships no engine C, so every module in
them is the untouched engine; two such builds were compared and agreed on all
73 shared modules.
The "free" figure is a stock project with this plugin and nothing else. Your own number will differ: other plugins, and any engine settings that change what the core compiles, move it independently of this plugin.
Grouped by the date each change was merged into the official gb-studio-plugins repository.
Only bug fixes, new features and feature changes are listed. Engine version bumps, patch regeneration, packaging fixes and documentation edits are omitted.
- Implemented the ContinuousScene plugin's auto-connect event variant for ScreenScroll.
- Added ContinuousScenePlugin compatibility.
- Added custom script parameter / stack support to the events.
First published in the official plugin repository. This entry covers everything developed since the plugin's standalone release in July 2024:
- New event to store the scroll offset in a variable, for tilemap editing.
- Script lock support.
- Refined the
#definesettings for the transition threshold and distance. - Exposed additional engine fields.
- Fixes: normal scene load after a scene scroll, and the small blip when scrolling up with a HUD margin.