LMS v14.02 It works on Honeycomb!

Ok, calm down. Everything’s going to be ok.

I’ve managed to get it going well on my Acer A500 Tablet and I’ve heard it works on the Motorola Xoom – for every one else, your mileage may vary.

If you’re on another device, please let me know :)

LMS Update 14.00

UI wise I’ve added an options menu which, depending on your device, allows you to manage your volume and control type.

Behind the scenes I’ve changed how the sprites draw method is called hoping to speed things up a bit, but also to help me enable Euler Integration at a later date.

As an aside, I just bought an Acer A500 tablet and have been playing around with that. It’s not running optimally on it, but it’s getting there.

Stay tuned for my “It works on Honeycomb!” post.

Note: Volume only works (so-so) on desktops (for a number of reasons), and control type can only be changed on devices with accelerometers (iPod/iPhone, iPad, and Honeycomb tablets)

RK4 in JavaScript

I’ve been reading a couple of articles on game physics by Glenn Fiedler at gafferongames.com and thought I’d implement his Integration Basics and Fix your Timestep in javascript.

Click in the box to see it in action

O


Check out the javascript here or below.

/** STATE
**************************************************/
var State = function(state) {
  this.setXV(state ? state.x : 0, state ? state.v : 0);
};

State.prototype.setXV = function(x, v) {
  this.x = x || 0;
  this.v = v || 0;
};

/** DERIVATIVE
**************************************************/
var Derivative = function() {
  this.dx = 0;
  this.dv = 0;
};

/** MAIN
**************************************************/
var Main = function() {
  var getTimeInSeconds = function() {
    return (new Date).getTime() / 1000;
  };
  
  var t = 0.0
    , dt = 0.01
    , currentTime = 0.0
    , accumulator = 0.0
    , previous = new State()
    , current = new State()
    , state = null
    ;

  this.setXV = function(x, v) {
    current.setXV(x, v);
  };
  
  function acceleration(state, t) {
    var k = 10
      , b = 1
      ;
    return -k * state.x - b * state.v;
  }
  
  function derive(state, t) {
    var output = new Derivative();
    output.dx = state.v;
    output.dv = acceleration(state, t); 
    return output;
  }
  
  function evaluate(initial, t, dt, derivative) {
    var state = new State();
    state.x = initial.x + derivative.dx * dt;
    state.v = initial.v + derivative.dv * dt;
    return derive(state, t + dt);
  }
  
  function integrate(state,t, dt) {
    var a = derive(state, t)
      , b = evaluate(state, t, dt * 0.5, a)
      , c = evaluate(state, t, dt * 0.5, b)
      , d = evaluate(state, t, dt, c)
      , dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx)
      , dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv)
      ;
    state.x += dxdt * dt;
    state.v += dvdt * dt;
  }
  
  function interpolate(previous, current, alpha)
  {
    var state = new State();
    state.x = current.x * alpha + previous.x * (1 - alpha);
    state.v = current.v * alpha + previous.v * (1 - alpha);
    return state;
  }

  // shim layer with setTimeout fallback 
  window.requestAnimFrame = window.requestAnimationFrame
      || window.webkitRequestAnimationFrame
      || window.mozRequestAnimationFrame
      || window.oRequestAnimationFrame
      || window.msRequestAnimationFrame
      || function (callback) { window.setTimeout(callback, 1000 / 60); };

  function loop() {
    if (mouseUp) {
      var newTime = getTimeInSeconds()
        , deltaTime = newTime - currentTime
        , maxDeltaTime = 0.25
        ;
      currentTime = newTime;
      
      if (deltaTime > maxDeltaTime) {
        // note: max frame time to avoid spiral of death
        deltaTime = maxDeltaTime;  
      }

      accumulator += deltaTime;
      
      while(accumulator >= dt) {
        previous = new State(current);
        integrate(current, t, dt);
        t += dt;
        accumulator -= dt;
      }
      
      var alpha = accumulator / dt;
      current = interpolate(previous, current, alpha);
    }
    render(current); 
  }
  (function animLoop() { requestAnimFrame(animLoop); loop(); })();
};

var main = new Main();

Edit: Thanks to Darren (see comment below), I’ve updated the code so that getTime now returns seconds, and set the currentTime to newtime after updating deltaTime.

LMS Update v12.00

LMS has had a face lift. New splash, menu, help, about and high score pages.

I’ve also implemented forced landscape for motion devices (iOS, and Android devices). It plays much better.

Hint: Make sure you lock your screen (in portrait mode). I’ll have a look at compensating for orientation in a later version.

Also… SOUND! Now listen to the satisfyingly realistic tones of automatic gunfire!

Unfortuntely sound only works on the PC. The iOS browser doesn’t handle the audio element well enough yet.