topical media & game development

talk show tell print

mobile-game-ch09-quintus.js / js



  /* Simple JavaScript Inheritance
   * By John Resig http://ejohn.org/
   * MIT Licensed.
   */
  // Inspired by base2 and Prototype
  (function(){
    var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
    // The base Class implementation (does nothing)
    this.Class = function(){};
    
    // Create a new Class that inherits from this class
    Class.extend = function(prop) {
      var _super = this.prototype;
      
      // Instantiate a base class (but only create the instance,
      // don't run the init constructor)
      initializing = true;
      var prototype = new this();
      initializing = false;
      
      // Copy the properties over onto the new prototype
      for (var name in prop) {
        // Check if we're overwriting an existing function
        prototype[name] = typeof prop[name] == "function" && 
          typeof _super[name] == "function" && fnTest.test(prop[name]) ?
          (function(name, fn){
            return function() {
              var tmp = this._super;
              
              // Add a new ._super() method that is the same method
              // but on the super-class
              this._super = _super[name];
              
              // The method only need to be bound temporarily, so we
              // remove it when we're done executing
              var ret = fn.apply(this, arguments);        
              this._super = tmp;
              
              return ret;
            };
          })(name, prop[name]) :
          prop[name];
      }
      
      // The dummy class constructor
      function Class() {
        // All construction is actually done in the init method
        if ( !initializing && this.init )
          this.init.apply(this, arguments);
      }
      
      // Populate our constructed prototype object
      Class.prototype = prototype;
      
      // Enforce the constructor to be what we expect
      Class.prototype.constructor = Class;
  
      // And make this class extendable
      Class.extend = arguments.callee;
      
      return Class;
    };
  })();
  
  (function() {
      var lastTime = 0;
      var vendors = ['ms', 'moz', 'webkit', 'o'];
      for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
          window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
          window.cancelAnimationFrame = 
            window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
      }
   
      if (!window.requestAnimationFrame)
          window.requestAnimationFrame = function(callback, element) {
              var currTime = new Date().getTime();
              var timeToCall = Math.max(0, 16 - (currTime - lastTime));
              var id = window.setTimeout(function() { callback(currTime + timeToCall); }, 
                timeToCall);
              lastTime = currTime + timeToCall;
              return id;
          };
   
      if (!window.cancelAnimationFrame)
          window.cancelAnimationFrame = function(id) {
              clearTimeout(id);
          };
  }());
  
  var Quintus = function(opts) {
    var Q = {};
  
    Q.options = {
      // TODO: set some sensible defaults
    };
    if(opts) { _(Q.options).extend(opts); }
  
    Q._normalizeArg = function(arg) {
      if(_.isString(arg)) {
        arg = arg.replace(/\s+/g,'').split(",");
      }
      if(!_.isArray(arg)) {
        arg = [ arg ];
      }
      return arg;
    };
  
    // Shortcut to extend Quintus with new functionality
    // binding the methods to Q
    Q.extend = function(obj) {
      _(Q).extend(obj);
      return Q;
    };
  
    // Syntax for including other modules into quintus
    Q.include = function(mod) {
      _.each(Q._normalizeArg(mod),function(m) {
        m = Quintus[m] || m;
        m(Q);
      });
      return Q;
    };
  
    Q.gameLoop = function(callback) {
  
      Q.lastGameLoopFrame = new Date().getTime();
  
      Q.gameLoopCallbackWrapper = function(now) {
        Q.loop = requestAnimationFrame(Q.gameLoopCallbackWrapper);
        var dt = now - Q.lastGameLoopFrame;
        if(dt > 100) { dt = 100; }
        callback.apply(Q,[dt / 1000]);  
        Q.lastGameLoopFrame = now;
      };
  
      requestAnimationFrame(Q.gameLoopCallbackWrapper);
    };
  
    Q.pauseGame = function() {
      if(Q.loop) {
        cancelAnimationFrame(Q.loop); 
      }
      Q.loop = null;
    };
  
    Q.unpauseGame = function() {
      if(!Q.loop) {
        Q.lastGameLoopFrame = new Date().getTime();
        Q.loop = requestAnimationFrame(Q.gameLoopCallbackWrapper);
      }
    };
   
    // Adds event support to any object
    // Extended with evented
    Q.Evented = Class.extend({
  
      // Binds a callback on a target object to an 
      // event on this object
      bind: function(event,target,callback) {
        // Handle the case where there is no target provided
        if(!callback) {
          callback = target;
          target = null;
        }
  
        // Handle case for callback that is a string
        if(_.isString(callback)) {
          callback = target[callback];
        }
  
        this.listeners = this.listeners || {};
        this.listeners[event] = this.listeners[event] || [];
        this.listeners[event].push([ target || this, callback]);
  
        if(target) {
          if(!target.binds) { target.binds = []; }
          target.binds.push([this,event,callback]);
        }
      },
  
      // Triggers an event on an object, 
      // triggering all listeners on the object
      trigger: function(event,data) {
        if(this.listeners && this.listeners[event]) {
          for(var i=0,len = this.listeners[event].length;i<len;i++) {
            var listener = this.listeners[event][i];
            listener[1].call(listener[0],data);
          }
        }
      },
  
      unbind: function(event,target,callback) {
        if(!target) {
          if(this.listeners[event]) {
            delete this.listeners[event];
            return;
          }
        } else {
          var l = this.listeners && this.listeners[event];
          if(l) {
            for(var i = l.length-1;i>=0;i--) {
              if(l[i][0] == target) {
                if(!callback || callback == l[i][1]) {
                  this.listeners[event].splice(i,1);
                }
              }
            }
          }
  
        }
      },
  
       // Removes any bound methods from 
       // this object
      debind: function() {
         if(this.binds) {
           for(var i=0,len=this.binds.length;i<len;i++) {
             var boundEvent = this.binds[i],
                 source = boundEvent[0],
                 event = boundEvent[1];
             source.unbind(event,this);
           }
         }
       }
    });
  
    Q.components = {};
  
    Q.register = function(name,methods) {
      methods.name = name;
      Q.components[name] = Q.Component.extend(methods);
    };
  
    Q.Component = Q.Evented.extend({
      init: function(entity) {
        this.entity = entity;
        if(this.extend) _.extend(entity,this.extend);
        entity[this.name] = this;
        entity.activeComponents.push(this.name);
        if(this.added) this.added();
      },
  
      destroy: function() {
        if(this.extend) {
          var extensions = _.keys(this.extend);
          for(var i=0,len=extensions.length;i<len;i++) {
            delete this.entity[extensions[i]];
          }
        }
  
        delete this.entity[this.name];
  
        var idx = this.entity.activeComponents.indexOf(this.name);
        if(idx != -1) { 
          this.entity.activeComponents.splice(idx,1);
        }
  
        this.debind();
        if(this.destroyed) this.destroyed();
      }
  
    });
  
   Q.GameObject = Q.Evented.extend({
  
      has: function(component) {
        return this[component] ? true : false; 
      },
  
      add: function(components) {
        components = Q._normalizeArg(components);
        if(!this.activeComponents) { this.activeComponents = []; }
  
        for(var i=0,len=components.length;i<len;i++) {
          var name = components[i],
              comp = Q.components[name];
          if(!this.has(name) && comp) { 
            var c = new comp(this); 
            this.trigger('addComponent',c);
          }
        }
        return this;
      }, 
  
      del: function(components) {
        components = Q._normalizeArg(components);
  
        for(var i=0,len=components.length;i<len;i++) {
          var name = components[i];
          if(name && this.has(name)) { 
            this.trigger('delComponent',this[name]);
            this[name].destroy(); 
          }
        }
        return this;
      },
  
      destroy: function() {
        if(this.destroyed) { return; }
        this.debind();
        if(this.parent && this.parent.remove) {
          this.parent.remove(this);
        }
        this.trigger('removed');
        this.destroyed = true;
      }
  
    });
  
    return Q;
  };
  


(C) Æliens 04/09/2009

You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.