| Copyright | (c) 2008-2010 Felipe A. Lessa |
|---|---|
| License | MIT (see LICENSE) |
| Maintainer | felipe.lessa@gmail.com |
| Stability | provisional |
| Portability | portable (needs FFI) |
| Safe Haskell | None |
| Language | Haskell98 |
Physics.Hipmunk.Callbacks
Contents
Description
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
aandbstarted touching for the first time on this step (that is, they were not touching on the last step). May returnFalseto ignore the collision entirely orTrueto process it normally. If the collision is ignored, then you will not receivePreSolveorPostSolveevents, however you will receive aSeparateevent when they stop overlapping.PreSolve- Shapes
aandbare touching during this step. May returnFalseto ignore the collision for this step orTrueto process it normally. You may also use this step to give custom friction or elasticity values. PostSolve- Shapes
aandbare 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
aandbstopped 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
CollisionTypes. 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
stepfunction. This is the only callback where you may remove entities from the space, usingcurrentSpaceAddorcurrentSpaceRemove.
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 Begin collision events.
Instances
Phantom type used in PreSolve collision events.
Instances
Phantom type used in PostSolve collision events.
Instances
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.
Instances
| 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.
Instances
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
stepfinishes.
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 Bool Source
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 Vector Source
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) Vector Source
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) Vector Source
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.
Constructors
| Handler | |
Fields
| |
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.