Map Colliders¶
Overview¶
If a scene has an actor moving, we may update its position from frame to frame using the discrete approximation:
velocity = velocity + acceleration * dt
position = position + velocity * dt
That’s fine when the actor does not find an obstacle, like a wall.
- If there is an obstacle, we usually want to
stop the position change so the actor touches the obstacle but does not overlap it.
do something sensible with the velocity (stop ?, bounce ?).
maybe do some action based on which obstacle (spikes ?, glass ?).
To do this we can represent the actor and the obstacles as rects with sides parallel to the axis, and do the calculations.
- This is what map colliders do given:
the actor velocity, rect before and tentative rect after
a map layer that contains a number of obstacles
- it will
Properly update the velocity and the position.
Call appropriate methods when it detects the actor bumped into an obstacle.
Tell if any collision in the x and / or y axis happened.
While some mapcolliders are meant to be used in tiled maps (
RectMapCollider
, RectMapWithPropsCollider
),
others (TmxObjectMapCollider
) can be used with no tiles at all.
Properly update the velocity and the position¶
The position and the velocity update can be written as:
vx, vy = actor.velocity
# using the player controls, gravity and other acceleration influences
# update the velocity
vx = (keyboard[key.RIGHT] - keyboard[key.LEFT]) * actor.MOVE_SPEED
vy += GRAVITY * dt
if actor.on_ground and keyboard[key.SPACE]:
vy = actor.JUMP_SPEED
# with the updated velocity calculate the (tentative) displacement
dx = vx * dt
dy = vy * dt
# get the player's current bounding rectangle
last = actor.get_rect()
# build the tentative displaced rect
new = last.copy()
new.x += dx
new.y += dy
# account for hitting obstacles, it will adjust new and vx, vy
actor.velocity = mapcollider.collide_map(maplayer, last, new, vx, vy)
# update on_ground status
actor.on_ground = (new.y == last.y)
# update player position; player position is anchored at the center of the image rect
actor.position = new.center
How velocity changes in a collision is handled by method
on_bump_handler()
; it can be set to
custom code or to one of the stock handlers:
on_bump_bounce()
: Bounces when a wall is touched.
on_bump_stick()
: Stops all movement when any wall is touched. (sticky bomb…).
on_bump_slide()
: Blocks movement only in the axis that touched a wall. (player…).
For convenience, a stock handler can be specified at instantiation time with
the velocity_on_bump
parameter.
Call appropriate methods when it detects actor bumped into an obstacle¶
When mapcollider.collide_map
detects that actor collides with obj
‘someobj’ from side ‘someside’, it will call
mapcollider.collide_<someside>(someobj)
.
There ‘someside’ is one of ‘left’, ‘right’, ‘top’ and ‘bottom’.
This provides an opportunity to do things based on which object the actor touched, like ‘spike’ ? -> init the spikes animation and kill the actor.
Note that each mapcollider.collide_*
can receive multiple calls in the
same mapcollider.collide_map
call.
Tell if any collision in the x and / or y axis happened¶
After collide_map
returns, the flags mapcollider.bumped_x
and
mapcollider.bumped_y
tells if there has been any collisions along the
respective axis.
This can be handy to flip the actor’s animation direction, by example from ‘walk_left’ to ‘walk_right’.
Variants¶
Currently they are three map colliders variants:
RectMapCollider¶
Obstacles are rectangular tiles grouped into a RectMapLayer
; all non
empty cells are deemed solid.
RectMapWithPropsCollider¶
Obstacles are rectangular tiles grouped into a RectMapLayer
, cells
use properties “left”, “right”, “top” and “bottom” to signal which side(s)
block movements.
A solid block would probably have all four sides set; a platform can set only the top so the player might jump up from underneath and pass through.
For convenience these properties would typically be set on the tiles themselves, rather than on individual cells. Of course for the cell which is the entrance to a secret area you could override a wall’s properties to set the side to False and allow ingress.
See Map, Cell and Tile Properties for information about properties.
TmxObjectMapCollider¶
Obstacles are TmxObjects
grouped into a TmxObjectLayer
,
each object is meant to block movement.
A common use case is to do moving platforms in a platform game.
How to use¶
There are basically two ways to include this functionality into an actor class:
as a component, essentially passing (mapcollider, maplayer) in the actor’s __init__ method.
mixin style, by using
RectMapCollider
or a subclass as a secondary base class for the actor.
The component way is more decoupled while the Mixin style is more powerful
because the collision code will have access to the entire actor through its
self
attribute.
To have a working instance, the behavior of the velocity in a collision must
be defined, and that’s the job of the method on_bump_handler
if one of the stock
on_bump_<variant>
suits the requirements, suffices to write:mapcollider.on_bump_handler = mapcollider.on_bump_<desired variant>or passing a selector at instantiation time:
mapcollider = MapCollider(<desired variant>)for custom behavior define
on_bump_handler
in a subclass and instantiate it.