├── .gitignore ├── .gitattributes ├── README.md └── modules ├── fps_counter.js ├── asset.js ├── polyfills.js ├── tile.js ├── sound.js ├── sprite.js ├── sprite_sheet.js ├── pixel.js ├── player.js ├── animated_sprite.js ├── layer.js ├── entity.js └── engine.js /.gitignore: -------------------------------------------------------------------------------- 1 | #Node.js 2 | node_modules/ 3 | 4 | #Pixel.js 5 | build/pixel.js 6 | build/pixel.min.js 7 | build/pixel.js.zip 8 | test/ 9 | dist/ -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pixel.js 2 | ========= 3 | A simple and flexible HTML5 and JavaScript powered game engine. 4 | 5 | ### Where can I find the documentation of the engine? 6 | The documentation as well as FAQs and a thorough 'Getting Started' guide can all be found on the website at http://rastating.github.io/pixel.js/ 7 | 8 | ### How do I build Pixel.js? 9 | Pixel.js, as you may have noticed, is split up into a number of different modules in order to ease the process of maintaining the code. In order to compile these modules into a single file you simply need to run the Python script that can be found in the build directory. 10 | 11 | This script requires that you have the SlimIt minifier package for Python, which can be found here: https://pypi.python.org/pypi/slimit 12 | 13 | ### Author 14 | Pixel.js was created and is currently maintained by [rastating](http://www.rastating.com/) ([@iamrastating](https://twitter.com/iamrastating)) 15 | -------------------------------------------------------------------------------- /modules/fps_counter.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013-2014 rastating 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see http://www.gnu.org/licenses/. 15 | 16 | PixelJS.FPSCounter = function (layer) { 17 | var self = this; 18 | this.fps = 0; 19 | this._lastUpdate = 1; 20 | this.layer = layer; 21 | }; 22 | 23 | PixelJS.extend(PixelJS.FPSCounter, PixelJS.Entity); 24 | 25 | PixelJS.FPSCounter.prototype.update = function (elapsedTime, dt) { 26 | if (this._lastUpdate >= 1) { 27 | this.fps = 1 / dt; 28 | this._lastUpdate = 0; 29 | } 30 | else { 31 | this._lastUpdate += dt; 32 | } 33 | 34 | return this; 35 | }; 36 | 37 | PixelJS.FPSCounter.prototype.draw = function () { 38 | this.layer.drawText('FPS: ' + Math.round(this.fps, 2), 39 | this.pos.x, 40 | this.pos.y, 41 | '18px "Courier New", Courier, monospace', 42 | '#00FF00' 43 | ); 44 | 45 | return this; 46 | }; -------------------------------------------------------------------------------- /modules/asset.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013-2014 rastating 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see http://www.gnu.org/licenses/. 15 | 16 | PixelJS.Asset = function () { 17 | "use strict"; 18 | this._prepInfo = undefined; 19 | }; 20 | 21 | PixelJS.Asset.prototype._loaded = false; 22 | PixelJS.Asset.prototype._onLoad = undefined; 23 | PixelJS.Asset.prototype.load = function (info, callback) { }; 24 | PixelJS.Asset.prototype.name = ''; 25 | 26 | PixelJS.Asset.prototype.onLoad = function (callback) { 27 | this._onLoad = callback; 28 | return this; 29 | }; 30 | 31 | PixelJS.Asset.prototype.prepare = function (info) { 32 | this._prepInfo = info; 33 | return this; 34 | }; 35 | 36 | Object.defineProperty(PixelJS.Asset.prototype, "loaded", { 37 | get: function () { 38 | return this._loaded; 39 | }, 40 | set: function (val) { 41 | this._loaded = val; 42 | if (this._onLoad !== undefined) { 43 | this._onLoad(this); 44 | } 45 | }, 46 | configurable: false, 47 | enumerable: false 48 | }); -------------------------------------------------------------------------------- /modules/polyfills.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var lastTime = 0; 3 | var vendors = ['webkit', 'moz']; 4 | for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 5 | window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; 6 | window.cancelAnimationFrame = 7 | window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; 8 | } 9 | 10 | if (!window.requestAnimationFrame) 11 | window.requestAnimationFrame = function(callback, element) { 12 | var currTime = new Date().getTime(); 13 | var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 14 | var id = window.setTimeout(function() { callback(currTime + timeToCall); }, 15 | timeToCall); 16 | lastTime = currTime + timeToCall; 17 | return id; 18 | }; 19 | 20 | if (!window.cancelAnimationFrame) 21 | window.cancelAnimationFrame = function(id) { 22 | clearTimeout(id); 23 | }; 24 | }()); 25 | 26 | /** 27 | * Provides requestAnimationFrame in a cross browser way. 28 | * @author paulirish / http://paulirish.com/ 29 | */ 30 | 31 | if ( !window.requestAnimationFrame ) { 32 | 33 | window.requestAnimationFrame = ( function() { 34 | 35 | return window.webkitRequestAnimationFrame || 36 | window.mozRequestAnimationFrame || 37 | window.oRequestAnimationFrame || 38 | window.msRequestAnimationFrame || 39 | function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) { 40 | 41 | window.setTimeout( callback, 1000 / 60 ); 42 | 43 | }; 44 | 45 | } )(); 46 | } 47 | 48 | HTMLDivElement.prototype.relMouseCoords = function (event) { 49 | var totalOffsetX = 0; 50 | var totalOffsetY = 0; 51 | var canvasX = 0; 52 | var canvasY = 0; 53 | var currentElement = this; 54 | 55 | do { 56 | totalOffsetX += currentElement.offsetLeft; 57 | totalOffsetY += currentElement.offsetTop; 58 | } 59 | while (currentElement = currentElement.offsetParent) 60 | 61 | canvasX = event.pageX - totalOffsetX; 62 | canvasY = event.pageY - totalOffsetY; 63 | 64 | // Fix for variable canvas width 65 | canvasX = Math.round( canvasX * (this.width / this.offsetWidth) ); 66 | canvasY = Math.round( canvasY * (this.height / this.offsetHeight) ); 67 | 68 | return {x:canvasX, y:canvasY} 69 | } -------------------------------------------------------------------------------- /modules/tile.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013-2014 rastating 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see http://www.gnu.org/licenses/. 15 | 16 | PixelJS.Tile = function () { 17 | "use strict"; 18 | this._scaledTile = new Image(); 19 | }; 20 | 21 | PixelJS.extend(PixelJS.Tile, PixelJS.Asset); 22 | 23 | PixelJS.Tile.prototype.load = function (info, callback) { 24 | var self = this; 25 | 26 | if (info !== undefined) { 27 | if (info.name !== undefined) { 28 | this.name = info.name; 29 | } 30 | 31 | if (info.size !== undefined) { 32 | this.size = info.size; 33 | } 34 | 35 | if (info.callback !== undefined) { 36 | this.onLoad(info.callback); 37 | } 38 | } 39 | 40 | if (callback !== undefined) { 41 | this.onLoad(callback); 42 | } 43 | 44 | var img = new Image(); 45 | img.onload = function () { 46 | var buffer = document.createElement('canvas'); 47 | var ctx = buffer.getContext('2d'); 48 | var pattern = ctx.createPattern(this, "repeat"); 49 | 50 | buffer.width = self.size.width; 51 | buffer.height = self.size.height; 52 | ctx.fillStyle = pattern; 53 | ctx.fillRect(0, 0, self.size.width, self.size.height); 54 | 55 | self._scaledTile.onload = function () { 56 | self.loaded = true; 57 | }; 58 | 59 | self._scaledTile.src = buffer.toDataURL(); 60 | }; 61 | 62 | img.src = PixelJS.assetPath + '/tiles/' + this.name; 63 | return this; 64 | }; 65 | 66 | PixelJS.Tile.prototype.update = function (elapsedTime, dt) { 67 | }; 68 | 69 | PixelJS.Tile.prototype.draw = function (entity) { 70 | if (this.loaded) { 71 | entity.layer.drawImage(this._scaledTile, entity.pos.x, entity.pos.y, undefined, entity.opacity); 72 | } 73 | 74 | return this; 75 | }; -------------------------------------------------------------------------------- /modules/sound.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013-2014 rastating 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see http://www.gnu.org/licenses/. 15 | 16 | PixelJS.Sound = function () { 17 | this._element = undefined; 18 | this._canPlay = false; 19 | this._prepInfo = undefined; 20 | }; 21 | 22 | PixelJS.Sound.prototype.load = function (info, callback) { 23 | var self = this; 24 | this._element = document.createElement('audio'); 25 | this._element.setAttribute('src', PixelJS.assetPath + '/sounds/' + info.name); 26 | this._element.addEventListener('canplaythrough', function () { 27 | self._canPlay = true; 28 | if (callback !== undefined) { 29 | callback(self); 30 | } 31 | }, true); 32 | this._element.load(); 33 | 34 | return this; 35 | }; 36 | 37 | PixelJS.Sound.prototype.pause = function () { 38 | if (this._canPlay) { 39 | this._element.pause(); 40 | } 41 | 42 | return this; 43 | }; 44 | 45 | PixelJS.Sound.prototype.play = function () { 46 | if (this._canPlay) { 47 | this._element.play(); 48 | } 49 | 50 | return this; 51 | }; 52 | 53 | PixelJS.Sound.prototype.prepare = function (info) { 54 | this._prepInfo = info; 55 | return this; 56 | }; 57 | 58 | PixelJS.Sound.prototype.seek = function (time) { 59 | if (this._canPlay) { 60 | this._element.currentTime = time; 61 | } 62 | 63 | return this; 64 | }; 65 | 66 | Object.defineProperty(PixelJS.Sound.prototype, "duration", { 67 | get: function () { return this._element.duration; }, 68 | configurable: false, 69 | enumerable: true 70 | }); 71 | 72 | Object.defineProperty(PixelJS.Sound.prototype, "loop", { 73 | get: function () { return this._element.loop; }, 74 | set: function (val) { this._element.loop = val; }, 75 | configurable: false, 76 | enumerable: true 77 | }); 78 | 79 | Object.defineProperty(PixelJS.Sound.prototype, "muted", { 80 | get: function () { return this._element.muted; }, 81 | set: function (val) { this._element.muted = val; }, 82 | configurable: false, 83 | enumerable: true 84 | }); 85 | 86 | Object.defineProperty(PixelJS.Sound.prototype, "paused", { 87 | get: function () { return this._element.paused; }, 88 | configurable: false, 89 | enumerable: true 90 | }); 91 | 92 | Object.defineProperty(PixelJS.Sound.prototype, "playbackRate", { 93 | get: function () { return this._element.defaultPlaybackRate; }, 94 | set: function (val) { this._element.defaultPlaybackRate = Math.max(val, 0.1); }, 95 | configurable: false, 96 | enumerable: true 97 | }); 98 | 99 | Object.defineProperty(PixelJS.Sound.prototype, "volume", { 100 | get: function () { return this._element.volume; }, 101 | set: function (val) { this._element.volume = val; }, 102 | configurable: false, 103 | enumerable: true 104 | }); -------------------------------------------------------------------------------- /modules/sprite.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013-2014 rastating 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see http://www.gnu.org/licenses/. 15 | 16 | PixelJS.Sprite = function () { 17 | "use strict"; 18 | this.image = new Image(); 19 | }; 20 | 21 | PixelJS.extend(PixelJS.Sprite, PixelJS.Asset); 22 | 23 | PixelJS.Sprite.prototype.hFlip = false; 24 | PixelJS.Sprite.prototype.rotation = undefined; 25 | PixelJS.Sprite.prototype.transparencyKey = undefined; 26 | 27 | PixelJS.Sprite.prototype._applyTransparencyKey = function (img, transparencyKey) { 28 | var canvas = document.createElement('canvas'); 29 | canvas.width = img.width; 30 | canvas.height = img.height; 31 | 32 | var ctx = canvas.getContext('2d'); 33 | ctx.drawImage(img, 0, 0); 34 | 35 | var pixels = ctx.getImageData(0, 0, img.width, img.height); 36 | var pixelData = pixels.data; 37 | 38 | // Each pixel consists of 4 integers to represent the RGBA value, hence stepping by 4. 39 | for (var i = 0, len = pixels.data.length; i < len; i += 4) { 40 | var r = pixelData[i]; 41 | var g = pixelData[i + 1]; 42 | var b = pixelData[i + 2]; 43 | 44 | // If the RGB values match, set the alpha pixel to zero (i.e. transparent). 45 | if (r === transparencyKey.r && g === transparencyKey.g && b === transparencyKey.b) { 46 | pixelData[i + 3] = 0; 47 | } 48 | 49 | // Data is supposed to be read only and thus this line will fail 50 | // in strict mode. A work around for this needs to be looked at. 51 | pixels.data = pixelData; 52 | } 53 | 54 | ctx.clearRect(0, 0, canvas.width, canvas.height); 55 | ctx.putImageData(pixels, 0, 0); 56 | 57 | img.src = canvas.toDataURL(); 58 | }; 59 | 60 | PixelJS.Sprite.prototype.load = function (info, callback) { 61 | "use strict"; 62 | var self = this; 63 | 64 | if (info !== undefined) { 65 | if (info.name !== undefined) { 66 | this.name = info.name; 67 | } 68 | 69 | if (info.transparencyKey !== undefined) { 70 | this.transparencyKey = info.transparencyKey; 71 | } 72 | 73 | if (info.callback !== undefined) { 74 | this.onLoad(info.callback); 75 | } 76 | } 77 | 78 | if (callback !== undefined) { 79 | this.onLoad(callback); 80 | } 81 | 82 | this.image.src = PixelJS.assetPath + '/sprites/' + this.name; 83 | this.image.onload = function () { 84 | self.image.onload = undefined; 85 | 86 | if (self.transparencyKey !== undefined) { 87 | self._applyTransparencyKey(self.image, self.transparencyKey); 88 | } 89 | 90 | self.loaded = true; 91 | }; 92 | 93 | return this; 94 | }; 95 | 96 | PixelJS.Sprite.prototype.draw = function (entity) { 97 | if (this.loaded && entity.visible) { 98 | entity.layer.drawImage(this.image, entity.pos.x, entity.pos.y, this.rotation, entity.opacity); 99 | } 100 | 101 | return this; 102 | }; 103 | -------------------------------------------------------------------------------- /modules/sprite_sheet.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013-2014 rastating 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see http://www.gnu.org/licenses/. 15 | 16 | PixelJS.SpriteSheet = function () { 17 | "use strict"; 18 | this.image = new Image(); 19 | 20 | this._frameImages = []; 21 | this._frameSize = { width: 0, height: 0 }; 22 | this._frameCount = 0; 23 | this._rowCount = 1; 24 | }; 25 | 26 | PixelJS.extend(PixelJS.SpriteSheet, PixelJS.Asset); 27 | 28 | Object.defineProperty(PixelJS.SpriteSheet.prototype, "frameSize", { 29 | get: function () { return this._frameSize; }, 30 | configurable: false, 31 | enumerable: false 32 | }); 33 | 34 | Object.defineProperty(PixelJS.SpriteSheet.prototype, "frameCount", { 35 | get: function () { return this._frameCount; }, 36 | configurable: false, 37 | enumerable: false 38 | }); 39 | 40 | Object.defineProperty(PixelJS.SpriteSheet.prototype, "rowCount", { 41 | get: function () { return this._rowCount; }, 42 | configurable: false, 43 | enumerable: false 44 | }); 45 | 46 | PixelJS.SpriteSheet.prototype._getFrameData = function (row, frame, transparencyKey) { 47 | var canvas = document.createElement('canvas'); 48 | canvas.width = this._frameSize.width; 49 | canvas.height = this._frameSize.height; 50 | 51 | var ctx = canvas.getContext('2d'); 52 | ctx.drawImage( 53 | this.image, 54 | 0 + Math.ceil(this._frameSize.width * frame), 55 | 0 + Math.ceil(this._frameSize.height * row), 56 | this._frameSize.width, this._frameSize.height, 57 | 0, 58 | 0, 59 | this._frameSize.width, this._frameSize.height 60 | ); 61 | 62 | var pixels = ctx.getImageData(0, 0, this._frameSize.width, this._frameSize.height); 63 | if (transparencyKey !== undefined) { 64 | // Each pixel consists of 4 integers to represent the RGBA value, hence stepping by 4. 65 | var pixelData = pixels.data; 66 | 67 | for (var i = 0, len = pixels.data.length; i < len; i += 4) { 68 | var r = pixelData[i]; 69 | var g = pixelData[i + 1]; 70 | var b = pixelData[i + 2]; 71 | 72 | // If the RGB values match, set the alpha pixel to zero (i.e. transparent). 73 | if (r === transparencyKey.r && g === transparencyKey.g && b === transparencyKey.b) { 74 | pixelData[i + 3] = 0; 75 | } 76 | } 77 | 78 | pixels.data = pixelData; 79 | } 80 | 81 | ctx.clearRect(0, 0, canvas.width, canvas.height); 82 | ctx.putImageData(pixels, 0, 0); 83 | 84 | var retval = new Image(); 85 | retval.src = canvas.toDataURL(); 86 | 87 | return retval; 88 | }; 89 | 90 | PixelJS.SpriteSheet.prototype.load = function (info, callback) { 91 | "use strict"; 92 | var self = this; 93 | var rows = info.rows != undefined ? info.rows : 1; 94 | this._rowCount = rows; 95 | this._frameCount = info.frames; 96 | 97 | if (info !== undefined) { 98 | if (info.name !== undefined) { 99 | this.name = info.name; 100 | } 101 | } 102 | 103 | this.image.src = PixelJS.assetPath + '/sprite_sheets/' + this.name; 104 | this.image.onload = function () { 105 | self._frameSize.width = Math.floor(self.image.width / info.frames); 106 | self._frameSize.height = Math.floor(self.image.height / rows); 107 | 108 | for (var r = 0; r < rows; r++) { 109 | self._frameImages[r] = []; 110 | for (var f = 0; f < info.frames; f++) { 111 | self._frameImages[r][f] = self._getFrameData(r, f, info.transparencyKey); 112 | } 113 | } 114 | 115 | callback(self); 116 | }; 117 | 118 | return this; 119 | }; -------------------------------------------------------------------------------- /modules/pixel.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013-2014 rastating 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see http://www.gnu.org/licenses/. 15 | 16 | var PixelJS = { 17 | AnimatedSprite: function () { }, 18 | Asset: function () { }, 19 | Engine: function () { }, 20 | Entity: function () { }, 21 | FPSCounter: function () { }, 22 | Layer: function () { }, 23 | Player: function () { }, 24 | Sprite: function () { }, 25 | SpriteSheet: function () { }, 26 | Tile: function () { }, 27 | 28 | _assetCache: [], 29 | assetPath: 'assets', 30 | 31 | existsInArray: function (item, arrayToSearch) { 32 | var i = arrayToSearch.length; 33 | while (i--) { 34 | if (arrayToSearch[i] == item) { 35 | return true; 36 | } 37 | } 38 | return false; 39 | }, 40 | 41 | extend: function (childClass, parentClass) { 42 | childClass.prototype = new parentClass(); 43 | childClass.prototype.constructor = childClass; 44 | }, 45 | 46 | proxy: function (callback, context, additionalArguments) { 47 | if (additionalArguments !== undefined) { 48 | callback.apply(context); 49 | } 50 | else { 51 | callback.apply(context, additionalArguments); 52 | } 53 | }, 54 | 55 | Keys: { 56 | Space: 32, 57 | Backspace: 8, 58 | Tab: 9, 59 | Enter: 13, 60 | Shift: 16, 61 | Control: 17, 62 | Alt: 18, 63 | Pause: 19, 64 | Break: 19, 65 | CapsLock: 20, 66 | Escape: 27, 67 | PageUp: 33, 68 | PageDown: 34, 69 | End: 35, 70 | Home: 36, 71 | Left: 37, 72 | Up: 38, 73 | Right: 39, 74 | Down: 40, 75 | Insert: 45, 76 | Delete: 46, 77 | Zero: 48, 78 | One: 49, 79 | Two: 50, 80 | Three: 51, 81 | Four: 52, 82 | Five: 53, 83 | Six: 54, 84 | Seven: 55, 85 | Eight: 56, 86 | Nine: 57, 87 | Colon: 59, 88 | NumPadFour: 100, 89 | NumPadFive: 101, 90 | NumPadSix: 102, 91 | NumPadSeven: 103, 92 | NumPadEight: 104, 93 | NumPadNine: 105, 94 | NumPadAsterisk: 106, 95 | NumPadPlus: 107, 96 | NumPadMinus: 109, 97 | Equals: 61, 98 | A: 65, 99 | B: 66, 100 | C: 67, 101 | D: 68, 102 | E: 69, 103 | NumPadPeriod: 110, 104 | NumPadSlash: 111, 105 | F1: 112, 106 | F2: 113, 107 | F3: 114, 108 | F4: 115, 109 | F5: 116, 110 | F6: 117, 111 | F7: 118, 112 | F8: 119, 113 | F: 70, 114 | G: 71, 115 | H: 72, 116 | I: 73, 117 | J: 74, 118 | K: 75, 119 | L: 76, 120 | M: 77, 121 | N: 78, 122 | O: 79, 123 | F9: 120, 124 | F10: 121, 125 | F11: 122, 126 | F12: 123, 127 | P: 80, 128 | Q: 81, 129 | R: 82, 130 | S: 83, 131 | T: 84, 132 | U: 85, 133 | V: 86, 134 | W: 87, 135 | X: 88, 136 | Y: 89, 137 | Z: 90, 138 | Windows: 91, 139 | ContextMenu: 93, 140 | NumPadZero: 96, 141 | NumPadOne: 97, 142 | NumPadTwo: 98, 143 | NumPadThree: 99, 144 | NumLock: 144, 145 | ScrollLock: 145, 146 | Pipe: 220, 147 | BackSlash: 220, 148 | OpeningSquareBracket: 219, 149 | OpeningCurlyBracket: 219, 150 | ClosingSquareBracket: 221, 151 | ClosingCurlyBracket: 221, 152 | Comma: 188, 153 | Period: 190, 154 | ForwardSlash: 191, 155 | Tilde: 222, 156 | Hash: 222 157 | }, 158 | 159 | Directions: { 160 | Left: 1, 161 | Right: 2, 162 | Up: 4, 163 | Down: 8 164 | }, 165 | 166 | Buttons: { 167 | Left: 1, 168 | Right: 2, 169 | Middle: 4 170 | } 171 | }; -------------------------------------------------------------------------------- /modules/player.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013-2014 rastating 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see http://www.gnu.org/licenses/. 15 | 16 | PixelJS.Player = function () { 17 | this._directionRowMap = { 18 | down: 0, 19 | left: 1, 20 | right: 2, 21 | up: 3 22 | }; 23 | 24 | this.direction = 0; 25 | this.allowDiagonalMovement = false; 26 | }; 27 | 28 | PixelJS.extend(PixelJS.Player, PixelJS.Entity); 29 | PixelJS.Player.prototype.isAnimatedSprite = true; 30 | 31 | PixelJS.Player.prototype.addToLayer = function (layer) { 32 | var self = this; 33 | layer.addComponent(this); 34 | this.layer = layer; 35 | 36 | this.layer.engine.on('keydown', function (keyCode) { 37 | switch (keyCode) { 38 | case PixelJS.Keys.Left: 39 | self.direction |= PixelJS.Directions.Left; 40 | break; 41 | 42 | case PixelJS.Keys.Up: 43 | self.direction |= PixelJS.Directions.Up; 44 | break; 45 | 46 | case PixelJS.Keys.Right: 47 | self.direction |= PixelJS.Directions.Right; 48 | break; 49 | 50 | case PixelJS.Keys.Down: 51 | self.direction |= PixelJS.Directions.Down; 52 | break; 53 | } 54 | 55 | self.layer.requiresDraw = true; 56 | }); 57 | 58 | this.layer.engine.on('keyup', function (keyCode) { 59 | switch (keyCode) { 60 | case PixelJS.Keys.Left: 61 | self.direction &= ~PixelJS.Directions.Left; 62 | break; 63 | 64 | case PixelJS.Keys.Up: 65 | self.direction &= ~PixelJS.Directions.Up; 66 | break; 67 | 68 | case PixelJS.Keys.Right: 69 | self.direction &= ~PixelJS.Directions.Right; 70 | break; 71 | 72 | case PixelJS.Keys.Down: 73 | self.direction &= ~PixelJS.Directions.Down; 74 | break; 75 | } 76 | 77 | self.layer.requiresDraw = true; 78 | }); 79 | 80 | return this; 81 | } 82 | 83 | PixelJS.Player.prototype.update = function (elapsedTime, dt) { 84 | if (this.allowDiagonalMovement) { 85 | if ((this.direction & PixelJS.Directions.Right) != 0) { 86 | this.moveRight(); 87 | } 88 | 89 | if ((this.direction & PixelJS.Directions.Left) != 0) { 90 | this.moveLeft(); 91 | } 92 | 93 | if ((this.direction & PixelJS.Directions.Up) != 0) { 94 | this.moveUp(); 95 | } 96 | 97 | if ((this.direction & PixelJS.Directions.Down) != 0) { 98 | this.moveDown(); 99 | } 100 | } 101 | else { 102 | if ((this.direction & PixelJS.Directions.Right) != 0) { 103 | if (this.isAnimatedSprite) { 104 | this.asset.startAnimating(); 105 | this.asset.row = this._directionRowMap.right; 106 | } 107 | this.moveRight(); 108 | } 109 | else if ((this.direction & PixelJS.Directions.Up) != 0) { 110 | if (this.isAnimatedSprite) { 111 | this.asset.row = this._directionRowMap.up; 112 | this.asset.startAnimating(); 113 | } 114 | this.moveUp(); 115 | } 116 | else if ((this.direction & PixelJS.Directions.Left) != 0) { 117 | if (this.isAnimatedSprite) { 118 | this.asset.row = this._directionRowMap.left; 119 | this.asset.startAnimating(); 120 | } 121 | this.moveLeft(); 122 | } 123 | else if ((this.direction & PixelJS.Directions.Down) != 0) { 124 | if (this.isAnimatedSprite) { 125 | this.asset.row = this._directionRowMap.down; 126 | this.asset.startAnimating(); 127 | } 128 | this.moveDown(); 129 | } 130 | else { 131 | if (this.isAnimatedSprite) { 132 | this.asset.stopAnimating(); 133 | } 134 | } 135 | } 136 | 137 | return this; 138 | }; -------------------------------------------------------------------------------- /modules/animated_sprite.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013-2014 rastating 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see http://www.gnu.org/licenses/. 15 | 16 | PixelJS.AnimatedSprite = function () { 17 | this.spriteSheet = {}; 18 | }; 19 | 20 | PixelJS.extend(PixelJS.AnimatedSprite, PixelJS.Sprite); 21 | 22 | PixelJS.AnimatedSprite.prototype._currentFrame = 0; 23 | PixelJS.AnimatedSprite.prototype._frameTickCount = 0; 24 | PixelJS.AnimatedSprite.prototype._isAnimating = true; 25 | PixelJS.AnimatedSprite.prototype._onAnimationComplete = undefined; 26 | PixelJS.AnimatedSprite.prototype.defaultFrame = 0; 27 | PixelJS.AnimatedSprite.prototype.lastFrame = 0; 28 | PixelJS.AnimatedSprite.prototype.loop = true; 29 | PixelJS.AnimatedSprite.prototype.row = 0; 30 | PixelJS.AnimatedSprite.prototype.speed = 0.2; 31 | 32 | Object.defineProperty(PixelJS.AnimatedSprite.prototype, "resetFrame", { 33 | get: function () { return this.lastFrame; }, 34 | set: function (value) { this.lastFrame = value; }, 35 | configurable: false, 36 | enumerable: true 37 | }); 38 | 39 | Object.defineProperty(PixelJS.AnimatedSprite.prototype, "isAnimating", { 40 | get: function () { return this._isAnimating; }, 41 | configurable: false, 42 | enumerable: false 43 | }); 44 | 45 | PixelJS.AnimatedSprite.prototype.startAnimating = function () { 46 | this._isAnimating = true; 47 | return this; 48 | } 49 | 50 | PixelJS.AnimatedSprite.prototype.stopAnimating = function () { 51 | this._isAnimating = false; 52 | return this; 53 | } 54 | 55 | PixelJS.AnimatedSprite.prototype.load = function (info, callback) { 56 | "use strict"; 57 | var self = this; 58 | 59 | if (info !== undefined) { 60 | if (info.name !== undefined) { 61 | this.name = info.name; 62 | } 63 | 64 | if (info.transparencyKey !== undefined) { 65 | this.transparencyKey = info.transparencyKey; 66 | } 67 | 68 | if (info.defaultFrame !== undefined) { 69 | this.defaultFrame = info.defaultFrame; 70 | } 71 | 72 | if (info.speed !== undefined) { 73 | this.speed = info.speed; 74 | } 75 | 76 | if (info.callback !== undefined) { 77 | this.onLoad(info.callback); 78 | } 79 | } 80 | 81 | if (callback !== undefined) { 82 | this.onLoad(callback); 83 | } 84 | 85 | this.spriteSheet = new PixelJS.SpriteSheet(); 86 | this.spriteSheet.load(info, function () { 87 | self.loaded = true; 88 | }); 89 | 90 | return this; 91 | }; 92 | 93 | PixelJS.AnimatedSprite.prototype.draw = function (entity) { 94 | "use strict"; 95 | 96 | if (this.loaded) { 97 | if (this.speed > 0 && this._isAnimating) { 98 | if (!isNaN(entity.layer.engine._deltaTime)) { 99 | this._frameTickCount += entity.layer.engine._deltaTime * 1000; 100 | if (this._frameTickCount >= this.speed) { 101 | this._frameTickCount = 0; 102 | if (!this.loop) { 103 | if (this.resetFrame < 1 && this._currentFrame === this.spriteSheet.frameCount - 1) { 104 | this._isAnimating = false; 105 | } 106 | else if (this.resetFrame >= 1 && this._currentFrame >= this.resetFrame) { 107 | this._isAnimating = false; 108 | } 109 | } 110 | 111 | if (this.resetFrame < 1) { 112 | this._currentFrame = this._currentFrame == this.spriteSheet.frameCount - 1 ? 0 : this._currentFrame + 1; 113 | } 114 | else { 115 | this._currentFrame = this._currentFrame >= this.resetFrame ? 0 : this._currentFrame + 1; 116 | } 117 | } 118 | } 119 | } 120 | else { 121 | this._currentFrame = this.defaultFrame; 122 | } 123 | 124 | entity.layer.drawImage(this.spriteSheet._frameImages[this.row][this._currentFrame], 125 | entity.pos.x, 126 | entity.pos.y, 127 | this.rotation, 128 | entity.opacity, 129 | this.hFlip 130 | ); 131 | } 132 | 133 | return this; 134 | }; -------------------------------------------------------------------------------- /modules/layer.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013-2014 rastating 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see http://www.gnu.org/licenses/. 15 | 16 | PixelJS.Layer = function (engine) { 17 | "use strict"; 18 | 19 | this._backBuffer = undefined; 20 | this._backBufferCtx = undefined; 21 | this._canvas = undefined; 22 | this._ctx = undefined; 23 | this._components = []; 24 | this._collidables = []; 25 | this._draggables = []; 26 | this.engine = engine; 27 | 28 | this._insertIntoDom(); 29 | }; 30 | 31 | PixelJS.Layer.prototype.redraw = true; 32 | PixelJS.Layer.prototype.static = false; 33 | PixelJS.Layer.prototype.visible = true; 34 | 35 | PixelJS.Layer.prototype._insertIntoDom = function() { 36 | var container = this.engine.scene.container; 37 | this._canvas = document.createElement('canvas'); 38 | this._canvas.width = this.engine.scene.width; 39 | this._canvas.height = this.engine.scene.height; 40 | this._canvas.style.position = 'absolute'; 41 | this._canvas.style.top = 0; 42 | this._canvas.style.left = 0; 43 | this._canvas.style.width = '100%'; 44 | this._canvas.style.height = '100%'; 45 | this._canvas.className = 'scene-layer'; 46 | this._ctx = this._canvas.getContext('2d'); 47 | container.appendChild(this._canvas); 48 | 49 | this._backBuffer = document.createElement('canvas'); 50 | this._backBuffer.width = this._canvas.width; 51 | this._backBuffer.height = this._canvas.height; 52 | this._backBufferCtx = this._backBuffer.getContext('2d'); 53 | }; 54 | 55 | PixelJS.Layer.prototype._registerDraggable = function (draggable) { 56 | if (this._draggables.length == 0) { 57 | var self = this; 58 | this.engine.on('mousemove', function (point) { 59 | for (var i = 0; i < self._draggables.length; i++) { 60 | if (self._draggables[i].isDragging) { 61 | self._draggables[i]._onDrag(point); 62 | } 63 | } 64 | }); 65 | } 66 | 67 | if (!PixelJS.existsInArray(draggable, this._draggables)) { 68 | this._draggables.push(draggable); 69 | } 70 | 71 | return this; 72 | }; 73 | 74 | PixelJS.Layer.prototype._unregisterDraggable = function (draggable) { 75 | for (var i = this._draggables.length - 1; i >= 0; i--) { 76 | if (this._draggables[i] == draggable) { 77 | this._draggables.splice(i, 1); 78 | } 79 | } 80 | 81 | return this; 82 | }; 83 | 84 | PixelJS.Layer.prototype.addComponent = function (component) { 85 | this._components.push(component); 86 | return this; 87 | }; 88 | 89 | PixelJS.Layer.prototype.createEntity = function () { 90 | var entity = new PixelJS.Entity(this); 91 | this._components.push(entity); 92 | 93 | return entity; 94 | }; 95 | 96 | PixelJS.Layer.prototype.draw = function () { 97 | if (this.redraw) { 98 | if (this.visible) { 99 | for (var i = 0; i < this._components.length; i++) { 100 | this._components[i].draw(); 101 | } 102 | } 103 | 104 | this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); 105 | this._ctx.drawImage(this._backBuffer, 0, 0); 106 | this._backBufferCtx.clearRect(0, 0, this._backBuffer.width, this._backBuffer.height); 107 | 108 | if (this.static) { 109 | this.redraw = false; 110 | } 111 | } 112 | 113 | return this; 114 | }; 115 | 116 | PixelJS.Layer.prototype.drawImage = function (img, x, y, rotation, opacity, hFlip) { 117 | this._backBufferCtx.save(); 118 | 119 | if (hFlip) { 120 | this._backBufferCtx.translate(this._canvas.width, 0); 121 | this._backBufferCtx.scale(-1, 1); 122 | x = this._canvas.width - (x + img.width); 123 | } 124 | 125 | if (rotation == undefined || rotation == 0) { 126 | if (opacity < 1) { 127 | this._backBufferCtx.globalAlpha = opacity; 128 | } 129 | 130 | this._backBufferCtx.drawImage(img, x, y); 131 | } 132 | else { 133 | this._backBufferCtx.translate(x + (img.width / 2), y + (img.height / 2)); 134 | this._backBufferCtx.rotate(rotation * Math.PI / 180); 135 | if (opacity < 1) { 136 | this._backBufferCtx.globalAlpha = opacity; 137 | } 138 | this._backBufferCtx.drawImage(img, (img.width / 2) * -1, (img.height / 2) * -1); 139 | } 140 | 141 | this._backBufferCtx.restore(); 142 | return this; 143 | }; 144 | 145 | PixelJS.Layer.prototype.drawFromCanvas = function (canvas, x, y) { 146 | this._backBufferCtx.drawImage(canvas, x, y); 147 | return this; 148 | }; 149 | 150 | PixelJS.Layer.prototype.drawRectangle = function (x, y, width, height, style) { 151 | this._backBufferCtx.save(); 152 | this._backBufferCtx.beginPath(); 153 | this._backBufferCtx.rect(x, y, width, height); 154 | 155 | if (style.fill !== undefined) { 156 | this._backBufferCtx.fillStyle = style.fill; 157 | this._backBufferCtx.fill(); 158 | } 159 | 160 | if (style.stroke !== undefined) { 161 | this._backBufferCtx.strokeStyle = style.stroke; 162 | this._backBufferCtx.stroke(); 163 | } 164 | 165 | this._backBufferCtx.closePath(); 166 | this._backBufferCtx.restore(); 167 | return this; 168 | }; 169 | 170 | PixelJS.Layer.prototype.drawText = function (text, x, y, font, fillStyle, textAlign) { 171 | this._backBufferCtx.save(); 172 | this._backBufferCtx.font = font; 173 | this._backBufferCtx.fillStyle = fillStyle; 174 | this._backBufferCtx.textAlign = textAlign; 175 | this._backBufferCtx.fillText(text, x, y); 176 | this._backBufferCtx.restore(); 177 | return this; 178 | }; 179 | 180 | PixelJS.Layer.prototype.load = function (callback) { 181 | var loading = this._components.length; 182 | if (loading === 0) { 183 | callback(); 184 | } 185 | else { 186 | for (var i = 0; i < this._components.length; i++) { 187 | var c = this._components[i]; 188 | if (c.asset !== undefined && c.asset._prepInfo !== undefined) { 189 | c.asset.load(c.asset._prepInfo, function () { 190 | loading -= 1; 191 | if (loading === 0) { 192 | callback(); 193 | } 194 | }); 195 | } 196 | else { 197 | loading -= 1; 198 | if (loading === 0) { 199 | callback(); 200 | } 201 | } 202 | } 203 | } 204 | return this; 205 | }; 206 | 207 | PixelJS.Layer.prototype.registerCollidable = function (collidable) { 208 | if (!PixelJS.existsInArray(collidable, this._collidables)) { 209 | this._collidables.push(collidable); 210 | } 211 | 212 | return this; 213 | }; 214 | 215 | PixelJS.Layer.prototype.removeComponent = function (component) { 216 | for (var i = this._components.length - 1; i >= 0; i--) { 217 | if (this._components[i] == component) { 218 | this._components.splice(i, 1); 219 | 220 | if (component.isCollidable) { 221 | this.unregisterCollidable(component); 222 | } 223 | 224 | if (component.isDraggable) { 225 | this._unregisterDraggable(component); 226 | } 227 | } 228 | } 229 | 230 | return this; 231 | }; 232 | 233 | PixelJS.Layer.prototype.unregisterCollidable = function (collidable) { 234 | for (var i = this._collidables.length - 1; i >= 0; i--) { 235 | if (this._collidables[i] == collidable) { 236 | this._collidables.splice(i, 1); 237 | } 238 | } 239 | 240 | return this; 241 | }; 242 | 243 | PixelJS.Layer.prototype.update = function(elapsedTime, dt) { 244 | for (var i = 0; i < this._components.length; i++) { 245 | this._components[i].update(elapsedTime, dt); 246 | } 247 | 248 | return this; 249 | }; 250 | 251 | Object.defineProperty(PixelJS.Layer.prototype, "zIndex", { 252 | get: function () { return this._canvas.style.zIndex; }, 253 | set: function (val) { this._canvas.style.zIndex = val; }, 254 | configurable: false, 255 | enumerable: false 256 | }); -------------------------------------------------------------------------------- /modules/entity.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013-2014 rastating 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see http://www.gnu.org/licenses/. 15 | 16 | PixelJS.Entity = function (layer) { 17 | "use strict"; 18 | 19 | this._dragAnchorPoint = { x: 0, y: 0 }; 20 | this.asset = undefined; 21 | this.layer = layer; 22 | this.opacity = 1.0; 23 | this.pos = { x: 0, y: 0 }; 24 | this.size = { width: 0, height: 0 }; 25 | this.velocity = { x: 0, y: 0 }; 26 | }; 27 | 28 | PixelJS.Entity.prototype._isClickable = false; 29 | PixelJS.Entity.prototype._isCollidable = false; 30 | PixelJS.Entity.prototype._isDraggable = false; 31 | PixelJS.Entity.prototype._isDragging = false; 32 | PixelJS.Entity.prototype._isMouseDown = false; 33 | PixelJS.Entity.prototype._isHoverable = false; 34 | PixelJS.Entity.prototype._isHovered = false; 35 | PixelJS.Entity.prototype.canMoveLeft = true; 36 | PixelJS.Entity.prototype.canMoveUp = true; 37 | PixelJS.Entity.prototype.canMoveRight = true; 38 | PixelJS.Entity.prototype.canMoveDown = true; 39 | PixelJS.Entity.prototype.dragButton = PixelJS.Buttons.Left; 40 | PixelJS.Entity.prototype.visible = true; 41 | 42 | PixelJS.Entity.prototype._onCollide = function (entity) { 43 | }; 44 | 45 | PixelJS.Entity.prototype._onDrag = function (point) { 46 | this.pos.x = point.x - this._dragAnchorPoint.x; 47 | this.pos.y = point.y - this._dragAnchorPoint.y; 48 | this._onDragCallback(this.pos); 49 | }; 50 | 51 | PixelJS.Entity.prototype._onDragCallback = function (point) { 52 | }; 53 | 54 | PixelJS.Entity.prototype._onDrop = function (point) { 55 | }; 56 | 57 | PixelJS.Entity.prototype._onMouseDown = function (point, button) { 58 | if (point.x >= this.pos.x && point.x <= this.pos.x + this.size.width) { 59 | if (point.y >= this.pos.y && point.y <= this.pos.y + this.size.height) { 60 | if (this._isDraggable && button == this.dragButton) { 61 | this._dragAnchorPoint.x = point.x - this.pos.x; 62 | this._dragAnchorPoint.y = point.y - this.pos.y; 63 | this._isDragging = true; 64 | } 65 | 66 | if (this._isClickable) { 67 | this._isMouseDown = true; 68 | this._onMouseDownCallback(point, button); 69 | } 70 | } 71 | } 72 | }; 73 | 74 | PixelJS.Entity.prototype._onMouseDownCallback = function (point, button) { 75 | }; 76 | 77 | PixelJS.Entity.prototype._onMouseHover = function (point, isHovering) { 78 | }; 79 | 80 | PixelJS.Entity.prototype._onMouseUpCallback = function (point, button) { 81 | }; 82 | 83 | 84 | PixelJS.Entity.prototype._onMouseUp = function (point, button) { 85 | if (this._isDraggable && this._isDragging && button == this.dragButton) { 86 | this._isDragging = false; 87 | this._onDrop(this.pos); 88 | } 89 | 90 | if (this._isClickable && this._isMouseDown) { 91 | this._isMouseDown = false; 92 | this._onMouseUpCallback(point, button); 93 | } 94 | } 95 | 96 | PixelJS.Entity.prototype._setIsClickable = function (val) { 97 | this._isClickable = val; 98 | if (val) { 99 | var self = this; 100 | 101 | // If the entity is already registered as a draggable, the mouse event 102 | // hooks will already be in place and don't need to be re-added. 103 | if (!this._isDraggable) { 104 | this._mousedownHook = function (p, b) { self._onMouseDown(p, b); }; 105 | this._mouseupHook = function (p, b) { self._onMouseUp(p, b); }; 106 | this.layer.engine.on('mousedown', this._mousedownHook); 107 | this.layer.engine.on('mouseup', this._mouseupHook); 108 | } 109 | } 110 | }; 111 | 112 | PixelJS.Entity.prototype._setIsCollidable = function (val) { 113 | if (val && !this._isCollidable) { 114 | this._isCollidable = val; 115 | this.layer.registerCollidable(this); 116 | } 117 | else { 118 | this._isCollidable = val; 119 | } 120 | }; 121 | 122 | PixelJS.Entity.prototype._setIsDraggable = function (val) { 123 | this._isDraggable = val; 124 | if (val) { 125 | var self = this; 126 | 127 | // If the entity is already registered as a clickable, the mouse event 128 | // hooks will already be in place and don't need to be re-added. 129 | if (!this._isClickable) { 130 | this._mousedownHook = function (p, b) { self._onMouseDown(p, b); }; 131 | this._mouseupHook = function (p, b) { self._onMouseUp(p, b); }; 132 | this.layer.engine.on('mousedown', this._mousedownHook); 133 | this.layer.engine.on('mouseup', this._mouseupHook); 134 | } 135 | 136 | this.layer._registerDraggable(this); 137 | } 138 | }; 139 | 140 | PixelJS.Entity.prototype.addToLayer = function (layer) { 141 | this.layer = layer; 142 | layer.addComponent(this); 143 | return this; 144 | } 145 | 146 | PixelJS.Entity.prototype.collidesWith = function (entity) { 147 | "use strict"; 148 | return this.pos.x + this.size.width > entity.pos.x && 149 | this.pos.x < entity.pos.x + entity.size.width && 150 | this.pos.y + this.size.height > entity.pos.y && 151 | this.pos.y < entity.pos.y + entity.size.height; 152 | }; 153 | 154 | PixelJS.Entity.prototype.dispose = function () { 155 | this.layer.removeComponent(this); 156 | 157 | if (this._isHoverable) { 158 | this.layer.engine.off('mousemove', this._mousemoveHook); 159 | } 160 | 161 | if (this.isClickable || this.isDraggable) { 162 | this.layer.engine.off('mousedown', this._mousedownHook); 163 | this.layer.engine.off('mouseup', this._mouseupHook); 164 | } 165 | 166 | return this; 167 | }; 168 | 169 | PixelJS.Entity.prototype.draw = function() { 170 | this.asset.draw(this); 171 | return this; 172 | }; 173 | 174 | PixelJS.Entity.prototype.fadeTo = function (opacity, duration, callback) { 175 | duration = duration === undefined ? 1 : duration; 176 | 177 | var animationSpeed = (this.opacity - opacity) / duration; 178 | var increasingOpacity = this.opacity < opacity; 179 | var self = this; 180 | 181 | if (self._animateOpacity !== undefined) { 182 | self.layer.engine._unregisterGameLoopCallback(self._animateOpacity); 183 | } 184 | 185 | this._animateOpacity = function (elapsedTime, dt) { 186 | dt = dt * 1000; // Convert into milliseconds from fractional seconds. 187 | 188 | if (increasingOpacity) { 189 | self.opacity += animationSpeed * dt; 190 | } 191 | else { 192 | self.opacity -= animationSpeed * dt; 193 | } 194 | 195 | if ((self.opacity >= opacity && increasingOpacity) || (self.opacity <= opacity && !increasingOpacity)) { 196 | self.opacity = opacity; 197 | self.layer.engine._unregisterGameLoopCallback(self._animateOpacity); 198 | self._animateOpacity = undefined; 199 | if (callback !== undefined) { 200 | PixelJS.proxy(callback, self); 201 | } 202 | } 203 | }; 204 | 205 | this.layer.engine._registerGameLoopCallback(this._animateOpacity); 206 | return this; 207 | }; 208 | 209 | PixelJS.Entity.prototype.moveLeft = function () { 210 | if (this.canMoveLeft) { 211 | this.pos.x -= this.velocity.x * this.layer.engine._deltaTime; 212 | } 213 | 214 | return this; 215 | }; 216 | 217 | PixelJS.Entity.prototype.moveRight = function () { 218 | if (this.canMoveRight) { 219 | this.pos.x += this.velocity.x * this.layer.engine._deltaTime; 220 | } 221 | 222 | return this; 223 | }; 224 | 225 | PixelJS.Entity.prototype.moveDown = function () { 226 | if (this.canMoveDown) { 227 | this.pos.y += this.velocity.y * this.layer.engine._deltaTime; 228 | } 229 | 230 | return this; 231 | }; 232 | 233 | PixelJS.Entity.prototype.moveTo = function (x, y, duration, callback) { 234 | duration = duration === undefined ? 1 : duration; 235 | 236 | var velocityX = (this.pos.x - x) / duration; 237 | var velocityY = (this.pos.y - y) / duration; 238 | var targetIsToTheLeft = x < this.pos.x; 239 | var targetIsAbove = y < this.pos.y; 240 | var self = this; 241 | 242 | if (this._animateMovement !== undefined) { 243 | self.layer.engine._unregisterGameLoopCallback(self._animateMovement); 244 | } 245 | 246 | this._animateMovement = function (elapsedTime, dt) { 247 | dt = dt * 1000; // Convert into milliseconds from fractional seconds. 248 | if (targetIsToTheLeft) { 249 | self.pos.x -= velocityX * dt; 250 | } 251 | else { 252 | self.pos.x += (velocityX * -1) * dt; 253 | } 254 | 255 | if (targetIsAbove) { 256 | self.pos.y -= velocityY * dt; 257 | } 258 | else { 259 | self.pos.y += (velocityY * -1) * dt; 260 | } 261 | 262 | if (((targetIsToTheLeft && self.pos.x <= x) || (!targetIsToTheLeft && self.pos.x >= x)) && ((targetIsAbove && self.pos.y <= y) || (!targetIsAbove && self.pos.y >= y))) { 263 | self.pos.x = x; 264 | self.pos.y = y; 265 | self.layer.engine._unregisterGameLoopCallback(self._animateMovement); 266 | self._animateMovement = undefined; 267 | if (callback !== undefined) { 268 | callback(self); 269 | } 270 | } 271 | }; 272 | 273 | this.layer.engine._registerGameLoopCallback(this._animateMovement); 274 | return this; 275 | }; 276 | 277 | PixelJS.Entity.prototype.moveUp = function () { 278 | if (this.canMoveUp) { 279 | this.pos.y -= this.velocity.y * this.layer.engine._deltaTime; 280 | } 281 | 282 | return this; 283 | }; 284 | 285 | PixelJS.Entity.prototype.onCollide = function (callback) { 286 | if (!this.isCollidable) { 287 | this.isCollidable = true; 288 | } 289 | 290 | this._onCollide = callback; 291 | return this; 292 | }; 293 | 294 | PixelJS.Entity.prototype.onDrag = function (callback) { 295 | if (!this.isDraggable) { 296 | this.isDraggable = true; 297 | } 298 | 299 | this._onDragCallback = callback; 300 | return this; 301 | }; 302 | 303 | PixelJS.Entity.prototype.onDrop = function (callback) { 304 | if (!this.isDraggable) { 305 | this.isDraggable = true; 306 | } 307 | 308 | this._onDrop = callback; 309 | return this; 310 | }; 311 | 312 | PixelJS.Entity.prototype.onMouseDown = function (callback) { 313 | if (!this.isClickable) { 314 | this.isClickable = true; 315 | } 316 | 317 | this._onMouseDownCallback = callback; 318 | return this; 319 | }; 320 | 321 | PixelJS.Entity.prototype.onMouseHover = function (callback) { 322 | if (!this._isHoverable) { 323 | var self = this; 324 | this._mousemoveHook = function (point) { 325 | if (point.x >= self.pos.x && point.x <= self.pos.x + self.size.width) { 326 | if (point.y >= self.pos.y && point.y <= self.pos.y + self.size.height) { 327 | if (!self._isHovered) { 328 | self._onMouseHover(point, true); 329 | self._isHovered = true; 330 | } 331 | } 332 | else { 333 | if (self._isHovered) { 334 | self._onMouseHover(point, false); 335 | } 336 | self._isHovered = false; 337 | } 338 | } 339 | else { 340 | if (self._isHovered) { 341 | self._onMouseHover(point, false); 342 | } 343 | self._isHovered = false; 344 | } 345 | }; 346 | 347 | this.layer.engine.on('mousemove', this._mousemoveHook); 348 | this._isHoverable = true; 349 | } 350 | 351 | this._onMouseHover = callback; 352 | return this; 353 | }; 354 | 355 | PixelJS.Entity.prototype.onMouseUp = function (callback) { 356 | if (!this.isClickable) { 357 | this.isClickable = true; 358 | } 359 | 360 | this._onMouseUpCallback = callback; 361 | return this; 362 | }; 363 | 364 | PixelJS.Entity.prototype.update = function(elapsedTime, dt) { 365 | }; 366 | 367 | Object.defineProperty(PixelJS.Entity.prototype, "isClickable", { 368 | get: function () { return this._isClickable; }, 369 | set: function (val) { this._setIsClickable(val) }, 370 | configurable: false, 371 | enumerable: false 372 | }); 373 | 374 | Object.defineProperty(PixelJS.Entity.prototype, "isCollidable", { 375 | get: function () { return this._isCollidable; }, 376 | set: function (val) { this._setIsCollidable(val) }, 377 | configurable: false, 378 | enumerable: false 379 | }); 380 | 381 | Object.defineProperty(PixelJS.Entity.prototype, "isDraggable", { 382 | get: function () { return this._isDraggable; }, 383 | set: function (val) { this._setIsDraggable(val) }, 384 | configurable: false, 385 | enumerable: false 386 | }); 387 | 388 | Object.defineProperty(PixelJS.Entity.prototype, "isDragging", { 389 | get: function () { return this._isDragging; }, 390 | configurable: false, 391 | enumerable: false 392 | }); 393 | -------------------------------------------------------------------------------- /modules/engine.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2013-2014 rastating 2 | // 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with this program. If not, see http://www.gnu.org/licenses/. 15 | 16 | PixelJS.Engine = function () { 17 | "use strict"; 18 | 19 | this.scene = { container: undefined, width: 0, height: 0 }; 20 | this._deltaTime = 0; // _deltaTime contains the time in fractional seconds since the last update 21 | this._fullscreenRequested = false; 22 | this._events = { keydown: [], keyup: [], mousemove: [], mousedown: [], mouseup: [] }; 23 | this._inputLayer = []; 24 | this._layerKeys = []; 25 | this._layers = {}; 26 | this._originalContainerStyle = {}; 27 | this._previousElapsedTime = 0; 28 | this._size = { width: 0, height: 0 }; 29 | this._soundKeys = []; 30 | this._sounds = {}; 31 | this._gameLoopCallbacks = []; 32 | 33 | var self = this; 34 | document.onkeydown = function (e) { 35 | e = e || event; // Small hack to get the event args in IE 36 | var keyCode = e.keyCode; 37 | var listeners = self._events.keydown; 38 | if (listeners.length > 0) { 39 | e.preventDefault(); 40 | listeners.forEach(function(listener) { 41 | listener(keyCode); 42 | }); 43 | } 44 | }; 45 | 46 | document.onkeyup = function (e) { 47 | e = e || event; // Small hack to get the event args in IE 48 | var keyCode = e.keyCode; 49 | var listeners = self._events.keyup; 50 | if (listeners.length > 0) { 51 | e.preventDefault(); 52 | listeners.forEach(function(listener) { 53 | listener(keyCode); 54 | }); 55 | } 56 | }; 57 | 58 | this._resizeHandler = function () { 59 | self.scene.container.style.width = window.innerWidth + "px"; 60 | self.scene.container.style.height = window.innerHeight + "px"; 61 | }; 62 | 63 | this._screenModeChangeHandler = function () { 64 | if (!self._fullscreenRequested) { 65 | var fullscreenElement = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement; 66 | var fullscreenEnabled = document.fullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled; 67 | 68 | if (fullscreenElement === null || fullscreenElement === undefined) { 69 | document.removeEventListener("fullscreenchange", self._screenModeChangeHandler, false); 70 | document.removeEventListener("webkitfullscreenchange", self._screenModeChangeHandler, false); 71 | document.removeEventListener("mozfullscreenchange", self._screenModeChangeHandler, false); 72 | self.fullscreen = false; 73 | } 74 | } 75 | else { 76 | self._fullscreenRequested = false; 77 | } 78 | } 79 | }; 80 | 81 | PixelJS.Engine.prototype._checkForCollissions = function () { 82 | for (var keyIndex = 0; keyIndex < this._layerKeys.length; keyIndex++) { 83 | // Check for collisions within the layer's own collidables 84 | var collidables = this._layers[this._layerKeys[keyIndex]]._collidables; 85 | for (var s = 0; s < collidables.length; s++) { 86 | for (var t = 0; t < collidables.length; t++) { 87 | if (s !== t) { 88 | if (collidables[s].collidesWith(collidables[t])) { 89 | collidables[s]._onCollide(collidables[t]); 90 | } 91 | } 92 | } 93 | } 94 | 95 | // Check for collisions with the other layers' collidables 96 | for (var k = 0; k < this._layerKeys.length; k++) { 97 | if (k !== keyIndex) { 98 | var otherCollidables = this._layers[this._layerKeys[k]]._collidables; 99 | for (var s = 0; s < collidables.length; s++) { 100 | for (var t = 0; t < otherCollidables.length; t++) { 101 | if (collidables[s].collidesWith(otherCollidables[t])) { 102 | collidables[s]._onCollide(otherCollidables[t]); 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } 109 | }; 110 | 111 | PixelJS.Engine.prototype._displayFPS = false; 112 | PixelJS.Engine.prototype._fullscreen = false; 113 | PixelJS.Engine.prototype.maxDeltaTime = 33; 114 | 115 | PixelJS.Engine.prototype._registerGameLoopCallback = function (callback) { 116 | this._gameLoopCallbacks.push(callback); 117 | }; 118 | 119 | PixelJS.Engine.prototype._unregisterGameLoopCallback = function (callback) { 120 | for (var i = this._gameLoopCallbacks.length - 1; i >= 0; i--) { 121 | if (this._gameLoopCallbacks[i] == callback) { 122 | this._gameLoopCallbacks.splice(i, 1); 123 | } 124 | } 125 | }; 126 | 127 | PixelJS.Engine.prototype._toggleFPSLayer = function () { 128 | if (this._displayFPS) { 129 | if (this._layers["__pixeljs_performanceLayer"] === undefined) { 130 | var layer = this.createLayer("__pixeljs_performanceLayer"); 131 | var counter = new PixelJS.FPSCounter(layer); 132 | counter.pos = { x: 5, y: 20 }; 133 | layer.addComponent(counter); 134 | layer.zIndex = 9999; 135 | } 136 | else { 137 | this._layers["__pixeljs_performanceLayer"].visible = true; 138 | } 139 | } 140 | else { 141 | if (this._layers["__pixeljs_performanceLayer"] !== undefined) { 142 | this._layers["__pixeljs_performanceLayer"].visible = false; 143 | } 144 | } 145 | }; 146 | 147 | PixelJS.Engine.prototype._updateScreenMode = function () { 148 | if (this._fullscreen) { 149 | this._fullscreenRequested = true; 150 | 151 | if (this.scene.container.requestFullScreen) { 152 | this.scene.container.requestFullScreen(); 153 | } 154 | else if (this.scene.container.webkitRequestFullScreen) { 155 | this.scene.container.webkitRequestFullScreen(); 156 | } 157 | else { 158 | this.scene.container.mozRequestFullScreen(); 159 | } 160 | 161 | this.scene.container.style.position = 'absolute'; 162 | this.scene.container.style.top = 0; 163 | this.scene.container.style.left = 0; 164 | window.addEventListener('resize', this._resizeHandler); 165 | 166 | document.addEventListener("fullscreenchange", this._screenModeChangeHandler, false); 167 | document.addEventListener("webkitfullscreenchange", this._screenModeChangeHandler, false); 168 | document.addEventListener("mozfullscreenchange", this._screenModeChangeHandler, false); 169 | } 170 | else { 171 | window.removeEventListener('resize', this._resizeHandler); 172 | 173 | this.scene.container.style.position = this._originalContainerStyle.position; 174 | this.scene.container.style.width = this._originalContainerStyle.width; 175 | this.scene.container.style.height = this._originalContainerStyle.height; 176 | 177 | if (document.cancelFullScreen) { 178 | document.cancelFullScreen(); 179 | } 180 | else if (document.mozCancelFullScreen) { 181 | document.mozCancelFullScreen(); 182 | } 183 | else if (document.webkitCancelFullScreen) { 184 | document.webkitCancelFullScreen(); 185 | } 186 | } 187 | }; 188 | 189 | PixelJS.Engine.prototype.addSound = function (key, sound) { 190 | this._sounds[key] = sound; 191 | return this; 192 | }; 193 | 194 | PixelJS.Engine.prototype.createLayer = function (name) { 195 | var layer = new PixelJS.Layer(this); 196 | this._layers[name] = layer; 197 | this._layerKeys.push(name); 198 | return layer; 199 | }; 200 | 201 | PixelJS.Engine.prototype.createSound = function (name) { 202 | var sound = new PixelJS.Sound(); 203 | this._sounds[name] = sound; 204 | this._soundKeys.push(name); 205 | return sound; 206 | } 207 | 208 | PixelJS.Engine.prototype.init = function (info) { 209 | this.scene.container = document.getElementById(info.container); 210 | this.scene.width = info.width; 211 | this.scene.height = info.height; 212 | 213 | this._originalContainerStyle.position = this.scene.container.style.position; 214 | this._originalContainerStyle.width = this.scene.container.style.width; 215 | this._originalContainerStyle.height = this.scene.container.style.height; 216 | this._originalContainerStyle.top = this.scene.container.style.top; 217 | this._originalContainerStyle.left = this.scene.container.style.left; 218 | 219 | if (info.maxDeltaTime !== undefined) { 220 | this.maxDeltaTime = info.maxDeltaTime; 221 | } 222 | 223 | var self = this; 224 | this._inputLayer = document.createElement('div'); 225 | this._inputLayer.width = this.scene.width; 226 | this._inputLayer.height = this.scene.height; 227 | this._inputLayer.style.position = 'absolute'; 228 | this._inputLayer.style.top = 0; 229 | this._inputLayer.style.left = 0; 230 | this._inputLayer.style.width = '100%'; 231 | this._inputLayer.style.height = '100%'; 232 | this._inputLayer.className = 'scene-layer'; 233 | this._inputLayer.style.zIndex = '9999'; 234 | this._inputLayer.onmousemove = function (e) { 235 | var listeners = self._events.mousemove; 236 | if (listeners.length > 0) { 237 | var point = self._inputLayer.relMouseCoords(e); 238 | listeners.forEach(function(listener) { 239 | listener(point); 240 | }); 241 | } 242 | }; 243 | 244 | this._inputLayer.onmousedown = function (e) { 245 | var listeners = self._events.mousedown; 246 | if (listeners.length > 0) { 247 | // The middle button is usually 1 but should be dispatched 248 | // as 4 to allow bitwise operations in the future. 249 | var button = e.button == 1 ? 4 : e.button == 0 ? 1 : 2; 250 | var point = self._inputLayer.relMouseCoords(e); 251 | 252 | listeners.forEach(function(listener) { 253 | listener(point, button); 254 | }); 255 | } 256 | }; 257 | 258 | this._inputLayer.onmouseup = function (e) { 259 | var listeners = self._events.mouseup; 260 | if (listeners.length > 0) { 261 | // The middle button is usually 1 but should be dispatched 262 | // as 4 to allow bitwise operations in the future. 263 | var button = e.button == 1 ? 4 : e.button == 0 ? 1 : 2; 264 | var point = self._inputLayer.relMouseCoords(e); 265 | 266 | listeners.forEach(function(listener) { 267 | listener(point, button); 268 | }); 269 | } 270 | }; 271 | 272 | this.scene.container.appendChild(this._inputLayer); 273 | return this; 274 | }; 275 | 276 | PixelJS.Engine.prototype.loadAndRun = function (gameLoop) { 277 | var self = this; 278 | self.loadScene(function () { 279 | self.loadSounds(function () { 280 | self.run(gameLoop); 281 | }); 282 | }); 283 | 284 | return this; 285 | }; 286 | 287 | PixelJS.Engine.prototype.loadScene = function (callback) { 288 | var loading = this._layerKeys.length; 289 | for (var k = 0; k < this._layerKeys.length; k++) { 290 | this._layers[this._layerKeys[k]].load(function () { 291 | loading -= 1; 292 | if (loading === 0) { 293 | callback(); 294 | } 295 | }); 296 | } 297 | 298 | return this; 299 | }; 300 | 301 | PixelJS.Engine.prototype.loadSounds = function (callback) { 302 | var loading = this._soundKeys.length; 303 | if (loading === 0) { 304 | callback(); 305 | } 306 | else { 307 | for (var k = 0; k < this._soundKeys.length; k++) { 308 | var key = this._soundKeys[k]; 309 | if (this._sounds[key]._canPlay || this._sounds[key]._prepInfo === undefined) { 310 | loading -= 1; 311 | if (loading === 0) { 312 | callback(); 313 | } 314 | } 315 | else { 316 | this._sounds[key].load(this._sounds[key]._prepInfo, function () { 317 | loading -= 1; 318 | if (loading === 0) { 319 | callback(); 320 | } 321 | }); 322 | } 323 | } 324 | } 325 | 326 | return this; 327 | }; 328 | 329 | PixelJS.Engine.prototype.off = function (event, callback) { 330 | for (var i = this._events[event.toLowerCase()].length - 1; i >= 0; i--) { 331 | if (this._events[event.toLowerCase()][i] == callback) { 332 | this._events[event.toLowerCase()].splice(i, 1); 333 | } 334 | } 335 | 336 | return this; 337 | }; 338 | 339 | PixelJS.Engine.prototype.on = function (event, callback) { 340 | this._events[event.toLowerCase()].push(callback); 341 | return this; 342 | }; 343 | 344 | PixelJS.Engine.prototype.playSound = function (key) { 345 | this._sounds[key].play(); 346 | return this; 347 | } 348 | 349 | PixelJS.Engine.prototype.run = function (gameLoop) { 350 | var self = this; 351 | (function loop(elapsedTime) { 352 | requestAnimationFrame(loop); 353 | self._deltaTime = Math.min(elapsedTime - self._previousElapsedTime, self.maxDeltaTime) / 1000; 354 | 355 | if (!isNaN(self._deltaTime)) { 356 | for (var i = 0; i < self._layerKeys.length; i++) { 357 | self._layers[self._layerKeys[i]].update(elapsedTime, self._deltaTime); 358 | } 359 | 360 | for (var i = 0; i < self._gameLoopCallbacks.length; i++) { 361 | self._gameLoopCallbacks[i](elapsedTime, self._deltaTime); 362 | } 363 | 364 | self._checkForCollissions(); 365 | 366 | gameLoop(elapsedTime, self._deltaTime); 367 | 368 | for (var i = 0; i < self._layerKeys.length; i++) { 369 | self._layers[self._layerKeys[i]].draw(); 370 | } 371 | } 372 | 373 | self._previousElapsedTime = elapsedTime; 374 | }()); 375 | 376 | return this; 377 | }; 378 | 379 | Object.defineProperty(PixelJS.Engine.prototype, "deltaTime", { 380 | get: function () { return this._deltaTime; }, 381 | configurable: false, 382 | enumerable: false 383 | }); 384 | 385 | Object.defineProperty(PixelJS.Engine.prototype, "displayFPS", { 386 | get: function () { return this._displayFPS; }, 387 | set: function (val) { 388 | this._displayFPS = val; 389 | this._toggleFPSLayer(); 390 | }, 391 | configurable: false, 392 | enumerable: false 393 | }); 394 | 395 | Object.defineProperty(PixelJS.Engine.prototype, "fullscreen", { 396 | get: function () { return this._fullscreen; }, 397 | set: function (val) { 398 | if (this._fullscreen !== val) { 399 | this._fullscreen = val; 400 | this._updateScreenMode(); 401 | } 402 | }, 403 | configurable: false, 404 | enumerable: true 405 | }); 406 | --------------------------------------------------------------------------------