BGE/TileMap

Alias: BGE.TileMap


Static Methods

getNeighbourMask4( grid: object, r: integer, c: integer, options?: BGE.TileMap.AutoTileOptions, ): integer

static

Which of a cell's four side neighbours are the same surface as it, as a 4-bit mask: N = 1, E = 2, S = 4, W = 8 (clockwise from the top). A bit is set when that neighbour matches. The result (0-15) indexes a 16-tile set directly.

The grid is a list of rows (row 0 at the top). Each row is either a string, one character per cell, or an array of cell values such as integer tile ids. Two cells are the same surface when their values are equal. Use one value type throughout a grid - comparing a string with an integer is a runtime error. A missing cell (past the end of a short row) or an invalid value counts as out of the map.

Parameters

  • grid (object) — the map: an array of rows, each a string or an array of cell values
  • r (integer) — row index (0 = top)
  • c (integer) — column index (0 = left)
  • options (BGE.TileMap.AutoTileOptions, optional, default: "{}")

Returns

  • integer — 0-15

Example

CODE
rows = [
"....",
".cc.",
"...."
]
mask = BGE.TileMap.getNeighbourMask4(rows, 1, 1) ' 2 - only the east neighbour matches

' The same map as tile ids
grid = [
[0, 0, 0, 0],
[0, 3, 3, 0],
[0, 0, 0, 0]
]
mask = BGE.TileMap.getNeighbourMask4(grid, 1, 1) ' 2

getNeighbourMask8( grid: object, r: integer, c: integer, options?: BGE.TileMap.AutoTileOptions, ): integer

static

Which of a cell's eight neighbours are the same surface as it, as an 8-bit mask, clockwise from the top: N = 1, NE = 2, E = 4, SE = 8, S = 16, SW = 32, W = 64, NW = 128. This is the common "blob" numbering - pass the result to getBlobTileIndex() to pick one of 47 tiles.

Parameters

  • grid (object) — the map: an array of rows, each a string or an array of cell values
  • r (integer) — row index (0 = top)
  • c (integer) — column index (0 = left)
  • options (BGE.TileMap.AutoTileOptions, optional, default: "{}")

Returns

  • integer — 0-255

reduceBlobMask(mask8: integer): integer

static

Simplifies an 8-bit neighbour mask (see getNeighbourMask8()) to the form a 47-tile blob set uses: a corner neighbour only matters when both sides next to it also match, so its bit is cleared otherwise. Every possible mask reduces to one of 47 values.

Use this to look tiles up yourself when your sheet's 47 tiles are laid out in some other order than getBlobTileIndex() assumes.

Parameters

  • mask8 (integer) — 0-255

Returns

  • integer — the reduced mask, still 0-255

getBlobTileIndex(mask8: integer): integer

static

Picks one of the 47 tiles in a blob tile set (0-46) from a cell's 8-bit neighbour mask. Tiles are numbered by their reduced mask (see reduceBlobMask()) in ascending order: tile 0 is an isolated cell with no matching neighbours, tile 46 is fully surrounded.

Blob sheets don't share one layout, so if yours is arranged differently, map reduceBlobMask()'s value to your own tile positions instead.

Parameters

  • mask8 (integer) — 0-255, from getNeighbourMask8()

Returns

  • integer — 0-46

Example

CODE
mask = BGE.TileMap.getNeighbourMask8(grid, r, c)
tile = BGE.TileMap.getBlobTileIndex(mask)
region = CreateObject("roRegion", sheet, (tile mod 8) * 32, (tile \ 8) * 32, 32, 32)

getEdgePiece3x3( grid: object, r: integer, c: integer, options?: BGE.TileMap.AutoTileOptions, ): object

static

Picks a piece from a 3x3 edge set - four corners, four edges and a centre, drawn as a surface bordered by another - using a cell's four side neighbours. This is the simplest auto-tile sheet, but it has no inner-corner or one-cell-wide pieces: an inner corner (only a diagonal neighbour differs) and a one-cell-wide run both get the centre piece. Use a 47-tile set with getBlobTileIndex() if you need those.

Parameters

  • grid (object) — the map: an array of rows, each a string or an array of cell values
  • r (integer) — row index (0 = top)
  • c (integer) — column index (0 = left)
  • options (BGE.TileMap.AutoTileOptions, optional, default: "{}")

Returns

  • object — {col, row}, 0-2 each: the piece's position within the 3x3 set

Example

CODE
piece = BGE.TileMap.getEdgePiece3x3(grid, r, c)
region = CreateObject("roRegion", sheet, piece.col * 32, piece.row * 32, 32, 32)

autoTileAxisPiece( leadingDiffers: boolean, trailingDiffers: boolean, ): integer

static

0 = leading edge (west/north), 2 = trailing edge (east/south), 1 = centre.

Parameters

  • leadingDiffers (boolean)
  • trailingDiffers (boolean)

Returns

  • integer

autoTileOutOfBoundsMatches( options: BGE.TileMap.AutoTileOptions, ): boolean

static

Parameters

  • options (BGE.TileMap.AutoTileOptions)

Returns

  • boolean

autoTileMatches( grid: object, r: integer, c: integer, surface: dynamic, outOfBoundsMatches: boolean, ): boolean

static

Parameters

  • grid (object)
  • r (integer)
  • c (integer)
  • surface (dynamic)
  • outOfBoundsMatches (boolean)

Returns

  • boolean

autoTileCell(grid: object, r: integer, c: integer): dynamic

static

