diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index c63af99..3d2407e 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -2,7 +2,7 @@ import VideoBaseTexture from './VideoBaseTexture'; import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; -import { Rectangle } from '../math'; +import { Rectangle, Point } from '../math'; import { TextureCache, getResolutionOfUrl } from '../utils'; import settings from '../settings'; @@ -40,8 +40,9 @@ * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ - constructor(baseTexture, frame, orig, trim, rotate) + constructor(baseTexture, frame, orig, trim, rotate, anchor) { super(); @@ -144,6 +145,14 @@ } /** + * Anchor point that is used as default if sprite is created with this texture. + * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point. + * @member {PIXI.Point} + * @default {0,0} + */ + this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0); + + /** * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. * * @event PIXI.Texture#update @@ -156,7 +165,7 @@ /** * Contains data for uvs. May contain clamp settings and some matrices. * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} + * @member {PIXI.TextureMatrix} * @default null */ this.transform = null; diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index c63af99..3d2407e 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -2,7 +2,7 @@ import VideoBaseTexture from './VideoBaseTexture'; import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; -import { Rectangle } from '../math'; +import { Rectangle, Point } from '../math'; import { TextureCache, getResolutionOfUrl } from '../utils'; import settings from '../settings'; @@ -40,8 +40,9 @@ * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ - constructor(baseTexture, frame, orig, trim, rotate) + constructor(baseTexture, frame, orig, trim, rotate, anchor) { super(); @@ -144,6 +145,14 @@ } /** + * Anchor point that is used as default if sprite is created with this texture. + * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point. + * @member {PIXI.Point} + * @default {0,0} + */ + this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0); + + /** * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. * * @event PIXI.Texture#update @@ -156,7 +165,7 @@ /** * Contains data for uvs. May contain clamp settings and some matrices. * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} + * @member {PIXI.TextureMatrix} * @default null */ this.transform = null; diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index e538dd4..134d058 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -20,7 +20,20 @@ * textureArray.push(texture); * }; * - * let mc = new PIXI.AnimatedSprite(textureArray); + * let animatedSprite = new PIXI.extras.AnimatedSprite(textureArray); + * ``` + * + * The more efficient and simpler way to create an animated sprite is using a {@link PIXI.Spritesheet} + * containing the animation definitions: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * animatedSprite = new PIXI.extras.AnimatedSprite(sheet.animations["image_sequence"]); + * ... + * } * ``` * * @class diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index c63af99..3d2407e 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -2,7 +2,7 @@ import VideoBaseTexture from './VideoBaseTexture'; import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; -import { Rectangle } from '../math'; +import { Rectangle, Point } from '../math'; import { TextureCache, getResolutionOfUrl } from '../utils'; import settings from '../settings'; @@ -40,8 +40,9 @@ * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ - constructor(baseTexture, frame, orig, trim, rotate) + constructor(baseTexture, frame, orig, trim, rotate, anchor) { super(); @@ -144,6 +145,14 @@ } /** + * Anchor point that is used as default if sprite is created with this texture. + * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point. + * @member {PIXI.Point} + * @default {0,0} + */ + this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0); + + /** * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. * * @event PIXI.Texture#update @@ -156,7 +165,7 @@ /** * Contains data for uvs. May contain clamp settings and some matrices. * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} + * @member {PIXI.TextureMatrix} * @default null */ this.transform = null; diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index e538dd4..134d058 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -20,7 +20,20 @@ * textureArray.push(texture); * }; * - * let mc = new PIXI.AnimatedSprite(textureArray); + * let animatedSprite = new PIXI.extras.AnimatedSprite(textureArray); + * ``` + * + * The more efficient and simpler way to create an animated sprite is using a {@link PIXI.Spritesheet} + * containing the animation definitions: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * animatedSprite = new PIXI.extras.AnimatedSprite(sheet.animations["image_sequence"]); + * ... + * } * ``` * * @class diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 2ee1136..2d1a737 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -15,12 +15,20 @@ const width = Math.floor(spritesheet.data.frames[id].frame.w * spritesheet.baseTexture.sourceScale); const height = Math.floor(spritesheet.data.frames[id].frame.h * spritesheet.baseTexture.sourceScale); - expect(Object.keys(textures).length).to.equal(1); - expect(Object.keys(spritesheet.textures).length).to.equal(1); + expect(Object.keys(textures).length).to.equal(5); + expect(Object.keys(spritesheet.textures).length).to.equal(5); expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(width / spritesheet.resolution); expect(textures[id].height).to.equal(height / spritesheet.resolution); + expect(textures[id].defaultAnchor.x).to.equal(0); + expect(textures[id].defaultAnchor.y).to.equal(0); expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); + + expect(this.animations).to.have.property('star').that.is.an('array'); + expect(this.animations.star.length).to.equal(4); + expect(this.animations.star[0].defaultAnchor.x).to.equal(0.5); + expect(this.animations.star[0].defaultAnchor.y).to.equal(0.5); + spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index c63af99..3d2407e 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -2,7 +2,7 @@ import VideoBaseTexture from './VideoBaseTexture'; import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; -import { Rectangle } from '../math'; +import { Rectangle, Point } from '../math'; import { TextureCache, getResolutionOfUrl } from '../utils'; import settings from '../settings'; @@ -40,8 +40,9 @@ * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ - constructor(baseTexture, frame, orig, trim, rotate) + constructor(baseTexture, frame, orig, trim, rotate, anchor) { super(); @@ -144,6 +145,14 @@ } /** + * Anchor point that is used as default if sprite is created with this texture. + * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point. + * @member {PIXI.Point} + * @default {0,0} + */ + this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0); + + /** * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. * * @event PIXI.Texture#update @@ -156,7 +165,7 @@ /** * Contains data for uvs. May contain clamp settings and some matrices. * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} + * @member {PIXI.TextureMatrix} * @default null */ this.transform = null; diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index e538dd4..134d058 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -20,7 +20,20 @@ * textureArray.push(texture); * }; * - * let mc = new PIXI.AnimatedSprite(textureArray); + * let animatedSprite = new PIXI.extras.AnimatedSprite(textureArray); + * ``` + * + * The more efficient and simpler way to create an animated sprite is using a {@link PIXI.Spritesheet} + * containing the animation definitions: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * animatedSprite = new PIXI.extras.AnimatedSprite(sheet.animations["image_sequence"]); + * ... + * } * ``` * * @class diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 2ee1136..2d1a737 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -15,12 +15,20 @@ const width = Math.floor(spritesheet.data.frames[id].frame.w * spritesheet.baseTexture.sourceScale); const height = Math.floor(spritesheet.data.frames[id].frame.h * spritesheet.baseTexture.sourceScale); - expect(Object.keys(textures).length).to.equal(1); - expect(Object.keys(spritesheet.textures).length).to.equal(1); + expect(Object.keys(textures).length).to.equal(5); + expect(Object.keys(spritesheet.textures).length).to.equal(5); expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(width / spritesheet.resolution); expect(textures[id].height).to.equal(height / spritesheet.resolution); + expect(textures[id].defaultAnchor.x).to.equal(0); + expect(textures[id].defaultAnchor.y).to.equal(0); expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); + + expect(this.animations).to.have.property('star').that.is.an('array'); + expect(this.animations.star.length).to.equal(4); + expect(this.animations.star[0].defaultAnchor.x).to.equal(0.5); + expect(this.animations.star[0].defaultAnchor.y).to.equal(0.5); + spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/test/core/resources/building1.json b/test/core/resources/building1.json old mode 100755 new mode 100644 index 03fa5c1..d8fbfe1 --- a/test/core/resources/building1.json +++ b/test/core/resources/building1.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":95,"h":115}, - "sourceSize": {"w":95,"h":115}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":95,"h":115} +}, +"star1.png": +{ + "frame": {"x":98,"y":33,"w":28,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":28,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":98,"y":95,"w":26,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":3,"y":1,"w":26,"h":30}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":98,"y":1,"w":30,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":30,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":98,"y":63,"w":30,"h":27}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":3,"w":30,"h":27}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1.png", "format": "RGBA8888", - "size": {"w":128,"h":128}, + "size": {"w":128,"h":126}, "scale": "0.5", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index c63af99..3d2407e 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -2,7 +2,7 @@ import VideoBaseTexture from './VideoBaseTexture'; import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; -import { Rectangle } from '../math'; +import { Rectangle, Point } from '../math'; import { TextureCache, getResolutionOfUrl } from '../utils'; import settings from '../settings'; @@ -40,8 +40,9 @@ * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ - constructor(baseTexture, frame, orig, trim, rotate) + constructor(baseTexture, frame, orig, trim, rotate, anchor) { super(); @@ -144,6 +145,14 @@ } /** + * Anchor point that is used as default if sprite is created with this texture. + * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point. + * @member {PIXI.Point} + * @default {0,0} + */ + this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0); + + /** * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. * * @event PIXI.Texture#update @@ -156,7 +165,7 @@ /** * Contains data for uvs. May contain clamp settings and some matrices. * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} + * @member {PIXI.TextureMatrix} * @default null */ this.transform = null; diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index e538dd4..134d058 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -20,7 +20,20 @@ * textureArray.push(texture); * }; * - * let mc = new PIXI.AnimatedSprite(textureArray); + * let animatedSprite = new PIXI.extras.AnimatedSprite(textureArray); + * ``` + * + * The more efficient and simpler way to create an animated sprite is using a {@link PIXI.Spritesheet} + * containing the animation definitions: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * animatedSprite = new PIXI.extras.AnimatedSprite(sheet.animations["image_sequence"]); + * ... + * } * ``` * * @class diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 2ee1136..2d1a737 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -15,12 +15,20 @@ const width = Math.floor(spritesheet.data.frames[id].frame.w * spritesheet.baseTexture.sourceScale); const height = Math.floor(spritesheet.data.frames[id].frame.h * spritesheet.baseTexture.sourceScale); - expect(Object.keys(textures).length).to.equal(1); - expect(Object.keys(spritesheet.textures).length).to.equal(1); + expect(Object.keys(textures).length).to.equal(5); + expect(Object.keys(spritesheet.textures).length).to.equal(5); expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(width / spritesheet.resolution); expect(textures[id].height).to.equal(height / spritesheet.resolution); + expect(textures[id].defaultAnchor.x).to.equal(0); + expect(textures[id].defaultAnchor.y).to.equal(0); expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); + + expect(this.animations).to.have.property('star').that.is.an('array'); + expect(this.animations.star.length).to.equal(4); + expect(this.animations.star[0].defaultAnchor.x).to.equal(0.5); + expect(this.animations.star[0].defaultAnchor.y).to.equal(0.5); + spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/test/core/resources/building1.json b/test/core/resources/building1.json old mode 100755 new mode 100644 index 03fa5c1..d8fbfe1 --- a/test/core/resources/building1.json +++ b/test/core/resources/building1.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":95,"h":115}, - "sourceSize": {"w":95,"h":115}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":95,"h":115} +}, +"star1.png": +{ + "frame": {"x":98,"y":33,"w":28,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":28,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":98,"y":95,"w":26,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":3,"y":1,"w":26,"h":30}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":98,"y":1,"w":30,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":30,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":98,"y":63,"w":30,"h":27}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":3,"w":30,"h":27}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1.png", "format": "RGBA8888", - "size": {"w":128,"h":128}, + "size": {"w":128,"h":126}, "scale": "0.5", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1.png b/test/core/resources/building1.png old mode 100755 new mode 100644 index 7e1e114..7e16cda --- a/test/core/resources/building1.png +++ b/test/core/resources/building1.png Binary files differ diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index c63af99..3d2407e 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -2,7 +2,7 @@ import VideoBaseTexture from './VideoBaseTexture'; import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; -import { Rectangle } from '../math'; +import { Rectangle, Point } from '../math'; import { TextureCache, getResolutionOfUrl } from '../utils'; import settings from '../settings'; @@ -40,8 +40,9 @@ * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ - constructor(baseTexture, frame, orig, trim, rotate) + constructor(baseTexture, frame, orig, trim, rotate, anchor) { super(); @@ -144,6 +145,14 @@ } /** + * Anchor point that is used as default if sprite is created with this texture. + * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point. + * @member {PIXI.Point} + * @default {0,0} + */ + this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0); + + /** * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. * * @event PIXI.Texture#update @@ -156,7 +165,7 @@ /** * Contains data for uvs. May contain clamp settings and some matrices. * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} + * @member {PIXI.TextureMatrix} * @default null */ this.transform = null; diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index e538dd4..134d058 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -20,7 +20,20 @@ * textureArray.push(texture); * }; * - * let mc = new PIXI.AnimatedSprite(textureArray); + * let animatedSprite = new PIXI.extras.AnimatedSprite(textureArray); + * ``` + * + * The more efficient and simpler way to create an animated sprite is using a {@link PIXI.Spritesheet} + * containing the animation definitions: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * animatedSprite = new PIXI.extras.AnimatedSprite(sheet.animations["image_sequence"]); + * ... + * } * ``` * * @class diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 2ee1136..2d1a737 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -15,12 +15,20 @@ const width = Math.floor(spritesheet.data.frames[id].frame.w * spritesheet.baseTexture.sourceScale); const height = Math.floor(spritesheet.data.frames[id].frame.h * spritesheet.baseTexture.sourceScale); - expect(Object.keys(textures).length).to.equal(1); - expect(Object.keys(spritesheet.textures).length).to.equal(1); + expect(Object.keys(textures).length).to.equal(5); + expect(Object.keys(spritesheet.textures).length).to.equal(5); expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(width / spritesheet.resolution); expect(textures[id].height).to.equal(height / spritesheet.resolution); + expect(textures[id].defaultAnchor.x).to.equal(0); + expect(textures[id].defaultAnchor.y).to.equal(0); expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); + + expect(this.animations).to.have.property('star').that.is.an('array'); + expect(this.animations.star.length).to.equal(4); + expect(this.animations.star[0].defaultAnchor.x).to.equal(0.5); + expect(this.animations.star[0].defaultAnchor.y).to.equal(0.5); + spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/test/core/resources/building1.json b/test/core/resources/building1.json old mode 100755 new mode 100644 index 03fa5c1..d8fbfe1 --- a/test/core/resources/building1.json +++ b/test/core/resources/building1.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":95,"h":115}, - "sourceSize": {"w":95,"h":115}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":95,"h":115} +}, +"star1.png": +{ + "frame": {"x":98,"y":33,"w":28,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":28,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":98,"y":95,"w":26,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":3,"y":1,"w":26,"h":30}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":98,"y":1,"w":30,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":30,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":98,"y":63,"w":30,"h":27}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":3,"w":30,"h":27}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1.png", "format": "RGBA8888", - "size": {"w":128,"h":128}, + "size": {"w":128,"h":126}, "scale": "0.5", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1.png b/test/core/resources/building1.png old mode 100755 new mode 100644 index 7e1e114..7e16cda --- a/test/core/resources/building1.png +++ b/test/core/resources/building1.png Binary files differ diff --git a/test/core/resources/building1@2x.json b/test/core/resources/building1@2x.json old mode 100755 new mode 100644 index 24e25ff..c8cb7bf --- a/test/core/resources/building1@2x.json +++ b/test/core/resources/building1@2x.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":190,"h":229}, - "sourceSize": {"w":190,"h":229}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":190,"h":229} +}, +"star1.png": +{ + "frame": {"x":193,"y":59,"w":54,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":5,"y":5,"w":54,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":193,"y":115,"w":50,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":7,"y":4,"w":50,"h":57}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":193,"y":1,"w":56,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":5,"w":56,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":193,"y":174,"w":56,"h":50}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":8,"w":56,"h":50}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1@2x.png", "format": "RGBA8888", - "size": {"w":256,"h":256}, + "size": {"w":249,"h":231}, "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index c63af99..3d2407e 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -2,7 +2,7 @@ import VideoBaseTexture from './VideoBaseTexture'; import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; -import { Rectangle } from '../math'; +import { Rectangle, Point } from '../math'; import { TextureCache, getResolutionOfUrl } from '../utils'; import settings from '../settings'; @@ -40,8 +40,9 @@ * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ - constructor(baseTexture, frame, orig, trim, rotate) + constructor(baseTexture, frame, orig, trim, rotate, anchor) { super(); @@ -144,6 +145,14 @@ } /** + * Anchor point that is used as default if sprite is created with this texture. + * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point. + * @member {PIXI.Point} + * @default {0,0} + */ + this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0); + + /** * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. * * @event PIXI.Texture#update @@ -156,7 +165,7 @@ /** * Contains data for uvs. May contain clamp settings and some matrices. * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} + * @member {PIXI.TextureMatrix} * @default null */ this.transform = null; diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index e538dd4..134d058 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -20,7 +20,20 @@ * textureArray.push(texture); * }; * - * let mc = new PIXI.AnimatedSprite(textureArray); + * let animatedSprite = new PIXI.extras.AnimatedSprite(textureArray); + * ``` + * + * The more efficient and simpler way to create an animated sprite is using a {@link PIXI.Spritesheet} + * containing the animation definitions: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * animatedSprite = new PIXI.extras.AnimatedSprite(sheet.animations["image_sequence"]); + * ... + * } * ``` * * @class diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 2ee1136..2d1a737 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -15,12 +15,20 @@ const width = Math.floor(spritesheet.data.frames[id].frame.w * spritesheet.baseTexture.sourceScale); const height = Math.floor(spritesheet.data.frames[id].frame.h * spritesheet.baseTexture.sourceScale); - expect(Object.keys(textures).length).to.equal(1); - expect(Object.keys(spritesheet.textures).length).to.equal(1); + expect(Object.keys(textures).length).to.equal(5); + expect(Object.keys(spritesheet.textures).length).to.equal(5); expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(width / spritesheet.resolution); expect(textures[id].height).to.equal(height / spritesheet.resolution); + expect(textures[id].defaultAnchor.x).to.equal(0); + expect(textures[id].defaultAnchor.y).to.equal(0); expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); + + expect(this.animations).to.have.property('star').that.is.an('array'); + expect(this.animations.star.length).to.equal(4); + expect(this.animations.star[0].defaultAnchor.x).to.equal(0.5); + expect(this.animations.star[0].defaultAnchor.y).to.equal(0.5); + spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/test/core/resources/building1.json b/test/core/resources/building1.json old mode 100755 new mode 100644 index 03fa5c1..d8fbfe1 --- a/test/core/resources/building1.json +++ b/test/core/resources/building1.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":95,"h":115}, - "sourceSize": {"w":95,"h":115}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":95,"h":115} +}, +"star1.png": +{ + "frame": {"x":98,"y":33,"w":28,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":28,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":98,"y":95,"w":26,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":3,"y":1,"w":26,"h":30}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":98,"y":1,"w":30,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":30,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":98,"y":63,"w":30,"h":27}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":3,"w":30,"h":27}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1.png", "format": "RGBA8888", - "size": {"w":128,"h":128}, + "size": {"w":128,"h":126}, "scale": "0.5", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1.png b/test/core/resources/building1.png old mode 100755 new mode 100644 index 7e1e114..7e16cda --- a/test/core/resources/building1.png +++ b/test/core/resources/building1.png Binary files differ diff --git a/test/core/resources/building1@2x.json b/test/core/resources/building1@2x.json old mode 100755 new mode 100644 index 24e25ff..c8cb7bf --- a/test/core/resources/building1@2x.json +++ b/test/core/resources/building1@2x.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":190,"h":229}, - "sourceSize": {"w":190,"h":229}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":190,"h":229} +}, +"star1.png": +{ + "frame": {"x":193,"y":59,"w":54,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":5,"y":5,"w":54,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":193,"y":115,"w":50,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":7,"y":4,"w":50,"h":57}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":193,"y":1,"w":56,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":5,"w":56,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":193,"y":174,"w":56,"h":50}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":8,"w":56,"h":50}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1@2x.png", "format": "RGBA8888", - "size": {"w":256,"h":256}, + "size": {"w":249,"h":231}, "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1@2x.png b/test/core/resources/building1@2x.png old mode 100755 new mode 100644 index d5ecd04..06e613f --- a/test/core/resources/building1@2x.png +++ b/test/core/resources/building1@2x.png Binary files differ diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index c63af99..3d2407e 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -2,7 +2,7 @@ import VideoBaseTexture from './VideoBaseTexture'; import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; -import { Rectangle } from '../math'; +import { Rectangle, Point } from '../math'; import { TextureCache, getResolutionOfUrl } from '../utils'; import settings from '../settings'; @@ -40,8 +40,9 @@ * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ - constructor(baseTexture, frame, orig, trim, rotate) + constructor(baseTexture, frame, orig, trim, rotate, anchor) { super(); @@ -144,6 +145,14 @@ } /** + * Anchor point that is used as default if sprite is created with this texture. + * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point. + * @member {PIXI.Point} + * @default {0,0} + */ + this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0); + + /** * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. * * @event PIXI.Texture#update @@ -156,7 +165,7 @@ /** * Contains data for uvs. May contain clamp settings and some matrices. * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} + * @member {PIXI.TextureMatrix} * @default null */ this.transform = null; diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index e538dd4..134d058 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -20,7 +20,20 @@ * textureArray.push(texture); * }; * - * let mc = new PIXI.AnimatedSprite(textureArray); + * let animatedSprite = new PIXI.extras.AnimatedSprite(textureArray); + * ``` + * + * The more efficient and simpler way to create an animated sprite is using a {@link PIXI.Spritesheet} + * containing the animation definitions: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * animatedSprite = new PIXI.extras.AnimatedSprite(sheet.animations["image_sequence"]); + * ... + * } * ``` * * @class diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 2ee1136..2d1a737 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -15,12 +15,20 @@ const width = Math.floor(spritesheet.data.frames[id].frame.w * spritesheet.baseTexture.sourceScale); const height = Math.floor(spritesheet.data.frames[id].frame.h * spritesheet.baseTexture.sourceScale); - expect(Object.keys(textures).length).to.equal(1); - expect(Object.keys(spritesheet.textures).length).to.equal(1); + expect(Object.keys(textures).length).to.equal(5); + expect(Object.keys(spritesheet.textures).length).to.equal(5); expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(width / spritesheet.resolution); expect(textures[id].height).to.equal(height / spritesheet.resolution); + expect(textures[id].defaultAnchor.x).to.equal(0); + expect(textures[id].defaultAnchor.y).to.equal(0); expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); + + expect(this.animations).to.have.property('star').that.is.an('array'); + expect(this.animations.star.length).to.equal(4); + expect(this.animations.star[0].defaultAnchor.x).to.equal(0.5); + expect(this.animations.star[0].defaultAnchor.y).to.equal(0.5); + spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/test/core/resources/building1.json b/test/core/resources/building1.json old mode 100755 new mode 100644 index 03fa5c1..d8fbfe1 --- a/test/core/resources/building1.json +++ b/test/core/resources/building1.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":95,"h":115}, - "sourceSize": {"w":95,"h":115}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":95,"h":115} +}, +"star1.png": +{ + "frame": {"x":98,"y":33,"w":28,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":28,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":98,"y":95,"w":26,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":3,"y":1,"w":26,"h":30}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":98,"y":1,"w":30,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":30,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":98,"y":63,"w":30,"h":27}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":3,"w":30,"h":27}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1.png", "format": "RGBA8888", - "size": {"w":128,"h":128}, + "size": {"w":128,"h":126}, "scale": "0.5", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1.png b/test/core/resources/building1.png old mode 100755 new mode 100644 index 7e1e114..7e16cda --- a/test/core/resources/building1.png +++ b/test/core/resources/building1.png Binary files differ diff --git a/test/core/resources/building1@2x.json b/test/core/resources/building1@2x.json old mode 100755 new mode 100644 index 24e25ff..c8cb7bf --- a/test/core/resources/building1@2x.json +++ b/test/core/resources/building1@2x.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":190,"h":229}, - "sourceSize": {"w":190,"h":229}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":190,"h":229} +}, +"star1.png": +{ + "frame": {"x":193,"y":59,"w":54,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":5,"y":5,"w":54,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":193,"y":115,"w":50,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":7,"y":4,"w":50,"h":57}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":193,"y":1,"w":56,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":5,"w":56,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":193,"y":174,"w":56,"h":50}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":8,"w":56,"h":50}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1@2x.png", "format": "RGBA8888", - "size": {"w":256,"h":256}, + "size": {"w":249,"h":231}, "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1@2x.png b/test/core/resources/building1@2x.png old mode 100755 new mode 100644 index d5ecd04..06e613f --- a/test/core/resources/building1@2x.png +++ b/test/core/resources/building1@2x.png Binary files differ diff --git a/test/core/resources/src/goldmine_10_5.png b/test/core/resources/src/goldmine_10_5.png new file mode 100755 index 0000000..de2ba5a --- /dev/null +++ b/test/core/resources/src/goldmine_10_5.png Binary files differ diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index c63af99..3d2407e 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -2,7 +2,7 @@ import VideoBaseTexture from './VideoBaseTexture'; import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; -import { Rectangle } from '../math'; +import { Rectangle, Point } from '../math'; import { TextureCache, getResolutionOfUrl } from '../utils'; import settings from '../settings'; @@ -40,8 +40,9 @@ * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ - constructor(baseTexture, frame, orig, trim, rotate) + constructor(baseTexture, frame, orig, trim, rotate, anchor) { super(); @@ -144,6 +145,14 @@ } /** + * Anchor point that is used as default if sprite is created with this texture. + * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point. + * @member {PIXI.Point} + * @default {0,0} + */ + this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0); + + /** * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. * * @event PIXI.Texture#update @@ -156,7 +165,7 @@ /** * Contains data for uvs. May contain clamp settings and some matrices. * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} + * @member {PIXI.TextureMatrix} * @default null */ this.transform = null; diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index e538dd4..134d058 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -20,7 +20,20 @@ * textureArray.push(texture); * }; * - * let mc = new PIXI.AnimatedSprite(textureArray); + * let animatedSprite = new PIXI.extras.AnimatedSprite(textureArray); + * ``` + * + * The more efficient and simpler way to create an animated sprite is using a {@link PIXI.Spritesheet} + * containing the animation definitions: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * animatedSprite = new PIXI.extras.AnimatedSprite(sheet.animations["image_sequence"]); + * ... + * } * ``` * * @class diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 2ee1136..2d1a737 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -15,12 +15,20 @@ const width = Math.floor(spritesheet.data.frames[id].frame.w * spritesheet.baseTexture.sourceScale); const height = Math.floor(spritesheet.data.frames[id].frame.h * spritesheet.baseTexture.sourceScale); - expect(Object.keys(textures).length).to.equal(1); - expect(Object.keys(spritesheet.textures).length).to.equal(1); + expect(Object.keys(textures).length).to.equal(5); + expect(Object.keys(spritesheet.textures).length).to.equal(5); expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(width / spritesheet.resolution); expect(textures[id].height).to.equal(height / spritesheet.resolution); + expect(textures[id].defaultAnchor.x).to.equal(0); + expect(textures[id].defaultAnchor.y).to.equal(0); expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); + + expect(this.animations).to.have.property('star').that.is.an('array'); + expect(this.animations.star.length).to.equal(4); + expect(this.animations.star[0].defaultAnchor.x).to.equal(0.5); + expect(this.animations.star[0].defaultAnchor.y).to.equal(0.5); + spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/test/core/resources/building1.json b/test/core/resources/building1.json old mode 100755 new mode 100644 index 03fa5c1..d8fbfe1 --- a/test/core/resources/building1.json +++ b/test/core/resources/building1.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":95,"h":115}, - "sourceSize": {"w":95,"h":115}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":95,"h":115} +}, +"star1.png": +{ + "frame": {"x":98,"y":33,"w":28,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":28,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":98,"y":95,"w":26,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":3,"y":1,"w":26,"h":30}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":98,"y":1,"w":30,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":30,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":98,"y":63,"w":30,"h":27}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":3,"w":30,"h":27}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1.png", "format": "RGBA8888", - "size": {"w":128,"h":128}, + "size": {"w":128,"h":126}, "scale": "0.5", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1.png b/test/core/resources/building1.png old mode 100755 new mode 100644 index 7e1e114..7e16cda --- a/test/core/resources/building1.png +++ b/test/core/resources/building1.png Binary files differ diff --git a/test/core/resources/building1@2x.json b/test/core/resources/building1@2x.json old mode 100755 new mode 100644 index 24e25ff..c8cb7bf --- a/test/core/resources/building1@2x.json +++ b/test/core/resources/building1@2x.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":190,"h":229}, - "sourceSize": {"w":190,"h":229}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":190,"h":229} +}, +"star1.png": +{ + "frame": {"x":193,"y":59,"w":54,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":5,"y":5,"w":54,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":193,"y":115,"w":50,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":7,"y":4,"w":50,"h":57}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":193,"y":1,"w":56,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":5,"w":56,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":193,"y":174,"w":56,"h":50}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":8,"w":56,"h":50}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1@2x.png", "format": "RGBA8888", - "size": {"w":256,"h":256}, + "size": {"w":249,"h":231}, "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1@2x.png b/test/core/resources/building1@2x.png old mode 100755 new mode 100644 index d5ecd04..06e613f --- a/test/core/resources/building1@2x.png +++ b/test/core/resources/building1@2x.png Binary files differ diff --git a/test/core/resources/src/goldmine_10_5.png b/test/core/resources/src/goldmine_10_5.png new file mode 100755 index 0000000..de2ba5a --- /dev/null +++ b/test/core/resources/src/goldmine_10_5.png Binary files differ diff --git a/test/core/resources/src/sheet.tps b/test/core/resources/src/sheet.tps new file mode 100644 index 0000000..d08b10c --- /dev/null +++ b/test/core/resources/src/sheet.tps @@ -0,0 +1,262 @@ + + + + fileFormatVersion + 4 + texturePackerVersion + 4.8.1 + autoSDSettings + + + scale + 1 + extension + @2x + spriteFilter + + acceptFractionalValues + + maxTextureSize + + width + -1 + height + -1 + + + + scale + 0.5 + extension + + spriteFilter + + acceptFractionalValues + + maxTextureSize + + width + -1 + height + -1 + + + + allowRotation + + shapeDebug + + dpi + 72 + dataFormat + pixijs4 + textureFileName + + flipPVR + + pvrCompressionQuality + PVR_QUALITY_NORMAL + atfCompressData + + mipMapMinSize + 32768 + etc1CompressionQuality + ETC1_QUALITY_LOW_PERCEPTUAL + etc2CompressionQuality + ETC2_QUALITY_LOW_PERCEPTUAL + dxtCompressionMode + DXT_PERCEPTUAL + jxrColorFormat + JXR_YUV444 + jxrTrimFlexBits + 0 + jxrCompressionLevel + 0 + ditherType + NearestNeighbour + backgroundColor + 0 + libGdx + + filtering + + x + Linear + y + Linear + + + shapePadding + 0 + jpgQuality + 80 + pngOptimizationLevel + 1 + webpQualityLevel + 101 + textureSubPath + + atfFormats + + textureFormat + png + borderPadding + 0 + maxTextureSize + + width + 2048 + height + 2048 + + fixedTextureSize + + width + -1 + height + -1 + + algorithmSettings + + algorithm + MaxRects + freeSizeMode + Best + sizeConstraints + AnySize + forceSquared + + maxRects + + heuristic + Best + + basic + + sortBy + Best + order + Ascending + + polygon + + alignToGrid + 1 + + + dataFileNames + + data + + name + ../building1{v}.json + + + multiPack + + forceIdenticalLayout + + outputFormat + RGBA8888 + alphaHandling + ClearTransparentPixels + contentProtection + + key + + + autoAliasEnabled + + trimSpriteNames + + prependSmartFolderName + + autodetectAnimations + + globalSpriteSettings + + scale + 1 + scaleMode + Smooth + extrude + 1 + trimThreshold + 1 + trimMargin + 1 + trimMode + Trim + tracerTolerance + 200 + heuristicMask + + defaultPivotPoint + 0,0 + writePivotPoints + + + individualSpriteSettings + + goldmine_10_5.png + + pivotPoint + 0,0 + scale9Enabled + + scale9Borders + 48,57,95,115 + scale9Paddings + 48,57,95,115 + scale9FromFile + + + star1.png + star2.png + star3.png + star4.png + + pivotPoint + 0.5,0.5 + scale9Enabled + + scale9Borders + 16,16,32,32 + scale9Paddings + 16,16,32,32 + scale9FromFile + + + + fileList + + goldmine_10_5.png + star1.png + star2.png + star3.png + star4.png + + ignoreFileList + + replaceList + + ignoredWarnings + + commonDivisorX + 1 + commonDivisorY + 1 + packNormalMaps + + autodetectNormalMaps + + normalMapFilter + + normalMapSuffix + + normalMapSheetFileName + + exporterProperties + + + diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index c63af99..3d2407e 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -2,7 +2,7 @@ import VideoBaseTexture from './VideoBaseTexture'; import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; -import { Rectangle } from '../math'; +import { Rectangle, Point } from '../math'; import { TextureCache, getResolutionOfUrl } from '../utils'; import settings from '../settings'; @@ -40,8 +40,9 @@ * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ - constructor(baseTexture, frame, orig, trim, rotate) + constructor(baseTexture, frame, orig, trim, rotate, anchor) { super(); @@ -144,6 +145,14 @@ } /** + * Anchor point that is used as default if sprite is created with this texture. + * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point. + * @member {PIXI.Point} + * @default {0,0} + */ + this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0); + + /** * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. * * @event PIXI.Texture#update @@ -156,7 +165,7 @@ /** * Contains data for uvs. May contain clamp settings and some matrices. * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} + * @member {PIXI.TextureMatrix} * @default null */ this.transform = null; diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index e538dd4..134d058 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -20,7 +20,20 @@ * textureArray.push(texture); * }; * - * let mc = new PIXI.AnimatedSprite(textureArray); + * let animatedSprite = new PIXI.extras.AnimatedSprite(textureArray); + * ``` + * + * The more efficient and simpler way to create an animated sprite is using a {@link PIXI.Spritesheet} + * containing the animation definitions: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * animatedSprite = new PIXI.extras.AnimatedSprite(sheet.animations["image_sequence"]); + * ... + * } * ``` * * @class diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 2ee1136..2d1a737 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -15,12 +15,20 @@ const width = Math.floor(spritesheet.data.frames[id].frame.w * spritesheet.baseTexture.sourceScale); const height = Math.floor(spritesheet.data.frames[id].frame.h * spritesheet.baseTexture.sourceScale); - expect(Object.keys(textures).length).to.equal(1); - expect(Object.keys(spritesheet.textures).length).to.equal(1); + expect(Object.keys(textures).length).to.equal(5); + expect(Object.keys(spritesheet.textures).length).to.equal(5); expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(width / spritesheet.resolution); expect(textures[id].height).to.equal(height / spritesheet.resolution); + expect(textures[id].defaultAnchor.x).to.equal(0); + expect(textures[id].defaultAnchor.y).to.equal(0); expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); + + expect(this.animations).to.have.property('star').that.is.an('array'); + expect(this.animations.star.length).to.equal(4); + expect(this.animations.star[0].defaultAnchor.x).to.equal(0.5); + expect(this.animations.star[0].defaultAnchor.y).to.equal(0.5); + spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/test/core/resources/building1.json b/test/core/resources/building1.json old mode 100755 new mode 100644 index 03fa5c1..d8fbfe1 --- a/test/core/resources/building1.json +++ b/test/core/resources/building1.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":95,"h":115}, - "sourceSize": {"w":95,"h":115}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":95,"h":115} +}, +"star1.png": +{ + "frame": {"x":98,"y":33,"w":28,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":28,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":98,"y":95,"w":26,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":3,"y":1,"w":26,"h":30}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":98,"y":1,"w":30,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":30,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":98,"y":63,"w":30,"h":27}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":3,"w":30,"h":27}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1.png", "format": "RGBA8888", - "size": {"w":128,"h":128}, + "size": {"w":128,"h":126}, "scale": "0.5", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1.png b/test/core/resources/building1.png old mode 100755 new mode 100644 index 7e1e114..7e16cda --- a/test/core/resources/building1.png +++ b/test/core/resources/building1.png Binary files differ diff --git a/test/core/resources/building1@2x.json b/test/core/resources/building1@2x.json old mode 100755 new mode 100644 index 24e25ff..c8cb7bf --- a/test/core/resources/building1@2x.json +++ b/test/core/resources/building1@2x.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":190,"h":229}, - "sourceSize": {"w":190,"h":229}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":190,"h":229} +}, +"star1.png": +{ + "frame": {"x":193,"y":59,"w":54,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":5,"y":5,"w":54,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":193,"y":115,"w":50,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":7,"y":4,"w":50,"h":57}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":193,"y":1,"w":56,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":5,"w":56,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":193,"y":174,"w":56,"h":50}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":8,"w":56,"h":50}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1@2x.png", "format": "RGBA8888", - "size": {"w":256,"h":256}, + "size": {"w":249,"h":231}, "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1@2x.png b/test/core/resources/building1@2x.png old mode 100755 new mode 100644 index d5ecd04..06e613f --- a/test/core/resources/building1@2x.png +++ b/test/core/resources/building1@2x.png Binary files differ diff --git a/test/core/resources/src/goldmine_10_5.png b/test/core/resources/src/goldmine_10_5.png new file mode 100755 index 0000000..de2ba5a --- /dev/null +++ b/test/core/resources/src/goldmine_10_5.png Binary files differ diff --git a/test/core/resources/src/sheet.tps b/test/core/resources/src/sheet.tps new file mode 100644 index 0000000..d08b10c --- /dev/null +++ b/test/core/resources/src/sheet.tps @@ -0,0 +1,262 @@ + + + + fileFormatVersion + 4 + texturePackerVersion + 4.8.1 + autoSDSettings + + + scale + 1 + extension + @2x + spriteFilter + + acceptFractionalValues + + maxTextureSize + + width + -1 + height + -1 + + + + scale + 0.5 + extension + + spriteFilter + + acceptFractionalValues + + maxTextureSize + + width + -1 + height + -1 + + + + allowRotation + + shapeDebug + + dpi + 72 + dataFormat + pixijs4 + textureFileName + + flipPVR + + pvrCompressionQuality + PVR_QUALITY_NORMAL + atfCompressData + + mipMapMinSize + 32768 + etc1CompressionQuality + ETC1_QUALITY_LOW_PERCEPTUAL + etc2CompressionQuality + ETC2_QUALITY_LOW_PERCEPTUAL + dxtCompressionMode + DXT_PERCEPTUAL + jxrColorFormat + JXR_YUV444 + jxrTrimFlexBits + 0 + jxrCompressionLevel + 0 + ditherType + NearestNeighbour + backgroundColor + 0 + libGdx + + filtering + + x + Linear + y + Linear + + + shapePadding + 0 + jpgQuality + 80 + pngOptimizationLevel + 1 + webpQualityLevel + 101 + textureSubPath + + atfFormats + + textureFormat + png + borderPadding + 0 + maxTextureSize + + width + 2048 + height + 2048 + + fixedTextureSize + + width + -1 + height + -1 + + algorithmSettings + + algorithm + MaxRects + freeSizeMode + Best + sizeConstraints + AnySize + forceSquared + + maxRects + + heuristic + Best + + basic + + sortBy + Best + order + Ascending + + polygon + + alignToGrid + 1 + + + dataFileNames + + data + + name + ../building1{v}.json + + + multiPack + + forceIdenticalLayout + + outputFormat + RGBA8888 + alphaHandling + ClearTransparentPixels + contentProtection + + key + + + autoAliasEnabled + + trimSpriteNames + + prependSmartFolderName + + autodetectAnimations + + globalSpriteSettings + + scale + 1 + scaleMode + Smooth + extrude + 1 + trimThreshold + 1 + trimMargin + 1 + trimMode + Trim + tracerTolerance + 200 + heuristicMask + + defaultPivotPoint + 0,0 + writePivotPoints + + + individualSpriteSettings + + goldmine_10_5.png + + pivotPoint + 0,0 + scale9Enabled + + scale9Borders + 48,57,95,115 + scale9Paddings + 48,57,95,115 + scale9FromFile + + + star1.png + star2.png + star3.png + star4.png + + pivotPoint + 0.5,0.5 + scale9Enabled + + scale9Borders + 16,16,32,32 + scale9Paddings + 16,16,32,32 + scale9FromFile + + + + fileList + + goldmine_10_5.png + star1.png + star2.png + star3.png + star4.png + + ignoreFileList + + replaceList + + ignoredWarnings + + commonDivisorX + 1 + commonDivisorY + 1 + packNormalMaps + + autodetectNormalMaps + + normalMapFilter + + normalMapSuffix + + normalMapSheetFileName + + exporterProperties + + + diff --git a/test/core/resources/src/star1.png b/test/core/resources/src/star1.png new file mode 100644 index 0000000..32824fd --- /dev/null +++ b/test/core/resources/src/star1.png Binary files differ diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index c63af99..3d2407e 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -2,7 +2,7 @@ import VideoBaseTexture from './VideoBaseTexture'; import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; -import { Rectangle } from '../math'; +import { Rectangle, Point } from '../math'; import { TextureCache, getResolutionOfUrl } from '../utils'; import settings from '../settings'; @@ -40,8 +40,9 @@ * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ - constructor(baseTexture, frame, orig, trim, rotate) + constructor(baseTexture, frame, orig, trim, rotate, anchor) { super(); @@ -144,6 +145,14 @@ } /** + * Anchor point that is used as default if sprite is created with this texture. + * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point. + * @member {PIXI.Point} + * @default {0,0} + */ + this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0); + + /** * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. * * @event PIXI.Texture#update @@ -156,7 +165,7 @@ /** * Contains data for uvs. May contain clamp settings and some matrices. * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} + * @member {PIXI.TextureMatrix} * @default null */ this.transform = null; diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index e538dd4..134d058 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -20,7 +20,20 @@ * textureArray.push(texture); * }; * - * let mc = new PIXI.AnimatedSprite(textureArray); + * let animatedSprite = new PIXI.extras.AnimatedSprite(textureArray); + * ``` + * + * The more efficient and simpler way to create an animated sprite is using a {@link PIXI.Spritesheet} + * containing the animation definitions: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * animatedSprite = new PIXI.extras.AnimatedSprite(sheet.animations["image_sequence"]); + * ... + * } * ``` * * @class diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 2ee1136..2d1a737 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -15,12 +15,20 @@ const width = Math.floor(spritesheet.data.frames[id].frame.w * spritesheet.baseTexture.sourceScale); const height = Math.floor(spritesheet.data.frames[id].frame.h * spritesheet.baseTexture.sourceScale); - expect(Object.keys(textures).length).to.equal(1); - expect(Object.keys(spritesheet.textures).length).to.equal(1); + expect(Object.keys(textures).length).to.equal(5); + expect(Object.keys(spritesheet.textures).length).to.equal(5); expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(width / spritesheet.resolution); expect(textures[id].height).to.equal(height / spritesheet.resolution); + expect(textures[id].defaultAnchor.x).to.equal(0); + expect(textures[id].defaultAnchor.y).to.equal(0); expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); + + expect(this.animations).to.have.property('star').that.is.an('array'); + expect(this.animations.star.length).to.equal(4); + expect(this.animations.star[0].defaultAnchor.x).to.equal(0.5); + expect(this.animations.star[0].defaultAnchor.y).to.equal(0.5); + spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/test/core/resources/building1.json b/test/core/resources/building1.json old mode 100755 new mode 100644 index 03fa5c1..d8fbfe1 --- a/test/core/resources/building1.json +++ b/test/core/resources/building1.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":95,"h":115}, - "sourceSize": {"w":95,"h":115}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":95,"h":115} +}, +"star1.png": +{ + "frame": {"x":98,"y":33,"w":28,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":28,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":98,"y":95,"w":26,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":3,"y":1,"w":26,"h":30}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":98,"y":1,"w":30,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":30,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":98,"y":63,"w":30,"h":27}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":3,"w":30,"h":27}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1.png", "format": "RGBA8888", - "size": {"w":128,"h":128}, + "size": {"w":128,"h":126}, "scale": "0.5", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1.png b/test/core/resources/building1.png old mode 100755 new mode 100644 index 7e1e114..7e16cda --- a/test/core/resources/building1.png +++ b/test/core/resources/building1.png Binary files differ diff --git a/test/core/resources/building1@2x.json b/test/core/resources/building1@2x.json old mode 100755 new mode 100644 index 24e25ff..c8cb7bf --- a/test/core/resources/building1@2x.json +++ b/test/core/resources/building1@2x.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":190,"h":229}, - "sourceSize": {"w":190,"h":229}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":190,"h":229} +}, +"star1.png": +{ + "frame": {"x":193,"y":59,"w":54,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":5,"y":5,"w":54,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":193,"y":115,"w":50,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":7,"y":4,"w":50,"h":57}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":193,"y":1,"w":56,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":5,"w":56,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":193,"y":174,"w":56,"h":50}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":8,"w":56,"h":50}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1@2x.png", "format": "RGBA8888", - "size": {"w":256,"h":256}, + "size": {"w":249,"h":231}, "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1@2x.png b/test/core/resources/building1@2x.png old mode 100755 new mode 100644 index d5ecd04..06e613f --- a/test/core/resources/building1@2x.png +++ b/test/core/resources/building1@2x.png Binary files differ diff --git a/test/core/resources/src/goldmine_10_5.png b/test/core/resources/src/goldmine_10_5.png new file mode 100755 index 0000000..de2ba5a --- /dev/null +++ b/test/core/resources/src/goldmine_10_5.png Binary files differ diff --git a/test/core/resources/src/sheet.tps b/test/core/resources/src/sheet.tps new file mode 100644 index 0000000..d08b10c --- /dev/null +++ b/test/core/resources/src/sheet.tps @@ -0,0 +1,262 @@ + + + + fileFormatVersion + 4 + texturePackerVersion + 4.8.1 + autoSDSettings + + + scale + 1 + extension + @2x + spriteFilter + + acceptFractionalValues + + maxTextureSize + + width + -1 + height + -1 + + + + scale + 0.5 + extension + + spriteFilter + + acceptFractionalValues + + maxTextureSize + + width + -1 + height + -1 + + + + allowRotation + + shapeDebug + + dpi + 72 + dataFormat + pixijs4 + textureFileName + + flipPVR + + pvrCompressionQuality + PVR_QUALITY_NORMAL + atfCompressData + + mipMapMinSize + 32768 + etc1CompressionQuality + ETC1_QUALITY_LOW_PERCEPTUAL + etc2CompressionQuality + ETC2_QUALITY_LOW_PERCEPTUAL + dxtCompressionMode + DXT_PERCEPTUAL + jxrColorFormat + JXR_YUV444 + jxrTrimFlexBits + 0 + jxrCompressionLevel + 0 + ditherType + NearestNeighbour + backgroundColor + 0 + libGdx + + filtering + + x + Linear + y + Linear + + + shapePadding + 0 + jpgQuality + 80 + pngOptimizationLevel + 1 + webpQualityLevel + 101 + textureSubPath + + atfFormats + + textureFormat + png + borderPadding + 0 + maxTextureSize + + width + 2048 + height + 2048 + + fixedTextureSize + + width + -1 + height + -1 + + algorithmSettings + + algorithm + MaxRects + freeSizeMode + Best + sizeConstraints + AnySize + forceSquared + + maxRects + + heuristic + Best + + basic + + sortBy + Best + order + Ascending + + polygon + + alignToGrid + 1 + + + dataFileNames + + data + + name + ../building1{v}.json + + + multiPack + + forceIdenticalLayout + + outputFormat + RGBA8888 + alphaHandling + ClearTransparentPixels + contentProtection + + key + + + autoAliasEnabled + + trimSpriteNames + + prependSmartFolderName + + autodetectAnimations + + globalSpriteSettings + + scale + 1 + scaleMode + Smooth + extrude + 1 + trimThreshold + 1 + trimMargin + 1 + trimMode + Trim + tracerTolerance + 200 + heuristicMask + + defaultPivotPoint + 0,0 + writePivotPoints + + + individualSpriteSettings + + goldmine_10_5.png + + pivotPoint + 0,0 + scale9Enabled + + scale9Borders + 48,57,95,115 + scale9Paddings + 48,57,95,115 + scale9FromFile + + + star1.png + star2.png + star3.png + star4.png + + pivotPoint + 0.5,0.5 + scale9Enabled + + scale9Borders + 16,16,32,32 + scale9Paddings + 16,16,32,32 + scale9FromFile + + + + fileList + + goldmine_10_5.png + star1.png + star2.png + star3.png + star4.png + + ignoreFileList + + replaceList + + ignoredWarnings + + commonDivisorX + 1 + commonDivisorY + 1 + packNormalMaps + + autodetectNormalMaps + + normalMapFilter + + normalMapSuffix + + normalMapSheetFileName + + exporterProperties + + + diff --git a/test/core/resources/src/star1.png b/test/core/resources/src/star1.png new file mode 100644 index 0000000..32824fd --- /dev/null +++ b/test/core/resources/src/star1.png Binary files differ diff --git a/test/core/resources/src/star2.png b/test/core/resources/src/star2.png new file mode 100644 index 0000000..8432cb5 --- /dev/null +++ b/test/core/resources/src/star2.png Binary files differ diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index c63af99..3d2407e 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -2,7 +2,7 @@ import VideoBaseTexture from './VideoBaseTexture'; import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; -import { Rectangle } from '../math'; +import { Rectangle, Point } from '../math'; import { TextureCache, getResolutionOfUrl } from '../utils'; import settings from '../settings'; @@ -40,8 +40,9 @@ * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ - constructor(baseTexture, frame, orig, trim, rotate) + constructor(baseTexture, frame, orig, trim, rotate, anchor) { super(); @@ -144,6 +145,14 @@ } /** + * Anchor point that is used as default if sprite is created with this texture. + * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point. + * @member {PIXI.Point} + * @default {0,0} + */ + this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0); + + /** * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. * * @event PIXI.Texture#update @@ -156,7 +165,7 @@ /** * Contains data for uvs. May contain clamp settings and some matrices. * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} + * @member {PIXI.TextureMatrix} * @default null */ this.transform = null; diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index e538dd4..134d058 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -20,7 +20,20 @@ * textureArray.push(texture); * }; * - * let mc = new PIXI.AnimatedSprite(textureArray); + * let animatedSprite = new PIXI.extras.AnimatedSprite(textureArray); + * ``` + * + * The more efficient and simpler way to create an animated sprite is using a {@link PIXI.Spritesheet} + * containing the animation definitions: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * animatedSprite = new PIXI.extras.AnimatedSprite(sheet.animations["image_sequence"]); + * ... + * } * ``` * * @class diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 2ee1136..2d1a737 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -15,12 +15,20 @@ const width = Math.floor(spritesheet.data.frames[id].frame.w * spritesheet.baseTexture.sourceScale); const height = Math.floor(spritesheet.data.frames[id].frame.h * spritesheet.baseTexture.sourceScale); - expect(Object.keys(textures).length).to.equal(1); - expect(Object.keys(spritesheet.textures).length).to.equal(1); + expect(Object.keys(textures).length).to.equal(5); + expect(Object.keys(spritesheet.textures).length).to.equal(5); expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(width / spritesheet.resolution); expect(textures[id].height).to.equal(height / spritesheet.resolution); + expect(textures[id].defaultAnchor.x).to.equal(0); + expect(textures[id].defaultAnchor.y).to.equal(0); expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); + + expect(this.animations).to.have.property('star').that.is.an('array'); + expect(this.animations.star.length).to.equal(4); + expect(this.animations.star[0].defaultAnchor.x).to.equal(0.5); + expect(this.animations.star[0].defaultAnchor.y).to.equal(0.5); + spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/test/core/resources/building1.json b/test/core/resources/building1.json old mode 100755 new mode 100644 index 03fa5c1..d8fbfe1 --- a/test/core/resources/building1.json +++ b/test/core/resources/building1.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":95,"h":115}, - "sourceSize": {"w":95,"h":115}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":95,"h":115} +}, +"star1.png": +{ + "frame": {"x":98,"y":33,"w":28,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":28,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":98,"y":95,"w":26,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":3,"y":1,"w":26,"h":30}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":98,"y":1,"w":30,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":30,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":98,"y":63,"w":30,"h":27}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":3,"w":30,"h":27}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1.png", "format": "RGBA8888", - "size": {"w":128,"h":128}, + "size": {"w":128,"h":126}, "scale": "0.5", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1.png b/test/core/resources/building1.png old mode 100755 new mode 100644 index 7e1e114..7e16cda --- a/test/core/resources/building1.png +++ b/test/core/resources/building1.png Binary files differ diff --git a/test/core/resources/building1@2x.json b/test/core/resources/building1@2x.json old mode 100755 new mode 100644 index 24e25ff..c8cb7bf --- a/test/core/resources/building1@2x.json +++ b/test/core/resources/building1@2x.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":190,"h":229}, - "sourceSize": {"w":190,"h":229}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":190,"h":229} +}, +"star1.png": +{ + "frame": {"x":193,"y":59,"w":54,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":5,"y":5,"w":54,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":193,"y":115,"w":50,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":7,"y":4,"w":50,"h":57}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":193,"y":1,"w":56,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":5,"w":56,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":193,"y":174,"w":56,"h":50}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":8,"w":56,"h":50}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1@2x.png", "format": "RGBA8888", - "size": {"w":256,"h":256}, + "size": {"w":249,"h":231}, "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1@2x.png b/test/core/resources/building1@2x.png old mode 100755 new mode 100644 index d5ecd04..06e613f --- a/test/core/resources/building1@2x.png +++ b/test/core/resources/building1@2x.png Binary files differ diff --git a/test/core/resources/src/goldmine_10_5.png b/test/core/resources/src/goldmine_10_5.png new file mode 100755 index 0000000..de2ba5a --- /dev/null +++ b/test/core/resources/src/goldmine_10_5.png Binary files differ diff --git a/test/core/resources/src/sheet.tps b/test/core/resources/src/sheet.tps new file mode 100644 index 0000000..d08b10c --- /dev/null +++ b/test/core/resources/src/sheet.tps @@ -0,0 +1,262 @@ + + + + fileFormatVersion + 4 + texturePackerVersion + 4.8.1 + autoSDSettings + + + scale + 1 + extension + @2x + spriteFilter + + acceptFractionalValues + + maxTextureSize + + width + -1 + height + -1 + + + + scale + 0.5 + extension + + spriteFilter + + acceptFractionalValues + + maxTextureSize + + width + -1 + height + -1 + + + + allowRotation + + shapeDebug + + dpi + 72 + dataFormat + pixijs4 + textureFileName + + flipPVR + + pvrCompressionQuality + PVR_QUALITY_NORMAL + atfCompressData + + mipMapMinSize + 32768 + etc1CompressionQuality + ETC1_QUALITY_LOW_PERCEPTUAL + etc2CompressionQuality + ETC2_QUALITY_LOW_PERCEPTUAL + dxtCompressionMode + DXT_PERCEPTUAL + jxrColorFormat + JXR_YUV444 + jxrTrimFlexBits + 0 + jxrCompressionLevel + 0 + ditherType + NearestNeighbour + backgroundColor + 0 + libGdx + + filtering + + x + Linear + y + Linear + + + shapePadding + 0 + jpgQuality + 80 + pngOptimizationLevel + 1 + webpQualityLevel + 101 + textureSubPath + + atfFormats + + textureFormat + png + borderPadding + 0 + maxTextureSize + + width + 2048 + height + 2048 + + fixedTextureSize + + width + -1 + height + -1 + + algorithmSettings + + algorithm + MaxRects + freeSizeMode + Best + sizeConstraints + AnySize + forceSquared + + maxRects + + heuristic + Best + + basic + + sortBy + Best + order + Ascending + + polygon + + alignToGrid + 1 + + + dataFileNames + + data + + name + ../building1{v}.json + + + multiPack + + forceIdenticalLayout + + outputFormat + RGBA8888 + alphaHandling + ClearTransparentPixels + contentProtection + + key + + + autoAliasEnabled + + trimSpriteNames + + prependSmartFolderName + + autodetectAnimations + + globalSpriteSettings + + scale + 1 + scaleMode + Smooth + extrude + 1 + trimThreshold + 1 + trimMargin + 1 + trimMode + Trim + tracerTolerance + 200 + heuristicMask + + defaultPivotPoint + 0,0 + writePivotPoints + + + individualSpriteSettings + + goldmine_10_5.png + + pivotPoint + 0,0 + scale9Enabled + + scale9Borders + 48,57,95,115 + scale9Paddings + 48,57,95,115 + scale9FromFile + + + star1.png + star2.png + star3.png + star4.png + + pivotPoint + 0.5,0.5 + scale9Enabled + + scale9Borders + 16,16,32,32 + scale9Paddings + 16,16,32,32 + scale9FromFile + + + + fileList + + goldmine_10_5.png + star1.png + star2.png + star3.png + star4.png + + ignoreFileList + + replaceList + + ignoredWarnings + + commonDivisorX + 1 + commonDivisorY + 1 + packNormalMaps + + autodetectNormalMaps + + normalMapFilter + + normalMapSuffix + + normalMapSheetFileName + + exporterProperties + + + diff --git a/test/core/resources/src/star1.png b/test/core/resources/src/star1.png new file mode 100644 index 0000000..32824fd --- /dev/null +++ b/test/core/resources/src/star1.png Binary files differ diff --git a/test/core/resources/src/star2.png b/test/core/resources/src/star2.png new file mode 100644 index 0000000..8432cb5 --- /dev/null +++ b/test/core/resources/src/star2.png Binary files differ diff --git a/test/core/resources/src/star3.png b/test/core/resources/src/star3.png new file mode 100644 index 0000000..a91eadc --- /dev/null +++ b/test/core/resources/src/star3.png Binary files differ diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index c63af99..3d2407e 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -2,7 +2,7 @@ import VideoBaseTexture from './VideoBaseTexture'; import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; -import { Rectangle } from '../math'; +import { Rectangle, Point } from '../math'; import { TextureCache, getResolutionOfUrl } from '../utils'; import settings from '../settings'; @@ -40,8 +40,9 @@ * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ - constructor(baseTexture, frame, orig, trim, rotate) + constructor(baseTexture, frame, orig, trim, rotate, anchor) { super(); @@ -144,6 +145,14 @@ } /** + * Anchor point that is used as default if sprite is created with this texture. + * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point. + * @member {PIXI.Point} + * @default {0,0} + */ + this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0); + + /** * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. * * @event PIXI.Texture#update @@ -156,7 +165,7 @@ /** * Contains data for uvs. May contain clamp settings and some matrices. * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} + * @member {PIXI.TextureMatrix} * @default null */ this.transform = null; diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index e538dd4..134d058 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -20,7 +20,20 @@ * textureArray.push(texture); * }; * - * let mc = new PIXI.AnimatedSprite(textureArray); + * let animatedSprite = new PIXI.extras.AnimatedSprite(textureArray); + * ``` + * + * The more efficient and simpler way to create an animated sprite is using a {@link PIXI.Spritesheet} + * containing the animation definitions: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * animatedSprite = new PIXI.extras.AnimatedSprite(sheet.animations["image_sequence"]); + * ... + * } * ``` * * @class diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 2ee1136..2d1a737 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -15,12 +15,20 @@ const width = Math.floor(spritesheet.data.frames[id].frame.w * spritesheet.baseTexture.sourceScale); const height = Math.floor(spritesheet.data.frames[id].frame.h * spritesheet.baseTexture.sourceScale); - expect(Object.keys(textures).length).to.equal(1); - expect(Object.keys(spritesheet.textures).length).to.equal(1); + expect(Object.keys(textures).length).to.equal(5); + expect(Object.keys(spritesheet.textures).length).to.equal(5); expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(width / spritesheet.resolution); expect(textures[id].height).to.equal(height / spritesheet.resolution); + expect(textures[id].defaultAnchor.x).to.equal(0); + expect(textures[id].defaultAnchor.y).to.equal(0); expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); + + expect(this.animations).to.have.property('star').that.is.an('array'); + expect(this.animations.star.length).to.equal(4); + expect(this.animations.star[0].defaultAnchor.x).to.equal(0.5); + expect(this.animations.star[0].defaultAnchor.y).to.equal(0.5); + spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/test/core/resources/building1.json b/test/core/resources/building1.json old mode 100755 new mode 100644 index 03fa5c1..d8fbfe1 --- a/test/core/resources/building1.json +++ b/test/core/resources/building1.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":95,"h":115}, - "sourceSize": {"w":95,"h":115}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":95,"h":115} +}, +"star1.png": +{ + "frame": {"x":98,"y":33,"w":28,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":28,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":98,"y":95,"w":26,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":3,"y":1,"w":26,"h":30}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":98,"y":1,"w":30,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":30,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":98,"y":63,"w":30,"h":27}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":3,"w":30,"h":27}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1.png", "format": "RGBA8888", - "size": {"w":128,"h":128}, + "size": {"w":128,"h":126}, "scale": "0.5", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1.png b/test/core/resources/building1.png old mode 100755 new mode 100644 index 7e1e114..7e16cda --- a/test/core/resources/building1.png +++ b/test/core/resources/building1.png Binary files differ diff --git a/test/core/resources/building1@2x.json b/test/core/resources/building1@2x.json old mode 100755 new mode 100644 index 24e25ff..c8cb7bf --- a/test/core/resources/building1@2x.json +++ b/test/core/resources/building1@2x.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":190,"h":229}, - "sourceSize": {"w":190,"h":229}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":190,"h":229} +}, +"star1.png": +{ + "frame": {"x":193,"y":59,"w":54,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":5,"y":5,"w":54,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":193,"y":115,"w":50,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":7,"y":4,"w":50,"h":57}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":193,"y":1,"w":56,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":5,"w":56,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":193,"y":174,"w":56,"h":50}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":8,"w":56,"h":50}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1@2x.png", "format": "RGBA8888", - "size": {"w":256,"h":256}, + "size": {"w":249,"h":231}, "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1@2x.png b/test/core/resources/building1@2x.png old mode 100755 new mode 100644 index d5ecd04..06e613f --- a/test/core/resources/building1@2x.png +++ b/test/core/resources/building1@2x.png Binary files differ diff --git a/test/core/resources/src/goldmine_10_5.png b/test/core/resources/src/goldmine_10_5.png new file mode 100755 index 0000000..de2ba5a --- /dev/null +++ b/test/core/resources/src/goldmine_10_5.png Binary files differ diff --git a/test/core/resources/src/sheet.tps b/test/core/resources/src/sheet.tps new file mode 100644 index 0000000..d08b10c --- /dev/null +++ b/test/core/resources/src/sheet.tps @@ -0,0 +1,262 @@ + + + + fileFormatVersion + 4 + texturePackerVersion + 4.8.1 + autoSDSettings + + + scale + 1 + extension + @2x + spriteFilter + + acceptFractionalValues + + maxTextureSize + + width + -1 + height + -1 + + + + scale + 0.5 + extension + + spriteFilter + + acceptFractionalValues + + maxTextureSize + + width + -1 + height + -1 + + + + allowRotation + + shapeDebug + + dpi + 72 + dataFormat + pixijs4 + textureFileName + + flipPVR + + pvrCompressionQuality + PVR_QUALITY_NORMAL + atfCompressData + + mipMapMinSize + 32768 + etc1CompressionQuality + ETC1_QUALITY_LOW_PERCEPTUAL + etc2CompressionQuality + ETC2_QUALITY_LOW_PERCEPTUAL + dxtCompressionMode + DXT_PERCEPTUAL + jxrColorFormat + JXR_YUV444 + jxrTrimFlexBits + 0 + jxrCompressionLevel + 0 + ditherType + NearestNeighbour + backgroundColor + 0 + libGdx + + filtering + + x + Linear + y + Linear + + + shapePadding + 0 + jpgQuality + 80 + pngOptimizationLevel + 1 + webpQualityLevel + 101 + textureSubPath + + atfFormats + + textureFormat + png + borderPadding + 0 + maxTextureSize + + width + 2048 + height + 2048 + + fixedTextureSize + + width + -1 + height + -1 + + algorithmSettings + + algorithm + MaxRects + freeSizeMode + Best + sizeConstraints + AnySize + forceSquared + + maxRects + + heuristic + Best + + basic + + sortBy + Best + order + Ascending + + polygon + + alignToGrid + 1 + + + dataFileNames + + data + + name + ../building1{v}.json + + + multiPack + + forceIdenticalLayout + + outputFormat + RGBA8888 + alphaHandling + ClearTransparentPixels + contentProtection + + key + + + autoAliasEnabled + + trimSpriteNames + + prependSmartFolderName + + autodetectAnimations + + globalSpriteSettings + + scale + 1 + scaleMode + Smooth + extrude + 1 + trimThreshold + 1 + trimMargin + 1 + trimMode + Trim + tracerTolerance + 200 + heuristicMask + + defaultPivotPoint + 0,0 + writePivotPoints + + + individualSpriteSettings + + goldmine_10_5.png + + pivotPoint + 0,0 + scale9Enabled + + scale9Borders + 48,57,95,115 + scale9Paddings + 48,57,95,115 + scale9FromFile + + + star1.png + star2.png + star3.png + star4.png + + pivotPoint + 0.5,0.5 + scale9Enabled + + scale9Borders + 16,16,32,32 + scale9Paddings + 16,16,32,32 + scale9FromFile + + + + fileList + + goldmine_10_5.png + star1.png + star2.png + star3.png + star4.png + + ignoreFileList + + replaceList + + ignoredWarnings + + commonDivisorX + 1 + commonDivisorY + 1 + packNormalMaps + + autodetectNormalMaps + + normalMapFilter + + normalMapSuffix + + normalMapSheetFileName + + exporterProperties + + + diff --git a/test/core/resources/src/star1.png b/test/core/resources/src/star1.png new file mode 100644 index 0000000..32824fd --- /dev/null +++ b/test/core/resources/src/star1.png Binary files differ diff --git a/test/core/resources/src/star2.png b/test/core/resources/src/star2.png new file mode 100644 index 0000000..8432cb5 --- /dev/null +++ b/test/core/resources/src/star2.png Binary files differ diff --git a/test/core/resources/src/star3.png b/test/core/resources/src/star3.png new file mode 100644 index 0000000..a91eadc --- /dev/null +++ b/test/core/resources/src/star3.png Binary files differ diff --git a/test/core/resources/src/star4.png b/test/core/resources/src/star4.png new file mode 100644 index 0000000..c629571 --- /dev/null +++ b/test/core/resources/src/star4.png Binary files differ diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 3eecf43..0294306 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -15,6 +15,18 @@ * let sprite = new PIXI.Sprite.fromImage('assets/image.png'); * ``` * + * The more efficient way to create sprites is using a {@link PIXI.Spritesheet}: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * let sprite = new PIXI.Sprite(sheet.textures["image.png"]); + * ... + * } + * ``` + * * @class * @extends PIXI.Container * @memberof PIXI @@ -30,14 +42,22 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left. + * The default is 0,0 or taken from the {@link PIXI.Texture#defaultAnchor|Texture} + * passed to the constructor. A value of 0,0 means the texture's origin is the top left. * Setting the anchor to 0.5,0.5 means the texture's origin is centered. * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner. + * Note: Updating the {@link PIXI.Texture#defaultAnchor} after a Texture is + * created does _not_ update the Sprite's anchor values. * * @member {PIXI.ObservablePoint} * @private */ - this._anchor = new ObservablePoint(this._onAnchorUpdate, this); + this._anchor = new ObservablePoint( + this._onAnchorUpdate, + this, + (texture ? texture.defaultAnchor.x : 0), + (texture ? texture.defaultAnchor.y : 0) + ); /** * The texture that the sprite is using @@ -524,9 +544,10 @@ /** * The anchor sets the origin point of the texture. - * The default is 0,0 this means the texture's origin is the top left - * Setting the anchor to 0.5,0.5 means the texture's origin is centered - * Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner + * The default is 0,0 or taken from the {@link PIXI.Texture|Texture} passed to the constructor. + * Setting the texture at a later point of time does not change the anchor. + * + * 0,0 means the texture's origin is the top left, 0.5,0.5 is the center, 1,1 the bottom right corner. * * @member {PIXI.ObservablePoint} */ diff --git a/src/core/textures/Spritesheet.js b/src/core/textures/Spritesheet.js index adb555c..ca73d23 100644 --- a/src/core/textures/Spritesheet.js +++ b/src/core/textures/Spritesheet.js @@ -5,6 +5,23 @@ * Utility class for maintaining reference to a collection * of Textures on a single Spritesheet. * + * To access a sprite sheet from your code pass its JSON data file to Pixi's loader: + * + * ```js + * PIXI.loader.add("images/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["images/spritesheet.json"].spritesheet; + * ... + * } + * ``` + * With the `sheet.textures` you can create Sprite objects,`sheet.animations` can be used to create an AnimatedSprite. + * + * Sprite sheets can be packed using tools like {@link https://codeandweb.com/texturepacker|TexturePacker}, + * {@link https://renderhjs.net/shoebox/|Shoebox} or {@link https://github.com/krzysztof-o/spritesheet.js|Spritesheet.js}. + * Default anchor points (see {@link PIXI.Texture#defaultAnchor}) and grouping of animation sprites are currently only + * supported by TexturePacker. + * * @class * @memberof PIXI */ @@ -37,12 +54,26 @@ this.baseTexture = baseTexture; /** - * Map of spritesheet textures. - * @type {Object} + * A map containing all textures of the sprite sheet. + * Can be used to create a {@link PIXI.Sprite|Sprite}: + * ```js + * new PIXI.Sprite(sheet.textures["image.png"]); + * ``` + * @member {Object} */ this.textures = {}; /** + * A map containing the textures for each animation. + * Can be used to create an {@link PIXI.extras.AnimatedSprite|AnimatedSprite}: + * ```js + * new PIXI.extras.AnimatedSprite(sheet.animations["anim_name"]) + * ``` + * @member {Object} + */ + this.animations = {}; + + /** * Reference to the original JSON data. * @type {Object} */ @@ -133,6 +164,7 @@ if (this._frameKeys.length <= Spritesheet.BATCH_SIZE) { this._processFrames(0); + this._processAnimations(); this._parseComplete(); } else @@ -208,7 +240,8 @@ frame, orig, trim, - data.rotated ? 2 : 0 + data.rotated ? 2 : 0, + data.anchor ); // lets also add the frame to pixi's global cache for fromFrame and fromImage functions @@ -220,6 +253,25 @@ } /** + * Parse animations config + * + * @private + */ + _processAnimations() + { + const animations = this.data.animations || {}; + + for (const animName in animations) + { + this.animations[animName] = []; + for (const frameName of animations[animName]) + { + this.animations[animName].push(this.textures[frameName]); + } + } + } + + /** * The parse has completed. * * @private @@ -250,6 +302,7 @@ } else { + this._processAnimations(); this._parseComplete(); } }, 0); diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index c63af99..3d2407e 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -2,7 +2,7 @@ import VideoBaseTexture from './VideoBaseTexture'; import TextureUvs from './TextureUvs'; import EventEmitter from 'eventemitter3'; -import { Rectangle } from '../math'; +import { Rectangle, Point } from '../math'; import { TextureCache, getResolutionOfUrl } from '../utils'; import settings from '../settings'; @@ -40,8 +40,9 @@ * @param {PIXI.Rectangle} [orig] - The area of original texture * @param {PIXI.Rectangle} [trim] - Trimmed rectangle of original texture * @param {number} [rotate] - indicates how the texture was rotated by texture packer. See {@link PIXI.GroupD8} + * @param {PIXI.Point} [anchor] - Default anchor point used for sprite placement / rotation */ - constructor(baseTexture, frame, orig, trim, rotate) + constructor(baseTexture, frame, orig, trim, rotate, anchor) { super(); @@ -144,6 +145,14 @@ } /** + * Anchor point that is used as default if sprite is created with this texture. + * Changing the `defaultAnchor` at a later point of time will not update Sprite's anchor point. + * @member {PIXI.Point} + * @default {0,0} + */ + this.defaultAnchor = anchor ? new Point(anchor.x, anchor.y) : new Point(0, 0); + + /** * Fired when the texture is updated. This happens if the frame or the baseTexture is updated. * * @event PIXI.Texture#update @@ -156,7 +165,7 @@ /** * Contains data for uvs. May contain clamp settings and some matrices. * Its a bit heavy, so by default that object is not created. - * @type {PIXI.TextureMatrix} + * @member {PIXI.TextureMatrix} * @default null */ this.transform = null; diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js index e538dd4..134d058 100644 --- a/src/extras/AnimatedSprite.js +++ b/src/extras/AnimatedSprite.js @@ -20,7 +20,20 @@ * textureArray.push(texture); * }; * - * let mc = new PIXI.AnimatedSprite(textureArray); + * let animatedSprite = new PIXI.extras.AnimatedSprite(textureArray); + * ``` + * + * The more efficient and simpler way to create an animated sprite is using a {@link PIXI.Spritesheet} + * containing the animation definitions: + * + * ```js + * PIXI.loader.add("assets/spritesheet.json").load(setup); + * + * function setup() { + * let sheet = PIXI.loader.resources["assets/spritesheet.json"].spritesheet; + * animatedSprite = new PIXI.extras.AnimatedSprite(sheet.animations["image_sequence"]); + * ... + * } * ``` * * @class diff --git a/test/core/Spritesheet.js b/test/core/Spritesheet.js index 2ee1136..2d1a737 100644 --- a/test/core/Spritesheet.js +++ b/test/core/Spritesheet.js @@ -15,12 +15,20 @@ const width = Math.floor(spritesheet.data.frames[id].frame.w * spritesheet.baseTexture.sourceScale); const height = Math.floor(spritesheet.data.frames[id].frame.h * spritesheet.baseTexture.sourceScale); - expect(Object.keys(textures).length).to.equal(1); - expect(Object.keys(spritesheet.textures).length).to.equal(1); + expect(Object.keys(textures).length).to.equal(5); + expect(Object.keys(spritesheet.textures).length).to.equal(5); expect(textures[id]).to.be.an.instanceof(PIXI.Texture); expect(textures[id].width).to.equal(width / spritesheet.resolution); expect(textures[id].height).to.equal(height / spritesheet.resolution); + expect(textures[id].defaultAnchor.x).to.equal(0); + expect(textures[id].defaultAnchor.y).to.equal(0); expect(textures[id].textureCacheIds.indexOf(id)).to.equal(0); + + expect(this.animations).to.have.property('star').that.is.an('array'); + expect(this.animations.star.length).to.equal(4); + expect(this.animations.star[0].defaultAnchor.x).to.equal(0.5); + expect(this.animations.star[0].defaultAnchor.y).to.equal(0.5); + spritesheet.destroy(true); expect(spritesheet.textures).to.be.null; expect(spritesheet.baseTexture).to.be.null; diff --git a/test/core/resources/building1.json b/test/core/resources/building1.json old mode 100755 new mode 100644 index 03fa5c1..d8fbfe1 --- a/test/core/resources/building1.json +++ b/test/core/resources/building1.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":95,"h":115}, - "sourceSize": {"w":95,"h":115}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":95,"h":115} +}, +"star1.png": +{ + "frame": {"x":98,"y":33,"w":28,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":2,"y":2,"w":28,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":98,"y":95,"w":26,"h":30}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":3,"y":1,"w":26,"h":30}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":98,"y":1,"w":30,"h":29}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":2,"w":30,"h":29}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":98,"y":63,"w":30,"h":27}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":1,"y":3,"w":30,"h":27}, + "sourceSize": {"w":32,"h":32}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1.png", "format": "RGBA8888", - "size": {"w":128,"h":128}, + "size": {"w":128,"h":126}, "scale": "0.5", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1.png b/test/core/resources/building1.png old mode 100755 new mode 100644 index 7e1e114..7e16cda --- a/test/core/resources/building1.png +++ b/test/core/resources/building1.png Binary files differ diff --git a/test/core/resources/building1@2x.json b/test/core/resources/building1@2x.json old mode 100755 new mode 100644 index 24e25ff..c8cb7bf --- a/test/core/resources/building1@2x.json +++ b/test/core/resources/building1@2x.json @@ -6,16 +6,54 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":190,"h":229}, - "sourceSize": {"w":190,"h":229}, - "pivot": {"x":0.5,"y":0.5} + "sourceSize": {"w":190,"h":229} +}, +"star1.png": +{ + "frame": {"x":193,"y":59,"w":54,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":5,"y":5,"w":54,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star2.png": +{ + "frame": {"x":193,"y":115,"w":50,"h":57}, + "rotated": false, + "trimmed": true, + "spriteSourceSize": {"x":7,"y":4,"w":50,"h":57}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star3.png": +{ + "frame": {"x":193,"y":1,"w":56,"h":55}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":5,"w":56,"h":55}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} +}, +"star4.png": +{ + "frame": {"x":193,"y":174,"w":56,"h":50}, + "rotated": true, + "trimmed": true, + "spriteSourceSize": {"x":4,"y":8,"w":56,"h":50}, + "sourceSize": {"w":64,"h":64}, + "anchor": {"x":0.5,"y":0.5} }}, +"animations": { + "star": ["star1.png","star2.png","star3.png","star4.png"] +}, "meta": { - "app": "http://www.codeandweb.com/texturepacker", + "app": "https://www.codeandweb.com/texturepacker", "version": "1.0", "image": "building1@2x.png", "format": "RGBA8888", - "size": {"w":256,"h":256}, + "size": {"w":249,"h":231}, "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:d7a5e54c8f8a3fecd508baf190c44807:99a0e3a4dc0f441e4aad77614191ab38:6046b8eb706ddefaa771c33ceb7cb6d5$" + "smartupdate": "$TexturePacker:SmartUpdate:1cb0f14cbdd5e7bcecd332ecd0eaa9f7:8acde9d234ecca966a410602c71bffad:6046b8eb706ddefaa771c33ceb7cb6d5$" } } diff --git a/test/core/resources/building1@2x.png b/test/core/resources/building1@2x.png old mode 100755 new mode 100644 index d5ecd04..06e613f --- a/test/core/resources/building1@2x.png +++ b/test/core/resources/building1@2x.png Binary files differ diff --git a/test/core/resources/src/goldmine_10_5.png b/test/core/resources/src/goldmine_10_5.png new file mode 100755 index 0000000..de2ba5a --- /dev/null +++ b/test/core/resources/src/goldmine_10_5.png Binary files differ diff --git a/test/core/resources/src/sheet.tps b/test/core/resources/src/sheet.tps new file mode 100644 index 0000000..d08b10c --- /dev/null +++ b/test/core/resources/src/sheet.tps @@ -0,0 +1,262 @@ + + + + fileFormatVersion + 4 + texturePackerVersion + 4.8.1 + autoSDSettings + + + scale + 1 + extension + @2x + spriteFilter + + acceptFractionalValues + + maxTextureSize + + width + -1 + height + -1 + + + + scale + 0.5 + extension + + spriteFilter + + acceptFractionalValues + + maxTextureSize + + width + -1 + height + -1 + + + + allowRotation + + shapeDebug + + dpi + 72 + dataFormat + pixijs4 + textureFileName + + flipPVR + + pvrCompressionQuality + PVR_QUALITY_NORMAL + atfCompressData + + mipMapMinSize + 32768 + etc1CompressionQuality + ETC1_QUALITY_LOW_PERCEPTUAL + etc2CompressionQuality + ETC2_QUALITY_LOW_PERCEPTUAL + dxtCompressionMode + DXT_PERCEPTUAL + jxrColorFormat + JXR_YUV444 + jxrTrimFlexBits + 0 + jxrCompressionLevel + 0 + ditherType + NearestNeighbour + backgroundColor + 0 + libGdx + + filtering + + x + Linear + y + Linear + + + shapePadding + 0 + jpgQuality + 80 + pngOptimizationLevel + 1 + webpQualityLevel + 101 + textureSubPath + + atfFormats + + textureFormat + png + borderPadding + 0 + maxTextureSize + + width + 2048 + height + 2048 + + fixedTextureSize + + width + -1 + height + -1 + + algorithmSettings + + algorithm + MaxRects + freeSizeMode + Best + sizeConstraints + AnySize + forceSquared + + maxRects + + heuristic + Best + + basic + + sortBy + Best + order + Ascending + + polygon + + alignToGrid + 1 + + + dataFileNames + + data + + name + ../building1{v}.json + + + multiPack + + forceIdenticalLayout + + outputFormat + RGBA8888 + alphaHandling + ClearTransparentPixels + contentProtection + + key + + + autoAliasEnabled + + trimSpriteNames + + prependSmartFolderName + + autodetectAnimations + + globalSpriteSettings + + scale + 1 + scaleMode + Smooth + extrude + 1 + trimThreshold + 1 + trimMargin + 1 + trimMode + Trim + tracerTolerance + 200 + heuristicMask + + defaultPivotPoint + 0,0 + writePivotPoints + + + individualSpriteSettings + + goldmine_10_5.png + + pivotPoint + 0,0 + scale9Enabled + + scale9Borders + 48,57,95,115 + scale9Paddings + 48,57,95,115 + scale9FromFile + + + star1.png + star2.png + star3.png + star4.png + + pivotPoint + 0.5,0.5 + scale9Enabled + + scale9Borders + 16,16,32,32 + scale9Paddings + 16,16,32,32 + scale9FromFile + + + + fileList + + goldmine_10_5.png + star1.png + star2.png + star3.png + star4.png + + ignoreFileList + + replaceList + + ignoredWarnings + + commonDivisorX + 1 + commonDivisorY + 1 + packNormalMaps + + autodetectNormalMaps + + normalMapFilter + + normalMapSuffix + + normalMapSheetFileName + + exporterProperties + + + diff --git a/test/core/resources/src/star1.png b/test/core/resources/src/star1.png new file mode 100644 index 0000000..32824fd --- /dev/null +++ b/test/core/resources/src/star1.png Binary files differ diff --git a/test/core/resources/src/star2.png b/test/core/resources/src/star2.png new file mode 100644 index 0000000..8432cb5 --- /dev/null +++ b/test/core/resources/src/star2.png Binary files differ diff --git a/test/core/resources/src/star3.png b/test/core/resources/src/star3.png new file mode 100644 index 0000000..a91eadc --- /dev/null +++ b/test/core/resources/src/star3.png Binary files differ diff --git a/test/core/resources/src/star4.png b/test/core/resources/src/star4.png new file mode 100644 index 0000000..c629571 --- /dev/null +++ b/test/core/resources/src/star4.png Binary files differ diff --git a/test/loaders/spritesheetParser.js b/test/loaders/spritesheetParser.js index 7d0d0c1..22fca43 100644 --- a/test/loaders/spritesheetParser.js +++ b/test/loaders/spritesheetParser.js @@ -58,6 +58,19 @@ .with.keys(Object.keys(getJsonSpritesheet().frames)) .and.has.property('0.png') .that.is.an.instanceof(PIXI.Texture); + + expect(res.textures['0.png'].frame.x).to.equal(14); + expect(res.textures['0.png'].frame.y).to.equal(28); + expect(res.textures['0.png'].defaultAnchor.x).to.equal(0.3); + expect(res.textures['0.png'].defaultAnchor.y).to.equal(0.4); + expect(res.textures['1.png'].defaultAnchor.x).to.equal(0.0); // default of defaultAnchor is 0,0 + expect(res.textures['1.png'].defaultAnchor.y).to.equal(0.0); + + expect(res).to.have.property('spritesheet') + .to.have.property('animations') + .to.have.property('png123'); + expect(res.spritesheet.animations.png123.length).to.equal(3); + expect(res.spritesheet.animations.png123[0]).to.equal(res.textures['1.png']); }); it('should not load binary images as an image loader type', function (done) @@ -171,7 +184,8 @@ "rotated": false, "trimmed": false, "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, - "sourceSize": {"w":14,"h":14} + "sourceSize": {"w":14,"h":14}, + "anchor": {"x":0.3,"y":0.4} }, "1.png": { @@ -245,6 +259,9 @@ "spriteSourceSize": {"x":0,"y":0,"w":14,"h":14}, "sourceSize": {"w":14,"h":14} }}, + "animations": { + "png123": [ "1.png", "2.png", "3.png" ] + }, "meta": { "app": "http://www.texturepacker.com", "version": "1.0",