Skip to content


Featured Posts

Why I switched from component-based game engine architecture to functional reactive programming

Components have become pretty popular these days and I'd like to share some issues we had with them in our game projects. I was experimenting with component-based game engine architectures for 2 years and eventually stumbled upon functional reactive programming (FRP) to solve some of the issues of game-object components. ...

Read More

Dataflow diagram of Yampa reactimate

download diagram (.svg), fonts (cmr10.ttf, cmtt10.ttf) For me as a Haskell beginner the biggest problem in understanding Yampa reactimate was how the objects are actually passed around and transformed as all the signatures are very, very... very generic. This diagram shows an example scenario staring Pacman (the player), a cherry (enemy ...

Read More

Aeon Racer mobile game

Today our little indie game start-up called Modern Alchemists released the first "bigger" game Aeon Racer for iPhone and Android! Check it out at http://aeon-racer.modern-alchemists.com (I declare an interest) I really do like the game mostly because of the controls. You really have to have fast reflexes to dodge the gates and I ...

Read More

FRP - Robotroannah Trailer

Robotroannah is a clone of the Robotron 2048 computer game. It uses functional reactive programming (FRP) with Haskell/Yampa and the hsSDL subsystem. FRP allows the functionality of a game to be connected in an incredible flexible and reusable way, resulting in about 250 lines of game specific code. Credits: FRP: ...

Read More

EclipseFP – Call for feature requests!

JP Moresmau’s, the (new) developer of EclipseFP Haskell IDE plugin, recently releast a new version 1.111 and wrote on EclipseFP blog:

There aren’t too many feature enhancements because I didn’t get any requests for it. I’m not too sure a lot of people are using EclipseFP and are interested in seeing it improved. I suppose now with a Cabalized gtk2hs Leksah becomes more attractive. Anyway, if you have requests for EclipseFP let me know!

There are probably a lot more users like me out there who use EclipseFP and actually do have feature requests. Maybe we should let the developers know and show the project some appreciation :).

For a user coming from the Windows world I think EclipseFP is the best Haskell IDE. It provides basic features for Haskell development in a familiar editor environment (unlike emacs) and is stable and easy to install (unlike Leksah on Ubuntu 10.04).

Tagged with , .


Dataflow diagram of Yampa reactimate

download diagram (.svg), fonts (cmr10.ttf, cmtt10.ttf)

For me as a Haskell beginner the biggest problem in understanding Yampa reactimate was how the objects are actually passed around and transformed as all the signatures are very, very… very generic. This diagram shows an example scenario staring Pacman (the player), a cherry (enemy trigger) and a ghost (the enemy).

  1. Starting at the upper left corner.
  2. Collect input events in init (which are empty here) and pass them through process, core all the way down to route and killAndSpawn.
  3. core is called with an initial empty object state (which is fed-back in recursively!*) and an initial list of object signal functions. It is very important to separate the logic of the objects (signal functions) and the output they produce (state).
  4. route gets the empty state and no input events, effectively keeping the object collection the same. Note that killAndSpawn doesn’t switch in this step. The object states are passed to output where they are rendered.
  5. In the next step (t=1), still having the same core (core=A), the user produces an input event which is routed to all objects and makes the Pacman move to the cherry. route only checks for collision events in the previous state, thus no collision events are recognized in this step.
  6. In t=2, still having the same core (core=A), route detects a collision between Pacman and the cherry and produces collision events, which are only routed to the objects in charge. This causes killAndSpawn to kill the cherry, spawn the ghost and therefore generate a switching event, which results in the creation of a new core in process.

*) I didn’t really know how to illustrate the recursion of core.

Tagged with , .


Activity diagram of Yampa reactimate

download diagram (.svg), fonts (cmr10.ttf, cmtt10.ttf)

  1. Collect the input events (init and input) in an IO task.
  2. Pass them to process (which is purely functional) and…
  3. core is a Yampa.dpSwitch which consists of route, the object list IL sf and killAndSpawn.
  4. route first reasons about all previous object states to produce logical events (collisions etc.) and…
  5. secondly bundles the input and logical events to the objects (IL (ObjEvents, sf)).
  6. Run all the signal functions which in turn may produce kill or spawn requests of new objects…
  7. which are applied in killAndSpawn to possibly get a new object collection (IL Object) which are then fed-back into core.
  8. Render all objects states and loop.

Tagged with , .


