Game development in Firefox

Martin Stransky <stransky@redhat.com>

Game development in Firefox

Why web games?

  • Pros:
    • Runs everywhere, platform independent (PC, tablets, phones...)
    • Fast development, good tools (Firebug)
  • Cons:
    • JS speed (1000 vs. 40 MFLOPS)
    • JS is not as strong as C/C++ (pointers, classes..)

Target audience

  • Expected skills:
    • C/C++ or JS
    • SDL/OpenGL
  • Optional:
    • HTML, JS
    • OpenGL ES

Native web application

  • Written in pure JS
  • Utilize <canvas>, WebGL
  • JS libraries - glMatrix, jQuery, CubicVR.js (3D engine)
  • Browser Quest
  • Emberwind

C/C++ conversion

C/C++ conversion on Fedora 18

          #yum install clang
          git clone git://github.com/kripken/emscripten.git
          Download node.js from http://nodejs.org/download/, unpack
          $export NODE=path_to_node_binary;
          $export PATH=$PATH:path_to_emscripten_dir
          $emcc test.cpp -o hello.html
           
          $emconfigure ./configure; emmake make;
          $emcc binary -o game.html
        

C/C++ conversion issues

  • Virtual filesystem (local vs. web server) or --preload-file
  • Libraries - only SDL, OpenGL ES, libc by default
  • Development/Debugging - emcc generated a chaotic JS code

Minimal Web App skeleton

        <html>
        <script>
            function GameStart() {
                ...
            }
        </script>
        <body onload="GameStart()">
        <canvas id="canvas_id" width="500" height="500"></canvas>
        </body>
        </html>
        

2D grawing - Cairo library (Linux)

        function GameStart() {
            var canvas = document.getElementById("canvas_id");
            context = canvas.getContext("2d");
            context.fillRect(0, 0, canvas.width, canvas.height);
        }
        
demo
Specification at www.w3.org
Tutorials at http://www.html5canvastutorials.com/

3D grawing - WebGL (OpenGL ES)

        function GameStart() {
            var canvas = document.getElementById("canvas_id");
            gl = canvas.getContext("experimental-webgl");
            gl.clearColor(0.0, 0.0, 0.0, 1.0);
            gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        }
        
demo
Specification at http://www.khronos.org
Tutorials at http://learningwebgl.com

Fullscreen mode

Game loop (screen refresh)

        <script type="text/javascript" src="webgl-utils.js">
        </script>
        
        function gameLoop() {
            /* keyboard and mouse input, draw scene */         
            requestAnimFrame(gameLoop);
        }
        function GameStart() {        
            gameLoop();
        }
        

Keyboard input

https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent
        
        function handleKeyDown(event) {
            // event.keyCode contains KeyEvent.DOM_VK_* 
        }
        function handleKeyUp(event) {
            ...
        }
        document.onkeydown = handleKeyDown;
        document.onkeyup = handleKeyUp;
        

Mouse input

https://developer.mozilla.org/en-US/docs/DOM/MouseEvent
        function handleMouse(event) {
            // event.screenX, event.screenY - global coordinates
            // event.clientX, event.clientY - local(canvas) coord.
            // event.buttons - 1|2|4|8...
        }
        canvas.onmousedown = handleMouse;
        document.onmouseup = handleMouse;
        document.onmousemove = handleMouse;
        

Data format - JSON files

  • Plain text format, JS syntax http://www.json.org/
  • Built in JSON object in JS
  • Not compressed by default
  • HTTP gzip compression on web server
    (Content-Encoding: gzip)
        var json_text_data = "[1, 5, 10]";
        obj = JSON.parse(json_text_data);
        /* obj is an array with 1,5,10 elements now */        
        
		

Data loading - AJAX

When HTML page is loaded, usually async. (non blocking)
        var request = new XMLHttpRequest();
        request.open("GET", "my_data_file.json");
        request.onreadystatechange = function() {
            if (request.readyState == 4) { // 'DONE'
                var obj = JSON.parse(request.responseText);
            }
        }
        request.send();
        

Data save

For game configuration, can be disabled or removed by user:

  • Cookies - text format, restricted by domain (server),
    small size (KB), stored on server
          document.cookie = "key1=value1;key2=value2;expires=date";
          
  • LocalStorage - binary format (array), bigger (5MB),
    stored on localhost
          window.localStorage[key] = data;
          

Image & Texture loading

        var image = new Image();
        image.onload = function () {
            var texture = gl.createTexture();
            gl.bindTexture(gl.TEXTURE_2D, texture);
            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,
                          gl.UNSIGNED_BYTE, image);
        }
        // Set this after image.onload() definition
        image.src = "image_file.jpg";
        

WebGL - OpenGL ES web interface

  • Based on OpenGL ES 2.0
  • PC, Android, iPad/iPhone, even Windows can run it
  • A lightweight OpenGL version
    • Removed fixed rendering pipeline, only shaders
    • Vertex buffers only (without glBegin() & glEnd())

WebGL - Shaders

3D data, textures > vertex shader > pixel shader > screen

  • Vertex shader - vertex processor
    • Transform 3D(4D) object vertex with matrixes (world, camera, projection) to screen (2D coordinates)
    • Old way is glMatrixMode(), glLoadMatrixf()..
  • Pixel shader (fragment shader) - 2D rasterizer
    • Define color of rendered pixels (texture, lighting...)

Vertex Shaders - GLSL

      attribute vec3 vertex;
      attribute vec2 vertexTextureCoord;      
      uniform mat4  modelView;
      uniform mat4  projection;      
      varying vec2  textureCoord;
       
      void main(void) {
        gl_Position = projection * modelView * vec4(vertex,1.0);
        textureCoord = vertexTextureCoord;
      }
      

Pixel Shaders - GLSL

      precision mediump float;
       
      varying vec2      textureCoord;
      uniform sampler2D texture; // "contains" texture image
       
      void main(void) {
          gl_FragColor = texture2D(texture, 
                         vec2(TextureCoord.s, TextureCoord.t));
      }
      

WebGL - rendering - extra steps

  • Compose & build shader program:

    gl.createProgram(), gl.linkProgram(), gl.useProgram()

  • Get shader data location:

    gl.getAttribLocation(), gl.enableVertexAttribArray()

  • Set shader data:

    gl.vertexAttribPointer(), gl.uniform(), gl.uniformMatrix()

WebGL Shaders demos

http://webglplayground.net/

http://www.iquilezles.org/apps/shadertoy/

http://www.cake23.de/traveling-wavefronts-lit-up.html

Berusky 2 level rendering

JS performance

  • Use typed variables - Float32Array(), Int32Array(),...
  • JIT friendly code (dont use eval(), try{}/catch())
  • Avoid automatic type conversion (int to double),
    by binary operators
Nicolas B. Pierron's presentation from MozCamp 2012

Firebug - Debug and Profile

  • Firefox Add-on
  • console.log() to print messages
  • Built in debugger (breakpoints, tracking)
  • Profiler, element inspector...

Panda wants you!

Ask me at <stransky@redhat.com>
Take web game as a Diploma/Bachelor's thesis
Publicate at https://marketplace.mozilla.org/

Questions?

Slides & demo: https://github.com/stransky