Tiled Colliders
Use the TilemapCollider and TileCollider
functions to give your entity a grid-based collider shape,
where each tile of the grid can be either solid or empty. A TilemapCollider can be used for many purposes, such as:
- Solid obstacles, such as the walls of a maze or the platforms in a platformer game
- Zoning areas, for example to determine if a player is standing on grass or water terrain
- Modifiable terrain, such dirt or rock you can dig through
Minimal Example
pub const Level1Data = [
[ 1, 0, 0, 0, 0 ],
[ 1, 0, 0, 0, 0 ],
[ 1, 0, 1, 0, 0 ],
[ 1, 0, 1, 1, 0 ],
[ 1, 0, 0, 0, 0 ],
[ 1, 1, 1, 1, 1 ],
]
pub game fn World.Main() {
Spawn walls {
use body=this
Body(pos=@(0,0), immovable=true) // immovable=true is a more performant for a TilemapCollider
// Create the tilemap
TilemapCollider(category=Category:Default)
// Fill the tilemap
for row, rowData in Level1Data {
for col, isSolid in rowData {
if !isSolid { continue }
TileCollider(coords=@(col, row), collide=isSolid)
}
}
}
}
- The tiled collider is first created using the TilemapCollider function, which takes parameters that apply to all the tiles in the tilemap, such as the category and the body it is attached to.
- Then the tiled collider is populated using the TileCollider function,
where
colandroware the column and row indices of the tile to set.
Bodies
The body parameter TilemapCollider determines which Body the collider is attached to.
Immovable Bodies
In most cases, you should attach your TilemapCollider to a Body(immovable=true).
That is because normally a tilemap represents the static parts of your level, such as walls and floors, which do not move.
This will improve the performance of your game, as the physics engine can skip some calculations for immovable bodies.
Attach your tilemaps to a Body with immovable=true to improve performance.
Movable Bodies
With that said, it is possible to attach a TilemapCollider to a Body with immovable=false.
This could be used for example in a game where the player designs their own spaceship, tile-by-tile,
leading to a unique shape.
While this can be done, note that this is not as performant as simply using a Polygon
to define the shape of the spaceship, and so is not recommended.
Categories
The category parameter of TilemapCollider determines which category or categories the collider belongs to.
All tiles in the TilemapCollider belong to that category.
Categories are used to determine which colliders can collide or sense each other, see Categories
for a more in-depth explanation of how categories work.
TilemapCollider(category=Category:Dirt)
If you need to assign different categories to different tiles, you can use multiple TilemapColliders, one for each category:
const LevelData = [
[$dirt, $dirt, $dirt, $rock, $rock],
[$dirt, $water, $water, $rock, $rock],
[$dirt, $dirt, $dirt, $dirt, $dirt],
]
// Three different TilemapColliders, one for each category
TilemapCollider<dirt>(category=Category:Dirt)
TilemapCollider<rock>(category=Category:Rock)
TilemapCollider<water>(category=Category:Water)
for row, rowData in LevelData {
for col, tileType in rowData {
match tileType {
$dirt => TileCollider<dirt>(coords=@(col, row), collide=true),
$rock => TileCollider<rock>(coords=@(col, row), collide=true),
$water => TileCollider<water>(coords=@(col, row), collide=true),
}
}
}
Modifying Tiles
Each tile in the TilemapCollider can be turned on and off at any time:
TilemapCollider(category=Category:Dirt)
TileCollider(coords=@(2, 3), collide=true)
await Tick(5s)
TileCollider(coords=@(2, 3), collide=false)
Performance Tips for Frequent Modification
When you modify a TilemapCollider:
- The rollback netcode has to snapshot the entire tilemap, not just the modified tile
- The physics engine has to recalculate contacts across the entire tilemap, not just the modified tile
For this reason, if you have a large tilemap and modify a TilemapCollider frequently, it may be better to
split it up into blocks, and only modify the block that contains the tile you want to change.