Diagram of Yampa primitives

download diagram (.svg), fonts (cmr10.ttf, cmtt10.ttf)

Tagged with , .


Yampa/SDL program stub

I just completed my first Yampa/SDL program stub. This stub is meant to provide a quickstart for using Yampa with SDL and explains the basic Yampa functions needed for game development in the most minimalistic way I could think of. You can also download the whole source file.

The “game” basically is a player object (black square) which can move around on a 3×3 field and an obstacle object (blue square) which gets killed on collision.

To get an overview of Yampa reactimate have a look at the diagrams of my 2 recent posts Activity diagram of Yampa reactimate and Dataflow diagram of Yampa reactimate.

definitions

At first we are defining some types:

  • Input: non-deterministic events from input devices which have to come from an IO task.
  • Logic: deterministic events from object preprocessor in route
  • ObjEvents: Input and Logic bundled together
  • State: the logical object states (position, velocity etc.) produced after each step which are used for collision detection and rendering
  • ObjOutput: the overall object output consisting of State and the produced kill- and respawn requests.
  • ObjOutput: just an abstract signal function type which takes the events and produces an output.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
module Main where

import IdentityList

import Maybe
import Control.Monad.Loops

import FRP.Yampa              as Yampa
import FRP.Yampa.Geometry
import Graphics.UI.SDL        as SDL
import Graphics.UI.SDL.Events as SDL.Events
import Graphics.UI.SDL.Keysym as SDL.Keysym

type Position2 = Point2  Double
type Velocity2 = Vector2 Double

type Input = [SDL.Event]    -- non-deterministic events from input devices
type Logic = Yampa.Event () -- deterministic events from object processor

data ObjEvents = ObjEvents
    { oeInput :: Input
    , oeLogic :: Logic
    } deriving (Show)

data State = Rectangle Position2 SDL.Rect SDL.Pixel | Debug String
     deriving (Show)

data ObjOutput = ObjOutput
    { ooState         :: State
    , ooKillRequest   :: Yampa.Event ()       -- NoEvent|Event ()
    , ooSpawnRequests :: Yampa.Event [Object]
    }

defaultObjOutput = ObjOutput
    { ooState         = undefined
    , ooKillRequest   = Yampa.NoEvent
    , ooSpawnRequests = Yampa.NoEvent
    }

type Object = SF ObjEvents ObjOutput

instance (Show a) => Show (Yampa.Event a) where
    show (Yampa.Event a) = "LogicEvent: " ++ (show a)
    show Yampa.NoEvent   = "NoEvent"

instance Show (SF a b) where
    show sf = "SF"

“IdentityList” is taken from the Yampa SpaceInvaders example which you can get via cabal unpack spaceinvaders.

main