A cell's value, or invalid when the column is past the end of its row.

Parameters

  • grid (object)
  • r (integer)
  • c (integer)

Returns

  • dynamic

getBlobTileIndexTable(): object

static

Reduced mask -> 0-46, built once on first use and kept on the global AA.

Returns

  • object

bakeTileMapImages( tiles: Array.<BGE.TileMap.TileSpec>, chunkSize: float, ): Array.<BGE.TileMap.BakedChunk>

static

Bakes a sparse grid of stationary tiles into one or more chunked bitmaps - so a whole screen's worth of static tiles (a platformer level's ground, say) costs one SceneObject/draw call per chunk instead of one per tile. Only chunks that actually contain at least one tile are returned.

Each tile is drawn into its chunk exactly once, at bake time (when this function runs) - not per frame - so this is a one-time cost paid when the level is built, not a recurring one.

chunkSize only controls which tiles get grouped together (tiles are bucketed by Int(worldX / chunkSize), Int(worldY / chunkSize)) - each resulting chunk's bitmap is then sized/anchored to the actual bounding box of the tiles bucketed into it, not to a fixed chunkSize x chunkSize square. This means a tile's footprint is never clipped, even when a visual offset/nudge (e.g. examples/platformer's Level.bs +-5px surface/fill adjustment) pushes it past a grid-aligned bucket boundary - it just makes that one chunk's bitmap a little larger than chunkSize, rather than losing part of the tile.

A screen-aligned Image above ~192px used to silently fail to draw on both the BrightScript Simulator and real hardware, even with fully correct content (confirmed via GetByteArray/tmp:/ PNG round-trip) and a draw call reporting success - traced to Renderer.render() never calling Finish() on its destination before the frame proceeds (see Renderer.bs's own comment on that call, and issue #211). With that fixed, chunkSize up to 512 has been confirmed reliable on real hardware (memory- and disk-sourced images, up to 16 simultaneous 320px blits/frame, sustained). Still keep it well under 768, though: a merged chunk spanning a large mostly-empty vertical gap (e.g. a floating platform merged with unrelated ground tiles far below it) can still silently fail to render at 768+ inside a real running game even though the identical dimensions render reliably in an isolated stress test - see examples/platformer's Level.bs for the specific confirmed-safe/confirmed-broken measurements and the follow-up (issue #213) tracking this remaining gap.

Parameters

  • tiles (Array.<BGE.TileMap.TileSpec>)
  • chunkSize (float) — grouping granularity, in world pixels (keep <= 512)

Returns

  • Array.<BGE.TileMap.BakedChunk>

floorDiv(value: float, divisor: float): integer

static

Floor division (not BrightScript's truncate-toward-zero Int()/\) - a tile at a negative world position must bucket into the chunk below/left of the origin, not get pulled back toward chunk 0.

Parameters

  • value (float)
  • divisor (float)

Returns

  • integer

mergeTileColliderRuns( cells: Array.<BGE.TileMap.ColliderCell>, ): Array.<BGE.TileMap.MergedColliderRun>

static

Merges a sparse grid of tagged collision cells into the minimal set of same-tag, contiguous-column runs per row - e.g. a solid platformer floor 20 tiles wide becomes one run instead of 20 separate per-tile colliders. Input order doesn't matter (cells are grouped/sorted internally); a gap in columns or a change in tag always starts a new run. Only merges within a single row - two vertically stacked identical runs are NOT combined into one taller rectangle (that's a further optimization, not implemented here - see bakeTileMapImages() for where the bigger per-frame cost actually lives).

Parameters

  • cells (Array.<BGE.TileMap.ColliderCell>)

Returns

  • Array.<BGE.TileMap.MergedColliderRun>

Other

AutoTileOptions

static

Extends: roAssociativeArray

Options for the auto-tiling functions.

Properties

  • outOfBoundsMatches (boolean) — When true (the default), a neighbour outside the map counts as the same surface, so a

TileSpec

static

One occupied tile to bake into a chunked tilemap image - addressed by world position (not row/col) since baking is fundamentally about pixels/chunks, not grid adjacency (compare BGE.TileMap.ColliderCell, which stays in grid space).

Properties

  • worldX (float)
  • worldY (float)
  • region (roRegion)
  • width (float)
  • height (float)

BakedChunk

static

One non-empty baked chunk: a single pre-composited bitmap, ready to become one Image drawable (one addImage() call instead of one per tile). worldX/worldY is the chunk's own top-left world corner - worldX the chunk's minimum X, but worldY the chunk's MAXIMUM Y (its top edge, matching TileSpec.worldY's own top-edge convention and Image's top-left-anchor convention, where pixel rows increase downward as world Y decreases) - i.e. where to position the resulting drawable via addImage()'s offset. The bitmap's own width/height are sized to this chunk's actual tile content (see bakeTileMapImages()'s doc comment) - not necessarily the chunkSize passed in.

Properties

  • bitmap (roBitmap)
  • worldX (float)
  • worldY (float)

ColliderCell

static

One occupied collision cell in a tile grid, addressed by integer row/column - merging is pure adjacency in grid space, so world position doesn't come into it at all (unlike bakeTileMapImages(), which bakes pixels and therefore does need world position/chunking).

Properties

  • row (integer)
  • col (integer)
  • tag (string)

MergedColliderRun

static

One merged run of contiguous same-tag cells within a single row.

Properties

  • row (integer)
  • startCol (integer)
  • endCol (integer)
  • tag (string) — inclusive