Hipmunk- A Haskell binding for Chipmunk.

Portabilityportable (needs FFI)
Safe HaskellNone




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.


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:

Shapes a and b started touching for the first time on this step (that is, they were not touching on the last step). May return False to ignore the collision entirely or True to process it normally. If the collision is ignored, then you will not receive PreSolve or PostSolve events, however you will receive a Separate event when they stop overlapping.
Shapes a and b are touching during this step. May return False to ignore the collision for this step or True to process it normally. You may also use this step to give custom friction or elasticity values.
Shapes a and b are touching and their collision response has been processed. You can retrive the collision force (e.g. to calculate sound volumes or damage amounts).
Shapes a and b stopped touching (or overlapping, if the collision was ignored on Begin) 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:

Called on the end of the step function. This is the only callback where you may remove entities from the space, using currentSpaceAdd or currentSpaceRemove.

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

data Begin Source

Phantom type used in Begin collision events.

data PreSolve Source

Phantom type used in PreSolve collision events.

data PostSolve Source

Phantom type used in PostSolve collision events.

data Separate Source

Phantom type used in Separate collision events.

data PostStep t Source

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.


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

data Callback t a Source

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

When the collision first occurs.
Before the collision is processed.
After the collision is processed.
When the collision ends.
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.


These functions can be used in any kind of callback.

shapes :: Callback t (Shape, Shape)Source

Shapes involved in this collision.

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

Add an entity to the current Space from where this callback was called. Don't add the same entity twice to a space.

You can add entities only in PostStep callbacks. You should not use liftIO and spaceAdd.

currentSpaceRemove :: Entity a => a -> Callback (PostStep t) ()Source

Remove an entity from the current Space from where this callback was called. Don't remove an entity that wasn't added.

You can remove entities only in PostStep callbacks. You should not use liftIO and spaceRemove.

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.

unsafePostStep :: Entity a => Space -> a -> IO () -> IO ()Source

As postStep, registers a PostStep callback. Unlike postStep, this function allows you to register a PostStep callback from anywhere. Also, from this callback you won't be in Callback monad. It is therefore unsafe and should not be used unless you really know what you are doing.

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.

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.