Don’t get scared by the long definition, it mostly consists of object bindings. I split main into 2 definitions which can be run seperately by uncommenting them (line 4-5). mainLoop runs the complete game: move via [Arrow] keys and quit with [Esc]. mainSteps runs each step individually and in isolation which should help to understand what is going on and how the types are passed around and transformed. The steps are commented in the source, try to understand them by reading the highlighted lines, the object bindings and the output they produce!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
main :: IO ()
main = do
    -- Uncomment 'mainSteps' or 'mainLoop'!
    --mainLoop  -- Runs the complete reactimate loop.
    --mainSteps -- Tests each reactimate step individually.
  where
    mainLoop :: IO ()
    mainLoop = do
        reactimate initialize input output (process objs)
        SDL.quit
       where
         playerObj   = playerObject (Point2 16 16)
                                    (SDL.Rect (-8) (-8) 8 8)
                                    (SDL.Pixel 0x00000000)
         obstacleObj = staticObject (Point2 48 48)
                                    (SDL.Rect (-8) (-8) 8 8)
                                    (SDL.Pixel 0x000000FF)
         objs = (listToIL [playerObj, obstacleObj])

    mainSteps :: IO ()
    mainSteps = do
        -- initialize :: IO Input
        -- Poll first 'SDL.Event's (should only be 'LostFocus').
        events <- initialize

        -- input :: IO (DTime, Maybe Input)
        -- Poll 'SDL.Event's at each step (probably []).
        events <- input False

        -- hits :: [(ILKey, State)] -> [ILKey]
        -- Testing player over obstacle => collision event.
        putStrLn $ "hits 1: " ++ (show $ hits $ assocsIL $ fmap ooState oos1)

        -- Testing player over enemy => no event.
        putStrLn $ "hits 2: " ++ (show $ hits $ assocsIL $ fmap ooState oos2)

        -- route :: (Input, IL ObjOutput) -> IL sf -> IL (ObjEvents, sf)
        -- Routes 'key' SDL.Event to all 'Object's and
        -- previous object 'State's, if there are any.

        -- First routing step.
        -- No collision events are checked as there are no 'State's yet.
        putStrLn "first route: "
        --mapM putStrLn $ showILObjEvents $ route ([key], emptyIL) objs
        putStrLn $ show $ assocsIL $ route ([key], emptyIL) objs

        -- Intermediate routing step.
        -- Assuming player over obstacle object => create collision event.
        putStrLn "route step: "
        putStrLn $ show $ assocsIL $ route ([key], oos1) objs

        -- killAndSpawn :: (Input, IL ObjOutput)
        --              -> (Yampa.Event (IL Object -> IL Object))
        -- Kill and spawn new objects corresponding to 'ObjOutput' requests.
    -- Note how 'ooObstacle' defined a kill and spawn request
        putStr "objs before kill&Spawn: "
        putStrLn $ show $ keysIL objs
        putStr "objs after kill&Spawn: "
        putStrLn $ show $ keysIL $
            case (killAndSpawn (([], emptyIL), oos1)) of
                (Event d) -> d objs
                _         -> objs

        -- output :: IL ObjOutput -> IO Bool
        -- Just render the 'State's or quit if there is none.
        o1 <- output False oos1
        putStrLn $ show o1
        o2 <- output False oos2
        putStrLn $ show o2
        o3 <- output False emptyIL
        putStrLn $ show o3

        SDL.quit
      where
        key = KeyDown (Keysym
            { symKey = SDL.SDLK_RIGHT
            , symModifiers = []
            , symUnicode = '\0'
            })
        playerObj   = playerObject (Point2 16 16)
                                   (SDL.Rect (-8) (-8) 8 8)
                                   (SDL.Pixel 0x00000000)
        obstacleObj = staticObject (Point2 48 48)
                                   (SDL.Rect (-8) (-8) 8 8)
                                   (SDL.Pixel 0x000000FF)
        objs = (listToIL [playerObj, obstacleObj])

        enemyObj = staticObject (Point2 80 80)
                                (SDL.Rect (-8) (-8) 8 8)
                                (SDL.Pixel 0x00FF0000)
        ooPlayer = defaultObjOutput
            { ooState = Rectangle (Point2 48 48)
                                  (SDL.Rect (-8) (-8) 8 8)
                                  (SDL.Pixel 0x00000000)
            }
        ooObstacle = defaultObjOutput
            { ooState = Rectangle (Point2 48 48)
                                  (SDL.Rect (-8) (-8) 8 8)
                                  (SDL.Pixel 0x000000FF)
            , ooKillRequest   = Event ()
            , ooSpawnRequests = Event [enemyObj]
            }
        ooEnemy = defaultObjOutput
            { ooState = Rectangle (Point2 80 80)
                                  (SDL.Rect (-8) (-8) 8 8)
                                  (SDL.Pixel 0x00FF0000)
            }
        oos1 = listToIL [ooPlayer, ooObstacle]
        oos2 = listToIL [ooPlayer, ooEnemy]

output from mainSteps

…slightly modified for better readability.

0 = playerObject, 1 = obstacleObject, 2 = enemyObject

initialize (sense): [LostFocus [MouseFocus]]
input (sense): []

hits 1: [1,0]
hits 2: []

first route:
[(1, (ObjEvents { oeInput = [KeyDown (Keysym { symKey = SDLK_RIGHT, ... })]
                , oeLogic = NoEvent
                }, SF)),
 (0, (ObjEvents {oeInput = [KeyDown (Keysym { symKey = SDLK_RIGHT, ... })],
                , oeLogic = NoEvent
                }, SF))]

route step:
[(1, (ObjEvents { oeInput = [KeyDown (Keysym { symKey = SDLK_RIGHT, ... })]
                , oeLogic = LogicEvent: ()
                }, SF)),
 (0, (ObjEvents {oeInput = [KeyDown (Keysym { symKey = SDLK_RIGHT, ... })]
                , oeLogic = LogicEvent: ()
                }, SF))]

