Phillip Trelford's Array

POKE 36879,255

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:

Pingbacks and trackbacks (1)+

Comments are closed