diff --git a/Phaser/Cache.ts b/Phaser/Cache.ts index d94577d9..88bb0942 100644 --- a/Phaser/Cache.ts +++ b/Phaser/Cache.ts @@ -84,12 +84,20 @@ module Phaser { * @param key {string} Asset key for the texture atlas. * @param url {string} URL of this texture atlas file. * @param data {object} Extra texture atlas data. - * @param data {object} Texture atlas frames data. + * @param atlasData {object} Texture atlas frames data. */ - public addTextureAtlas(key: string, url: string, data, jsonData) { + public addTextureAtlas(key: string, url: string, data, atlasData, format) { this._images[key] = { url: url, data: data, spriteSheet: true }; - this._images[key].frameData = AnimationLoader.parseJSONData(this._game, jsonData); + + if (format == Phaser.Loader.TEXTURE_ATLAS_JSON_ARRAY) + { + this._images[key].frameData = AnimationLoader.parseJSONData(this._game, atlasData); + } + else if (format == Phaser.Loader.TEXTURE_ATLAS_XML_STARLING) + { + this._images[key].frameData = AnimationLoader.parseXMLData(this._game, atlasData, format); + } } diff --git a/Phaser/Loader.ts b/Phaser/Loader.ts index 6b604c19..a698178c 100644 --- a/Phaser/Loader.ts +++ b/Phaser/Loader.ts @@ -82,6 +82,13 @@ module Phaser { */ public crossOrigin: string = ''; + /** + * TextureAtlas data format constants + */ + public static TEXTURE_ATLAS_JSON_ARRAY: number = 0; + public static TEXTURE_ATLAS_JSON_HASH: number = 1; + public static TEXTURE_ATLAS_XML_STARLING: number = 2; + /** * Reset loader, this will remove all loaded assets. */ @@ -133,44 +140,92 @@ module Phaser { /** * Add a new texture atlas loading request. * @param key {string} Unique asset key of the texture atlas file. - * @param url {string} URL of texture atlas file. - * @param [jsonURL] {string} url of JSON data file. - * @param [jsonData] {object} JSON data object. + * @param textureURL {string} The url of the texture atlas image file. + * @param [atlasURL] {string} The url of the texture atlas data file (json/xml) + * @param [atlasData] {object} A JSON or XML data object. + * @param [format] {number} A value describing the format of the data. */ - public addTextureAtlas(key: string, url: string, jsonURL?: string = null, jsonData? = null) { + public addTextureAtlas(key: string, textureURL: string, atlasURL?: string = null, atlasData? = null, format?:number = Loader.TEXTURE_ATLAS_JSON_ARRAY) { if (this.checkKeyExists(key) === false) { - if (jsonURL !== null) + if (atlasURL !== null) { - // A URL to a json file has been given + // A URL to a json/xml file has been given this._queueSize++; - this._fileList[key] = { type: 'textureatlas', key: key, url: url, data: null, jsonURL: jsonURL, jsonData: null, error: false, loaded: false }; + this._fileList[key] = { type: 'textureatlas', key: key, url: textureURL, atlasURL: atlasURL, data: null, format: format, error: false, loaded: false }; this._keys.push(key); } else { - // A json string or object has been given - if (typeof jsonData === 'string') + if (format == Loader.TEXTURE_ATLAS_JSON_ARRAY) { - var data = JSON.parse(jsonData); + // A json string or object has been given + if (typeof atlasData === 'string') + { + atlasData = JSON.parse(atlasData); + } + // Malformed? - if (data['frames']) + if (atlasData['frames']) { this._queueSize++; - this._fileList[key] = { type: 'textureatlas', key: key, url: url, data: null, jsonURL: null, jsonData: data['frames'], error: false, loaded: false }; + this._fileList[key] = { type: 'textureatlas', key: key, url: textureURL, data: null, atlasURL: null, atlasData: atlasData['frames'], format: format, error: false, loaded: false }; this._keys.push(key); } + else + { + throw new Error("Phaser.Loader. Invalid Texture Atlas JSON given, missing frames block"); + } } - else + else if (format == Loader.TEXTURE_ATLAS_XML_STARLING) { + // An xml string or object has been given + if (typeof atlasData === 'string') + { + var tmp; + var xml; + + try + { + if (window['DOMParser']) + { + tmp = new DOMParser(); + xml = tmp.parseFromString(atlasData, "text/xml"); + } + else + { + xml = new ActiveXObject("Microsoft.XMLDOM"); + xml.async = 'false'; + xml.loadXML(atlasData); + } + } + catch (e) + { + xml = undefined; + } + + if (!xml || !xml.documentElement || xml.getElementsByTagName("parsererror").length) + { + throw new Error("Phaser.Loader. Invalid Texture Atlas XML given"); + } + else + { + atlasData = xml; + } + } + // Malformed? - if (jsonData['frames']) + if (atlasData.getElementsByTagName('TextureAtlas')) { this._queueSize++; - this._fileList[key] = { type: 'textureatlas', key: key, url: url, data: null, jsonURL: null, jsonData: jsonData['frames'], error: false, loaded: false }; + this._fileList[key] = { type: 'textureatlas', key: key, url: textureURL, data: null, atlasURL: null, atlasData: atlasData, format: format, error: false, loaded: false }; this._keys.push(key); } + else + { + throw new Error("Phaser.Loader. Invalid Texture Atlas XML given, missing tag"); + } } } @@ -344,18 +399,27 @@ module Phaser { break; case 'textureatlas': - if (file.jsonURL == null) + if (file.atlasURL == null) { - this._game.cache.addTextureAtlas(file.key, file.url, file.data, file.jsonData); + this._game.cache.addTextureAtlas(file.key, file.url, file.data, file.atlasData, file.format); } else { - // Load the JSON before carrying on with the next file + // Load the JSON or XML before carrying on with the next file loadNext = false; - this._xhr.open("GET", file.jsonURL, true); + this._xhr.open("GET", file.atlasURL, true); this._xhr.responseType = "text"; - this._xhr.onload = () => this.jsonLoadComplete(file.key); - this._xhr.onerror = () => this.jsonLoadError(file.key); + + if (file.format == Loader.TEXTURE_ATLAS_JSON_ARRAY) + { + this._xhr.onload = () => this.jsonLoadComplete(file.key); + } + else if (file.format == Loader.TEXTURE_ATLAS_XML_STARLING) + { + this._xhr.onload = () => this.xmlLoadComplete(file.key); + } + + this._xhr.onerror = () => this.dataLoadError(file.key); this._xhr.send(); } break; @@ -390,7 +454,7 @@ module Phaser { if (data['frames']) { var file = this._fileList[key]; - this._game.cache.addTextureAtlas(file.key, file.url, file.data, data['frames']); + this._game.cache.addTextureAtlas(file.key, file.url, file.data, data['frames'], file.format); } this.nextFile(key, true); @@ -401,7 +465,7 @@ module Phaser { * Error occured when load a JSON. * @param key {string} Key of the error loading JSON file. */ - private jsonLoadError(key: string) { + private dataLoadError(key: string) { var file = this._fileList[key]; file.error = true; @@ -409,6 +473,51 @@ module Phaser { } + private xmlLoadComplete(key: string) { + + var atlasData = this._xhr.response; // xml? + var tmp; + var xml; + + try + { + if (window['DOMParser']) + { + tmp = new DOMParser(); + xml = tmp.parseFromString(atlasData, "text/xml"); + } + else + { + xml = new ActiveXObject("Microsoft.XMLDOM"); + xml.async = 'false'; + xml.loadXML(atlasData); + } + } + catch (e) + { + xml = undefined; + } + + if (!xml || !xml.documentElement || xml.getElementsByTagName("parsererror").length) + { + throw new Error("Phaser.Loader. Invalid Texture Atlas XML given"); + } + + // Malformed? + if (xml.getElementsByTagName('TextureAtlas')) + { + var file = this._fileList[key]; + this._game.cache.addTextureAtlas(file.key, file.url, file.data, xml, file.format); + } + else + { + throw new Error("Phaser.Loader. Invalid Texture Atlas XML given, missing tag"); + } + + this.nextFile(key, true); + + } + /** * Handle loading next file. * @param previousKey {string} Key of previous loaded asset. diff --git a/Phaser/State.ts b/Phaser/State.ts index 99800280..8925dd7a 100644 --- a/Phaser/State.ts +++ b/Phaser/State.ts @@ -152,11 +152,17 @@ module Phaser { public destroy() { } /** - * Call this method to see if one object collids another. - * @return {boolean} Whether the given objects or groups collids. + * Checks for overlaps between two objects using the world QuadTree. Can be GameObject vs. GameObject, GameObject vs. Group or Group vs. Group. + * Note: Does not take the objects scrollFactor into account. All overlaps are check in world space. + * @param object1 The first GameObject or Group to check. If null the world.group is used. + * @param object2 The second GameObject or Group to check. + * @param notifyCallback A callback function that is called if the objects overlap. The two objects will be passed to this function in the same order in which you passed them to Collision.overlap. + * @param processCallback A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then notifyCallback will only be called if processCallback returns true. + * @param context The context in which the callbacks will be called + * @returns {boolean} true if the objects overlap, otherwise false. */ - public collide(ObjectOrGroup1: Basic = null, ObjectOrGroup2: Basic = null, NotifyCallback = null): bool { - return this.collision.overlap(ObjectOrGroup1, ObjectOrGroup2, NotifyCallback, Collision.separate); + public collide(objectOrGroup1: Basic = null, objectOrGroup2: Basic = null, notifyCallback = null, context? = this.game.callbackContext): bool { + return this.collision.overlap(objectOrGroup1, objectOrGroup2, notifyCallback, Collision.separate, context); } } diff --git a/Phaser/system/animation/AnimationLoader.ts b/Phaser/system/animation/AnimationLoader.ts index f1bd19c9..51bb3c7f 100644 --- a/Phaser/system/animation/AnimationLoader.ts +++ b/Phaser/system/animation/AnimationLoader.ts @@ -96,6 +96,32 @@ module Phaser { } + public static parseXMLData(game: Game, xml, format: number): FrameData { + + // Let's create some frames then + var data: FrameData = new FrameData(); + + var frames = xml.getElementsByTagName('SubTexture'); + + var newFrame: Frame; + + for (var i = 0; i < frames.length; i++) + { + var frame = frames[i].attributes; + + newFrame = data.addFrame(new Frame(frame.x.nodeValue, frame.y.nodeValue, frame.width.nodeValue, frame.height.nodeValue, frame.name.nodeValue)); + + // Trimmed? + if (frame.frameX.nodeValue != '-0' || frame.frameY.nodeValue != '-0') + { + newFrame.setTrim(true, frame.width.nodeValue, frame.height.nodeValue, Math.abs(frame.frameX.nodeValue), Math.abs(frame.frameY.nodeValue), frame.frameWidth.nodeValue, frame.frameHeight.nodeValue); + } + + } + + return data; + } + } } \ No newline at end of file diff --git a/Phaser/system/animation/Frame.ts b/Phaser/system/animation/Frame.ts index 9d4b4d2a..2a46f754 100644 --- a/Phaser/system/animation/Frame.ts +++ b/Phaser/system/animation/Frame.ts @@ -38,16 +38,19 @@ module Phaser { * @type {number} */ public x: number; + /** * Y position within the image to cut from. * @type {number} */ public y: number; + /** * Width of the frame. * @type {number} */ public width: number; + /** * Height of the frame. * @type {number} @@ -82,31 +85,37 @@ module Phaser { public trimmed: bool; // The coordinates of the trimmed sprite inside the original sprite + /** * Width of the original sprite. * @type {number} */ public sourceSizeW: number; + /** * Height of the original sprite. * @type {number} */ public sourceSizeH: number; + /** * X position of the trimmed sprite inside original sprite. * @type {number} */ public spriteSourceSizeX: number; + /** * Y position of the trimmed sprite inside original sprite. * @type {number} */ public spriteSourceSizeY: number; + /** * Width of the trimmed sprite. * @type {number} */ public spriteSourceSizeW: number; + /** * Height of the trimmed sprite. * @type {number} @@ -130,7 +139,7 @@ module Phaser { * @param destWidth {number} Destiny draw width. * @param destHeight {number} Destiny draw height. */ - public setTrim(trimmed: bool, actualWidth, actualHeight, destX, destY, destWidth, destHeight, ) { + public setTrim(trimmed: bool, actualWidth: number, actualHeight: number, destX: number, destY: number, destWidth: number, destHeight: number) { this.trimmed = trimmed; diff --git a/README.md b/README.md index 142085d2..6bbaa15b 100644 --- a/README.md +++ b/README.md @@ -106,11 +106,12 @@ V0.9.6 * Added the GameObjectFactory to Game. You now make Sprites like this: game.add.sprite(). Much better separation of game object creation methods now. But you'll have to update ALL code, sorry! * Added GameObjectFactory methods to add existing objects to the game world, such as existingSprite(), existingTween(), etc. * Added the GameObjectFactory to Phaser.State +* Added new format parameter to Loader.addTextureAtlas defining the format. Currently supported: JSON Array and Starling/Sparrow XML. * TODO: Check that tween pausing works with the new performance.now * TODO: Game.Time should monitor pause duration * TODO: Investigate bug re: tilemap collision and animation frames -* TODO: Update tests that use arrow keys and include touch/mouse support +* TODO: Update tests that use arrow keys and include touch/mouse support (FlxControlHandler style) * TODO: GameObject.clipRect * TODO: Polygon geom primitive * TODO: Move GameObject transforms to a single matrix diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 71198992..769ebab5 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -239,6 +239,10 @@ + + + starling texture atlas 1.ts + collide with tile.ts diff --git a/Tests/assets/sprites/shoebot.png b/Tests/assets/sprites/shoebot.png new file mode 100644 index 00000000..9521ddeb Binary files /dev/null and b/Tests/assets/sprites/shoebot.png differ diff --git a/Tests/assets/sprites/shoebot.xml b/Tests/assets/sprites/shoebot.xml new file mode 100644 index 00000000..4969a59b --- /dev/null +++ b/Tests/assets/sprites/shoebot.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tests/assets/sprites/shoebox.png b/Tests/assets/sprites/shoebox.png new file mode 100644 index 00000000..0297797b Binary files /dev/null and b/Tests/assets/sprites/shoebox.png differ diff --git a/Tests/assets/sprites/shoebox.xml b/Tests/assets/sprites/shoebox.xml new file mode 100644 index 00000000..b336af18 --- /dev/null +++ b/Tests/assets/sprites/shoebox.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/Tests/phaser.js b/Tests/phaser.js index e4136383..82bbd8e6 100644 --- a/Tests/phaser.js +++ b/Tests/phaser.js @@ -2379,6 +2379,21 @@ var Phaser; } return data; }; + AnimationLoader.parseXMLData = function parseXMLData(game, xml, format) { + // Let's create some frames then + var data = new Phaser.FrameData(); + var frames = xml.getElementsByTagName('SubTexture'); + var newFrame; + for(var i = 0; i < frames.length; i++) { + var frame = frames[i].attributes; + newFrame = data.addFrame(new Phaser.Frame(frame.x.nodeValue, frame.y.nodeValue, frame.width.nodeValue, frame.height.nodeValue, frame.name.nodeValue)); + // Trimmed? + if(frame.frameX.nodeValue != '-0' || frame.frameY.nodeValue != '-0') { + newFrame.setTrim(true, frame.width.nodeValue, frame.height.nodeValue, Math.abs(frame.frameX.nodeValue), Math.abs(frame.frameY.nodeValue), frame.frameWidth.nodeValue, frame.frameHeight.nodeValue); + } + } + return data; + }; return AnimationLoader; })(); Phaser.AnimationLoader = AnimationLoader; @@ -2440,6 +2455,7 @@ var Phaser; * @param destHeight {number} Destiny draw height. */ function (trimmed, actualWidth, actualHeight, destX, destY, destWidth, destHeight) { + console.log('setTrim', actualWidth, actualHeight, destX, destY, destWidth, destHeight); this.trimmed = trimmed; this.sourceSizeW = actualWidth; this.sourceSizeH = actualHeight; @@ -2844,15 +2860,19 @@ var Phaser; * @param key {string} Asset key for the texture atlas. * @param url {string} URL of this texture atlas file. * @param data {object} Extra texture atlas data. - * @param data {object} Texture atlas frames data. + * @param atlasData {object} Texture atlas frames data. */ - function (key, url, data, jsonData) { + function (key, url, data, atlasData, format) { this._images[key] = { url: url, data: data, spriteSheet: true }; - this._images[key].frameData = Phaser.AnimationLoader.parseJSONData(this._game, jsonData); + if(format == Phaser.Loader.TEXTURE_ATLAS_JSON_ARRAY) { + this._images[key].frameData = Phaser.AnimationLoader.parseJSONData(this._game, atlasData); + } else if(format == Phaser.Loader.TEXTURE_ATLAS_XML_STARLING) { + this._images[key].frameData = Phaser.AnimationLoader.parseXMLData(this._game, atlasData, format); + } }; Cache.prototype.addImage = /** * Add a new image. @@ -9039,6 +9059,9 @@ var Phaser; this._xhr = new XMLHttpRequest(); this._queueSize = 0; } + Loader.TEXTURE_ATLAS_JSON_ARRAY = 0; + Loader.TEXTURE_ATLAS_JSON_HASH = 1; + Loader.TEXTURE_ATLAS_XML_STARLING = 2; Loader.prototype.reset = /** * Reset loader, this will remove all loaded assets. */ @@ -9100,62 +9123,95 @@ var Phaser; Loader.prototype.addTextureAtlas = /** * Add a new texture atlas loading request. * @param key {string} Unique asset key of the texture atlas file. - * @param url {string} URL of texture atlas file. - * @param [jsonURL] {string} url of JSON data file. - * @param [jsonData] {object} JSON data object. + * @param textureURL {string} The url of the texture atlas image file. + * @param [atlasURL] {string} The url of the texture atlas data file (json/xml) + * @param [atlasData] {object} A JSON or XML data object. + * @param [format] {number} A value describing the format of the data. */ - function (key, url, jsonURL, jsonData) { - if (typeof jsonURL === "undefined") { jsonURL = null; } - if (typeof jsonData === "undefined") { jsonData = null; } + function (key, textureURL, atlasURL, atlasData, format) { + if (typeof atlasURL === "undefined") { atlasURL = null; } + if (typeof atlasData === "undefined") { atlasData = null; } + if (typeof format === "undefined") { format = Loader.TEXTURE_ATLAS_JSON_ARRAY; } + console.log('addTextureAtlas', key, textureURL, atlasURL, atlasData, format); if(this.checkKeyExists(key) === false) { - if(jsonURL !== null) { - // A URL to a json file has been given + if(atlasURL !== null) { + // A URL to a json/xml file has been given this._queueSize++; this._fileList[key] = { type: 'textureatlas', key: key, - url: url, + url: textureURL, + atlasURL: atlasURL, data: null, - jsonURL: jsonURL, - jsonData: null, + format: format, error: false, loaded: false }; this._keys.push(key); } else { - // A json string or object has been given - if(typeof jsonData === 'string') { - var data = JSON.parse(jsonData); - // Malformed? - if(data['frames']) { - this._queueSize++; - this._fileList[key] = { - type: 'textureatlas', - key: key, - url: url, - data: null, - jsonURL: null, - jsonData: data['frames'], - error: false, - loaded: false - }; - this._keys.push(key); + if(format == Loader.TEXTURE_ATLAS_JSON_ARRAY) { + // A json string or object has been given + if(typeof atlasData === 'string') { + atlasData = JSON.parse(atlasData); } - } else { // Malformed? - if(jsonData['frames']) { + if(atlasData['frames']) { this._queueSize++; this._fileList[key] = { type: 'textureatlas', key: key, - url: url, + url: textureURL, data: null, - jsonURL: null, - jsonData: jsonData['frames'], + atlasURL: null, + atlasData: atlasData['frames'], + format: format, error: false, loaded: false }; this._keys.push(key); + } else { + throw new Error("Phaser.Loader. Invalid Texture Atlas JSON given, missing frames block"); + } + } else if(format == Loader.TEXTURE_ATLAS_XML_STARLING) { + // An xml string or object has been given + if(typeof atlasData === 'string') { + var tmp; + var xml; + try { + if(window['DOMParser']) { + tmp = new DOMParser(); + xml = tmp.parseFromString(atlasData, "text/xml"); + } else { + xml = new ActiveXObject("Microsoft.XMLDOM"); + xml.async = 'false'; + xml.loadXML(atlasData); + } + } catch (e) { + xml = undefined; + } + if(!xml || !xml.documentElement || xml.getElementsByTagName("parsererror").length) { + throw new Error("Phaser.Loader. Invalid Texture Atlas XML given"); + } else { + atlasData = xml; + } + } + // Malformed? + if(atlasData.getElementsByTagName('TextureAtlas')) { + this._queueSize++; + this._fileList[key] = { + type: 'textureatlas', + key: key, + url: textureURL, + data: null, + atlasURL: null, + atlasData: atlasData, + format: format, + error: false, + loaded: false + }; + this._keys.push(key); + } else { + throw new Error("Phaser.Loader. Invalid Texture Atlas XML given, missing tag"); } } } @@ -9313,18 +9369,24 @@ var Phaser; this._game.cache.addSpriteSheet(file.key, file.url, file.data, file.frameWidth, file.frameHeight, file.frameMax); break; case 'textureatlas': - if(file.jsonURL == null) { - this._game.cache.addTextureAtlas(file.key, file.url, file.data, file.jsonData); + if(file.atlasURL == null) { + this._game.cache.addTextureAtlas(file.key, file.url, file.data, file.atlasData, file.format); } else { - // Load the JSON before carrying on with the next file + // Load the JSON or XML before carrying on with the next file loadNext = false; - this._xhr.open("GET", file.jsonURL, true); + this._xhr.open("GET", file.atlasURL, true); this._xhr.responseType = "text"; - this._xhr.onload = function () { - return _this.jsonLoadComplete(file.key); - }; + if(file.format == Loader.TEXTURE_ATLAS_JSON_ARRAY) { + this._xhr.onload = function () { + return _this.jsonLoadComplete(file.key); + }; + } else if(file.format == Loader.TEXTURE_ATLAS_XML_STARLING) { + this._xhr.onload = function () { + return _this.xmlLoadComplete(file.key); + }; + } this._xhr.onerror = function () { - return _this.jsonLoadError(file.key); + return _this.dataLoadError(file.key); }; this._xhr.send(); } @@ -9351,11 +9413,11 @@ var Phaser; // Malformed? if(data['frames']) { var file = this._fileList[key]; - this._game.cache.addTextureAtlas(file.key, file.url, file.data, data['frames']); + this._game.cache.addTextureAtlas(file.key, file.url, file.data, data['frames'], file.format); } this.nextFile(key, true); }; - Loader.prototype.jsonLoadError = /** + Loader.prototype.dataLoadError = /** * Error occured when load a JSON. * @param key {string} Key of the error loading JSON file. */ @@ -9364,6 +9426,35 @@ var Phaser; file.error = true; this.nextFile(key, true); }; + Loader.prototype.xmlLoadComplete = function (key) { + var atlasData = this._xhr.response;// xml? + + var tmp; + var xml; + try { + if(window['DOMParser']) { + tmp = new DOMParser(); + xml = tmp.parseFromString(atlasData, "text/xml"); + } else { + xml = new ActiveXObject("Microsoft.XMLDOM"); + xml.async = 'false'; + xml.loadXML(atlasData); + } + } catch (e) { + xml = undefined; + } + if(!xml || !xml.documentElement || xml.getElementsByTagName("parsererror").length) { + throw new Error("Phaser.Loader. Invalid Texture Atlas XML given"); + } + // Malformed? + if(xml.getElementsByTagName('TextureAtlas')) { + var file = this._fileList[key]; + this._game.cache.addTextureAtlas(file.key, file.url, file.data, xml, file.format); + } else { + throw new Error("Phaser.Loader. Invalid Texture Atlas XML given, missing tag"); + } + this.nextFile(key, true); + }; Loader.prototype.nextFile = /** * Handle loading next file. * @param previousKey {string} Key of previous loaded asset. @@ -17375,14 +17466,21 @@ var Phaser; function () { }; State.prototype.collide = /** - * Call this method to see if one object collids another. - * @return {boolean} Whether the given objects or groups collids. + * Checks for overlaps between two objects using the world QuadTree. Can be GameObject vs. GameObject, GameObject vs. Group or Group vs. Group. + * Note: Does not take the objects scrollFactor into account. All overlaps are check in world space. + * @param object1 The first GameObject or Group to check. If null the world.group is used. + * @param object2 The second GameObject or Group to check. + * @param notifyCallback A callback function that is called if the objects overlap. The two objects will be passed to this function in the same order in which you passed them to Collision.overlap. + * @param processCallback A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then notifyCallback will only be called if processCallback returns true. + * @param context The context in which the callbacks will be called + * @returns {boolean} true if the objects overlap, otherwise false. */ - function (ObjectOrGroup1, ObjectOrGroup2, NotifyCallback) { - if (typeof ObjectOrGroup1 === "undefined") { ObjectOrGroup1 = null; } - if (typeof ObjectOrGroup2 === "undefined") { ObjectOrGroup2 = null; } - if (typeof NotifyCallback === "undefined") { NotifyCallback = null; } - return this.collision.overlap(ObjectOrGroup1, ObjectOrGroup2, NotifyCallback, Phaser.Collision.separate); + function (objectOrGroup1, objectOrGroup2, notifyCallback, context) { + if (typeof objectOrGroup1 === "undefined") { objectOrGroup1 = null; } + if (typeof objectOrGroup2 === "undefined") { objectOrGroup2 = null; } + if (typeof notifyCallback === "undefined") { notifyCallback = null; } + if (typeof context === "undefined") { context = this.game.callbackContext; } + return this.collision.overlap(objectOrGroup1, objectOrGroup2, notifyCallback, Phaser.Collision.separate, context); }; return State; })(); diff --git a/Tests/sprites/starling texture atlas 1.js b/Tests/sprites/starling texture atlas 1.js new file mode 100644 index 00000000..fc7cdeee --- /dev/null +++ b/Tests/sprites/starling texture atlas 1.js @@ -0,0 +1,32 @@ +/// +(function () { + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + function init() { + // Starling/Sparrow XML Texture Atlas Method 1 + // + // In this example we assume that the XML data is stored in an external file + myGame.loader.addTextureAtlas('bits', 'assets/sprites/shoebox.png', 'assets/sprites/shoebox.xml', null, Phaser.Loader.TEXTURE_ATLAS_XML_STARLING); + myGame.loader.addTextureAtlas('bot', 'assets/sprites/shoebot.png', 'assets/sprites/shoebot.xml', null, Phaser.Loader.TEXTURE_ATLAS_XML_STARLING); + myGame.loader.load(); + } + var bits; + var bot; + function create() { + bot = myGame.add.sprite(800, 200, 'bot'); + bot.animations.add('run'); + bot.animations.play('run', 10, true); + bits = myGame.add.sprite(200, 200, 'bits'); + bits.frame = 0; + bot.velocity.x = -300; + } + function update() { + if(bot.x < -bot.width) { + bot.x = myGame.stage.width; + bits.frame++; + console.log(bits.frame, bits.animations.frameTotal); + if(bits.frame == bits.animations.frameTotal - 1) { + bits.frame = 0; + } + } + } +})(); diff --git a/Tests/sprites/starling texture atlas 1.ts b/Tests/sprites/starling texture atlas 1.ts new file mode 100644 index 00000000..c6dc0d8f --- /dev/null +++ b/Tests/sprites/starling texture atlas 1.ts @@ -0,0 +1,52 @@ +/// + +(function () { + + var myGame = new Phaser.Game(this, 'game', 800, 600, init, create, update); + + function init() { + + // Starling/Sparrow XML Texture Atlas Method 1 + // + // In this example we assume that the XML data is stored in an external file + myGame.loader.addTextureAtlas('bits', 'assets/sprites/shoebox.png', 'assets/sprites/shoebox.xml', null, Phaser.Loader.TEXTURE_ATLAS_XML_STARLING); + myGame.loader.addTextureAtlas('bot', 'assets/sprites/shoebot.png', 'assets/sprites/shoebot.xml', null, Phaser.Loader.TEXTURE_ATLAS_XML_STARLING); + + myGame.loader.load(); + + } + + var bits: Phaser.Sprite; + var bot: Phaser.Sprite; + + function create() { + + bot = myGame.add.sprite(800, 200, 'bot'); + bot.animations.add('run'); + bot.animations.play('run', 10, true); + + bits = myGame.add.sprite(200, 200, 'bits'); + bits.frame = 0; + + bot.velocity.x = -300; + + } + + function update() { + + if (bot.x < -bot.width) + { + bot.x = myGame.stage.width; + + bits.frame++; + console.log(bits.frame, bits.animations.frameTotal); + + if (bits.frame == bits.animations.frameTotal - 1) + { + bits.frame = 0; + } + } + + } + +})(); diff --git a/build/phaser.d.ts b/build/phaser.d.ts index d139098b..97e04558 100644 --- a/build/phaser.d.ts +++ b/build/phaser.d.ts @@ -1427,6 +1427,7 @@ module Phaser { * @return {FrameData} Generated FrameData object. */ static parseJSONData(game: Game, json): FrameData; + static parseXMLData(game: Game, xml, format: number): FrameData; } } /** @@ -1533,7 +1534,7 @@ module Phaser { * @param destWidth {number} Destiny draw width. * @param destHeight {number} Destiny draw height. */ - public setTrim(trimmed: bool, actualWidth, actualHeight, destX, destY, destWidth, destHeight): void; + public setTrim(trimmed: bool, actualWidth: number, actualHeight: number, destX: number, destY: number, destWidth: number, destHeight: number): void; } } /** @@ -1764,9 +1765,9 @@ module Phaser { * @param key {string} Asset key for the texture atlas. * @param url {string} URL of this texture atlas file. * @param data {object} Extra texture atlas data. - * @param data {object} Texture atlas frames data. + * @param atlasData {object} Texture atlas frames data. */ - public addTextureAtlas(key: string, url: string, data, jsonData): void; + public addTextureAtlas(key: string, url: string, data, atlasData, format): void; /** * Add a new image. * @param key {string} Asset key for the image. @@ -5192,6 +5193,12 @@ module Phaser { */ public crossOrigin: string; /** + * TextureAtlas data format constants + */ + static TEXTURE_ATLAS_JSON_ARRAY: number; + static TEXTURE_ATLAS_JSON_HASH: number; + static TEXTURE_ATLAS_XML_STARLING: number; + /** * Reset loader, this will remove all loaded assets. */ public reset(): void; @@ -5214,11 +5221,12 @@ module Phaser { /** * Add a new texture atlas loading request. * @param key {string} Unique asset key of the texture atlas file. - * @param url {string} URL of texture atlas file. - * @param [jsonURL] {string} url of JSON data file. - * @param [jsonData] {object} JSON data object. + * @param textureURL {string} The url of the texture atlas image file. + * @param [atlasURL] {string} The url of the texture atlas data file (json/xml) + * @param [atlasData] {object} A JSON or XML data object. + * @param [format] {number} A value describing the format of the data. */ - public addTextureAtlas(key: string, url: string, jsonURL?: string, jsonData?): void; + public addTextureAtlas(key: string, textureURL: string, atlasURL?: string, atlasData?, format?: number): void; /** * Add a new audio file loading request. * @param key {string} Unique asset key of the audio file. @@ -5269,7 +5277,8 @@ module Phaser { * Error occured when load a JSON. * @param key {string} Key of the error loading JSON file. */ - private jsonLoadError(key); + private dataLoadError(key); + private xmlLoadComplete(key); /** * Handle loading next file. * @param previousKey {string} Key of previous loaded asset. @@ -9783,9 +9792,15 @@ module Phaser { */ public destroy(): void; /** - * Call this method to see if one object collids another. - * @return {boolean} Whether the given objects or groups collids. + * Checks for overlaps between two objects using the world QuadTree. Can be GameObject vs. GameObject, GameObject vs. Group or Group vs. Group. + * Note: Does not take the objects scrollFactor into account. All overlaps are check in world space. + * @param object1 The first GameObject or Group to check. If null the world.group is used. + * @param object2 The second GameObject or Group to check. + * @param notifyCallback A callback function that is called if the objects overlap. The two objects will be passed to this function in the same order in which you passed them to Collision.overlap. + * @param processCallback A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then notifyCallback will only be called if processCallback returns true. + * @param context The context in which the callbacks will be called + * @returns {boolean} true if the objects overlap, otherwise false. */ - public collide(ObjectOrGroup1?: Basic, ObjectOrGroup2?: Basic, NotifyCallback?): bool; + public collide(objectOrGroup1?: Basic, objectOrGroup2?: Basic, notifyCallback?, context?): bool; } } diff --git a/build/phaser.js b/build/phaser.js index e4136383..82bbd8e6 100644 --- a/build/phaser.js +++ b/build/phaser.js @@ -2379,6 +2379,21 @@ var Phaser; } return data; }; + AnimationLoader.parseXMLData = function parseXMLData(game, xml, format) { + // Let's create some frames then + var data = new Phaser.FrameData(); + var frames = xml.getElementsByTagName('SubTexture'); + var newFrame; + for(var i = 0; i < frames.length; i++) { + var frame = frames[i].attributes; + newFrame = data.addFrame(new Phaser.Frame(frame.x.nodeValue, frame.y.nodeValue, frame.width.nodeValue, frame.height.nodeValue, frame.name.nodeValue)); + // Trimmed? + if(frame.frameX.nodeValue != '-0' || frame.frameY.nodeValue != '-0') { + newFrame.setTrim(true, frame.width.nodeValue, frame.height.nodeValue, Math.abs(frame.frameX.nodeValue), Math.abs(frame.frameY.nodeValue), frame.frameWidth.nodeValue, frame.frameHeight.nodeValue); + } + } + return data; + }; return AnimationLoader; })(); Phaser.AnimationLoader = AnimationLoader; @@ -2440,6 +2455,7 @@ var Phaser; * @param destHeight {number} Destiny draw height. */ function (trimmed, actualWidth, actualHeight, destX, destY, destWidth, destHeight) { + console.log('setTrim', actualWidth, actualHeight, destX, destY, destWidth, destHeight); this.trimmed = trimmed; this.sourceSizeW = actualWidth; this.sourceSizeH = actualHeight; @@ -2844,15 +2860,19 @@ var Phaser; * @param key {string} Asset key for the texture atlas. * @param url {string} URL of this texture atlas file. * @param data {object} Extra texture atlas data. - * @param data {object} Texture atlas frames data. + * @param atlasData {object} Texture atlas frames data. */ - function (key, url, data, jsonData) { + function (key, url, data, atlasData, format) { this._images[key] = { url: url, data: data, spriteSheet: true }; - this._images[key].frameData = Phaser.AnimationLoader.parseJSONData(this._game, jsonData); + if(format == Phaser.Loader.TEXTURE_ATLAS_JSON_ARRAY) { + this._images[key].frameData = Phaser.AnimationLoader.parseJSONData(this._game, atlasData); + } else if(format == Phaser.Loader.TEXTURE_ATLAS_XML_STARLING) { + this._images[key].frameData = Phaser.AnimationLoader.parseXMLData(this._game, atlasData, format); + } }; Cache.prototype.addImage = /** * Add a new image. @@ -9039,6 +9059,9 @@ var Phaser; this._xhr = new XMLHttpRequest(); this._queueSize = 0; } + Loader.TEXTURE_ATLAS_JSON_ARRAY = 0; + Loader.TEXTURE_ATLAS_JSON_HASH = 1; + Loader.TEXTURE_ATLAS_XML_STARLING = 2; Loader.prototype.reset = /** * Reset loader, this will remove all loaded assets. */ @@ -9100,62 +9123,95 @@ var Phaser; Loader.prototype.addTextureAtlas = /** * Add a new texture atlas loading request. * @param key {string} Unique asset key of the texture atlas file. - * @param url {string} URL of texture atlas file. - * @param [jsonURL] {string} url of JSON data file. - * @param [jsonData] {object} JSON data object. + * @param textureURL {string} The url of the texture atlas image file. + * @param [atlasURL] {string} The url of the texture atlas data file (json/xml) + * @param [atlasData] {object} A JSON or XML data object. + * @param [format] {number} A value describing the format of the data. */ - function (key, url, jsonURL, jsonData) { - if (typeof jsonURL === "undefined") { jsonURL = null; } - if (typeof jsonData === "undefined") { jsonData = null; } + function (key, textureURL, atlasURL, atlasData, format) { + if (typeof atlasURL === "undefined") { atlasURL = null; } + if (typeof atlasData === "undefined") { atlasData = null; } + if (typeof format === "undefined") { format = Loader.TEXTURE_ATLAS_JSON_ARRAY; } + console.log('addTextureAtlas', key, textureURL, atlasURL, atlasData, format); if(this.checkKeyExists(key) === false) { - if(jsonURL !== null) { - // A URL to a json file has been given + if(atlasURL !== null) { + // A URL to a json/xml file has been given this._queueSize++; this._fileList[key] = { type: 'textureatlas', key: key, - url: url, + url: textureURL, + atlasURL: atlasURL, data: null, - jsonURL: jsonURL, - jsonData: null, + format: format, error: false, loaded: false }; this._keys.push(key); } else { - // A json string or object has been given - if(typeof jsonData === 'string') { - var data = JSON.parse(jsonData); - // Malformed? - if(data['frames']) { - this._queueSize++; - this._fileList[key] = { - type: 'textureatlas', - key: key, - url: url, - data: null, - jsonURL: null, - jsonData: data['frames'], - error: false, - loaded: false - }; - this._keys.push(key); + if(format == Loader.TEXTURE_ATLAS_JSON_ARRAY) { + // A json string or object has been given + if(typeof atlasData === 'string') { + atlasData = JSON.parse(atlasData); } - } else { // Malformed? - if(jsonData['frames']) { + if(atlasData['frames']) { this._queueSize++; this._fileList[key] = { type: 'textureatlas', key: key, - url: url, + url: textureURL, data: null, - jsonURL: null, - jsonData: jsonData['frames'], + atlasURL: null, + atlasData: atlasData['frames'], + format: format, error: false, loaded: false }; this._keys.push(key); + } else { + throw new Error("Phaser.Loader. Invalid Texture Atlas JSON given, missing frames block"); + } + } else if(format == Loader.TEXTURE_ATLAS_XML_STARLING) { + // An xml string or object has been given + if(typeof atlasData === 'string') { + var tmp; + var xml; + try { + if(window['DOMParser']) { + tmp = new DOMParser(); + xml = tmp.parseFromString(atlasData, "text/xml"); + } else { + xml = new ActiveXObject("Microsoft.XMLDOM"); + xml.async = 'false'; + xml.loadXML(atlasData); + } + } catch (e) { + xml = undefined; + } + if(!xml || !xml.documentElement || xml.getElementsByTagName("parsererror").length) { + throw new Error("Phaser.Loader. Invalid Texture Atlas XML given"); + } else { + atlasData = xml; + } + } + // Malformed? + if(atlasData.getElementsByTagName('TextureAtlas')) { + this._queueSize++; + this._fileList[key] = { + type: 'textureatlas', + key: key, + url: textureURL, + data: null, + atlasURL: null, + atlasData: atlasData, + format: format, + error: false, + loaded: false + }; + this._keys.push(key); + } else { + throw new Error("Phaser.Loader. Invalid Texture Atlas XML given, missing tag"); } } } @@ -9313,18 +9369,24 @@ var Phaser; this._game.cache.addSpriteSheet(file.key, file.url, file.data, file.frameWidth, file.frameHeight, file.frameMax); break; case 'textureatlas': - if(file.jsonURL == null) { - this._game.cache.addTextureAtlas(file.key, file.url, file.data, file.jsonData); + if(file.atlasURL == null) { + this._game.cache.addTextureAtlas(file.key, file.url, file.data, file.atlasData, file.format); } else { - // Load the JSON before carrying on with the next file + // Load the JSON or XML before carrying on with the next file loadNext = false; - this._xhr.open("GET", file.jsonURL, true); + this._xhr.open("GET", file.atlasURL, true); this._xhr.responseType = "text"; - this._xhr.onload = function () { - return _this.jsonLoadComplete(file.key); - }; + if(file.format == Loader.TEXTURE_ATLAS_JSON_ARRAY) { + this._xhr.onload = function () { + return _this.jsonLoadComplete(file.key); + }; + } else if(file.format == Loader.TEXTURE_ATLAS_XML_STARLING) { + this._xhr.onload = function () { + return _this.xmlLoadComplete(file.key); + }; + } this._xhr.onerror = function () { - return _this.jsonLoadError(file.key); + return _this.dataLoadError(file.key); }; this._xhr.send(); } @@ -9351,11 +9413,11 @@ var Phaser; // Malformed? if(data['frames']) { var file = this._fileList[key]; - this._game.cache.addTextureAtlas(file.key, file.url, file.data, data['frames']); + this._game.cache.addTextureAtlas(file.key, file.url, file.data, data['frames'], file.format); } this.nextFile(key, true); }; - Loader.prototype.jsonLoadError = /** + Loader.prototype.dataLoadError = /** * Error occured when load a JSON. * @param key {string} Key of the error loading JSON file. */ @@ -9364,6 +9426,35 @@ var Phaser; file.error = true; this.nextFile(key, true); }; + Loader.prototype.xmlLoadComplete = function (key) { + var atlasData = this._xhr.response;// xml? + + var tmp; + var xml; + try { + if(window['DOMParser']) { + tmp = new DOMParser(); + xml = tmp.parseFromString(atlasData, "text/xml"); + } else { + xml = new ActiveXObject("Microsoft.XMLDOM"); + xml.async = 'false'; + xml.loadXML(atlasData); + } + } catch (e) { + xml = undefined; + } + if(!xml || !xml.documentElement || xml.getElementsByTagName("parsererror").length) { + throw new Error("Phaser.Loader. Invalid Texture Atlas XML given"); + } + // Malformed? + if(xml.getElementsByTagName('TextureAtlas')) { + var file = this._fileList[key]; + this._game.cache.addTextureAtlas(file.key, file.url, file.data, xml, file.format); + } else { + throw new Error("Phaser.Loader. Invalid Texture Atlas XML given, missing tag"); + } + this.nextFile(key, true); + }; Loader.prototype.nextFile = /** * Handle loading next file. * @param previousKey {string} Key of previous loaded asset. @@ -17375,14 +17466,21 @@ var Phaser; function () { }; State.prototype.collide = /** - * Call this method to see if one object collids another. - * @return {boolean} Whether the given objects or groups collids. + * Checks for overlaps between two objects using the world QuadTree. Can be GameObject vs. GameObject, GameObject vs. Group or Group vs. Group. + * Note: Does not take the objects scrollFactor into account. All overlaps are check in world space. + * @param object1 The first GameObject or Group to check. If null the world.group is used. + * @param object2 The second GameObject or Group to check. + * @param notifyCallback A callback function that is called if the objects overlap. The two objects will be passed to this function in the same order in which you passed them to Collision.overlap. + * @param processCallback A callback function that lets you perform additional checks against the two objects if they overlap. If this is set then notifyCallback will only be called if processCallback returns true. + * @param context The context in which the callbacks will be called + * @returns {boolean} true if the objects overlap, otherwise false. */ - function (ObjectOrGroup1, ObjectOrGroup2, NotifyCallback) { - if (typeof ObjectOrGroup1 === "undefined") { ObjectOrGroup1 = null; } - if (typeof ObjectOrGroup2 === "undefined") { ObjectOrGroup2 = null; } - if (typeof NotifyCallback === "undefined") { NotifyCallback = null; } - return this.collision.overlap(ObjectOrGroup1, ObjectOrGroup2, NotifyCallback, Phaser.Collision.separate); + function (objectOrGroup1, objectOrGroup2, notifyCallback, context) { + if (typeof objectOrGroup1 === "undefined") { objectOrGroup1 = null; } + if (typeof objectOrGroup2 === "undefined") { objectOrGroup2 = null; } + if (typeof notifyCallback === "undefined") { notifyCallback = null; } + if (typeof context === "undefined") { context = this.game.callbackContext; } + return this.collision.overlap(objectOrGroup1, objectOrGroup2, notifyCallback, Phaser.Collision.separate, context); }; return State; })();