Portability | portable (needs FFI) |
---|---|
Stability | provisional |
Maintainer | felipe.lessa@gmail.com |
Safe Haskell | None |
Callbacks are functions that are called whenever certain events happen. For example, you may use a callback to know when a player bumps into an enemy. Or when a bullet hits its target. Or how strong was a collision.
- data Begin
- data PreSolve
- data PostSolve
- data Separate
- data PostStep t
- class NotSeparate t
- class NotPostStep t
- data Callback t a
- shapes :: Callback t (Shape, Shape)
- isFirstContact :: Callback t Bool
- normal :: NotSeparate t => Callback t Vector
- points :: NotSeparate t => Callback t [Position]
- totalImpulse :: NotSeparate t => Callback (PostStep t) Vector
- totalImpulseWithFriction :: NotSeparate t => Callback (PostStep t) Vector
- currentSpaceAdd :: Entity a => a -> Callback (PostStep t) ()
- currentSpaceRemove :: Entity a => a -> Callback (PostStep t) ()
- postStep :: (Entity a, NotPostStep t) => a -> Callback (PostStep t) () -> Callback t ()
- unsafePostStep :: Entity a => Space -> a -> IO () -> IO ()
- data CollisionHandler = Handler {
- beginHandler :: Maybe (Callback Begin Bool)
- preSolveHandler :: Maybe (Callback PreSolve Bool)
- postSolveHandler :: Maybe (Callback PostSolve ())
- separateHandler :: Maybe (Callback Separate ())
- setDefaultCollisionHandler :: Space -> CollisionHandler -> IO ()
- addCollisionHandler :: Space -> CollisionType -> CollisionType -> CollisionHandler -> IO ()
- removeCollisionHandler :: Space -> CollisionType -> CollisionType -> IO ()
Collision handlers
Collision handlers (CollisionHandler
) are tuples of 4
callback functions. Each function is called when a
different kind of collision events happens. Most of the
time they are triggered inside a
step
function call, however they can
also be called when a shape is removed.
Collision events always happen between two shapes, say a
and b
. Possible events are:
Begin
- Shapes
a
andb
started touching for the first time on this step (that is, they were not touching on the last step). May returnFalse
to ignore the collision entirely orTrue
to process it normally. If the collision is ignored, then you will not receivePreSolve
orPostSolve
events, however you will receive aSeparate
event when they stop overlapping. PreSolve
- Shapes
a
andb
are touching during this step. May returnFalse
to ignore the collision for this step orTrue
to process it normally. You may also use this step to give custom friction or elasticity values. PostSolve
- Shapes
a
andb
are touching and their collision response has been processed. You can retrive the collision force (e.g. to calculate sound volumes or damage amounts). Separate
- Shapes
a
andb
stopped touching (or overlapping, if the collision was ignored onBegin
) for the first time on this step (that is, on the last step they were touching or overlapping).
You may have many different kinds of collision handlers.
Each collision handler is associated with two
CollisionType
s. Whenever shapes a
and b
collide, if
there was a callback associated with a
's and b
's
collision types, then it is called. Otherwise the default
callback is called. The default callback is also
overrideable.
The callbacks themselves may execute arbitrary operations with a simple exception: callbacks cannot add or remove entities from the space. To that end, there exist another kind of callback:
PostStep
- Called on the end of the
step
function. This is the only callback where you may remove entities from the space, usingcurrentSpaceAdd
orcurrentSpaceRemove
.
Post-step callbacks are not collision handlers, because they
aren't called for each collision. Instead, inside a
collision handler you may use postStep
to add a post-step
callback that will be called when the step
ends. Each
post-step callback is associated with an entity, and there
can be only one post-step callback per entity.
Callback types
Phantom type used in PreSolve
collision events.
Phantom type used in PostSolve
collision events.
Phantom type used in PostStep
callbacks.
The phantom type t
inside this PostStep
phantom type is
the collision event that originated this PostStep
callback.
For example, if you add a PostStep
from a Begin
handler,
then it will have type PostStep Begin
. It is used by the
PostStep
's instance of NotSeparate
.
NotSeparate t => NotSeparate (PostStep t) |
class NotSeparate t Source
Class of collision events other than Separate
. That is,
collision events where the shapes are touching or overlapping.
class NotPostStep t Source
Class of callbacks called from collision events. That is,
everything other than PostStep
.
Callback monad
Monad where callbacks are run. Within this monad you have
access to functions describing the collision. You can also
run any IO actions using liftIO
from transformers
package.
However, remember not to call spaceAdd
or spaceRemove
outside a PostStep
callback -- use postStep
instead, for example:
postStep entity (currentSpaceRemove entity)
The phantom type t
describes the type of callback, which can be
Begin
- When the collision first occurs.
PreSolve
- Before the collision is processed.
PostSolve
- After the collision is processed.
Separate
- When the collision ends.
PostStep
- After the
step
finishes.
This phantom type is used to disallow invalid operations. For
example, you can't calculate the normal of a collision if you
are in a Separate
event, as there is no collision inside
this event. And you can't add a new post-step callback inside
a post-step callback.
Callback functions
Functions that may be called within a callback. We divide them in groups according to the kinds of callbacks allowed to use them.
General
These functions can be used in any kind of callback.
isFirstContact :: Callback t BoolSource
True
iff this is the first step that the shapes touched.
Only when colliding
These functions make sense only if the shapes a
and
b
are colliding, i.e., outside a Separate
event.
normal :: NotSeparate t => Callback t VectorSource
The normal vector of the collision.
points :: NotSeparate t => Callback t [Position]Source
Points where the collision occured.
Only in PostStep
These functions can be used only in PostStep
events. Use the postStep
function to add a
PostStep
callback.
totalImpulse :: NotSeparate t => Callback (PostStep t) VectorSource
The total impulse that was applied to resolve the collision. Returns incorrect results if elastic iterations are being used.
totalImpulseWithFriction :: NotSeparate t => Callback (PostStep t) VectorSource
The total impulse with friction that was applied to resolve the collision. Returns incorrect results if elastic iterations are being used.
currentSpaceAdd :: Entity a => a -> Callback (PostStep t) ()Source
currentSpaceRemove :: Entity a => a -> Callback (PostStep t) ()Source
Adding post-step callbacks
postStep :: (Entity a, NotPostStep t) => a -> Callback (PostStep t) () -> Callback t ()Source
postStep e cb
registers a callback cb
for the PostStep
phase on a given entity e
. PostStep
callbacks are called
once when the step
call finishes (and only on the current
time step). This is the only kind of callbacks that may call
currentSpaceAdd
and currentSpaceRemove
.
Each entity may have at most one callback registered on it.
If a second callback cb2
gets registered on the same entity
e
, then callback cb
will not be called, only cb2
.
This is not a bug, but a feature. This allows you to say, for
example, postStep shape (currentSpaceRemove shape)
every
time shape
collides. Even if shape
collided many times in
a single time step, only the last callback would be called and
shape
would be removed just once.
Note that this function registers a callback from within
another callback, as this is the motivation of using
PostStep
callbacks.
Adding collision handlers
data CollisionHandler Source
A 4-tuple of callbacks, one for each kind of collision event.
beginHandler
and preSolveHandler
should return a Bool
stating True
if the collision should be processed or False
if the collision should be ignored. If beginHandler
returns
False
, the collision will be completely ignored. If
preSolveHandler
returns False
, then the collision will be
ignored only for this time step.
You may also use Nothing
to use the default handlers. The
default is to process all collisions. That is, Handler
Nothing Nothing Nothing Nothing
is the same as
Handler {beginHandler = Just (return True) ,preSolveHandler = Just (return True) ,postSolveHandler = Just (return ()) ,separateHandler = Just (return ())}
however using Nothing
is more efficient (the Chipmunk
library won't need to call a Haskell function).
Note that assigning Nothing
does not mean that the default
set with setDefaultCollisionHandler
will be called. That
default is called only if there isn't a registered handler for
the given collision types.
Handler | |
|
setDefaultCollisionHandler :: Space -> CollisionHandler -> IO ()Source
Defines a new default collision handler. This handler is
used whenever two shapes a
and b
collide such that no
other collision pair function was defined to a
's and b
's
collision types. The default is Handler Nothing Nothing
Nothing Nothing
.
addCollisionHandler :: Space -> CollisionType -> CollisionType -> CollisionHandler -> IO ()Source
addCollisionHandler sp (cta,ctb) handler
defines handler
as the handler to be used whenever a collision occurs between
a shape of collision type cta
and another of collision type
ctb
(and vice versa). Any other callback already registered
to handle (cta,ctb)
will be removed.
Note that you should not add handlers to both combinations
of (cta,ctb)
and (ctb,cta)
. Doing so results in undefined
behaviour. A good rule of thumb is to always use cta <=
ctb
, although this is not necessary.
removeCollisionHandler :: Space -> CollisionType -> CollisionType -> IO ()Source
removeCollisionHandler sp (cta,ctb)
removes the handler
that was registered to handle (cta,ctb)
, if any (see
addCollisionHandler
). Any collisions that would be handled
by the removed handler will be handled by the default one (see
setDefaultCollisionHandler
).
Note that you should always use the same order that was
passed to addCollisionHandler
. In other words, after
addCollisionHandler sp (cta,ctb) handler
you should use
removeCollisionHandler sp (cta,ctb)
, and never
removeCollisionHandler sp (ctb,cta)
(note the swapped
tuple).
Although pointless, it is harmless to remove a callback that was not added.