HGE2D

written in Haskell source documentation

A 2D game engine.

Connecting the state

Games can be written by defining a GameState and the EngineState event handlers for it. Example of a possible GameState you might define:

data GameState = GameState
    { gsSize :: (Double, Double) -- size of the window
    , time   :: Millisecond      -- current time of your game's state
    , player :: Player           -- your definition for a player
    , world  :: World            -- your definition for the world    
    }

The EngineState which has to be implemented for your GameState:

data EngineState a = EngineState
    { click           :: PosX -> PosY -> a -> a -- how your game should change when clicked
    , hover           :: PosX -> PosY -> a -> a -- how your game should change when hovered
    , drag            :: PosX -> PosY -> a -> a -- how your game should change when dragged
    , resize          :: (Width, Height) -> a -> a -- how to resize your game
    , getSize         :: a -> (Width, Height) -- how to get the size of your game
    , moveTime        :: Millisecond -> a -> a -- how your game should change over time
    , getTime         :: a -> Millisecond -- how to get the current time of your game
    , setTime         :: Millisecond -> a -> a -- how to set the time of your game
    , getTitle        :: a -> String -- how to get the title of your game
    , toGlInstr       :: a -> RenderInstruction -- how to receive a render instruction to display your game
    }

Rendering

Rendering is done by implementing GlInstructable for your GameState, which requires you to define a RenderInstruction for your state. This type is very composable and can be used to define any combination of basic shapes:


--- The class you have to implement
class GlInstructable a where
    toGlInstruction :: a -> RenderInstruction

--- The RenderInstruction type and its constructors
data RenderInstruction = RenderNothing
                       | RenderWithCamera GlPosX GlPosY GlScaleX GlScaleY RenderInstruction
                       | RenderText String
                       | RenderLineStrip GlShape GL.GLfloat
                       | RenderTriangle GlShape
                       | RenderLineLoop GlShape GL.GLfloat
                       | RenderScale GlScaleX GlScaleY
                       | RenderTranslate GlPosX GlPosY
                       | RenderRotate Double
                       | RenderColorize GlColorRGB
                       | RenderColorizeAlpha GlColorRGBA
                       | RenderPreserve RenderInstruction
                       | RenderMany [RenderInstruction]

--- Some example shapes included within HGE2D which can be used to define the RenderInstruction of your GameState
rectangle :: GlWidth -> GlHeight -> RenderInstruction
rectangle w h = RenderTriangle [ll, lr, ur, ur, ul, ll]
  where
    ll = point2 xMin yMin
    lr = point2 xMax yMin
    ur = point2 xMax yMax
    ul = point2 xMin yMax

    xMin = - (w/2)
    xMax =   (w/2)
    yMin = - (h/2)
    yMax = (h/2)

borderedRectangle :: GlWidth -> GlHeight -> GlThickness -> GlColorRGB -> GlColorRGBA -> RenderInstruction
borderedRectangle w h t colorInner colorBorder = RenderMany
    [ RenderColorize colorInner
    , rectangle w h
    , RenderColorizeAlpha colorBorder
    , wireFrame w h t
    ]

--- In a game this might look like this
instance GlInstructable Tower where
    toGlInstruction t = RenderMany
      [ baseRender (base t)
      , centerRender (center t)
      , RenderPreserve $ RenderMany
        [ RenderTranslate (rtf centerX) (rtf centerY)
        , RenderRotate (radGun $ gun t)
        , gunSkinRender (gunSkin $ gun t)
        , radarRender (radarSkin $ radar t)
        , rangeRenders
        ]
      ]
      where
          centerX = getX t
          centerY = getY t

          rangeRenders | not (towerHighlighted t) = RenderNothing
                       | otherwise = RenderMany [shotRangeRender (shotRange $ shot $ gun t), radarRangeRender (scanRange $ radar t)]

          baseRender :: Base -> RenderInstruction
          ...

          centerRender :: Center -> RenderInstruction
          ...

Some more types, classes and functions

-- | For types which are positioned in space
class Positioned a where
    getPos :: a -> RealPosition
    getX   :: a -> Double
    getY   :: a -> Double

-- | For types which are affected by time
class Dynamic a where
    moveInTime :: Millisecond -> a -> a

-- | Tests whether two objects collide (overlap in any way)
doCollide :: (HasBoundingBox a, HasBoundingBox b) => a -> b -> Bool

-- | A bounding box tree for fast collision detection
data (HasBoundingBox a) => AABBTree a = AABBTreeEmpty
                                      | AABBTreeLeaf [a] BoundingBox
                                      | AABBTreeBranch (AABBTree a) (AABBTree a) BoundingBox