objs before kill&Spawn: [1,0]
objs after kill&Spawn:  [2,0]

output (actuate) + 500ms delay: False
output (actuate) + 500ms delay: False
output (actuate) + 500ms delay: True

reactimation IO (sense and actuate)

The IO steps are very simple. initialize and inputjust collect the input events (line 10, 22) and output defines the rendering to draw a rectangle or print a debug string and maps over the object output states to draw them.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
initialize :: IO Input
initialize = do
    SDL.init [SDL.InitVideo]
    screen <- SDL.setVideoMode windowWidth windowHeight
                               windowDepth [SDL.HWSurface]
    SDL.setCaption windowCaption []

    SDL.fillRect screen Nothing (SDL.Pixel 0x006495ED) -- 0x00RRGGBB
    SDL.flip screen
    events <- unfoldWhileM (/= SDL.NoEvent) SDL.pollEvent

    putStrLn $ "initialize (sense): " ++ show events
    return events
  where
    windowWidth   = 96
    windowHeight  = 96
    windowDepth   = 32
    windowCaption = "Yampa/SDL Stub"

input :: Bool -> IO (DTime, Maybe Input)
input _ = do
    events <- unfoldWhileM (/= SDL.NoEvent) SDL.pollEvent
    putStrLn $ "input (sense): " ++ show events
    return (1.0, Just events)

output :: Bool -> IL ObjOutput -> IO Bool
output _ oos = do
    putStrLn $ "output (actuate) + " ++ (show delayMs) ++ "ms delay: "

    screen <- SDL.getVideoSurface
    SDL.fillRect screen Nothing (SDL.Pixel 0x006495ED) -- Pixel 0x--RRGGBB

    mapM_ (\oo -> render (ooState oo) screen) (elemsIL oos) -- render 'State'!

    SDL.flip screen
    SDL.delay delayMs

    return $ null $ keysIL oos
  where
    delayMs = 500

    render :: State -> SDL.Surface -> IO ()
    render (Rectangle pos rect color) screen = do
        SDL.fillRect screen gRect color
        return ()
      where
        -- center rectangle around position
        x0 = round (point2X pos) + (rectX rect)
        y0 = round (point2Y pos) + (rectY rect)
        x1 = round (point2X pos) + (rectW rect)
        y1 = round (point2Y pos) + (rectH rect)
        gRect = Just (SDL.Rect x0 y0 (x1 - x0) (y1 - y0))
    render (Debug s) screen = putStrLn s

reactimation process (SF)

This is the most important step in reactimate (in -> SF in out -> out) and took me a while to understand. Again, try to get an overview first with the Activity diagram and Dataflow diagram!

process actually just wraps the core to be consistent with the reactimate signature and also feeds the previous output states back into core. The last expression is very interesting as it applies a list of insertIL and deleteIL functions (which are composited together in killAndSpawn) to the object list and switches into the new core. We can say the core is valid as long as the same objects exist.

1
2
3
4
5
6
7
process :: IL Object -> SF Input (IL ObjOutput)
process objs0 = proc input -> do
    rec
        -- 'process' stores the 'State's (note: rec) and
        -- passes them over to core
        oos <- core objs0 -< (input, oos)
    returnA -< oos

Note that core actually takes Input AND the previous object states (IL ObjOutput) as input signals. The dpSwitch is performed on a SF collection (hence parallel and the ‘p’) and the result is observable and applied at the next step (hence delayed and the ‘d’).

1
2
3
4
5
core :: IL Object -> SF (Input, IL ObjOutput) (IL ObjOutput)
core objs = dpSwitch route
                     objs
                     (arr killAndSpawn >>> notYet)
                     (\sfs' f -> core (f sfs'))

The route function actually has 2 tasks:

  1. Reason about the previous object state (if any) and generate logical events like collisions etc.
  2. Distribute input- and logical-events to the corresponding objects.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
route :: (Input, IL ObjOutput) -> IL sf -> IL (ObjEvents, sf)
route (input, oos) objs = mapIL routeAux objs
  where
    hs = hits (assocsIL (fmap ooState oos)) -- process all object 'State's
    routeAux (k, obj) = (ObjEvents
        { oeInput = input
        -- hit events are only routed to the objects they belong to (hence: routing)
        , oeLogic = if k `elem` hs then Event () else Yampa.NoEvent
        }, obj)

