Quickstarting Hipmunk
This blog post was originally written back in 2011-04-23
Abstract
Youtube - Hipmunk tutorial video
This post describes a minimal Hipmunk program, a Haskell binding to the Chipmunk 2D physics engine, featuring a simple falling circle with circle collision shape. The goal was to exemplify how a Hipmunk program is structured corresponding to a FRP game engine model (see The Yampa Arcade (archived)) and testing a sophisticated 2D physics engine (for simplicity) with Haskell. A physics engine could actually be completely pure, investigations retrieved Hpysics, a pure functional physics engine. Didn’t use it, looks very basic, yet complete, but it’s an abandoned project.
Description
Most rigid body physics engine define the following objects:
Space: a physical simulation space independent from other spaces, describes gravity. When a body is added, the object becomes dynamic. When the shape is added too, collisions are also handled.
Body: a point which describes the position, using mass, moment of inertia (rotation), velocity, force, torque etc.
Shape: a geometrical figure which describes collision shapes and optionally has an offset to the body (f.e. when using multiple shapes)
Constraints: describe different restrictions on the movement of Bodies, like Pins, Springs, Ropes…
The scene definition thus is pretty straight forward: setup scene, run simulation, handle collisions, loop. What is unfortunate though is that – like all FFI bindings – the binding to the external Chipmunk engine is fundamentally unsafe, so there are lots of IO Monads to do basic stuff. Using Hipmunk in a FRP environment like Yampa would require lots of “task-” (or “message-”) definitions and communication from the logic all the way to the physics engine, only to apply forces. Note the many unnecessary IO and State Monads. Here is a small code snipped which does 3 simulation steps:
import Control.Monad
import Data.IORef
import Data.StateVar -- defines $=
import qualified Physics.Hipmunk as H
main :: IO ()
main = do
H.initChipmunk -- initChipmunk :: IO ()
space <- H.newSpace -- newSpace :: IO Space
H.gravity space $= H.Vector 0 (-10) -- gravity :: Space -> StateVar Gravity
body <- H.newBody mass momentOfInertia -- newBody :: Mass -> Moment -> IO Body
-- (+:) is a convenient function for vector construction defined in Playground.hs
-- compare with H.Vector above
H.position body $= 0 +: 0 -- position :: Body -> StateVar Position
-- class Entity a
-- instances: Shape, Body, StaticShape, (Constraint a)
H.spaceAdd space body -- spaceAdd :: Space -> a -> IO ()
-- newShape :: Body -> ShapeType -> Position -> IO Shape
shape <- H.newShape body (H.Circle 5) offset
-- step :: Space -> Time -> IO ()
H.step space elapsedSeconds -- IO () => y = 0
H.step space elapsedSeconds -- IO () => y = -10
H.step space elapsedSeconds -- IO () => y = -30
pos <- get . H.position $ body
putStrLn . show $ pos
where
mass = 1.0
momentOfInertia = H.infinity
offset = H.Vector 0 0
elapsedSeconds = 1.0
-- | Constructs a Vector.
(+:) :: H.CpFloat -> H.CpFloat -> H.Vector
(+:) = H.Vector
infix 4 +: