tilemap-parserv5.2.0GITHUB

CollisionRunner guide

The runner is the single public surface for movement. It composes five movement implementations behind one object: configure it once, call a move_* every frame, read the result. Everything below is the runner's actual behavior from runtime/movement/.

GAME-TYPE PRESETS

CollisionRunner.from_game_type(name, tile_size=(32, 32), strict=False, render_scale=1.0) is the recommended constructor. Presets:

game_typeModeGravityJumpSprite needs
platformerPLATFORMER800 px/s²-400 px/sx, y, collision_shape, vx, vy, on_ground
topdownSLIDE00x, y, collision_shape
rpgRPG00x, y, collision_shape

Unknown names raise ValueError. Everything the presets set is just attributes; tweak after construction. The generic CollisionRunner(tile_size, mode, render_scale) constructor also exists;runner.move(sprite, tileset, tile_map, delta_x, delta_y, dt, ...) dispatches on the configured mode (slide → move_and_slide, platformer → move_platformer, rpg → move_rpg, grounded → move_grounded).

TUNABLES

AttributeDefaultWhat it doesChange it when…
gravity800.0px/s² applied to airborne sprites each frame (physics modes)your jump arcs feel floaty or stiff
max_fall_speed600.0terminal velocity cap on fallingsprites punch through thin floors at high fall speed
jump_strength-400.0negative vy applied on jump (negative = up)tuning jump height
horizontal_speed200.0built-in input_x * horizontal_speed sets vxwalk/run speed feels wrong
step_height4.0max stair/step height a grounded sprite climbs (px)you want to hop small ledges (raise) or not (lower)
ground_snap_tolerance2.0how far the runner snaps a sprite onto groundsprites slide off 1px lips
max_walk_angle60.0degrees from horizontal; steeper slopes are walls in move_platformer_with_slideslopes feel too climbable / not climbable enough
slide_friction0.1accepted for editor/game-type compatibility; no movement method reads it (must be in [0, 1])
rpg_snap_to_gridFalseRPG-mode config flag (kept false; movement stays free)

ATTACH / DETACH / FROM_WORLD

Without an attached world the runner resolves against whatever (tileset_collision, tile_map) you pass per call. Attach a PhysicsWorld and it resolves against the world's tiles and bodies uniformly; you pass None, None:

attachment rulesPYTHON
runner = CollisionRunner.from_world(world, game_type="platformer")   # preset + attach
# or the legacy two-step:
runner = CollisionRunner()
runner.attach(world)         # adopts world.tile_size + render_scale
runner.detach()              # back to per-call tile arguments

# one-off override of an attached world (does not change the attachment):
result = runner.move_platformer(player, None, None, dt, input_x=1.0, world=other_world)
  • Attaching overrides the tile source and grid geometry; detach() falls back to per-call args.
  • Multiple maps → one world per map, re-attach (or the per-call world= override).
  • The runner also offers get_tile_at(world_x, world_y) and get_nearby_tile_shapes(...) for queries outside movement.

GID ROUTING — MULTI-TILESET MAPS

Maps with several type:"tile" resources encode cells as global ids: gid = firstgid + local_variant, stacked in resource order. Collision files, however, are keyed by the local variant of their own tileset. Feeding a plain local-keyed file into a GID world used to miss every lookup — floors silently stopped being solid.

Since 5.0.5, PhysicsWorld.from_map(..., use_gids=True) fixes this automatically. It records every grid resource's [firstgid, firstgid + tile_count) window and stem-matches tileset_collision.tileset_name against them. Every tile lookup is then range-routed:

how a gid resolvesPYTHON
1. find the grid resource whose window contains the gid
2. owner != collision file's tileset  -> None   (decoration grids are never solid)
3. owner == collision file's tileset  -> tiles[gid - firstgid]
nothing to configurePYTHON
col = load_tileset_collision("data/collision/tileset.collision.json")  # local keys ("2", "575", ...)
world = PhysicsWorld.from_map(map_data, col, use_gids=True)            # map has N grid tilesets

world.has_collision_gid(92)    # True  -> jungle local 2
world.has_collision_gid(1813)  # False -> belongs to another grid resource, never aliases

COLLISIONRESULT FLAGS

One dataclass, reset before each call: collided, final_x, final_y, hit_wall_x, hit_wall_y, hit_ceiling, on_ground, slide_vector. Full semantics on the Physics & Bodies page; the short version: branch on hit_wall_x for walls, on_ground for landing, and read slide_vector in slide mode.

PER-MODE WIRING

move modesDIAGRAM
CollisionRunner
from_game_type / from_world
presets + tunables
move_and_slide
topdown · slide
delta_x + delta_y
move_platformer
platformer
input_x + jump_pressed
move_grounded
explicit velocity
knockback, crates

Top-down (slide)

game loopPYTHON
runner = CollisionRunner.from_game_type("topdown", tile_size=(32, 32))
# sprite needs only x, y, collision_shape

dx = (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT]) * 160.0 * dt
dy = (keys[pygame.K_DOWN] - keys[pygame.K_UP]) * 160.0 * dt
result = runner.move(player, tileset, tile_map, delta_x=dx, delta_y=dy)
if result.slide_vector:
    # move_and_slide kept a component you can build on
    pass

Platformer

game loopPYTHON
runner = CollisionRunner.from_game_type("platformer", tile_size=(32, 32))
# sprite needs x, y, collision_shape, vx, vy, on_ground

axis = (keys[pygame.K_RIGHT] - keys[pygame.K_LEFT])          # -1 / 0 / 1
result = runner.move_platformer(
    player, tileset, tile_map, dt,
    input_x=float(axis), jump_pressed=keys[pygame.K_SPACE],
)
if result.on_ground:
    # step-up worked; you can jump next frame

Explicit velocity (knockback, crates, custom controllers)

velocity contractPYTHON
# velocity= skips gravity, input and jump; adopts (vx, vy) onto the sprite
result = runner.move_grounded(enemy, None, None, dt, velocity=(enemy.vx, enemy.vy))
if result.hit_wall_x:
    enemy.vx = 0.0      # runner zeroed vy on landing for you

VALIDATE_CONFIG & STRICT

Presets call validate_config() automatically. Manual rules worth knowing:

  • PLATFORMER mode requires gravity > 0; zero gravity is a ValueError.
  • RPG mode with gravity > 0 is a ValueError; top-down with gravity just warns (it's ignored in move_and_slide).
  • gravity < 0 and max_fall_speed < 0 are errors; positive jump_strength warns.
  • strict=True turns warnings into raised ValueError.