Mario

Playing Mario with HTML5 Canvas

Playing Mario with HTML5 Canvas

×

Mario game sample, based on functional reactive Elm script. The F# version is using HTML 5 canvas to render the background and an img tag showing the Mario (using animated GIFs). To play the game, click the "Open sample" button on the right and use keyboard keys to play!

Mario step

We keep information about Mario in a single record type with fields that represent the current x and y coordinates (x and y), current velocity (vx and vy) and the current direction dir. The direction is used to pick the correct Mario image when rendering:

1: 
type Mario = { x:float; y:float; vx:float; vy:float; dir:string }

The step function of the game takes previvous Mario value and returns a new one. It is composed from 4 functions that represent different aspects of the game.

The functions that depend on keyboard take the current keyboard state as the first argument. This is represented as a tuple int*int consisting of x and y directions. For example, when the left key is pressed, the value is (-1, 0).

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
// If the Up key is pressed (y > 0) and Mario is on the ground, 
// then create Mario with the y velocity 'vy' set to 5
let jump (_,y) m = if y > 0 && m.y = 0. then  { m with vy = 5. } else m
// If Mario is in the air, then his "up" velocity is decreasing
let gravity m = if m.y > 0. then { m with vy = m.vy - 0.1 } else m
// Apply physics - move Mario according to the current velocities
let physics m = { m with x = m.x + m.vx; y = max 0. (m.y + m.vy) }
// When Left or Right keys are pressed, change the 'vx' velocity and direction
let walk (x,_) m = 
    { m with vx = float x 
             dir = if x < 0 then "left" elif x > 0 then "right" else m.dir }

The step function takes a dir parameter representing the keyboard status and a current Mario state. It simply runs 4 components in a pipeline:

1: 
let step dir mario = mario |> physics |> walk dir |> gravity |> jump dir

Rendering Mario

Now we're ready to render Mario using HTML 5 canvas! To do that, we need the width and height of the canvas and the current state of Mario. The following function fills the bottom half of the canvas with green, upper half with blue and then chooses the right Mario image:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
/// Render mario on canvas 
let render (w,h) (mario:Mario) =
    // Render background
    (0., 0., w, h) |> filled (rgb 174 238 238)
    (0., h-50., w, 50.) |> filled (rgb 74 163 41)
    // Select and position Mario
    // (walking is represented as an animated gif)
    let verb =
        if mario.y > 0. then "jump"
        elif mario.vx <> 0. then "walk"
        else "stand"
    "mario" + verb + mario.dir + ".gif" |> image 
    |> position (w/2.-16.+mario.x,  h-50.-31.-mario.y)

Driving the game

The last thing that needs to be done is to write the main function that drives the game. The function does some initialization and then starts a recursive update function that calculates a new game state using step and renders it in a loop:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
let main() =
  // Some initialization
  Keyboard.init()
  let w,h = dimensions()
  // Recursive function that updates the state & renders it
  let rec update mario () =
      let mario = mario |> step (Keyboard.arrows())
      render (w,h) mario      
      Globals.setTimeout(update mario, 1000. / 60.) |> ignore
  // Start the game with Mario in the center
  let mario = { x=0.; y=0.; vx=0.; vy=0.; dir="right" }
  update mario ()
   
type Mario =
  {x: float;
   y: float;
   vx: float;
   vy: float;
   dir: string;}

Full name: Program.Mario
Mario.x: float
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = System.Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
Mario.y: float
Mario.vx: float
Mario.vy: float
Mario.dir: string
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
val jump : 'a * y:int -> m:Mario -> Mario

Full name: Program.jump
val y : int
val m : Mario
val gravity : m:Mario -> Mario

Full name: Program.gravity
val physics : m:Mario -> Mario

Full name: Program.physics
val max : e1:'T -> e2:'T -> 'T (requires comparison)

Full name: Microsoft.FSharp.Core.Operators.max
val walk : x:int * 'a -> m:Mario -> Mario

Full name: Program.walk
val x : int
val step : int * int -> mario:Mario -> Mario

Full name: Program.step
val dir : int * int
val mario : Mario
val render : w:float * h:float -> mario:Mario -> unit

Full name: Program.render


 Render mario on canvas
val w : float
val h : float
val filled : color:'a -> float * float * float * float -> unit

Full name: Program.Window.filled
val rgb : r:'a -> g:'b -> b:'c -> string

Full name: Program.Window.rgb
val verb : string
val image : src:string -> HTMLImageElement

Full name: Program.Window.image
val position : x:'a * y:float -> img:HTMLImageElement -> unit

Full name: Program.Window.position
val main : unit -> unit

Full name: Program.main
module Keyboard

from Program


 Handles keydown and keyup events of the window and exposes them
 using the 'arrows' property (which is a tuple int*int with -1 if the
 left/up key is pressed, 1 if right/down key is pressed and 0 otherwise)
val init : unit -> unit

Full name: Program.Keyboard.init
val dimensions : unit -> float * float

Full name: Program.Window.dimensions
val update : (Mario -> unit -> unit)
val arrows : unit -> int * int

Full name: Program.Keyboard.arrows
type Globals

Full name: FunScript.TypeScript.Globals
static member Globals.setTimeout : handler:obj * ?timeout:obj -> float
static member Globals.setTimeout : handler:obj * timeout:obj * params args:obj array -> float
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore

Live demo

Click here to open the sample live, running using JavaScript in a dialog window.

To look at the generated JavaScript, view the source of this web page and scroll to the end of the file.

Tutorials

Learn FunScript from the following introductory tutorials

Samples

Browse other amazing samples created with FunScript

Fork me on GitHub