hits :: [(ILKey, State)] -> [ILKey]
hits kooss = concat (hitsAux kooss)
  where
    hitsAux [] = []
    -- Check each object 'State' against each other
    hitsAux ((k,oos):kooss) =
        [ [k, k'] | (k', oos') <- kooss, oos `hit` oos' ]
        ++ hitsAux kooss

    hit :: State -> State -> Bool
    (Rectangle p1 _ _) `hit` (Rectangle p2 _ _) = p1 == p2
    _ `hit` _ = False

killAndSpawn is actually pretty simply once you know what it is doing. It just looks up every object for kill and spawn requests and produces a function composition of deleteIL and insertIL which – in case of a event – is performed on the objects. Remember the expression from core: (\sfs' f -> core (f sfs'))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
killAndSpawn :: ((Input, IL ObjOutput), IL ObjOutput)
             -> Yampa.Event (IL Object -> IL Object)
killAndSpawn ((input, _), oos) =
    if any checkEscKey input
        then Event (\_ -> emptyIL) -- kill all 'State' on [Esc] => quit
        else foldl (mergeBy (.)) noEvent events
  where
    events :: [Yampa.Event (IL Object -> IL Object)]
    events = [ mergeBy (.)
                      (ooKillRequest oo `tag` (deleteIL k))
                      (fmap  (foldl (.) id . map insertIL_)
                             (ooSpawnRequests oo))
             | (k, oo) <- assocsIL oos ]
    checkEscKey (SDL.KeyDown (SDL.Keysym SDL.SDLK_ESCAPE  _ _)) = True
    checkEscKey _ = False

objects

The interesting parts here are that a Object can take parameters just like any other function to produce signal functions. Here it is used to specify the initial position for example. The actual position is calculated by a simple integrator based on the user input.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
playerObject :: Position2 -> SDL.Rect -> SDL.Pixel -> Object
playerObject p0 rect color = proc objEvents -> do
    -- .+^ is Point-Vector-addition
    -- ^+^ is Vector-Vector addition
    -- here we sum up all vectors based on the possibly multiple
    -- user inputs, thus allowing diagonal moves
    p <- (p0 .+^) ^<< integral -<
        foldl (^+^) (vector2 0 0) $ mapMaybe checkKey (oeInput objEvents)
    returnA -< defaultObjOutput { ooState = Rectangle p rect color }
    where
        checkKey (SDL.KeyUp (SDL.Keysym SDL.SDLK_UP    _ _)) =
            Just $ vector2    0 (-32)
        checkKey (SDL.KeyUp (SDL.Keysym SDL.SDLK_LEFT  _ _)) =
            Just $ vector2 (-32)   0
        checkKey (SDL.KeyUp (SDL.Keysym SDL.SDLK_DOWN  _ _)) =
            Just $ vector2    0   32
        checkKey (SDL.KeyUp (SDL.Keysym SDL.SDLK_RIGHT _ _)) =
            Just $ vector2   32    0
        checkKey _ = Nothing

staticObject :: Position2 -> SDL.Rect -> SDL.Pixel -> Object
staticObject p0 rect color = proc objEvents -> do
    returnA -< defaultObjOutput { ooState         = Rectangle p0 rect color
                                , ooKillRequest   = (oeLogic objEvents)
                                , ooSpawnRequests = (debugIfKilled objEvents)
                                }
  where
    debugIfKilled objEvents =
        case (oeLogic objEvents) of
            Yampa.Event () -> Event [debugObject "hit"]
            _              -> Event []

debugObject :: String -> Object
debugObject s = proc objEvents -> do
    returnA -< defaultObjOutput { ooState       = Debug s
                                , ooKillRequest = Event ()
                                }

Download the whole source file! (.hs)

Please let me know if the tutorial was helpful or if you didn’t understand something!

Tagged with , , , , , .


Learning Yampa and Functional Reactive Programming

Help us, help you! If you are just starting to learn Yampa, please share your experiences! This post just covers my personal opinion on how to tackle Yampa, maybe you have additional or different recommendations.

