Phillip Trelford's Array

POKE 36879,255

Lunar Landurz

In F# integer and floating point values can be associated with units of measure, typically used to indicate lengths, mass, etc. F# units of measure give compile time type safety with no runtime performance penalty!.Below is a playable Silverlight mini-game that applies units of measures in F# to 2D game mechanics.

Keys: Z = Left, X = Right, Space = Thrust

 

To complete the game you must land your craft on the flat ground (to make it a little easier the speed of landing and the orientation of the craft are not significant.

Note: this is a port of an XNA sample I wrote back in 2008.

Gravity

Use the Measure attribute to define your own measure types :

[<Measure>]
type m
[<Measure>]
type s

module Gravity = 
    let Earth = 9.8<m/s^2> // Standard Gravity
    let Lunar = Earth / 6.0 

Code: https://bitbucket.org/ptrelford/lunarlandurz

References:

Light Cycles

Back in 2007 I wrote a little game in F# using Windows Forms and Visual Studio 2005. It is based on the Light Cycle sequence in the 80s movie Tron which has been recently seen a sequel. The game is about as simple as they come weighing in at just under 200 lines of code. For fun I’ve ported it over to Silverlight:

 

The original code supported XBox 360 controllers via DirectX. For now in the Silverlight version you’ll have to can use joy2key to map your controller to keys.

Techie Bits

Where business applications tend to react to keyboard events, a game typically polls the keyboard state at a regular interval. Here I use a class to encapsulate the set of keys down:

/// Tracks which keys are down
type KeyState (control:Control) =
    let mutable keysDown = Set.empty
    do  control.KeyDown.Add (fun e -> keysDown <- keysDown.Add e.Key)
    do  control.KeyUp.Add (fun e -> keysDown <- keysDown.Remove e.Key)
    member this.IsKeyDown key = keysDown.Contains key
    member this.IsAnyKeyDown () = keysDown.Count > 0

The player’s colour, position and keys are encapsulated simply as a type so you could easily extend the game from 2 to 3 or 4 players or even add your own AI player:

/// Player state
type Player (color,startX,startY,startDirection,keys,keyHandler:KeyState) =
    let mutable x, y, direction = startX, startY, startDirection
    let up, down, left, right = keys
    member this.X = x
    member this.Y = y
    member this.Color = color
/// Player array
let players = 
    [|Player(Colors.Red,playArea/2-20,playArea/2,Down,
             (Key.Q,Key.A,Key.Z,Key.X),keys)
      Player(Colors.Cyan,playArea/2+20,playArea/2,Up,
             (Key.P,Key.L,Key.N,Key.M),keys)|]

The play area for the light cycles is represented simply as a WriteableBitmap:

let playArea = 500
let bitmap = WriteableBitmap(playArea,playArea)

Finally game updates are synchronised with Silverlight’s rendering by hooking the CompositionTarget.Rendering event:

/// Run game
let runGame () =
    let state = gameState.GetEnumerator()
    let rate = TimeSpan.FromSeconds(1.0/50.0)
    let lastUpdate = ref DateTime.Now
    let residual = ref (TimeSpan())
    CompositionTarget.Rendering.Add (fun x -> 
        let now = DateTime.Now
        residual := !residual + (now - !lastUpdate)
        while !residual > rate do
            state.MoveNext() |> ignore
            residual := !residual - rate
        lastUpdate := now
    )

 

References:

Invadurz

Keys: Z = left, X = right, Space = fire

 

Play along to the Prodigy:

Techie bit

The game was developed in F# using the freely available Visual Studio 2010 Shell and targets Silverlight 4. F# is a multi-paradigm .Net programming language, that encompasses functional programming as well as imperative and object-oriented programming styles.

Graphics

The graphics were drawn in binary as 0s and 1s:

let invader1 = 
    [|
    12,
        [|
        0b000001100000
        0b000011110000
        0b000111111000
        0b001101101100
        0b001111111100
        0b000010010000
        0b000101101000
        0b001010010100
        |]
    |]

and transformed to a WriteableBitmap

let toBitmap (width,xs:int []) =
    let bitmap = WriteableBitmap(width,xs.Length)
    let pixels = bitmap.Pixels
    xs |> Array.iteri (fun y xs ->
        for x = 0 to width-1 do
            let bit = 1 <<< (width - 1 - x) 
            pixels.[x+y*width] <-
                xs &&& bit = bit
                |> (function true -> 0xffffffff | false -> 0x00000000)
    )
    bitmap

Yield!

Sprite animation is implemented as state machines using F# Sequences:

let wait n = seq { for x = 1 to n do yield () }
let animation = seq {
    let i = ref i
    while true do
        yield! wait 6
        images.[!i].Visibility <- Visibility.Collapsed
        i := (!i+1) % images.Length
        images.[!i].Visibility <- Visibility.Visible
}

 

the sequence cycles through the provided images.

Keys

Games need to know if a key is down. F# has a lightweight equivalent of Reactive Extensions (Rx) baked in the Observable Module which provides higher order functions over Events:

    type Action = Left | Right | Fire
 
    let toAction = function
        | Key.Z -> Some Left
        | Key.X -> Some Right
        | Key.Space -> Some Fire
        | _ -> None

    let mutable actions = set []

    let startObservingKeys () =
        control.KeyDown
        |> Observable.choose (fun ke -> toAction ke.Key)
        |> Observable.subscribe(fun action -> 
            actions <- Set.add action actions)
        |> remember
        control.KeyUp
        |> Observable.choose (fun ke -> toAction ke.Key)
        |> Observable.subscribe (fun action -> 
            actions <- Set.remove action actions)
        |> remember

 

Selective memory

Well-behaved classes should remember the resources they have acquired and forget them once they are finished with them:

let mutable disposables = []
let remember disposable = disposables <- disposable :: disposables
let dispose (d:IDisposable) = d.Dispose()
let forget () = disposables |> List.iter dispose; disposables <- []

Je regrette rien:

interface System.IDisposable with
    member this.Dispose() = forget()

References