Backbone.Engine
new Backbone.Engine([attributes], [options])
Backbone.Engine
is a Backbone model that holds a Backbone collection of sprite models.It uses HTML5's <a href="https://developer.mozilla.org/en/docs/Web/API/window.requestAnimationFrame" target="_blank">requestAnimationFrame</a> to provide a 60 frames per second game loop.</p><p>The sprite collection is stored in property <code>sprites</code>. You may directly access it however for convenience, methods <code>add</code>, <code>remove</code> and <code>reset</code> exist as proxy the engine.</p><h4>Attributes</h4><ul><li><code>clearOnDraw</code>: Optional. Boolean to ask for a clear of the canvas before redraw. Defaults to false. Note that this is an expensive call. Better to only clear the area that changed.</li><li><code>tapDetectionDelay</code>: Optional. The delay in ms before a tap gestured is detected. Defaults to 50ms</li><li><code>tapMoveTolerance</code>: Optional. The amount of pixel move tolerated to detect a tap gesture. Defaults to +/-5 pixels. Beyond that, a drag gesture will be trigerred.</li></ul><h4>Options</h4><p>Upon instantiation, these options can be passed. They will be stored as properties on the <code>Backbone.Engine</code> model instance.</p><ul><li><code>canvas</code>: The canvas to draw upon. Drawing is on its 2d context.</li><li><code>input</code>: Optional. The user control input instance. If passed and the pause button is enabled, will stop/start then engine when pressed.</li><li><code>debugPanel</code>: Optional. A <a href="#documentation-DebugPanel">Backbone.DebugPanel</a> instance. If passed <code>fps</code> and <code>cycleTime</code> are output.</li></ul><h4>Methods</h4><ul><li><code>add()</code>: Adds one or multiple models delegating to the sprite collection's add
method.remove()
: Removes one or multiple models delegating to the sprite collection's <code>remove</code> method.</li><li><code>reset()</code>: Clears or sets the sprites collection delegating to the <code>reset</code> method.</li><li><code>isRunning()</code>: Returns true if the engine is running, or false if not.</li><li><code>start(), stop()</code>: Starts or stops the engine.</li><li><code>toggle()</code>: Toggle start/stop the engine.</li></ul><h4>Events</h4><ul><li><code>tap</code>: Trigerred when the user clicks or taps on the canvas. A tap is defined when the user presses/clicks on a position without moving for more than <code>tapDetectionDelay</code> ms. The event callback function is passed the DOM event object, with these extra properties attached: <code>canvas</code>, <code>canvasX</code> and <code>canvasY</code>. In addition, property <code>canvasHandled</code> is provided as a mechanism to stop propagation (see below). </li><li><code>key</code>: Trigerred when the user types in a key. The event callback function is passed the DOM event object, with additional property <code>canvas</code>.</li><li><code>dragstart</code>, <code>dragmove</code> and <code>dragend</code>: Trigerred when a drag gesture occurs. This happens when the user presses/clicks and holds and moves. When these events are trigerred, the <code>tap</code> event does not get trigerred.</li></ul><div class="alert alert-info">Note: The <code>tap</code> and <code>drag*</code> events are broadcasted to whomever is listening. Event property <code>canvasHandled</code> is used to to prevent propagation to many overlapping objects. At first it is set to <code>false</code>. The first object to intercept and handle the event should set it to <code>true</code>. Subsequent objects intercepting the event should look at this property and return without action when <code>true</code>. <code>Backbone.Button</code> and <code>Backbone.WorldEditor</code> implement this behavior.</div><h4>How it works</h4><p>During every animation frame, the engine performs these things:</p><ul><li>Loop through models (in order), and calls their <code>update</code> method. Passing <code>dt</code>, the time in milliseconds since the last call to update. The update method must return <code>true</code> to ask for a redraw, or <code>false</code> not to.</li><li>Loop through all models that requested a redraw, and call their <code>draw</code> method passing <code>context</code>, the canvas 2d context. Perform whatever magic you like in the draw method.</li><li>Call itself again upon the next animation frame.</li></ul><div class="alert alert-info">Note: By default the engine does not clear the canvas before redraw. You can set the <code>clearOnDraw</code> option to do so however it is an expensive call. Better to do it only when required. See class <a href="#documentation-World">Backbone.World</a> for an example.</div><p>The <code>update</code> method is used to update the model position, animation, detect collisions, or whatever you like. If it requests a redraw, the engine will then call its <code>draw</code> method. The engine ensures that models are updated and drawn in the order they are sorted in the collection. You can define the sort order by defining a <a href="http://backbonejs.org/#Collection-comparator" target="_blank">comparator</a>.</p><p>Models added to the collection receive an <code>attach</code> event and have property <code>engine</code> set as backreference. When removed, they receive a <code>detach</code> event.</p><p>To measure performance, two properties are set: <code>fps</code> and <code>cycleTime</code>. If you passed option <code>debugPanel</code>, they will be drawn on screen.</p><p>The engine can be started and stopped. When running, will perform an update/draw sequence 60 times per second. Use methods <code>start</code>, <code>stop</code> or <code>toggle</code>. Use method <code>isRunning</code> to determine if the engine is running. If you passed option <code>input</code>, the engine will bind to the <em>pause</em> button (or the <em>p</em> key) to toggle start/stop.</p><h4>Usage</h4><pre>var canvas = document.getElementById("foreground");var debugPanel = new Backbone.DebugPanel();var ball = new Backbone.Ball({x: 100, y: 100, color: "blue"});var engine = new Backbone.Engine({clearOnDraw: true}, {canvas: canvas,debugPanel: debugPanel});engine.add([ball,debugPanel]);</pre><p>Taken from the <a href="ball/index.html" target="_blank">Bouncing Ball</a> example. Draws two models: the debug panel and a bouncing ball.</p></div></div><div id="documentation-SpriteSheet" class="row"><div class="col-md-12"><h3>Backbone.SpriteSheet</h3><pre>new Backbone.SpriteSheet([attributes], [options]);</pre><p><code>Backbone.SpriteSheet</code> is a Backbone model which breaks an image into frames used for animation.</p><h4>Attributes</h4><ul><li><code>img</code>: The <code>Image</code> object or element id selector of the image to find in the DOM (i.e. <code>#icons). A pointer to the Image object is then stored in property img
.<li><code>imgUrl</code>: Optional. The url to the image to load dynamically. If specified, and the image element does not exist, will try to load the image.</li><li><code>tileWidth, tileHeight</code>: Size of tiles in pixels.</li><li><code>tileColumns, tileRows</code>: Number of tiles in the image.</li></ul><h4>Properties</h4><ul><li><code>frames</code>: Array of animation frames. Automatically set when model is instantiated.</li><li><code>img</code>: Pointer to <code>Image</code> object. Automatically set when model is instantiated.</li></ul><h4>Events</h4><ul><li><code>spawnImg</code>: Trigerred when the image is fully loaded.</li><li><code>destroyImg</code>: Trigerred when the image is unloaded.</li></ul><p>When a sprite sheet is instantiated, an array of frames is built and stored in property <code>frames</code>. A frame object contains the coordinates of the frame. It consists of <code>{x, y, width, height}</code> representing the pixel position and size of the frame. These will be passed to the <a href="https://developer.mozilla.org/en/docs/Web/API/CanvasRenderingContext2D#drawImage()" target="_blank">HTML5 canvas drawImage</a> function as arguments <code>sx, sy, sw, sh</code> by the <code>draw</code> method.</p><p>Sprite sheets are not generally created on their own, but rather in a <code>Backbone.SpriteSheetCollection</code>. See below for usage.</p></div></div><div id="documentation-SpriteSheetCollection" class="row"><div class="col-md-12"><h3>Backbone.SpriteSheetCollection</h3><pre>new Backbone.SpriteSheetCollection([models], [options]);</pre><p><code>Backbone.SpriteSheetCollection</code> is a Backbone collection of <code>Backbone.SpriteSheet</code> models.</p><h4>Methods</h4><ul><li><code>attachToSpriteClasses()</code>: Attaches sprite sheets to sprite class prototypes. Does so by finding all defined sprite classes (<code>Backbone.*</code>) with default attribute <code>spriteSheet</code> matching a sprite sheet id in the collection. Then sets their <code>spriteSheet</code> property to point to the correct sprite sheet in the collection.</li></ul><h4>Events</h4><ul><li><code>allSpawnImg</code>: Trigerred when all images are fully loaded.</li></ul><h4>Usage</h4><p>Define your sprite sheets by creating a collection as such:</p><pre>this.spriteSheets = new Backbone.SpriteSheetCollection([{id: "mario",img: "#mario",tileWidth: 32,tileHeight: 64,tileColumns: 21,tileRows: 2}, {id: "tiles",img: "#tiles",tileWidth: 32,tileHeight: 32,tileColumns: 29,tileRows: 28}]).attachToSpriteClasses();</pre><p><img src="docs/super-mario-sprite.png" class="img-responsive" alt="Backbone.Input" /></p><p>Here two sprite sheets are created <code>mario</code> and <code>tiles</code>. Their graphics are in <code>Image</code> objects found in the DOM. The attribute <code>img</code> is the selector to retrieve them.</p><p>Calling method <code>attachToSpriteClasses</code> will attach the sprite sheets to each sprite class found in the <code>Backbone</code> namespace.</p></div></div><div id="documentation-Sprite" class="row"><div class="col-md-12"><h3>Backbone.Sprite</h3><pre>new Backbone.Sprite([attributes], [options]);</pre><p><code>Backbone.Sprite</code> is a <a href="http://backbonejs.org/#Model" target="_blank">Backbone Model</a> which implements the required <code>update</code> and <code>draw</code> methods to animate a sprite, frame by frame.</p><h4>Attributes</h4><ul><li><code>x, y</code>: The coordinates in pixels.</li><li><code>width, height</code>: Size of the sprite in pixels.</li><li><code>paddingLeft, paddingRight, paddingTop, paddingBottom</code>: Optional. Internal padding to account for empty space inside the tile of a sprite. Useful to specify empty zones for collision detection.</li><li><code>sequenceIndex</code>: The current animation sequence frame. Automatically set.</li><li><code>state</code>: The current animation.</li><li><code>spriteSheet</code>: Sprite sheet id. </li><li><code>collision</code>: Optional. For use with <a href="#documentation-World">Backbone.World</a>.</li><li><code>static</code>: Optional. For use with <a href="#documentation-World">Backbone.World</a>.</li><li><code>visible</code>: Optional boolean. If true, the sprite is not drawn. Default is false.</li><li><code>zIndex</code>: Optional. Specifies the drawing order. Higher value is drawn above sprites with lower values. Default is 0.</li></ul><div class="alert alert-info">Note: <code>zIndex</code> is only used in <code>Backbone.World</code>. Currently, it is partially implemented - only 0 and 1 values are recognized.</div><h4>Properties</h4><ul><li><code>animations</code>: Hash of animations of the sprite. Described further below.</li><li><code>spriteSheet</code>: Instance of the <code>Backbone.SpriteSheet</code> holding the images to animate. This is automatically set when you define your sprite sheet collection. See <a href="#documentation-SpriteSheetCollection">Backbone.SpriteSheetCollection</a> for details.</li><li><code>saveAttributes</code>: Attributes serialized for persistence. Defaults to <code>["name", "state", "sequenceIndex", "x", "y"]</code>.</li></ul><h4>Methods</h4><ul><li><code>toSave()</code>: Serializes attributes for persistence. Attributes to be serialized are specified in the <code>saveAttributes</code> property.</li><li><code>update(dt)</code>: Called by then engine 60 times a second. Updates sprite attribues and implements behavior. Returns true to ask for a redraw, or false for none.</li><li><code>onUpdate(dt)</code>: Not defined by default. If you define it, it is called at the end of <code>update</code>. Useful for extending the behavior of a sprite without having to overload metod <code>update</code>. Note that <code>draw</code> is called based on the Boolean return value of this method to decide whether to draw or not. It must therefore return <code>true</code> to perform a draw, or <code>false</code> not to.</li><li><code>draw(context, options)</code>: Called by the engine after <code>update</code>, if a redraw was asked. Takes care of rendering the sprite, and its proper animation at the correct position. Argument <code>options</code> can be used by a model serving as proxy. For example it is passed when drawn by a <code>Backbone.World</code>instance. It will contain <code>offsetX</code> and <code>offsetY</code> to transform <code>x</code> and <code>y</code> from world coordinates to canvas coordinates.</li><li><code>onDraw(context, options)</code>: Not defined by default. If defined, it is called at the end of <code>draw</code>. Useful for extending the rendering of a sprite without having to overload metod <code>draw</code>.</li><li><code>getAnimation([state])</code>: Returns the current animation based on argument <code>state</code>. If argument <code>state</code> is omitted, the attribute is used instead.</li><li><code>overlaps(x, y):</code>: Checks to see if the sprite overlaps with the passed coordinates. Returns a Boolean.</li><li><code>getLeft(withPadding), getRight(withPadding), getTop(withPadding), getBotttom(withPadding)</code>: Returns the left, right, top or bottom-most position of a sprite. Argument withPadding is a boolean specifying whether to include the padding or not. Defaults to false.</li><li><code>bbox(withPadding)</code>: Returns the bounding box of the sprite as an object <code>{x1, y1, x2, y2}</code>. Argument <code>withPadding</code> is a boolean specifying whether to include the padding or not. Defaults to false.</li><li><code>getCenterX(withPadding)</code>: Returns the center <code>x</code> of the sprite. Argument <code>withPadding</code> is a boolean specifying whether to include the padding or not. Defaults to false.</li><li><code>getCenterX(withPadding)</code>: Returns the center <code>y</code> of the sprite. Argument <code>withPadding</code> is a boolean specifying whether to include the padding or not. Defaults to false.</li></ul><h4>Events</h4><ul><li><code>attach</code>: Triggered when the sprite is attached to the engine.</li><li><code>detach</code>: Triggered when the sprite is detached to the engine.</li></ul><p>The <code>attach</code> and <code>detach</code> events can be used to start/stop listening to events. For example, the <code>Backbone.Hero</code> sprite starts listening to user input when attached, and stops when detached.</p><h4>Sprite and sprite sheets</h4><p>Graphics are obtained from a <a href="#documentation-SpriteSheet">Backbone.SpriteSheet</a> model. In attribute <code>spriteSheet</code>, specify the sprite sheet id you previously defined in a <code>Backbone.SpriteSheetCollection</code> instance. The collection will automatically attach it to the sprite by setting property <code>spriteSheet</code> as back reference. Building on the example above:</p><pre>var spriteSheets = new Backbone.SpriteSheetCollection([{id: "mario",img: "#mario",tileWidth: 32,tileHeight: 64,tileColumns: 21,tileRows: 2}]).attachToSpriteClasses();var mario = new Backbone.Mario({spriteSheet: "mario"});mario.get("spriteSheet");// mariospriteSheets.get("mario");// child {cid: "c2", attributes: Object, collection: child, _changing: false, _previousAttributes: Object…}mario.spriteSheet;// child {cid: "c2", attributes: Object, collection: child, _changing: false, _previousAttributes: Object…}</pre><h4>Inheritance</h4><p>Instantiating a <code>Backbone.Sprite</code> model is not very useful by itself. You must first extend the <code>Backbone.Sprite</code> class to provide your own animations and a pointer to the sprite sheet. For example this defines a sprite with 3 animations <code>idle</code>, <code>walk-left</code> and <code>walk-right</code>. It points to the sprite sheet id <code>mario</code>.</p><pre>Backbone.MySprite = Backbone.Sprite.extend({defaults: _.extend(_.deepClone(Backbone.Sprite.prototype.defaults), {x: 400,y: 400,spriteSheet: "mario",state: "idle",sequenceIndex: 0,static: false,collision: true}),animations: {idle: {sequences: [0]},"walk-right": {sequences: [1, 2, 3, 2],delay: 200},"walk-left": {sequences: [1, 2, 3, 2],scaleX: -1,delay: 200}}});</pre><p>Above, the <code>Backbone.Sprite</code> was sub-classed using <code>extend</code> method. <code>defaults</code> are the default attributes to give any new <code>Backbone.MySprite</code> instance. They extend the Sprite class' defaults.
If you want to reuse parts of defaults
or animations
from a parent class, make sure to make a copy. You can use the helper function _.deepClone
for that purpose (_.clone
only goes one level deep). Otherwise you may change the parent class behavior. For example the Backbone.PennieUg
class reuses the Backbone.Pennie
class' properties <code>defaults</code> and <code>animations</code> by first creating copies.</p><pre>Backbone.PennieUg = Backbone.Pennie.extend({defaults: _.extend(_.deepClone(Backbone.Pennie.prototype.defaults), {name: "pennie-ug"}),animations: _.deepClone(Backbone.Pennie.prototype.animations)});Backbone.PennieUg.prototype.animations.idle.sequences = [168, 168, 169, 170, 169, 168];</pre><div class="alert alert-info">Helper function <code>deepClone</code> was created as a mixin of <code>underscore</code>. When it makes sense, make general functions available that way. Look at the end of <code>src/core.js</code> for all mixins.</div><h4>Animations</h4><p>Sprite property <code>animations</code> contains a hash of animations. Each animation contains a sequence of frames and a delay between frames for animation. For example:</p><pre>animations: {idle: {sequences: [0, 1],delay: 200}}</pre><p>This defines an animation of two frames, alternating at an interval of 200ms. Values 0 and 1 in array <code>sequences</code> are frame indices defined in the sprite sheet. Sprite attributes <code>state</code> and <code>sequenceIndex</code> control which animation and sequence are currently used. The <code>sequenceIndex</code> is automatically incremented (and reset to 0) by the sprite's draw function. Attribute state
determines the current animation. It must be set to idle
in the above example (as there is only one).
Extra animation options are available. Here is a complete list:
sequences
: Array of frame indices, or squence objects. A sequence object looks like this: {frame: 52, x: 0, y: -32, scaleX: 1.00, scaleY: 1}
. It allows you to specify an offset to apply when the sprite is drawn, and a scaling factor.scaleX, scaleY
: Optional. Scaling factors. Set scaleX
to -1 to flip horizontally. Defaults to 1 if omitted.delay
: Optional. The time to change to the next sequence. No need to specify if there is only one frame (as there is no animation). You can also define a sprite method sequenceDelay
to programmatically return the delay. It will be passed the current animation.
For detailed examples of animations, look at file artifacts.js
in the super-mario-bros
folder. Class Backbone.Pennie
implements a basic animation sequence using frame indices, while Backbone.FlyingPennie
implements a more complex animation with sequence objects.
Backbone.Pennie = Backbone.AnimatedTile.extend({...animations: {idle: {sequences: [52, 52, 53, 54, 53, 52],delay: 50}},...Backbone.FlyingPennie = Backbone.Sprite.extend({...animations: {anim: {sequences: [{frame: 52, x: 0, y: -32, scaleX: 1.00, scaleY: 1},{frame: 52, x: 0, y: -64, scaleX: 0.50, scaleY: 1},{frame: 53, x: 0, y: -90, scaleX: 0.50, scaleY: 1},{frame: 53, x: 0, y: -128, scaleX: 1.00, scaleY: 1},{frame: 53, x: 0, y: -128, scaleX: 0.50, scaleY: 1},{frame: 52, x: 0, y: -112, scaleX: 0.50, scaleY: 1},{frame: 52, x: 0, y: -90, scaleX: 1.00, scaleY: 1},{frame: 52, x: 0, y: -80, scaleX: 0.50, scaleY: 1},{frame: 53, x: 0, y: -80, scaleX: 0.50, scaleY: 1}],delay: 50}},...