Update: over the time I wrote some “tutorials” myself, maybe you should start there first

  • Recommendations for learning

    I recommend reading the following papers/presentations to learn Yampa and FRP in general:

    1. A Brief Introduction to Functional Reactive Programming and Yampa (slides)
    2. Arrows, FRP, and Functional Reactive Programming (PPT)
    3. Arrows, Robots and Functional Programming: covers Yampa basics in detail (Section 3 is very domain specific and may be omitted)
    4. Functional Reactive Programming, Continued: more Yampa basics
    5. The Yampa Arcade: standard paper for games
    6. Dynamic, Interactive Virtual Environments: read chapter 3 – Time and appendix A – Functional Reactive Programming
    7. Functional Programming and 3D Games: Yampa basics in games, not very detailed though
    8. Functional Reactive Programming from First Principles: Yampa implementation details
    9. Dynamic Optimization for Functional Reactive Programming: Yampa optimization details

    Understanding FRP

    I think to learn FRP (for games) you have to especially understand the following aspects:

    • Signals make time omnipresent.
    • Systems are built with Signal Functions (SF a b).
    • FRP is implemented in standard Haskell.
    • Arrow notation makes using FRP convenient and more readable.
    • Signal functions diagrams look just mirrored to the actual arrow notation code :)
    • The signal function systems need to be updated somehow – usually via reactimate.
    • reactimate divides the programm into input IO (sense), the signal function (SF) and output IO (actuate).
    • Switches allow dynamic changes of the reactive system. Note that in Yampa signal functions are continuation-based so they “switch into” a new signal function.
    • To handle dynamic game object collections use the delayed parallel switch (dpSwitch) signal function.
    • Input events are propagated to the objects via route.
    • routealso reasons about the whole object collection to produce logical events (f.e. hit detection).
    • killOrSpawn collects all kill and spawn events into one big function composition (insertion/deletion) which is applied to the object collection.
    • In the Space Invaders example gameCore :: IL Object -> SF (GameInput, IL ObjOutput) (IL ObjOutput) is actually embedded in the function core :: ... -> SF GameInput (IL ObjOutput) which acts as the intermediate between sense and actuate.

    Complete list of recommended papers

    Covering FRP in general and FRP in games:

    List of discarded papers

    The reason for discarding was mostly because they are too old, too theoretical or off-topic from games:

    • A Functional Reactive Animation Of A Lift Using Fran
    • A Language for Declarative Robotic Programming
    • Crafting Game-Models Using Reactive System Design
    • Event-Driven FRP
    • FrTime – A Language for Reactive Programs
    • Functional Reactive Animation
    • Functional Reactive Programming for Real-Time Reactive Systems
    • Genuinely Functional User Interfaces
    • Interactive Functional Objects in Clean
    • Modelling Reactive Multimedia – Events and Behaviours
    • Modular Domain Specific Languages and Tools
    • Prototyping Real-Time Vision Systems
    • Reactive Multimedia Documents in a Functional Framework
    • Real-Time FRP

    Tagged with , , .


    Running primitive signal functions in Yampa

    To test signal functions in Yampa, use the embed function. Enter the following commands in the Haskell command-line to show the header definition:

    > :type FRP.Yampa.embed

    FRP.Yampa.embed :: FRP.Yampa.SF a b -> (a, [(FRP.Yampa.DTime, Maybe a)]) -> [b]

    So the parameters are:

    1. the signal function to run
    2. a tuple of…
      1. the first input value at time=0
      2. and a list of…
        1. (time, Nothing|Just nextValue)

    and return a list of values produced by the signal function.

    Primitive signal functions include: time, identity and constant

    main :: IO ()
    main = do
        putStrLn $ show $ embed time (Nothing, [(1.0, Nothing), (0.2, Nothing), (0.03, Nothing)])
        putStrLn $ show $ embed time (123, [(1.0, Just 234), (0.2, Just 345), (0.03, Just 456)])
        -- [0.0,1.0,1.2,1.23]
        -- [0.0,1.0,1.2,1.23]

        putStrLn $ show $ embed identity (123, [(1.0, Just 234), (0.2, Just 345), (0.03, Just 456)])
        putStrLn $ show $ embed identity (537, [(1.0, Nothing), (0.2, Nothing), (0.03, Just 123)])
        -- [123,234,345,456]
        -- [537,537,537,537]

        putStrLn $ show $ embed (constant 537) (Nothing, [(1.0, Nothing), (0.2, Nothing), (0.03, Nothing)])
        putStrLn $ show $ embed (constant 537) (123, [(1.0, Just 234), (0.2, Just 345), (0.03, Just 456)])
        -- putStrLn $ show (embed constant (123, [(1.0, Just 234), (0.2, Just 345), (0.03, Just 456)])) -- ERROR
        -- [537,537,537,537]
        -- [537,537,537,537]

    Tagged with , , .


    Concept of my game engine architecture

    this is the concept of my game engine architecture i just scribbled together (please don’t mind the bad design)

    Further descriptions will follow.

    Tagged with , , , .


    Quickstarting game development in Haskell and Ubuntu


    Frag — a first-person shooter by Mun Hon Cheong written in Haskell using GLUT and OpenGL.

    Quickstart

    This tutorial describes a quick overview and steps on how to get started in game development with Haskell and Ubuntu 10.04.

    In case you just want to see some examples get the newest Haskell platform package. Just type in console (you may want to update the download URL):

    sudo apt-get install ghc6 ghc6-prof ghc6-doc haddock libglut3-dev happy alex libedit-dev zlib1g-dev checkinstall cabal-install
    wget http://hackage.haskell.org/platform/2010.1.0.0/haskell-platform-2010.1.0.0.tar.gz
    tar zxf haskell-platform-2010.1.0.0.tar.gz
    cd haskell-platform-2010.1.0.0
    ./configure
    make
    sudo make install
    sudo echo PATH=$PATH:~/.cabal/bin
    cabal update
    cabal install cabal-install

    See Hackage games for a list of games written in Haskell. To install and run Frag f.e., type in console:

    cabal install frag
    cd ~/.cabal/bin/frag
    cabal unpack frag
    ./frag frag/leveleg

    This should be sufficient for all other games, some need external libraries however (i.e. “sudo apt-get install lib…”).

    Haskell IDEs

    If you are used to visual IDEs like Visual Studio or Eclipse (and want to avoid Vim or Emacs) you may consider the following 2 Haskell IDEs (don’t expect too much though @May 2010):

    I stuck with EclipseFP. To install EclipseFP, type in console:

    sudo apt-get install git-core eclipse
    git clone http://github.com/JPMoresmau/scion.git
    cd scion
    cabal install
    eclipse &

    (scion from Hackage seems broke (with GHC 6.12.1) so we use the version from JP Moresmau)

    in Eclipse do:

    1. Help -> Install new software -> Add -> “EclipseFP — http://eclipsefp.sf.net/updates”
    2. Window -> Open perspective… -> Haskell

    Graphics

    The Haskell community and libraries are still in heavy development, again don’t expect too much (@May 2010). Basically there are the following libraries available which you might be interested:

    • SDL: libsdl is a very mature and widespread library collection for 2D graphics and input (see also SDL-gfx, SDL-image, SDL-mixer, SDL-mpeg, SDL-ttf). tutorials available at: LazyFooHaskell, Animal-Machine.Com and all original SDL tutorials
    • hogre: Haskell bindings to a subset of the popular OGRE graphics engine, includes 3 small examples, library looks very clean, no documentation. note the version number though (build no. 3, no more activity). Couldn’t get it running on Windows 7 or Ubuntu, even with OGRE 1.6.4 or 1.7.1.
    • lambdacube-engine: pure functional graphics engine, includes some examples and runs out-of-the-box. note the version number though (build no. 2, no more activity)
    • HGL based on book The Haskell School of Expression
    • GLFW, GLUT, GLUT: as a last option, doing the low-level stuff manually

    I stuck with SDL (standard in 2D, quite mature, input handling included). To install SDL, type in console:

    sudo apt-get install libsdl1.2-all libsdl-dev
    cabal install SDL SDL-gfx SDL-image SDL-mixer SDL-mpeg SDL-ttf

    Reactive Programming

    Reactive programming (or more precise temporal programming with events) abstracts away time and events which is very useful in game development as most games are interactive real-time simulations. Use: Yampa! It’s maintained by the Yale Haskell Group, very mature, the 5th iteration or so of a reactive programming library especially designed for games and there are also some scientific papers available.

    Games using Yampa include: cuboid, Haskelloids, SpaceInvaders

    Blogs

    Also check out these blogs:

    • Antti Salonen: developer of HOGRE, FunGEn and some Haskell games
    • James Hague: former game developer, writing about functional programming and games
    • Luke Palmer: FRP and games in Haskell
    • Just Bottom: developer of minimalistic FRP library and games in Haskell

    Tagged with , , , , , , , .


    Welcome to my developer blog

    This blog is about game development, general programming and random stuff in computers and science.

    I’m a student in game development currently researching functional reactive programming and component-based game engines.