Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I wrap up my engine?

I'm creating a game engine, or more like a large library of useful classes and functions, for Javascript. I plan to use it both for some scientific simulations on the server side and on the client side, so the spectrum of functionality will be quite broad, but always it will revolve around a virtual world (a game, for example).

I'm not sure how to wrap it up, though. If I just provide all the classes, it would pollute the global namespace, which is quite bad. Can I just place everything inside one object, that acts as a namespace? Should the framework itself be a class that can be instanced?

If the later option is chosen, how do I handle classes inside classes (constructor functions)?

var Engine = function()
{
    this.someVar = 4;
}

Engine.prototype.Scene = function()
{
    this.entities = [];
    //What if the scene object needs some classes that are in the engine? How does it get it's parent engine object?
}

Engine.prototype.Scene.prototype.render = function()
{
    //"this" should now represent an instance of a scene. But how can I get someVar from the engine? How can I traverse up in the hierarchy of classes?
}
like image 810
corazza Avatar asked Dec 28 '25 04:12

corazza


1 Answers

I prefer to use what's sometimes called a "revealing module" (... pattern). It looks like:

var Engine = (function($)
{
    $ = $ || {};
    var someVar = 4;

    $.Scene = function()
    {
        this.entities = [];
    }

    $.Scene.prototype.render = function()
    {
        // this function can access someVar just fine because of JavaScript's scoping rules
    }

    return $;
})(Engine);

This uses what's called an immediately-invoked function expression (hereafter referred to as an IIFE) to form a closure within the Engine object. Due to JavaScript's handling of scope, someVar is accessible to any function defined within the IIFE. The implication, however, is that no function can define it's own someVar if it wants to refer to the someVar you define in the IIFE.

The magic comes from the return statement. You can see that an object is returned, and within this object you must define anything you want to be "public".

The constructors, utility methods, etc. can then be accessed via Engine.Scene, which nicely namespaces your code.

As for the $ argument, this is so that you can pass Engine to the function in each file, add some methods/properties/constructors (or create a new one if it doesn't exist) and then pass the return value to another IIFE for further expansion.

This is the method used in many popular JavaScript frameworks including jQuery, dojo and LimeJS


scene.js:

var Engine = (function ($) {
  // this creates a new object if Engine is undefined in the 
  // invocation below, and keeps the old object otherwise.
  // alternatively: if ($ === undefined) { $ = new Object; }
  $ = $ || {};

  $.foo = "foo";
  $.Scene = function () {
    // blah blah
  }

  // Engine holds either a newly created object,
  // or the old one if it was already defined
  return $;
})(Engine);

sprite.js:

var Engine = (function ($) {
  $ = $ || {};

  $.Sprite = function () {
    // totally works
    this.bar = $.foo;
  }

  return $;
})(Engine);

You can then use them with something like:

<script type="text/javascript" src="bar.js"></script>
<script type="text/javascript" src="foo.js"></script>
<script type="text/javascript">
  var mySprite = new Engine.Sprite;
  var myScene = new Engine.Scene;
</script>

You can substitute $ with whatever you like, $$ is common, or you can be clever. It's just a placeholder for the global object you're adding on to.

like image 94
bkconrad Avatar answered Dec 30 '25 16:12

bkconrad