gamecore.js is a framework to help build high-performance (and large) games 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)
We (Playcraft) like building games in javascript, but along the way we found some issues:
- Object-oriented - call us old fashioned, but object-orientation is just in our dna now, so we took what we felt was 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 game, so rather than implementing it sporadically, we built a way to easily pool any class. Ashley Gullen wrote a good post on this recently.
- 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.
Lastly, and probably most importantly, we open-sourced all this so that other library developers (creating code for use in games) can implement things like object pooling easily (and thus making it useful in a gaming context).
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;
}
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');
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();
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
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 and nextLinked members, such as:
var next = list.first;
while (next)
{
next.obj.DOSOMETHING();
next = next.nextLinked;
}
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().
Tim Down's awesome hashtable.
Example:
var map = new gamecore.Hashtable();
map.put('test1', obj);
var obj = map.get('test1');
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.
- Stats for the pooling system (object counts by type etc).
- Type introspection: RocketTurret.isa(‘EnemyBase’);
- 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)
Email us at support@playcraftlabs.com; we really like it if you included things like:
- A test case
- Error messages
- Line numbers of offending code
- Browser/Device you are testing on
See the included license.txt file.