Skip to content

martinwells/objects.js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

57 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

objects.js

objects.js is a framework to help build high-performance (and large) games and apps using javascript.

It is comprised of:

  • Classes - an implementation of class.js (static inheritance, super methods and introspection)
  • General purpose object pooling - garbage collection bad, automatic super-fast object pooling good!
  • Class and object IDs
  • LinkedList - a high-performance double linked list
  • Device - a device independent map (well, a start at one anyway)

Why?

I like building big, high-performance things like games in javascript, but along the way I found some issues:

  • Object-oriented - the best of the javascript class systems (thanks to Prototype and class.js/Javascript MVC) and tweaked a few things.
  • Pooling - garbage collection is a pain for a high-performance apps, so rather than implementing it sporadically, we built a way to easily pool any class.
  • Linked Lists - we needed a way of storing game objects in a super-fast way, so we included a high-speed linked list.
  • We included some other tools, like a simple performance measurement, Tim Down's awesome hashtable and a device lookup for some general game shimming.

Using

gamecore.Class

A modified version of class.js to cater to static inheritance and deep object cloning. Based almost completely on class.js (Javascript MVC -- Justin Meyer, Brian Moschel, Michael Mayer and others) (http://javascriptmvc.com/contribute.html). Some portions adapted from Prototype JavaScript framework, version 1.6.0.1 (c) 2005-2007 Sam Stephenson

Easy class creation:

  var Fighter = gamecore.Base.extend('Fighter',
  {
      // static (this is inherited as well)
      firingSpeed: 1000
  },
  {
      // instance

      hp: 0,
      lastFireTime: 0,

      init: function(hp)    // instance constructor
      {
          this.hp = hp;
      },

      fire: function()
      {
          this._super(); // super methods!

          // do firing!
      }
  });

 var gunship = new Fighter(100);

Introspection:

  gamecore.Base.extend('Fighter.Gunship');
  Fighter.Gunship.shortName; // ‘Gunship’
  Fighter.Gunship.fullName;  // ‘Fighter.Gunship’
  Fighter.Gunship.namespace; // ‘Fighter’

Setup method will be called prior to any init -- nice if you want to do things without needing the users to call _super in the init, as well as for normalizing parameters.

  setup: function()
  {
     this.objectId = this.Class.totalObjects++;
     this.uniqueId = this.Class.fullName + ':' + this.objectId;
  }

gamecore.Base

A base class providing logging, object counting and unique object id's

Examples:

Unique ID and total objects:

var Fighter = gamecore.Base.extend('Fighter', {}, {});
var fighter1 = new Fighter();
var fighter2 = new Fighter();
fighter1.uniqueId;    // -> 'Fighter:0'
fighter2.uniqueId;    // -> 'Fighter:1'
Fighter.totalObjects; // -> 2

Logging: (log, info, warn, error, debug)

fighter1.warn('oops'); // == console.log('Fighter:0 [WARN] oops');

gamecore.Pooled

Easy (high-performance) object pooling.

A pool of objects for use in situations where you want to minimize object life cycling (and subsequently garbage collection). It also serves as a very high speed, minimal overhead collection for small numbers of objects.

This class maintains mutual set of doubly-linked lists in order to differentiate between objects that are in use and those that are unallocated from the pool. This allows for much faster cycling of only the in-use objects.

Pools are managed by class type, and will auto-expand as required. You can create a custom initial pool size by deriving from the Pool class and statically overriding INITIAL_POOL_SIZE.

Keep in mind that objects that are pooled are not constructed; they are "reset" when handed out. You need to "acquire" one and then reset its state, usually via a static create factory method.

Example:

Point = gamecore.Pooled('Point',  // derive from gamecore.Pooled
{
  // Static constructor
  create:function (x, y)   // super will handle allocation from a managed pool of objects
                           // the pool will autoexpand as required
  {
     var n = this._super();
     n.x = x;
     n.y = y;
     return n;
  }
},
{
   x:0, y:0,   // instance

   init: function(x, y)
   {
      this.x = x;
      this.y = y;
   }
}

To then access the object from the pool, use create, instead of new. Then release it.

var p = Point.create(100, 100);
// ... do something
p.release();

gamecore.PerformanceMeasure

A simple tool for measuring performance in ms and an (experimental) memory usage tracker.

var measure = new gamecore.PerformanceMeasure('A test');
// ... do something
console.log(measure.end()); // end returns a string you can easily log

gamecore.LinkedList

A high-speed doubly linked list of objects. Note that for speed reasons (using a dictionary lookup of cached nodes) there can only be a single instance of an object in the list at the same time. Adding the same object a second time will result in a silent return from the add method.

In order to keep a track of node links, an object must be able to identify itself with a getUniqueId() function.

To add/remove an item use:

list.add(newItem);
list.remove(newItem);

You can iterate using the first member of the list, then the next() method of each node, such as:

   var node = list.first;
   while (node)
   {
       node.object().DOSOMETHING();
       node = node.next();
   }

gamecore.Device

Static class with lots of device information, including:

  • pixelRatio - pixel ratio of the display (iPhone4 will return 2, everything else is a 1 generally)
  • isiPhone - is an iphone
  • isiPhone4 - is an iphone 4
  • isiPad - is an ipad
  • isAndroid - is an android device
  • isTouch - has a touch interface
  • isFirefox - is firefox
  • isChrome - is chrome
  • isOpera - is opera
  • isIE - is internet explorer
  • ieVersion - which version of explorer is it
  • requestAnimFrame - a platform shimmed requestAnimFrame that falls back to setTimeout
  • hasMemoryProfiling - determines if you can get access to heap memory

To enable memory profiling on Chrome, use: --enable-memory-info

To access memory use getUsedHeap() and getTotalHeap().

gamecore.Hashtable

Tim Down's awesome hashtable.

Example:

var map = new gamecore.Hashtable();
map.put('test1', obj);
var obj = map.get('test1');

Contribute

You can contribute to the core code by forking it and make a pull request.

  • Testing across a broader range of browsers.
  • A plugin system.
  • Memory leak protection (or detection) (autorelease?)
  • Interface enforcement at runtime (implements x)
  • Expand gamecore.Device to cover fullscreen api, mouse lock, audio, etc
  • Base math functions (using pooled objects and lots of caching -- we're working on this)

New Things

Typing

Classes are now automatically populated with three type related components:

  • _types: a variable that contains an array of types of this class (essentially the extends history)
  • _fullTypeName: a string representation of the extends hierarchy
  • isA(string): a function you can call which will return true if the class is of a given type string.

Example:

 Animal.extend('Tiger', {}, {});
 Tiger._types; // ['Animal', 'Tiger']
 Tiger._fullTypeName; // 'Animal | Tiger |"
 Tiger.isA('Animal'); // true

Pool Stats

Use pool stats to dump the number of free and used objects in pools.

 console.log( gamecore.Pool.getStats() );

This will dump stats for all currently created pools.

Pool stats are enabled by default (and have very little overhead).

Pool Tracing

Tracing is useful for tracking down where you've forgotten to release an object back into the pool. By turning on tracing for a pool, the system will track every acquire request with the source code file and line number. You can run your game with tracing on, and then dump the stats to see where acquire calls are being made in your code. The output is in the form of:

 class.methodname (source file url:line:column) (usage count)

For example:

 Class.pc.Input.fireAction (http://localhost:2020/playcraftjs/lib/input.js:320:46) (59)

Tracing is SLOW, so only turn it on for the pools you're debugging.

To enable tracing, grab the pool class you're interested in and set tracing to true.

 gamecore.Pool.getPool(Fighter).startTracing();

You can stop the tracing by calling:

 gamecore.Pool.getPool(Fighter).stopTracing();

And dump the traces (along with the pool stats) by calling getStats().

 console.log( gamecore.Pool.getPool(Fighter).getStats() );

If you want to enable tracing at the start of your code, you may want to manually force a pool to be constructed, for example:

 var p = Fighter.create( );                      // make sure the pool has been created
 gamecore.Pool.getPool(Fighter).startTracing();  // enable tracing
 p.release();

About

Core class tools and object pooling for high-performance gaming in Javascript

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published