Gameplay in HTML5: Time

Homework #6 review

Instructor's solution

http://EpsilonDelta.us/UW_GameplayHTML5/Homework/Solutions/6

Game time

Objective

Provide a time standard for the game that can be reset, paused, and resumed.

System time as the basis

JavaScript provides a built-in time source, the Date function, that we can uses as a basis for our gameTime. It returns a time in milliseconds. I prefer to use seconds.
         function getSystemSeconds( )
         {
             return new Date().getTime() / 1000.0;
         }
          

Initialization and reset

In its simplest form, we save the startTime and count seconds from there. Reset sets the startTime to now.
         var startTime = getSystemSeconds();
         
    //=========================================================================

         function getSeconds( )
         {
             return getSystemSeconds() - startTime;
         }

    //=========================================================================

         function reset( )
         {
             startTime = getSystemSeconds();
         }
          

Pause

We also keep a pauseTime variable, which is 0 except when the gameTime is paused. In that case, it is set to the time of pausing and the gameTime is stuck at that time. (Pausing a paused game has no effect.) Notice that reset() maintains the paused state, but the gameTime will be at 0 seconds.
         var startTime = getSystemSeconds(),
             pauseTime = 0;
         
    //=========================================================================

         function getSeconds( )
         {
             if ( pauseTime !== 0 )
             {
                 return pauseTime - startTime;
             }
             else
             {
                 return getSystemSeconds() - startTime;
             }
         }

    //=========================================================================

         function reset( )
         {
             startTime = getSystemSeconds();
             if ( pauseTime !== 0 )
             {
                 pauseTime = startTime;
             }
         }

    //=========================================================================

         function pause( )
         {
             if ( pauseTime === 0 )
             {
                 pauseTime = getSystemSeconds();
             }
         }
          

Resume

To resume a paused game, we reset pauseTime to 0, and also move the startTime forward by the amount of time the gameTime was paused.
         function resume( )
         {
             var now = getSystemSeconds();
             if ( pauseTime !== 0 )
             {
                 startTime += (now - pauseTime);
                 pauseTime = 0;
             }
         }
          

Example

Example 1

Scheduling actions

Objective

JavaScript provides methods for scheduling events, so that we can request that it call a function we provide at some future time.

setTimeout

The best-known function is setTimeout(), which we used in SplashScreen.js to update the progress bar. It takes two arguments, a function to be called, and the number of milliseconds in the future to call it.
    setTimeout( showProgress, 30 );
          
It only calls this function once, so if we want to call a function repeatedly, we need to call setTimeout() again, which is usually done inside the callback function itself. It is a good idea to have a flag or test to stop the repetition.
function myFunc( )
{
    doSomethingFun();
    if ( keepGoing )
        setTimeout( myFunc, 16 );
}
          

setInterval

The setInterval() function automatically repeats the call at the specified interval.
var interval = setInterval( myFunc, 30 );
          
You need to save the return value if you want to stop the repetition:
clearInterval( interval );
          
(setTimeout() also returns a handle that can be used by clearTimeout(), but since it has to be called explicitly on each repetition, as noted above, there are other ways to stop it.)

requestAnimationFrame

Overview

  • New to HTML5; still "experimental"
  • Specifically intended for functions that regularly update the screen, i.e. animations.
  • No fixed time between calls. Browser decides.
  • Presumably visible foreground windows and tabs get updated often; minimized or hidden ones, less often, maybe much less.
  • Rate may also be dependent on how fast and how busy the client machine is.

Polyfills

Because these functions are new, browser makers have provided their own variations in the meantime. We can provide "polyfills" to set the standard functions, if not already defined. The cancelAnimationFrame versions have gone by a couple names during their evolution. (In a browser, global functions are methods of the window.)
         window.requestAnimationFrame =
             (function()
              {
                  return window.requestAnimationFrame ||
                      window.webkitRequestAnimationFrame ||
                      window.mozRequestAnimationFrame ||
                      window.oRequestAnimationFrame ||
                      window.msRequestAnimationFrame ||
                      function( callback, element ) {
                          return window.setTimeout(
                              function() {
                                  callback( Date.now() );
                              },
                              1000 / 60 );
                      };
              }
             )();

    //.........................................................................

         window.cancelAnimationFrame =
             (function()
              {
                  return window.cancelAnimationFrame ||
                      window.cancelRequestAnimationFrame ||
                      window.webkitCancelAnimationFrame ||
                      window.webkitCancelRequestAnimationFrame ||
                      window.mozCancelAnimationFrame ||
                      window.mozCancelRequestAnimationFrame ||
                      window.oCancelAnimationFrame ||
                      window.oCancelRequestAnimationFrame ||
                      window.msCancelAnimationFrame ||
                      window.msCancelRequestAnimationFrame ||
                      window.clearTimeout;
              }
             )();
            

Usage

We pass a callback function and, optionally, a DOM element to requestAnimationFrame(). It returns a handle that can be used to cancel the request. Like setTimeout, the callback function is only called once, so for repetition requestAnimationFrame() needs to be re-called; this is usually done in the callback.
The callback function gets the current time passed to it as a parameter.
function myFunc( nowMillis )
{
    doSomethingFun( nowMillis );
    if ( keepGoing )
        requestAnimationFrame( myFunc );
}

requestAnimationFrame( myFunc );
            

A GameLoop module

Objective

A simple system for setting (and resetting) a function that gets called regularly for game updates and such.

Implementation

We provide a single public function setLoopFunction(), which accepts a function or null. The supplied function, if any, will be called periodically and passed the time, converted to seconds.
app.gameLoop =
    (function()
     {
    //-------------------------------------------------------------------------

         var loopFunction;

    //=========================================================================

         function setLoopFunction( func )
         {
             loopFunction = func;
         }
         
    //=========================================================================

         function doLoop( nowMillis )
         {
             if ( typeof loopFunction === "function" )
             {
                 loopFunction( nowMillis / 1000.0 );
             }
             requestAnimationFrame( doLoop );
         }

    //-------------------------------------------------------------------------

         requestAnimationFrame( doLoop ); //Begin looping
         
    //=========================================================================

        return {
            setLoopFunction: setLoopFunction
        };
         
    //-------------------------------------------------------------------------
     }
)();
          

Animating BeachSpin

In SpinScreen.js, we add an update function, which updates the model and view, and set this to loop. (This uses our gameTime, rather than the time passed to update().)
         function run( )
         {
             ...
             app.gameLoop.setLoopFunction( update );
         }

    //=========================================================================

         function update( )
         {
             var now = app.gameTime.getSeconds();
             app.spin.model.update( now );
             app.spin.view.update( );
         }
          
We do the same in BeachScreen.js. (Beach's view's update takes a time parameter so that it can update the background.)
         function update( )
         {
             var now = app.gameTime.getSeconds();
             app.beach.model.update( now );
             app.beach.view.update( now );
         }
          
In App.js we reset the loop function when switching screens:
app.showScreen = function( screenId )
{
    ...
    app.gameLoop.setLoopFunction( null );
    ...
};
          

Example

Example 2

Homework #7

http://EpsilonDelta.us/UW_GameplayHTML5/Homework/Homework_7.html