diff --git a/Phaser/Game.ts b/Phaser/Game.ts index 9c887f1c..e8bbf764 100644 --- a/Phaser/Game.ts +++ b/Phaser/Game.ts @@ -295,9 +295,12 @@ module Phaser { this.setRenderer(Phaser.Types.RENDERER_CANVAS); + this.world.boot(); + this.stage.boot(); + this.input.boot(); + this.framerate = 60; this.isBooted = true; - this.input.start(); // Display the default game screen? if (this.onInitCallback == null && this.onCreateCallback == null && this.onUpdateCallback == null && this.onRenderCallback == null && this._pendingState == null) @@ -354,7 +357,7 @@ module Phaser { } /** - * Game loop method will be called when it's booting. + * The bootLoop is called while the game is still booting (waiting for the DOM and resources to be available) */ private bootLoop() { @@ -365,7 +368,7 @@ module Phaser { } /** - * Game loop method will be called when it's paused. + * The pausedLoop is called when the game is paused. */ private pausedLoop() { @@ -563,7 +566,7 @@ module Phaser { } /** - * Nuke the whole game from orbit + * Nuke the entire game from orbit */ public destroy() { diff --git a/Phaser/Stage.ts b/Phaser/Stage.ts index d3ec2896..8754c1f0 100644 --- a/Phaser/Stage.ts +++ b/Phaser/Stage.ts @@ -57,9 +57,6 @@ module Phaser { this.scaleMode = StageScaleMode.NO_SCALE; this.scale = new StageScaleMode(this._game); - this._bootScreen = new BootScreen(this._game); - this._pauseScreen = new PauseScreen(this._game, width, height); - document.addEventListener('visibilitychange', (event) => this.visibilityChange(event), false); document.addEventListener('webkitvisibilitychange', (event) => this.visibilityChange(event), false); window.onblur = (event) => this.visibilityChange(event); @@ -153,6 +150,16 @@ module Phaser { */ public scaleMode: number; + /** + * Stage boot + */ + public boot() { + + this._bootScreen = new BootScreen(this._game); + this._pauseScreen = new PauseScreen(this._game, this.width, this.height); + + } + /** * Update stage for rendering. This will handle scaling, clearing * and PauseScreen/BootScreen updating and rendering. diff --git a/Phaser/State.ts b/Phaser/State.ts index da7bc39c..86a925ea 100644 --- a/Phaser/State.ts +++ b/Phaser/State.ts @@ -21,11 +21,10 @@ module Phaser { this.add = game.add; this.camera = game.camera; this.cache = game.cache; - //this.collision = game.collision; this.input = game.input; this.loader = game.loader; this.math = game.math; - //this.motion = game.motion; + this.motion = game.motion; this.sound = game.sound; this.stage = game.stage; this.time = game.time; @@ -51,12 +50,6 @@ module Phaser { */ public cache: Cache; - /** - * Reference to the collision helper. - * @type {Collision} - */ - //public collision: Collision; - /** * Reference to the GameObject Factory. * @type {GameObjectFactory} @@ -85,7 +78,7 @@ module Phaser { * Reference to the motion helper. * @type {Motion} */ - //public motion: Motion; + public motion: Motion; /** * Reference to the sound manager. diff --git a/Phaser/World.ts b/Phaser/World.ts index 00c6172a..2eb6dd88 100644 --- a/Phaser/World.ts +++ b/Phaser/World.ts @@ -31,12 +31,8 @@ module Phaser { this.cameras = new CameraManager(this._game, 0, 0, width, height); - this.group = new Group(this._game, 0); - this.bounds = new Rectangle(0, 0, width, height); - this.physics = new Physics.PhysicsManager(this._game, width, height); - } /** @@ -68,6 +64,26 @@ module Phaser { */ public physics: Physics.PhysicsManager; + /** + * Object container stores every object created with `create*` methods. + * @type {Group} + */ + private _groupCounter: number = 0; + + public getNextGroupID(): number { + return this._groupCounter++; + } + + /** + * Called one by Game during the boot process. + */ + public boot() { + + this.group = new Group(this._game, 0); + + this.physics = new Physics.PhysicsManager(this._game, this.width, this.height); + + } /** * This is called automatically every frame, and is where main logic happens. diff --git a/Phaser/components/sprite/Events.ts b/Phaser/components/sprite/Events.ts index 64150922..468531ad 100644 --- a/Phaser/components/sprite/Events.ts +++ b/Phaser/components/sprite/Events.ts @@ -19,6 +19,11 @@ module Phaser.Components { this.game = parent.game; this._sprite = parent; + this.onAddedToGroup = new Phaser.Signal; + this.onRemovedFromGroup = new Phaser.Signal; + this.onKilled = new Phaser.Signal; + this.onRevived = new Phaser.Signal; + this.onInputOver = new Phaser.Signal; this.onInputOut = new Phaser.Signal; this.onInputDown = new Phaser.Signal; @@ -36,15 +41,26 @@ module Phaser.Components { */ private _sprite: Sprite; - // Creation and destruction - public onAdded: Phaser.Signal; + /** + * Dispatched by the Group this Sprite is added to. + */ + public onAddedToGroup: Phaser.Signal; + + /** + * Dispatched by the Group this Sprite is removed from. + */ + public onRemovedFromGroup: Phaser.Signal; + + /** + * Dispatched when this Sprite is killed. + */ public onKilled: Phaser.Signal; + + /** + * Dispatched when this Sprite is revived. + */ public onRevived: Phaser.Signal; - public onOutOfBounds: Phaser.Signal; - - // Input related events - /** * Dispatched by the Input component when a pointer moves over an Input enabled sprite. */ @@ -65,6 +81,11 @@ module Phaser.Components { */ public onInputUp: Phaser.Signal; + + + + public onOutOfBounds: Phaser.Signal; + } } \ No newline at end of file diff --git a/Phaser/components/sprite/Input.ts b/Phaser/components/sprite/Input.ts index 6b64f606..758fea17 100644 --- a/Phaser/components/sprite/Input.ts +++ b/Phaser/components/sprite/Input.ts @@ -39,12 +39,48 @@ module Phaser.Components { */ private _sprite: Sprite; + private dragOffsetX: number; + private dragOffsetY: number; + private dragFromPoint: bool; + private dragPixelPerfect:bool = false; + private dragPixelPerfectAlpha:number; + private allowHorizontalDrag: bool = true; + private allowVerticalDrag: bool = true; + private snapOnDrag: bool = false; + private snapOnRelease: bool = false; + private snapX: number; + private snapY: number; + /** * If enabled the Input component will be updated by the parent Sprite * @type {Boolean} */ public enabled: bool; + /** + * Is this sprite being dragged by the mouse or not? + * @default false + */ + public isDragged: bool = false; + + /** + * Is this sprite allowed to be dragged by the mouse? true = yes, false = no + * @default false + */ + public draggable: bool = false; + + /** + * An FlxRect region of the game world within which the sprite is restricted during mouse drag + * @default null + */ + public boundsRect: Rectangle = null; + + /** + * An FlxSprite the bounds of which this sprite is restricted during mouse drag + * @default null + */ + public boundsSprite: Sprite = null; + /** * The Input component can monitor either the physics body of the Sprite or the frameBounds * If checkBody is set to true it will monitor the bounds of the physics body. @@ -129,6 +165,11 @@ module Phaser.Components { return; } + if (this.draggable && this.isDragged) + { + this.updateDrag(); + } + if (this.game.input.x != this.oldX || this.game.input.y != this.oldY) { this.oldX = this.game.input.x; @@ -172,14 +213,54 @@ module Phaser.Components { } + // click handler to add to stack for sorting + } + /** + * Updates the Mouse Drag on this Sprite. + */ + private updateDrag():void + { + if (this.isUp) + { + this.stopDrag(); + return; + } + + if (this.allowHorizontalDrag) + { + this._sprite.x = this.game.input.x - this.dragOffsetX; + } + + if (this.allowVerticalDrag) + { + this._sprite.y = this.game.input.y - this.dragOffsetY; + } + + if (this.boundsRect) + { + this.checkBoundsRect(); + } + + if (this.boundsSprite) + { + this.checkBoundsSprite(); + } + + if (this.snapOnDrag) + { + this._sprite.x = Math.floor(this._sprite.x / this.snapX) * this.snapX; + this._sprite.y = Math.floor(this._sprite.y / this.snapY) * this.snapY; + } + } + /** * Returns true if the pointer has entered the Sprite within the specified delay time (defaults to 500ms, half a second) * @param delay The time below which the pointer is considered as just over. * @returns {boolean} */ - public justOver(delay?:number = 500): bool { + public justOver(delay?: number = 500): bool { return (this.isOver && this.duration < delay); } @@ -188,7 +269,7 @@ module Phaser.Components { * @param delay The time below which the pointer is considered as just out. * @returns {boolean} */ - public justOut(delay?:number = 500): bool { + public justOut(delay?: number = 500): bool { return (this.isOut && (this.game.time.now - this.timeOut < delay)); } @@ -207,6 +288,172 @@ module Phaser.Components { } + /** + * Make this Sprite draggable by the mouse. You can also optionally set mouseStartDragCallback and mouseStopDragCallback + * + * @param lockCenter If false the Sprite will drag from where you click it. If true it will center itself to the tip of the mouse pointer. + * @param pixelPerfect If true it will use a pixel perfect test to see if you clicked the Sprite. False uses the bounding box. + * @param alphaThreshold If using pixel perfect collision this specifies the alpha level from 0 to 255 above which a collision is processed (default 255) + * @param boundsRect If you want to restrict the drag of this sprite to a specific FlxRect, pass the FlxRect here, otherwise it's free to drag anywhere + * @param boundsSprite If you want to restrict the drag of this sprite to within the bounding box of another sprite, pass it here + */ + public enableDrag(lockCenter:bool = false, pixelPerfect:bool = false, alphaThreshold:number = 255, boundsRect:Rectangle = null, boundsSprite:Sprite = null):void + { + this.draggable = true; + + this.dragFromPoint = lockCenter; + this.dragPixelPerfect = pixelPerfect; + this.dragPixelPerfectAlpha = alphaThreshold; + + if (boundsRect) + { + this.boundsRect = boundsRect; + } + + if (boundsSprite) + { + this.boundsSprite = boundsSprite; + } + } + + /** + * Stops this sprite from being able to be dragged. If it is currently the target of an active drag it will be stopped immediately. Also disables any set callbacks. + */ + public disableDrag():void + { + if (this.isDragged) + { + //FlxMouseControl.dragTarget = null; + //FlxMouseControl.isDragging = false; + } + + this.isDragged = false; + this.draggable = false; + + //mouseStartDragCallback = null; + //mouseStopDragCallback = null; + } + + /** + * Restricts this sprite to drag movement only on the given axis. Note: If both are set to false the sprite will never move! + * + * @param allowHorizontal To enable the sprite to be dragged horizontally set to true, otherwise false + * @param allowVertical To enable the sprite to be dragged vertically set to true, otherwise false + */ + public setDragLock(allowHorizontal:bool = true, allowVertical:bool = true):void + { + this.allowHorizontalDrag = allowHorizontal; + this.allowVerticalDrag = allowVertical; + } + + /** + * Make this Sprite snap to the given grid either during drag or when it's released. + * For example 16x16 as the snapX and snapY would make the sprite snap to every 16 pixels. + * + * @param snapX The width of the grid cell in pixels + * @param snapY The height of the grid cell in pixels + * @param onDrag If true the sprite will snap to the grid while being dragged + * @param onRelease If true the sprite will snap to the grid when released + */ + public enableSnap(snapX:number , snapY:number, onDrag:bool = true, onRelease:bool = false):void + { + this.snapOnDrag = onDrag; + this.snapOnRelease = onRelease; + this.snapX = snapX; + this.snapY = snapY; + } + + /** + * Stops the sprite from snapping to a grid during drag or release. + */ + public disableSnap():void + { + this.snapOnDrag = false; + this.snapOnRelease = false; + } + + /** + * Called by FlxMouseControl when Mouse Drag starts on this Sprite. Should not usually be called directly. + */ + public startDrag():void + { + this.isDragged = true; + + if (this.dragFromPoint == false) + { + this.dragOffsetX = this.game.input.x - this._sprite.x; + this.dragOffsetY = this.game.input.y - this._sprite.y; + } + else + { + // Move the sprite to the middle of the mouse + this.dragOffsetX = this._sprite.frameBounds.halfWidth; + this.dragOffsetY = this._sprite.frameBounds.halfHeight; + } + } + + /** + * Bounds Rect check for the sprite drag + */ + private checkBoundsRect():void + { + if (this._sprite.x < this.boundsRect.left) + { + this._sprite.x = this.boundsRect.x; + } + else if ((this._sprite.x + this._sprite.width) > this.boundsRect.right) + { + this._sprite.x = this.boundsRect.right - this._sprite.width; + } + + if (this._sprite.y < this.boundsRect.top) + { + this._sprite.y = this.boundsRect.top; + } + else if ((this._sprite.y + this._sprite.height) > this.boundsRect.bottom) + { + this._sprite.y = this.boundsRect.bottom - this._sprite.height; + } + } + + /** + * Parent Sprite Bounds check for the sprite drag + */ + private checkBoundsSprite():void + { + if (this._sprite.x < this.boundsSprite.x) + { + this._sprite.x = this.boundsSprite.x; + } + else if ((this._sprite.x + this._sprite.width) > (this.boundsSprite.x + this.boundsSprite.width)) + { + this._sprite.x = (this.boundsSprite.x + this.boundsSprite.width) - this._sprite.width; + } + + if (this._sprite.y < this.boundsSprite.y) + { + this._sprite.y = this.boundsSprite.y; + } + else if ((this._sprite.y + this._sprite.height) > (this.boundsSprite.y + this.boundsSprite.height)) + { + this._sprite.y = (this.boundsSprite.y + this.boundsSprite.height) - this._sprite.height; + } + } + + /** + * Called by FlxMouseControl when Mouse Drag is stopped on this Sprite. Should not usually be called directly. + */ + public stopDrag():void + { + this.isDragged = false; + + if (this.snapOnRelease) + { + this._sprite.x = Math.floor(this._sprite.x / this.snapX) * this.snapX; + this._sprite.y = Math.floor(this._sprite.y / this.snapY) * this.snapY; + } + } + /** * Render debug infos. (including name, bounds info, position and some other properties) * @param x {number} X position of the debug info to be rendered. diff --git a/Phaser/core/Group.ts b/Phaser/core/Group.ts index 45c85dd6..fd9f6cea 100644 --- a/Phaser/core/Group.ts +++ b/Phaser/core/Group.ts @@ -27,6 +27,8 @@ module Phaser { this.cameraBlacklist = []; + this.ID = this.game.world.getNextGroupID(); + } /** @@ -43,7 +45,7 @@ module Phaser { /** * Helper for sort. */ - private _sortIndex: string; + private _sortIndex: string = ''; /** * Helper for sort. @@ -59,6 +61,11 @@ module Phaser { private _prevAlpha: number; private _count: number; + /** + * This keeps track of the z value of any game object added to this Group + */ + private _zCounter: number = 0; + /** * Reference to the main game object */ @@ -69,6 +76,21 @@ module Phaser { */ public type: number; + /** + * The unique Group ID + */ + public ID: number = -1; + + /** + * The z value of this Group (within its parent Group, if any) + */ + public z: number = -1; + + /** + * The Group this Group is a child of (if any). + */ + public group: Group = null; + /** * If this Group exists or not. Can be set to false to skip certain loop checks. */ @@ -122,6 +144,13 @@ module Phaser { */ public cameraBlacklist: number[]; + /** + * Gets the next z index value for children of this Group + */ + public getNextZIndex(): number { + return this._zCounter++; + } + /** * Override this function to handle any deleting or "shutdown" type operations you might need, * such as removing traditional Flash children like Basic objects. @@ -266,7 +295,7 @@ module Phaser { } /** - * Adds a new Basic subclass (Basic, GameObject, Sprite, etc) to the group. + * Adds a new Game Object to the group. * Group will try to replace a null member of the array first. * Failing that, Group will add it to the end of the member array, * assuming there is room for it, and doubling the size of the array if necessary. @@ -275,17 +304,19 @@ module Phaser { * the object will NOT be added to the group!

* * @param {Basic} Object The object you want to add to the group. - * @return {Basic} The same Basic object that was passed in. + * @return {Basic} The same object that was passed in. */ public add(object): any { - //Don't bother adding an object twice. - if (this.members.indexOf(Object) >= 0) + // Is this object already in another Group? + + // You can't add a Group to itself or an object to the same Group twice + if (object.group && (object.group.ID == this.ID || (object.type == Types.GROUP && object.ID == this.ID))) { return object; } - //First, look for a null entry where we can add the object. + // First, look for a null entry where we can add the object. this._i = 0; this._length = this.members.length; @@ -295,6 +326,8 @@ module Phaser { { this.members[this._i] = object; + this.setObjectIDs(object); + if (this._i >= this.length) { this.length = this._i + 1; @@ -306,7 +339,7 @@ module Phaser { this._i++; } - //Failing that, expand the array (if we can) and add the object. + // Failing that, expand the array (if we can) and add the object. if (this._maxSize > 0) { if (this.members.length >= this._maxSize) @@ -327,15 +360,57 @@ module Phaser { this.members.length *= 2; } - //If we made it this far, then we successfully grew the group, - //and we can go ahead and add the object at the first open slot. + // If we made it this far, then we successfully grew the group, + // and we can go ahead and add the object at the first open slot. this.members[this._i] = object; this.length = this._i + 1; + this.setObjectIDs(object); + return object; } + /** + * Create a new Sprite within this Group at the specified position. + * + * @param x {number} X position of the new sprite. + * @param y {number} Y position of the new sprite. + * @param [key] {string} The image key as defined in the Game.Cache to use as the texture for this sprite + * @param [bodyType] {number} The physics body type of the object (defaults to BODY_DISABLED) + * @returns {Sprite} The newly created sprite object. + */ + public addNewSprite(x: number, y: number, key?: string = '', bodyType?: number = Phaser.Types.BODY_DISABLED): Sprite { + return this.add(new Sprite(this.game, x, y, key, bodyType)); + } + + /** + * Sets all of the game object properties needed to exist within this Group. + */ + private setObjectIDs(object, zIndex: number = -1) { + + // If the object is already in another Group, inform that Group it has left + if (object.group !== null) + { + object.group.remove(object); + } + + object.group = this; + + if (zIndex == -1) + { + zIndex = this.getNextZIndex(); + } + + object.z = zIndex; + + if (object['events']) + { + object['events'].onAddedToGroup.dispatch(object, this, object.z); + } + + } + /** * Recycling is designed to help you reuse game objects without always re-allocating or "newing" them. * @@ -405,7 +480,7 @@ module Phaser { /** * Removes an object from the group. * - * @param {Basic} object The Basic you want to remove. + * @param {Basic} object The Game Object you want to remove. * @param {boolean} splice Whether the object should be cut from the array entirely or not. * * @return {Basic} The removed object. @@ -429,12 +504,19 @@ module Phaser { this.members[this._i] = null; } + if (object['events']) + { + object['events'].onRemovedFromGroup.dispatch(object, this); + } + + object.group = null; + object.z = -1; return object; } /** - * Replaces an existing Basic with a new one. + * Replaces an existing game object in this Group with a new one. * * @param {Basic} oldObject The object you want to replace. * @param {Basic} newObject The new object you want to use instead. @@ -450,12 +532,38 @@ module Phaser { return null; } + this.setObjectIDs(newObject, this.members[this._i].z); + + // Null the old object + this.remove(this.members[this._i]); + this.members[this._i] = newObject; return newObject; } + public swap(child1, child2, sort?: bool = true): bool { + + if (child1.group.ID != this.ID || child2.group.ID != this.ID || child1 === child2) + { + return false; + } + + var tempZ: number = child1.z; + + child1.z = child2.z; + child2.z = tempZ; + + if (sort) + { + this.sort(); + } + + return true; + + } + /** * Call this function to sort the group according to a particular value and order. * For example, to sort game objects for Zelda-style overlaps you might call @@ -466,11 +574,34 @@ module Phaser { * @param {string} index The string name of the member variable you want to sort on. Default value is "y". * @param {number} order A Group constant that defines the sort order. Possible values are Group.ASCENDING and Group.DESCENDING. Default value is Group.ASCENDING. */ - public sort(index: string = "y", order: number = Group.ASCENDING) { + public sort(index: string = 'z', order: number = Group.ASCENDING) { this._sortIndex = index; this._sortOrder = order; - this.members.sort(this.sortHandler); + this.members.sort((a, b) => this.sortHandler(a, b)); + + } + + /** + * Helper function for the sort process. + * + * @param {Basic} Obj1 The first object being sorted. + * @param {Basic} Obj2 The second object being sorted. + * + * @return {number} An integer value: -1 (Obj1 before Obj2), 0 (same), or 1 (Obj1 after Obj2). + */ + public sortHandler(obj1, obj2): number { + + if (obj1[this._sortIndex] < obj2[this._sortIndex]) + { + return this._sortOrder; + } + else if (obj1[this._sortIndex] > obj2[this._sortIndex]) + { + return -this._sortOrder; + } + + return 0; } @@ -821,29 +952,6 @@ module Phaser { } - /** - * Helper function for the sort process. - * - * @param {Basic} Obj1 The first object being sorted. - * @param {Basic} Obj2 The second object being sorted. - * - * @return {number} An integer value: -1 (Obj1 before Obj2), 0 (same), or 1 (Obj1 after Obj2). - */ - public sortHandler(obj1, obj2): number { - - if (obj1[this._sortIndex] < obj2[this._sortIndex]) - { - return this._sortOrder; - } - else if (obj1[this._sortIndex] > obj2[this._sortIndex]) - { - return -this._sortOrder; - } - - return 0; - - } - } } \ No newline at end of file diff --git a/Phaser/gameobjects/IGameObject.ts b/Phaser/gameobjects/IGameObject.ts index bd86c5e5..c76a436f 100644 --- a/Phaser/gameobjects/IGameObject.ts +++ b/Phaser/gameobjects/IGameObject.ts @@ -9,6 +9,16 @@ module Phaser { */ game: Game; + /** + * The type of game object. + */ + type: number; + + /** + * The ID of the Group this Sprite belongs to. + */ + group: Group; + /** * x value of the object. */ @@ -24,16 +34,6 @@ module Phaser { */ z: number; - /** - * The type of game object. - */ - type: number; - - /** - * Reference to the Renderer.renderSprite method. Can be overriden by custom classes. - */ - //render; - /** * Controls if both update and render are called by the core game loop. */ diff --git a/Phaser/gameobjects/Sprite.ts b/Phaser/gameobjects/Sprite.ts index 636d5bfd..211b712a 100644 --- a/Phaser/gameobjects/Sprite.ts +++ b/Phaser/gameobjects/Sprite.ts @@ -41,7 +41,8 @@ module Phaser { this.x = x; this.y = y; - this.z = 0; // not used yet + this.z = -1; + this.group = null; // If a texture has been given the body will be set to that size, otherwise 16x16 this.body = new Phaser.Physics.Body(this, bodyType); @@ -69,6 +70,11 @@ module Phaser { */ public type: number; + /** + * The Group this Sprite belongs to. + */ + public group: Group; + /** * Controls if both update and render are called by the core game loop. */ @@ -310,6 +316,7 @@ module Phaser { * Clean up memory. */ public destroy() { + } /** @@ -319,9 +326,18 @@ module Phaser { * like to animate an effect or whatever, you should override this, * setting only alive to false, and leaving exists true. */ - public kill() { + public kill(removeFromGroup:bool = false) { + this.alive = false; this.exists = false; + + if (removeFromGroup && this.group) + { + this.group.remove(this); + } + + this.events.onKilled.dispatch(this); + } /** @@ -329,8 +345,12 @@ module Phaser { * In practice, this is most often called by Object.reset(). */ public revive() { + this.alive = true; this.exists = true; + + this.events.onRevived.dispatch(this); + } } diff --git a/Phaser/input/Input.ts b/Phaser/input/Input.ts index 557b7e6c..5bea2755 100644 --- a/Phaser/input/Input.ts +++ b/Phaser/input/Input.ts @@ -23,6 +23,7 @@ module Phaser { constructor(game: Game) { this._game = game; + this._stack = []; this.mousePointer = new Pointer(this._game, 0); this.pointer1 = new Pointer(this._game, 1); @@ -42,7 +43,9 @@ module Phaser { this.onTap = new Phaser.Signal(); this.onHold = new Phaser.Signal(); + this.speed = new Vec2; this.position = new Vec2; + this._oldPosition = new Vec2; this.circle = new Circle(0, 0, 44); this.currentPointers = 0; @@ -54,6 +57,18 @@ module Phaser { */ private _game: Game; + /** + * Temporary click sorting stack + */ + private _stack; + + /** + * A vector object representing the previous position of the Pointer. + * @property vector + * @type {Vec2} + **/ + private _oldPosition: Vec2 = null; + /** * You can disable all Input by setting Input.disabled = true. While set all new input related events will be ignored. * If you need to disable just one type of input, for example mouse, use Input.mouse.disabled = true instead @@ -121,6 +136,14 @@ module Phaser { **/ public position: Vec2 = null; + /** + * A vector object representing the speed of the Pointer. Only really useful in single Pointer games, + * otherwise see the Pointer objects directly. + * @property vector + * @type {Vec2} + **/ + public speed: Vec2 = null; + /** * A Circle object centered on the x/y screen coordinates of the Input. * Default size of 44px (Apples recommended "finger tip" size) but can be changed to anything @@ -415,7 +438,7 @@ module Phaser { * Starts the Input Manager running * @method start **/ - public start() { + public boot() { this.mouse.start(); this.keyboard.start(); @@ -431,6 +454,11 @@ module Phaser { **/ public update() { + this.speed.x = this.position.x - this._oldPosition.x; + this.speed.y = this.position.y - this._oldPosition.y; + + this._oldPosition.copyFrom(this.position); + this.mousePointer.update(); this.pointer1.update(); this.pointer2.update(); @@ -446,6 +474,12 @@ module Phaser { } + public addToStack(item) { + + this._stack.push(item); + + } + /** * Reset all of the Pointers and Input states * @method reset diff --git a/Phaser/phaser.js b/Phaser/phaser.js index 4d4cebd0..7b20a194 100644 --- a/Phaser/phaser.js +++ b/Phaser/phaser.js @@ -1,5 +1,20 @@ +/** +* Phaser +* +* v1.0.0 - June XX 2013 +* +* A small and feature-packed 2D canvas game framework born from the firey pits of Flixel and Kiwi. +* +* Richard Davey (@photonstorm) +* +* Many thanks to Adam Saltsman (@ADAMATOMIC) for releasing Flixel, from both which Phaser +* and my love of game development took a lot of inspiration. +* +* "If you want your children to be intelligent, read them fairy tales." +* "If you want them to be more intelligent, read them more fairy tales." +* -- Albert Einstein +*/ var Phaser; (function (Phaser) { Phaser.VERSION = 'Phaser version 1.0.0'; })(Phaser || (Phaser = {})); -//@ sourceMappingURL=Phaser.js.map diff --git a/README.md b/README.md index 390b90f4..4ffae8f0 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,11 @@ V1.0.0 * Added Sprite.Input.useHandCursor (for desktop) * Added Sprite.Input.justOver and justOut with a configurable ms delay * Added Sprite.Events component for a global easy to access area to listen to events from +* Added Group.ID, each Group has a unique ID. Added Sprite.group (and Group.group) which is a reference to the Group it was added to. +* Added Group.addNewSprite(x,y,key) for quick addition of new Sprites to a Group +* Fixed Group.sort so the sortHandler is called correctly +* Added Group.swap(a,b) to swap the z-index of 2 objects with optional rendering update boolean + V0.9.6 diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index 9d4f3105..757510bb 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -68,6 +68,18 @@ + + + create group 1.ts + + + + display order.ts + + + + swap children.ts + over sprite 1.ts diff --git a/Tests/groups/create group 1.js b/Tests/groups/create group 1.js new file mode 100644 index 00000000..a9bc5e9b --- /dev/null +++ b/Tests/groups/create group 1.js @@ -0,0 +1,26 @@ +/// +(function () { + var game = new Phaser.Game(this, 'game', 800, 600, init, create); + function init() { + // Using Phasers asset loader we load up a PNG from the assets folder + game.loader.addImageFile('sonic', 'assets/sprites/sonic_havok_sanity.png'); + game.loader.load(); + } + var firstGroup; + function create() { + // Here we'll create a new Group + firstGroup = game.add.group(); + // And add some sprites to it + for(var i = 0; i < 10; i++) { + // Create a new sprite at a random screen location + var newSprite = new Phaser.Sprite(game, game.stage.randomX, game.stage.randomY, 'sonic'); + // This set-ups a listener for the event, view your console.log output to see the result + newSprite.events.onAddedToGroup.add(logGroupAdd); + // Add the sprite to the Group + firstGroup.add(newSprite); + } + } + function logGroupAdd(sprite, group, zIndex) { + console.log('Sprite added to Group', group.ID, 'at z-index:', zIndex); + } +})(); diff --git a/Tests/groups/create group 1.ts b/Tests/groups/create group 1.ts new file mode 100644 index 00000000..017c4020 --- /dev/null +++ b/Tests/groups/create group 1.ts @@ -0,0 +1,43 @@ +/// + +(function () { + + var game = new Phaser.Game(this, 'game', 800, 600, init, create); + + function init() { + + // Using Phasers asset loader we load up a PNG from the assets folder + game.loader.addImageFile('sonic', 'assets/sprites/sonic_havok_sanity.png'); + game.loader.load(); + + } + + var firstGroup: Phaser.Group; + + function create() { + + // Here we'll create a new Group + firstGroup = game.add.group(); + + // And add some sprites to it + for (var i = 0; i < 10; i++) + { + // Create a new sprite at a random screen location + var newSprite: Phaser.Sprite = new Phaser.Sprite(game, game.stage.randomX, game.stage.randomY, 'sonic'); + + // This set-ups a listener for the event, view your console.log output to see the result + newSprite.events.onAddedToGroup.add(logGroupAdd); + + // Add the sprite to the Group + firstGroup.add(newSprite); + } + + } + + function logGroupAdd(sprite: Phaser.Sprite, group: Phaser.Group, zIndex: number) { + + console.log('Sprite added to Group', group.ID, 'at z-index:', zIndex); + + } + +})(); diff --git a/Tests/groups/display order.js b/Tests/groups/display order.js new file mode 100644 index 00000000..8c5ee7e4 --- /dev/null +++ b/Tests/groups/display order.js @@ -0,0 +1,30 @@ +/// +(function () { + var game = new Phaser.Game(this, 'game', 800, 600, init, create); + function init() { + game.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + game.loader.addImageFile('atari2', 'assets/sprites/atari800xl.png'); + game.loader.addImageFile('card', 'assets/sprites/mana_card.png'); + game.loader.load(); + } + var items; + var card; + function create() { + items = game.add.group(); + // Items are rendered in the depth order in which they are added to the Group + items.addNewSprite(64, 100, 'atari1'); + card = items.addNewSprite(240, 80, 'card'); + items.addNewSprite(280, 100, 'atari2'); + game.input.onTap.addOnce(removeCard, this); + } + function removeCard() { + // Now let's kill the card sprite + card.kill(); + game.input.onTap.addOnce(replaceCard, this); + } + function replaceCard() { + // And bring it back to life again - I assume it will render in the same place as before? + var bob = items.getFirstDead(); + bob.revive(); + } +})(); diff --git a/Tests/groups/display order.ts b/Tests/groups/display order.ts new file mode 100644 index 00000000..f9cb0621 --- /dev/null +++ b/Tests/groups/display order.ts @@ -0,0 +1,52 @@ +/// + +(function () { + + var game = new Phaser.Game(this, 'game', 800, 600, init, create); + + function init() { + + game.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + game.loader.addImageFile('atari2', 'assets/sprites/atari800xl.png'); + game.loader.addImageFile('card', 'assets/sprites/mana_card.png'); + + game.loader.load(); + + } + + var items: Phaser.Group; + var card: Phaser.Sprite; + + function create() { + + items = game.add.group(); + + // Items are rendered in the depth order in which they are added to the Group + + items.addNewSprite(64, 100, 'atari1'); + card = items.addNewSprite(240, 80, 'card'); + items.addNewSprite(280, 100, 'atari2'); + + game.input.onTap.addOnce(removeCard, this); + + } + + function removeCard() { + + // Now let's kill the card sprite + card.kill(); + + game.input.onTap.addOnce(replaceCard, this); + + } + + function replaceCard() { + + // And bring it back to life again - I assume it will render in the same place as before? + var bob = items.getFirstDead(); + + bob.revive(); + + } + +})(); diff --git a/Tests/groups/swap children.js b/Tests/groups/swap children.js new file mode 100644 index 00000000..ecdf4bf3 --- /dev/null +++ b/Tests/groups/swap children.js @@ -0,0 +1,21 @@ +/// +(function () { + var game = new Phaser.Game(this, 'game', 800, 600, init, create); + function init() { + game.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + game.loader.addImageFile('atari2', 'assets/sprites/atari800xl.png'); + game.loader.load(); + } + var atari1; + var atari2; + function create() { + // Items are rendered in the depth order in which they are added to the Group + atari1 = game.add.sprite(100, 100, 'atari1'); + atari2 = game.add.sprite(250, 90, 'atari2'); + game.input.onTap.add(swapSprites, this); + } + function swapSprites() { + // The 2 Sprites are in the global world Group, but this will work for any Group: + game.world.group.swap(atari1, atari2); + } +})(); diff --git a/Tests/groups/swap children.ts b/Tests/groups/swap children.ts new file mode 100644 index 00000000..4044a0bb --- /dev/null +++ b/Tests/groups/swap children.ts @@ -0,0 +1,38 @@ +/// + +(function () { + + var game = new Phaser.Game(this, 'game', 800, 600, init, create); + + function init() { + + game.loader.addImageFile('atari1', 'assets/sprites/atari130xe.png'); + game.loader.addImageFile('atari2', 'assets/sprites/atari800xl.png'); + + game.loader.load(); + + } + + var atari1: Phaser.Sprite; + var atari2: Phaser.Sprite; + + function create() { + + // Items are rendered in the depth order in which they are added to the Group + + atari1 = game.add.sprite(100, 100, 'atari1'); + atari2 = game.add.sprite(250, 90, 'atari2'); + + game.input.onTap.add(swapSprites, this); + + } + + function swapSprites() { + + // The 2 Sprites are in the global world Group, but this will work for any Group: + game.world.group.swap(atari1, atari2); + + } + + +})(); diff --git a/Tests/phaser.js b/Tests/phaser.js index fc10aa62..ddf43b67 100644 --- a/Tests/phaser.js +++ b/Tests/phaser.js @@ -1276,6 +1276,26 @@ var Phaser; function Group(game, maxSize) { if (typeof maxSize === "undefined") { maxSize = 0; } /** + * Helper for sort. + */ + this._sortIndex = ''; + /** + * This keeps track of the z value of any game object added to this Group + */ + this._zCounter = 0; + /** + * The unique Group ID + */ + this.ID = -1; + /** + * The z value of this Group (within its parent Group, if any) + */ + this.z = -1; + /** + * The Group this Group is a child of (if any). + */ + this.group = null; + /** * You can set a globalCompositeOperation that will be applied before the render method is called on this Groups children. * This is useful if you wish to apply an effect like 'lighten' to a whole group of children as it saves doing it one-by-one. * If this value is set it will call a canvas context save and restore before and after the render pass. @@ -1298,9 +1318,16 @@ var Phaser; this._marker = 0; this._sortIndex = null; this.cameraBlacklist = []; + this.ID = this.game.world.getNextGroupID(); } Group.ASCENDING = -1; Group.DESCENDING = 1; + Group.prototype.getNextZIndex = /** + * Gets the next z index value for children of this Group + */ + function () { + return this._zCounter++; + }; Group.prototype.destroy = /** * Override this function to handle any deleting or "shutdown" type operations you might need, * such as removing traditional Flash children like Basic objects. @@ -1400,7 +1427,7 @@ var Phaser; configurable: true }); Group.prototype.add = /** - * Adds a new Basic subclass (Basic, GameObject, Sprite, etc) to the group. + * Adds a new Game Object to the group. * Group will try to replace a null member of the array first. * Failing that, Group will add it to the end of the member array, * assuming there is room for it, and doubling the size of the array if necessary. @@ -1409,19 +1436,21 @@ var Phaser; * the object will NOT be added to the group!

* * @param {Basic} Object The object you want to add to the group. - * @return {Basic} The same Basic object that was passed in. + * @return {Basic} The same object that was passed in. */ function (object) { - //Don't bother adding an object twice. - if(this.members.indexOf(Object) >= 0) { + // Is this object already in another Group? + // You can't add a Group to itself or an object to the same Group twice + if(object.group && (object.group.ID == this.ID || (object.type == Phaser.Types.GROUP && object.ID == this.ID))) { return object; } - //First, look for a null entry where we can add the object. + // First, look for a null entry where we can add the object. this._i = 0; this._length = this.members.length; while(this._i < this._length) { if(this.members[this._i] == null) { this.members[this._i] = object; + this.setObjectIDs(object); if(this._i >= this.length) { this.length = this._i + 1; } @@ -1429,7 +1458,7 @@ var Phaser; } this._i++; } - //Failing that, expand the array (if we can) and add the object. + // Failing that, expand the array (if we can) and add the object. if(this._maxSize > 0) { if(this.members.length >= this._maxSize) { return object; @@ -1441,12 +1470,45 @@ var Phaser; } else { this.members.length *= 2; } - //If we made it this far, then we successfully grew the group, - //and we can go ahead and add the object at the first open slot. + // If we made it this far, then we successfully grew the group, + // and we can go ahead and add the object at the first open slot. this.members[this._i] = object; this.length = this._i + 1; + this.setObjectIDs(object); return object; }; + Group.prototype.addNewSprite = /** + * Create a new Sprite within this Group at the specified position. + * + * @param x {number} X position of the new sprite. + * @param y {number} Y position of the new sprite. + * @param [key] {string} The image key as defined in the Game.Cache to use as the texture for this sprite + * @param [bodyType] {number} The physics body type of the object (defaults to BODY_DISABLED) + * @returns {Sprite} The newly created sprite object. + */ + function (x, y, key, bodyType) { + if (typeof key === "undefined") { key = ''; } + if (typeof bodyType === "undefined") { bodyType = Phaser.Types.BODY_DISABLED; } + return this.add(new Phaser.Sprite(this.game, x, y, key, bodyType)); + }; + Group.prototype.setObjectIDs = /** + * Sets all of the game object properties needed to exist within this Group. + */ + function (object, zIndex) { + if (typeof zIndex === "undefined") { zIndex = -1; } + // If the object is already in another Group, inform that Group it has left + if(object.group !== null) { + object.group.remove(object); + } + object.group = this; + if(zIndex == -1) { + zIndex = this.getNextZIndex(); + } + object.z = zIndex; + if(object['events']) { + object['events'].onAddedToGroup.dispatch(object, this, object.z); + } + }; Group.prototype.recycle = /** * Recycling is designed to help you reuse game objects without always re-allocating or "newing" them. * @@ -1499,7 +1561,7 @@ var Phaser; Group.prototype.remove = /** * Removes an object from the group. * - * @param {Basic} object The Basic you want to remove. + * @param {Basic} object The Game Object you want to remove. * @param {boolean} splice Whether the object should be cut from the array entirely or not. * * @return {Basic} The removed object. @@ -1516,10 +1578,15 @@ var Phaser; } else { this.members[this._i] = null; } + if(object['events']) { + object['events'].onRemovedFromGroup.dispatch(object, this); + } + object.group = null; + object.z = -1; return object; }; Group.prototype.replace = /** - * Replaces an existing Basic with a new one. + * Replaces an existing game object in this Group with a new one. * * @param {Basic} oldObject The object you want to replace. * @param {Basic} newObject The new object you want to use instead. @@ -1531,9 +1598,25 @@ var Phaser; if(this._i < 0 || (this._i >= this.members.length)) { return null; } + this.setObjectIDs(newObject, this.members[this._i].z); + // Null the old object + this.remove(this.members[this._i]); this.members[this._i] = newObject; return newObject; }; + Group.prototype.swap = function (child1, child2, sort) { + if (typeof sort === "undefined") { sort = true; } + if(child1.group.ID != this.ID || child2.group.ID != this.ID || child1 === child2) { + return false; + } + var tempZ = child1.z; + child1.z = child2.z; + child2.z = tempZ; + if(sort) { + this.sort(); + } + return true; + }; Group.prototype.sort = /** * Call this function to sort the group according to a particular value and order. * For example, to sort game objects for Zelda-style overlaps you might call @@ -1545,11 +1628,30 @@ var Phaser; * @param {number} order A Group constant that defines the sort order. Possible values are Group.ASCENDING and Group.DESCENDING. Default value is Group.ASCENDING. */ function (index, order) { - if (typeof index === "undefined") { index = "y"; } + if (typeof index === "undefined") { index = 'z'; } if (typeof order === "undefined") { order = Group.ASCENDING; } + var _this = this; this._sortIndex = index; this._sortOrder = order; - this.members.sort(this.sortHandler); + this.members.sort(function (a, b) { + return _this.sortHandler(a, b); + }); + }; + Group.prototype.sortHandler = /** + * Helper function for the sort process. + * + * @param {Basic} Obj1 The first object being sorted. + * @param {Basic} Obj2 The second object being sorted. + * + * @return {number} An integer value: -1 (Obj1 before Obj2), 0 (same), or 1 (Obj1 after Obj2). + */ + function (obj1, obj2) { + if(obj1[this._sortIndex] < obj2[this._sortIndex]) { + return this._sortOrder; + } else if(obj1[this._sortIndex] > obj2[this._sortIndex]) { + return -this._sortOrder; + } + return 0; }; Group.prototype.setAll = /** * Go through and set the specified variable to the specified value on all members of the group. @@ -1791,22 +1893,6 @@ var Phaser; } } }; - Group.prototype.sortHandler = /** - * Helper function for the sort process. - * - * @param {Basic} Obj1 The first object being sorted. - * @param {Basic} Obj2 The second object being sorted. - * - * @return {number} An integer value: -1 (Obj1 before Obj2), 0 (same), or 1 (Obj1 after Obj2). - */ - function (obj1, obj2) { - if(obj1[this._sortIndex] < obj2[this._sortIndex]) { - return this._sortOrder; - } else if(obj1[this._sortIndex] > obj2[this._sortIndex]) { - return -this._sortOrder; - } - return 0; - }; return Group; })(); Phaser.Group = Group; @@ -5328,6 +5414,11 @@ var Phaser; */ (function (Components) { var Texture = (function () { + /** + * Creates a new Sprite Texture component + * @param parent The Sprite using this Texture to render + * @param key An optional Game.Cache key to load an image from + */ function Texture(parent, key) { if (typeof key === "undefined") { key = ''; } /** @@ -5340,7 +5431,7 @@ var Phaser; */ this.dynamicTexture = null; /** - * The status of the texture image. + * The load status of the texture image. * @type {boolean} */ this.loaded = false; @@ -5365,7 +5456,7 @@ var Phaser; * @type {boolean} */ this.isDynamic = false; - this._game = parent.game; + this.game = parent.game; this._sprite = parent; this.canvas = parent.game.stage.canvas; this.context = parent.game.stage.context; @@ -5401,7 +5492,6 @@ var Phaser; * The graphic can be SpriteSheet or Texture Atlas. If you need to use a DynamicTexture see loadDynamicTexture. * @param key {string} Key of the graphic you want to load for this sprite. * @param clearAnimations {boolean} If this Sprite has a set of animation data already loaded you can choose to keep or clear it with this boolean - * @return {Sprite} Sprite instance itself. */ function (key, clearAnimations, updateBody) { if (typeof clearAnimations === "undefined") { clearAnimations = true; } @@ -5409,9 +5499,9 @@ var Phaser; if(clearAnimations && this._sprite.animations.frameData !== null) { this._sprite.animations.destroy(); } - if(this._game.cache.getImage(key) !== null) { - this.setTo(this._game.cache.getImage(key), null); - if(this._game.cache.isSpriteSheet(key)) { + if(this.game.cache.getImage(key) !== null) { + this.setTo(this.game.cache.getImage(key), null); + if(this.game.cache.isSpriteSheet(key)) { this._sprite.animations.loadFrameData(this._sprite.game.cache.getFrameData(key)); } else { this._sprite.frameBounds.width = this.width; @@ -5426,7 +5516,6 @@ var Phaser; Texture.prototype.loadDynamicTexture = /** * Load a DynamicTexture as its texture. * @param texture {DynamicTexture} The texture object to be used by this sprite. - * @return {Sprite} Sprite instance itself. */ function (texture) { if(this._sprite.animations.frameData !== null) { @@ -5485,8 +5574,47 @@ var Phaser; */ (function (Components) { var Input = (function () { + /** + * Sprite Input component constructor + * @param parent The Sprite using this Input component + */ function Input(parent) { + this.dragPixelPerfect = false; + this.allowHorizontalDrag = true; + this.allowVerticalDrag = true; + this.snapOnDrag = false; + this.snapOnRelease = false; + /** + * Is this sprite being dragged by the mouse or not? + * @default false + */ + this.isDragged = false; + /** + * Is this sprite allowed to be dragged by the mouse? true = yes, false = no + * @default false + */ + this.draggable = false; + /** + * An FlxRect region of the game world within which the sprite is restricted during mouse drag + * @default null + */ + this.boundsRect = null; + /** + * An FlxSprite the bounds of which this sprite is restricted during mouse drag + * @default null + */ + this.boundsSprite = null; + /** + * The x coordinate of the Input pointer, relative to the top-left of the parent Sprite. + * This value is only set with the pointer is over this Sprite. + * @type {number} + */ this.x = 0; + /** + * The y coordinate of the Input pointer, relative to the top-left of the parent Sprite + * This value is only set with the pointer is over this Sprite. + * @type {number} + */ this.y = 0; /** * If the Pointer is touching the touchscreen, or the mouse button is held down, isDown is set to true @@ -5537,6 +5665,9 @@ var Phaser; if(this.enabled == false) { return; } + if(this.draggable && this.isDragged) { + this.updateDrag(); + } if(this.game.input.x != this.oldX || this.game.input.y != this.oldY) { this.oldX = this.game.input.x; this.oldY = this.game.input.y; @@ -5565,16 +5696,55 @@ var Phaser; } } }; - Input.prototype.justOver = function (delay) { + Input.prototype.updateDrag = /** + * Updates the Mouse Drag on this Sprite. + */ + function () { + if(this.isUp) { + this.stopDrag(); + return; + } + if(this.allowHorizontalDrag) { + this._sprite.x = this.game.input.x - this.dragOffsetX; + } + if(this.allowVerticalDrag) { + this._sprite.y = this.game.input.y - this.dragOffsetY; + } + if(this.boundsRect) { + this.checkBoundsRect(); + } + if(this.boundsSprite) { + this.checkBoundsSprite(); + } + if(this.snapOnDrag) { + this._sprite.x = Math.floor(this._sprite.x / this.snapX) * this.snapX; + this._sprite.y = Math.floor(this._sprite.y / this.snapY) * this.snapY; + } + }; + Input.prototype.justOver = /** + * Returns true if the pointer has entered the Sprite within the specified delay time (defaults to 500ms, half a second) + * @param delay The time below which the pointer is considered as just over. + * @returns {boolean} + */ + function (delay) { if (typeof delay === "undefined") { delay = 500; } return (this.isOver && this.duration < delay); }; - Input.prototype.justOut = function (delay) { + Input.prototype.justOut = /** + * Returns true if the pointer has left the Sprite within the specified delay time (defaults to 500ms, half a second) + * @param delay The time below which the pointer is considered as just out. + * @returns {boolean} + */ + function (delay) { if (typeof delay === "undefined") { delay = 500; } return (this.isOut && (this.game.time.now - this.timeOut < delay)); }; Object.defineProperty(Input.prototype, "duration", { - get: function () { + get: /** + * If the pointer is currently over this Sprite this returns how long it has been there for in milliseconds. + * @returns {number} The number of milliseconds the pointer has been over the Sprite, or -1 if not over. + */ + function () { if(this.isOver) { return this.game.time.now - this.timeOver; } @@ -5583,6 +5753,135 @@ var Phaser; enumerable: true, configurable: true }); + Input.prototype.enableDrag = /** + * Make this Sprite draggable by the mouse. You can also optionally set mouseStartDragCallback and mouseStopDragCallback + * + * @param lockCenter If false the Sprite will drag from where you click it. If true it will center itself to the tip of the mouse pointer. + * @param pixelPerfect If true it will use a pixel perfect test to see if you clicked the Sprite. False uses the bounding box. + * @param alphaThreshold If using pixel perfect collision this specifies the alpha level from 0 to 255 above which a collision is processed (default 255) + * @param boundsRect If you want to restrict the drag of this sprite to a specific FlxRect, pass the FlxRect here, otherwise it's free to drag anywhere + * @param boundsSprite If you want to restrict the drag of this sprite to within the bounding box of another sprite, pass it here + */ + function (lockCenter, pixelPerfect, alphaThreshold, boundsRect, boundsSprite) { + if (typeof lockCenter === "undefined") { lockCenter = false; } + if (typeof pixelPerfect === "undefined") { pixelPerfect = false; } + if (typeof alphaThreshold === "undefined") { alphaThreshold = 255; } + if (typeof boundsRect === "undefined") { boundsRect = null; } + if (typeof boundsSprite === "undefined") { boundsSprite = null; } + this.draggable = true; + this.dragFromPoint = lockCenter; + this.dragPixelPerfect = pixelPerfect; + this.dragPixelPerfectAlpha = alphaThreshold; + if(boundsRect) { + this.boundsRect = boundsRect; + } + if(boundsSprite) { + this.boundsSprite = boundsSprite; + } + }; + Input.prototype.disableDrag = /** + * Stops this sprite from being able to be dragged. If it is currently the target of an active drag it will be stopped immediately. Also disables any set callbacks. + */ + function () { + if(this.isDragged) { + //FlxMouseControl.dragTarget = null; + //FlxMouseControl.isDragging = false; + } + this.isDragged = false; + this.draggable = false; + //mouseStartDragCallback = null; + //mouseStopDragCallback = null; + }; + Input.prototype.setDragLock = /** + * Restricts this sprite to drag movement only on the given axis. Note: If both are set to false the sprite will never move! + * + * @param allowHorizontal To enable the sprite to be dragged horizontally set to true, otherwise false + * @param allowVertical To enable the sprite to be dragged vertically set to true, otherwise false + */ + function (allowHorizontal, allowVertical) { + if (typeof allowHorizontal === "undefined") { allowHorizontal = true; } + if (typeof allowVertical === "undefined") { allowVertical = true; } + this.allowHorizontalDrag = allowHorizontal; + this.allowVerticalDrag = allowVertical; + }; + Input.prototype.enableSnap = /** + * Make this Sprite snap to the given grid either during drag or when it's released. + * For example 16x16 as the snapX and snapY would make the sprite snap to every 16 pixels. + * + * @param snapX The width of the grid cell in pixels + * @param snapY The height of the grid cell in pixels + * @param onDrag If true the sprite will snap to the grid while being dragged + * @param onRelease If true the sprite will snap to the grid when released + */ + function (snapX, snapY, onDrag, onRelease) { + if (typeof onDrag === "undefined") { onDrag = true; } + if (typeof onRelease === "undefined") { onRelease = false; } + this.snapOnDrag = onDrag; + this.snapOnRelease = onRelease; + this.snapX = snapX; + this.snapY = snapY; + }; + Input.prototype.disableSnap = /** + * Stops the sprite from snapping to a grid during drag or release. + */ + function () { + this.snapOnDrag = false; + this.snapOnRelease = false; + }; + Input.prototype.startDrag = /** + * Called by FlxMouseControl when Mouse Drag starts on this Sprite. Should not usually be called directly. + */ + function () { + this.isDragged = true; + if(this.dragFromPoint == false) { + this.dragOffsetX = this.game.input.x - this._sprite.x; + this.dragOffsetY = this.game.input.y - this._sprite.y; + } else { + // Move the sprite to the middle of the mouse + this.dragOffsetX = this._sprite.frameBounds.halfWidth; + this.dragOffsetY = this._sprite.frameBounds.halfHeight; + } + }; + Input.prototype.checkBoundsRect = /** + * Bounds Rect check for the sprite drag + */ + function () { + if(this._sprite.x < this.boundsRect.left) { + this._sprite.x = this.boundsRect.x; + } else if((this._sprite.x + this._sprite.width) > this.boundsRect.right) { + this._sprite.x = this.boundsRect.right - this._sprite.width; + } + if(this._sprite.y < this.boundsRect.top) { + this._sprite.y = this.boundsRect.top; + } else if((this._sprite.y + this._sprite.height) > this.boundsRect.bottom) { + this._sprite.y = this.boundsRect.bottom - this._sprite.height; + } + }; + Input.prototype.checkBoundsSprite = /** + * Parent Sprite Bounds check for the sprite drag + */ + function () { + if(this._sprite.x < this.boundsSprite.x) { + this._sprite.x = this.boundsSprite.x; + } else if((this._sprite.x + this._sprite.width) > (this.boundsSprite.x + this.boundsSprite.width)) { + this._sprite.x = (this.boundsSprite.x + this.boundsSprite.width) - this._sprite.width; + } + if(this._sprite.y < this.boundsSprite.y) { + this._sprite.y = this.boundsSprite.y; + } else if((this._sprite.y + this._sprite.height) > (this.boundsSprite.y + this.boundsSprite.height)) { + this._sprite.y = (this.boundsSprite.y + this.boundsSprite.height) - this._sprite.height; + } + }; + Input.prototype.stopDrag = /** + * Called by FlxMouseControl when Mouse Drag is stopped on this Sprite. Should not usually be called directly. + */ + function () { + this.isDragged = false; + if(this.snapOnRelease) { + this._sprite.x = Math.floor(this._sprite.x / this.snapX) * this.snapX; + this._sprite.y = Math.floor(this._sprite.y / this.snapY) * this.snapY; + } + }; Input.prototype.renderDebugInfo = /** * Render debug infos. (including name, bounds info, position and some other properties) * @param x {number} X position of the debug info to be rendered. @@ -5593,7 +5892,7 @@ var Phaser; if (typeof color === "undefined") { color = 'rgb(255,255,255)'; } this._sprite.texture.context.font = '14px Courier'; this._sprite.texture.context.fillStyle = color; - this._sprite.texture.context.fillText('Input: (' + this._sprite.frameBounds.width + ' x ' + this._sprite.frameBounds.height + ')', x, y); + this._sprite.texture.context.fillText('Sprite Input: (' + this._sprite.frameBounds.width + ' x ' + this._sprite.frameBounds.height + ')', x, y); this._sprite.texture.context.fillText('x: ' + this.x.toFixed(1) + ' y: ' + this.y.toFixed(1), x, y + 14); this._sprite.texture.context.fillText('over: ' + this.isOver + ' duration: ' + this.duration.toFixed(0), x, y + 28); this._sprite.texture.context.fillText('just over: ' + this.justOver() + ' just out: ' + this.justOut(), x, y + 42); @@ -5614,9 +5913,17 @@ var Phaser; */ (function (Components) { var Events = (function () { + /** + * The Events component is a collection of events fired by the parent Sprite and its other components. + * @param parent The Sprite using this Input component + */ function Events(parent) { this.game = parent.game; this._sprite = parent; + this.onAddedToGroup = new Phaser.Signal(); + this.onRemovedFromGroup = new Phaser.Signal(); + this.onKilled = new Phaser.Signal(); + this.onRevived = new Phaser.Signal(); this.onInputOver = new Phaser.Signal(); this.onInputOut = new Phaser.Signal(); this.onInputDown = new Phaser.Signal(); @@ -6181,8 +6488,8 @@ var Phaser; this.scrollFactor = new Phaser.Vec2(1, 1); this.x = x; this.y = y; - this.z = 0// not used yet - ; + this.z = -1; + this.group = null; // If a texture has been given the body will be set to that size, otherwise 16x16 this.body = new Phaser.Physics.Body(this, bodyType); this.animations = new Phaser.Components.AnimationManager(this); @@ -6334,9 +6641,14 @@ var Phaser; * like to animate an effect or whatever, you should override this, * setting only alive to false, and leaving exists true. */ - function () { + function (removeFromGroup) { + if (typeof removeFromGroup === "undefined") { removeFromGroup = false; } this.alive = false; this.exists = false; + if(removeFromGroup && this.group) { + this.group.remove(this); + } + this.events.onKilled.dispatch(this); }; Sprite.prototype.revive = /** * Handy for bringing game objects "back to life". Just sets alive and exists back to true. @@ -6345,6 +6657,7 @@ var Phaser; function () { this.alive = true; this.exists = true; + this.events.onRevived.dispatch(this); }; return Sprite; })(); @@ -6595,7 +6908,7 @@ var Phaser; this._stageX = x; this._stageY = y; this.scaledX = x; - this.scaledY = x; + this.scaledY = y; this.fx = new Phaser.CameraFX(this._game, this); // The view into the world canvas we wish to render this.worldView = new Phaser.Rectangle(0, 0, width, height); @@ -6622,10 +6935,7 @@ var Phaser; * @param object {Sprite/Group} The object to check. */ function (object) { - if(object['cameraBlacklist'] && object['cameraBlacklist'].length > 0 && object['cameraBlacklist'].indexOf(this.ID) == -1) { - return true; - } - return false; + return (object['cameraBlacklist'] && object['cameraBlacklist'].length > 0 && object['cameraBlacklist'].indexOf(this.ID) == -1); }; Camera.prototype.show = /** * Un-hides an object previously hidden to this Camera. @@ -8270,7 +8580,7 @@ var Phaser; if (typeof speedY === "undefined") { speedY = 0; } if(x > this.width || y > this.height || x < 0 || y < 0 || (x + width) > this.width || (y + height) > this.height) { throw Error('Invalid ScrollRegion defined. Cannot be larger than parent ScrollZone'); - return; + return null; } this.currentRegion = new Phaser.ScrollRegion(x, y, width, height, speedX, speedY); this.regions.push(this.currentRegion); @@ -8995,7 +9305,7 @@ var Phaser; this.generateTiles(tileQuantity); }; Tilemap.prototype.parseTiledJSON = /** - * Parset JSON map data and generate tiles. + * Parse JSON map data and generate tiles. * @param data {string} JSON map data. * @param key {string} Asset key for tileset image. */ @@ -9132,14 +9442,19 @@ var Phaser; if (typeof layer === "undefined") { layer = 0; } return this.tiles[this.layers[layer].getTileFromWorldXY(x, y)]; }; - Tilemap.prototype.getTileFromInputXY = function (layer) { + Tilemap.prototype.getTileFromInputXY = /** + * Gets the tile underneath the Input.x/y position + * @param layer The layer to check, defaults to 0 + * @returns {Tile} + */ + function (layer) { if (typeof layer === "undefined") { layer = 0; } return this.tiles[this.layers[layer].getTileFromWorldXY(this.game.input.getWorldX(), this.game.input.getWorldY())]; }; Tilemap.prototype.getTileOverlaps = /** * Get tiles overlaps the given object. * @param object {GameObject} Tiles you want to get that overlaps this. - * @return {array} Array with tiles informations. (Each contains x, y and the tile.) + * @return {array} Array with tiles information. (Each contains x, y and the tile.) */ function (object) { return this.currentLayer.getTileOverlaps(object); @@ -10156,8 +10471,6 @@ var Phaser; this.aspectRatio = width / height; this.scaleMode = Phaser.StageScaleMode.NO_SCALE; this.scale = new Phaser.StageScaleMode(this._game); - this._bootScreen = new Phaser.BootScreen(this._game); - this._pauseScreen = new Phaser.PauseScreen(this._game, width, height); document.addEventListener('visibilitychange', function (event) { return _this.visibilityChange(event); }, false); @@ -10171,6 +10484,13 @@ var Phaser; return _this.visibilityChange(event); }; } + Stage.prototype.boot = /** + * Stage boot + */ + function () { + this._bootScreen = new Phaser.BootScreen(this._game); + this._pauseScreen = new Phaser.PauseScreen(this._game, this.width, this.height); + }; Stage.prototype.update = /** * Update stage for rendering. This will handle scaling, clearing * and PauseScreen/BootScreen updating and rendering. @@ -11678,12 +11998,25 @@ var Phaser; * @param height {number} Height of the world bound. */ function World(game, width, height) { + /** + * Object container stores every object created with `create*` methods. + * @type {Group} + */ + this._groupCounter = 0; this._game = game; this.cameras = new Phaser.CameraManager(this._game, 0, 0, width, height); - this.group = new Phaser.Group(this._game, 0); this.bounds = new Phaser.Rectangle(0, 0, width, height); - this.physics = new Phaser.Physics.PhysicsManager(this._game, width, height); } + World.prototype.getNextGroupID = function () { + return this._groupCounter++; + }; + World.prototype.boot = /** + * Called one by Game during the boot process. + */ + function () { + this.group = new Phaser.Group(this._game, 0); + this.physics = new Phaser.Physics.PhysicsManager(this._game, this.width, this.height); + }; World.prototype.update = /** * This is called automatically every frame, and is where main logic happens. */ @@ -13846,6 +14179,12 @@ var Phaser; (function (Phaser) { var Input = (function () { function Input(game) { + /** + * A vector object representing the previous position of the Pointer. + * @property vector + * @type {Vec2} + **/ + this._oldPosition = null; /** * You can disable all Input by setting Input.disabled = true. While set all new input related events will be ignored. * If you need to disable just one type of input, for example mouse, use Input.mouse.disabled = true instead @@ -13863,6 +14202,13 @@ var Phaser; **/ this.position = null; /** + * A vector object representing the speed of the Pointer. Only really useful in single Pointer games, + * otherwise see the Pointer objects directly. + * @property vector + * @type {Vec2} + **/ + this.speed = null; + /** * A Circle object centered on the x/y screen coordinates of the Input. * Default size of 44px (Apples recommended "finger tip" size) but can be changed to anything * @property circle @@ -13984,6 +14330,7 @@ var Phaser; **/ this.pointer10 = null; this._game = game; + this._stack = []; this.mousePointer = new Phaser.Pointer(this._game, 0); this.pointer1 = new Phaser.Pointer(this._game, 1); this.pointer2 = new Phaser.Pointer(this._game, 2); @@ -13999,7 +14346,9 @@ var Phaser; this.onUp = new Phaser.Signal(); this.onTap = new Phaser.Signal(); this.onHold = new Phaser.Signal(); + this.speed = new Phaser.Vec2(); this.position = new Phaser.Vec2(); + this._oldPosition = new Phaser.Vec2(); this.circle = new Phaser.Circle(0, 0, 44); this.currentPointers = 0; } @@ -14067,7 +14416,7 @@ var Phaser; return this['pointer' + next]; } }; - Input.prototype.start = /** + Input.prototype.boot = /** * Starts the Input Manager running * @method start **/ @@ -14083,6 +14432,9 @@ var Phaser; * @method update **/ function () { + this.speed.x = this.position.x - this._oldPosition.x; + this.speed.y = this.position.y - this._oldPosition.y; + this._oldPosition.copyFrom(this.position); this.mousePointer.update(); this.pointer1.update(); this.pointer2.update(); @@ -14105,6 +14457,9 @@ var Phaser; this.pointer10.update(); } }; + Input.prototype.addToStack = function (item) { + this._stack.push(item); + }; Input.prototype.reset = /** * Reset all of the Pointers and Input states * @method reset @@ -14852,9 +15207,11 @@ var Phaser; (Date.now() * Math.random()).toString() ]); this.setRenderer(Phaser.Types.RENDERER_CANVAS); + this.world.boot(); + this.stage.boot(); + this.input.boot(); this.framerate = 60; this.isBooted = true; - this.input.start(); // Display the default game screen? if(this.onInitCallback == null && this.onCreateCallback == null && this.onUpdateCallback == null && this.onRenderCallback == null && this._pendingState == null) { this._raf = new Phaser.RequestAnimationFrame(this, this.bootLoop); @@ -14889,7 +15246,7 @@ var Phaser; this._loadComplete = true; }; Game.prototype.bootLoop = /** - * Game loop method will be called when it's booting. + * The bootLoop is called while the game is still booting (waiting for the DOM and resources to be available) */ function () { this.tweens.update(); @@ -14897,7 +15254,7 @@ var Phaser; this.stage.update(); }; Game.prototype.pausedLoop = /** - * Game loop method will be called when it's paused. + * The pausedLoop is called when the game is paused. */ function () { this.tweens.update(); @@ -15036,7 +15393,7 @@ var Phaser; } }; Game.prototype.destroy = /** - * Nuke the whole game from orbit + * Nuke the entire game from orbit */ function () { this.callbackContext = null; @@ -15574,11 +15931,10 @@ var Phaser; this.add = game.add; this.camera = game.camera; this.cache = game.cache; - //this.collision = game.collision; this.input = game.input; this.loader = game.loader; this.math = game.math; - //this.motion = game.motion; + this.motion = game.motion; this.sound = game.sound; this.stage = game.stage; this.time = game.time; diff --git a/build/phaser.d.ts b/build/phaser.d.ts index 2208a9be..6fd13af7 100644 --- a/build/phaser.d.ts +++ b/build/phaser.d.ts @@ -232,6 +232,14 @@ module Phaser { */ game: Game; /** + * The type of game object. + */ + type: number; + /** + * The ID of the Group this Sprite belongs to. + */ + group: Group; + /** * x value of the object. */ x: number; @@ -244,10 +252,6 @@ module Phaser { */ z: number; /** - * The type of game object. - */ - type: number; - /** * Controls if both update and render are called by the core game loop. */ exists: bool; @@ -906,6 +910,10 @@ module Phaser { private _prevAlpha; private _count; /** + * This keeps track of the z value of any game object added to this Group + */ + private _zCounter; + /** * Reference to the main game object */ public game: Game; @@ -914,6 +922,18 @@ module Phaser { */ public type: number; /** + * The unique Group ID + */ + public ID: number; + /** + * The z value of this Group (within its parent Group, if any) + */ + public z: number; + /** + * The Group this Group is a child of (if any). + */ + public group: Group; + /** * If this Group exists or not. Can be set to false to skip certain loop checks. */ public exists: bool; @@ -958,6 +978,10 @@ module Phaser { */ public cameraBlacklist: number[]; /** + * Gets the next z index value for children of this Group + */ + public getNextZIndex(): number; + /** * Override this function to handle any deleting or "shutdown" type operations you might need, * such as removing traditional Flash children like Basic objects. */ @@ -980,7 +1004,7 @@ module Phaser { */ public maxSize : number; /** - * Adds a new Basic subclass (Basic, GameObject, Sprite, etc) to the group. + * Adds a new Game Object to the group. * Group will try to replace a null member of the array first. * Failing that, Group will add it to the end of the member array, * assuming there is room for it, and doubling the size of the array if necessary. @@ -989,10 +1013,24 @@ module Phaser { * the object will NOT be added to the group!

* * @param {Basic} Object The object you want to add to the group. - * @return {Basic} The same Basic object that was passed in. + * @return {Basic} The same object that was passed in. */ public add(object): any; /** + * Create a new Sprite within this Group at the specified position. + * + * @param x {number} X position of the new sprite. + * @param y {number} Y position of the new sprite. + * @param [key] {string} The image key as defined in the Game.Cache to use as the texture for this sprite + * @param [bodyType] {number} The physics body type of the object (defaults to BODY_DISABLED) + * @returns {Sprite} The newly created sprite object. + */ + public addNewSprite(x: number, y: number, key?: string, bodyType?: number): Sprite; + /** + * Sets all of the game object properties needed to exist within this Group. + */ + private setObjectIDs(object, zIndex?); + /** * Recycling is designed to help you reuse game objects without always re-allocating or "newing" them. * *

If you specified a maximum size for this group (like in Emitter), @@ -1019,14 +1057,14 @@ module Phaser { /** * Removes an object from the group. * - * @param {Basic} object The Basic you want to remove. + * @param {Basic} object The Game Object you want to remove. * @param {boolean} splice Whether the object should be cut from the array entirely or not. * * @return {Basic} The removed object. */ public remove(object, splice?: bool); /** - * Replaces an existing Basic with a new one. + * Replaces an existing game object in this Group with a new one. * * @param {Basic} oldObject The object you want to replace. * @param {Basic} newObject The new object you want to use instead. @@ -1034,6 +1072,7 @@ module Phaser { * @return {Basic} The new object. */ public replace(oldObject, newObject); + public swap(child1, child2, sort?: bool): bool; /** * Call this function to sort the group according to a particular value and order. * For example, to sort game objects for Zelda-style overlaps you might call @@ -1046,6 +1085,15 @@ module Phaser { */ public sort(index?: string, order?: number): void; /** + * Helper function for the sort process. + * + * @param {Basic} Obj1 The first object being sorted. + * @param {Basic} Obj2 The second object being sorted. + * + * @return {number} An integer value: -1 (Obj1 before Obj2), 0 (same), or 1 (Obj1 after Obj2). + */ + public sortHandler(obj1, obj2): number; + /** * Go through and set the specified variable to the specified value on all members of the group. * * @param {string} VariableName The string representation of the variable name you want to modify, for example "visible" or "scrollFactor". @@ -1139,15 +1187,6 @@ module Phaser { * Calls kill on the group's members and then on the group itself. */ public kill(): void; - /** - * Helper function for the sort process. - * - * @param {Basic} Obj1 The first object being sorted. - * @param {Basic} Obj2 The second object being sorted. - * - * @return {number} An integer value: -1 (Obj1 before Obj2), 0 (same), or 1 (Obj1 after Obj2). - */ - public sortHandler(obj1, obj2): number; } } /** @@ -3101,13 +3140,18 @@ module Phaser { */ module Phaser.Components { class Texture { + /** + * Creates a new Sprite Texture component + * @param parent The Sprite using this Texture to render + * @param key An optional Game.Cache key to load an image from + */ constructor(parent: Sprite, key?: string); /** - * + * Reference to Phaser.Game */ - private _game; + public game: Game; /** - * Reference to the Image stored in the Game.Cache that is used as the texture for the Sprite. + * Reference to the parent Sprite */ private _sprite; /** @@ -3120,7 +3164,7 @@ module Phaser.Components { */ public dynamicTexture: DynamicTexture; /** - * The status of the texture image. + * The load status of the texture image. * @type {boolean} */ public loaded: bool; @@ -3178,13 +3222,11 @@ module Phaser.Components { * The graphic can be SpriteSheet or Texture Atlas. If you need to use a DynamicTexture see loadDynamicTexture. * @param key {string} Key of the graphic you want to load for this sprite. * @param clearAnimations {boolean} If this Sprite has a set of animation data already loaded you can choose to keep or clear it with this boolean - * @return {Sprite} Sprite instance itself. */ public loadImage(key: string, clearAnimations?: bool, updateBody?: bool): void; /** * Load a DynamicTexture as its texture. * @param texture {DynamicTexture} The texture object to be used by this sprite. - * @return {Sprite} Sprite instance itself. */ public loadDynamicTexture(texture: DynamicTexture): void; /** @@ -3206,21 +3248,78 @@ module Phaser.Components { */ module Phaser.Components { class Input { + /** + * Sprite Input component constructor + * @param parent The Sprite using this Input component + */ constructor(parent: Sprite); /** - * + * Reference to Phaser.Game */ public game: Game; /** * Reference to the Image stored in the Game.Cache that is used as the texture for the Sprite. */ private _sprite; + private dragOffsetX; + private dragOffsetY; + private dragFromPoint; + private dragPixelPerfect; + private dragPixelPerfectAlpha; + private allowHorizontalDrag; + private allowVerticalDrag; + private snapOnDrag; + private snapOnRelease; + private snapX; + private snapY; + /** + * If enabled the Input component will be updated by the parent Sprite + * @type {Boolean} + */ public enabled: bool; + /** + * Is this sprite being dragged by the mouse or not? + * @default false + */ + public isDragged: bool; + /** + * Is this sprite allowed to be dragged by the mouse? true = yes, false = no + * @default false + */ + public draggable: bool; + /** + * An FlxRect region of the game world within which the sprite is restricted during mouse drag + * @default null + */ + public boundsRect: Rectangle; + /** + * An FlxSprite the bounds of which this sprite is restricted during mouse drag + * @default null + */ + public boundsSprite: Sprite; + /** + * The Input component can monitor either the physics body of the Sprite or the frameBounds + * If checkBody is set to true it will monitor the bounds of the physics body. + * @type {Boolean} + */ public checkBody: bool; + /** + * Turn the mouse pointer into a hand image by temporarily setting the CSS style of the Game canvas + * on Input over. Only works on desktop browsers or browsers with a visible input pointer. + * @type {Boolean} + */ public useHandCursor: bool; - public oldX: number; - public oldY: number; + /** + * The x coordinate of the Input pointer, relative to the top-left of the parent Sprite. + * This value is only set with the pointer is over this Sprite. + * @type {number} + */ public x: number; + /** + * The y coordinate of the Input pointer, relative to the top-left of the parent Sprite + * This value is only set with the pointer is over this Sprite. + * @type {number} + */ public y: number; /** * If the Pointer is touching the touchscreen, or the mouse button is held down, isDown is set to true @@ -3258,14 +3357,85 @@ module Phaser.Components { * @type {Boolean} **/ public isOut: bool; + public oldX: number; + public oldY: number; /** * Update */ public update(): void; + /** + * Updates the Mouse Drag on this Sprite. + */ + private updateDrag(); + /** + * Returns true if the pointer has entered the Sprite within the specified delay time (defaults to 500ms, half a second) + * @param delay The time below which the pointer is considered as just over. + * @returns {boolean} + */ public justOver(delay?: number): bool; + /** + * Returns true if the pointer has left the Sprite within the specified delay time (defaults to 500ms, half a second) + * @param delay The time below which the pointer is considered as just out. + * @returns {boolean} + */ public justOut(delay?: number): bool; + /** + * If the pointer is currently over this Sprite this returns how long it has been there for in milliseconds. + * @returns {number} The number of milliseconds the pointer has been over the Sprite, or -1 if not over. + */ public duration : number; /** + * Make this Sprite draggable by the mouse. You can also optionally set mouseStartDragCallback and mouseStopDragCallback + * + * @param lockCenter If false the Sprite will drag from where you click it. If true it will center itself to the tip of the mouse pointer. + * @param pixelPerfect If true it will use a pixel perfect test to see if you clicked the Sprite. False uses the bounding box. + * @param alphaThreshold If using pixel perfect collision this specifies the alpha level from 0 to 255 above which a collision is processed (default 255) + * @param boundsRect If you want to restrict the drag of this sprite to a specific FlxRect, pass the FlxRect here, otherwise it's free to drag anywhere + * @param boundsSprite If you want to restrict the drag of this sprite to within the bounding box of another sprite, pass it here + */ + public enableDrag(lockCenter?: bool, pixelPerfect?: bool, alphaThreshold?: number, boundsRect?: Rectangle, boundsSprite?: Sprite): void; + /** + * Stops this sprite from being able to be dragged. If it is currently the target of an active drag it will be stopped immediately. Also disables any set callbacks. + */ + public disableDrag(): void; + /** + * Restricts this sprite to drag movement only on the given axis. Note: If both are set to false the sprite will never move! + * + * @param allowHorizontal To enable the sprite to be dragged horizontally set to true, otherwise false + * @param allowVertical To enable the sprite to be dragged vertically set to true, otherwise false + */ + public setDragLock(allowHorizontal?: bool, allowVertical?: bool): void; + /** + * Make this Sprite snap to the given grid either during drag or when it's released. + * For example 16x16 as the snapX and snapY would make the sprite snap to every 16 pixels. + * + * @param snapX The width of the grid cell in pixels + * @param snapY The height of the grid cell in pixels + * @param onDrag If true the sprite will snap to the grid while being dragged + * @param onRelease If true the sprite will snap to the grid when released + */ + public enableSnap(snapX: number, snapY: number, onDrag?: bool, onRelease?: bool): void; + /** + * Stops the sprite from snapping to a grid during drag or release. + */ + public disableSnap(): void; + /** + * Called by FlxMouseControl when Mouse Drag starts on this Sprite. Should not usually be called directly. + */ + public startDrag(): void; + /** + * Bounds Rect check for the sprite drag + */ + private checkBoundsRect(); + /** + * Parent Sprite Bounds check for the sprite drag + */ + private checkBoundsSprite(); + /** + * Called by FlxMouseControl when Mouse Drag is stopped on this Sprite. Should not usually be called directly. + */ + public stopDrag(): void; + /** * Render debug infos. (including name, bounds info, position and some other properties) * @param x {number} X position of the debug info to be rendered. * @param y {number} Y position of the debug info to be rendered. @@ -3281,17 +3451,52 @@ module Phaser.Components { */ module Phaser.Components { class Events { + /** + * The Events component is a collection of events fired by the parent Sprite and its other components. + * @param parent The Sprite using this Input component + */ constructor(parent: Sprite); + /** + * Reference to Phaser.Game + */ public game: Game; + /** + * Reference to the Image stored in the Game.Cache that is used as the texture for the Sprite. + */ private _sprite; - public onAdded: Signal; + /** + * Dispatched by the Group this Sprite is added to. + */ + public onAddedToGroup: Signal; + /** + * Dispatched by the Group this Sprite is removed from. + */ + public onRemovedFromGroup: Signal; + /** + * Dispatched when this Sprite is killed. + */ public onKilled: Signal; + /** + * Dispatched when this Sprite is revived. + */ public onRevived: Signal; - public onOutOfBounds: Signal; + /** + * Dispatched by the Input component when a pointer moves over an Input enabled sprite. + */ public onInputOver: Signal; + /** + * Dispatched by the Input component when a pointer moves out of an Input enabled sprite. + */ public onInputOut: Signal; + /** + * Dispatched by the Input component when a pointer is pressed down on an Input enabled sprite. + */ public onInputDown: Signal; + /** + * Dispatched by the Input component when a pointer is released over an Input enabled sprite + */ public onInputUp: Signal; + public onOutOfBounds: Signal; } } /** @@ -3564,6 +3769,10 @@ module Phaser { */ public type: number; /** + * The Group this Sprite belongs to. + */ + public group: Group; + /** * Controls if both update and render are called by the core game loop. */ public exists: bool; @@ -3697,7 +3906,7 @@ module Phaser { * like to animate an effect or whatever, you should override this, * setting only alive to false, and leaving exists true. */ - public kill(): void; + public kill(removeFromGroup?: bool): void; /** * Handy for bringing game objects "back to life". Just sets alive and exists back to true. * In practice, this is most often called by Object.reset(). @@ -5163,7 +5372,7 @@ module Phaser { */ private parseCSV(data, key, tileWidth, tileHeight); /** - * Parset JSON map data and generate tiles. + * Parse JSON map data and generate tiles. * @param data {string} JSON map data. * @param key {string} Asset key for tileset image. */ @@ -5222,11 +5431,16 @@ module Phaser { * @return {Tile} The tile with specific properties. */ public getTileFromWorldXY(x: number, y: number, layer?: number): Tile; + /** + * Gets the tile underneath the Input.x/y position + * @param layer The layer to check, defaults to 0 + * @returns {Tile} + */ public getTileFromInputXY(layer?: number): Tile; /** * Get tiles overlaps the given object. * @param object {GameObject} Tiles you want to get that overlaps this. - * @return {array} Array with tiles informations. (Each contains x, y and the tile.) + * @return {array} Array with tiles information. (Each contains x, y and the tile.) */ public getTileOverlaps(object: Sprite); /** @@ -5855,6 +6069,10 @@ module Phaser { */ public scaleMode: number; /** + * Stage boot + */ + public boot(): void; + /** * Update stage for rendering. This will handle scaling, clearing * and PauseScreen/BootScreen updating and rendering. */ @@ -6319,6 +6537,16 @@ module Phaser { */ public physics: Physics.PhysicsManager; /** + * Object container stores every object created with `create*` methods. + * @type {Group} + */ + private _groupCounter; + public getNextGroupID(): number; + /** + * Called one by Game during the boot process. + */ + public boot(): void; + /** * This is called automatically every frame, and is where main logic happens. */ public update(): void; @@ -7479,6 +7707,16 @@ module Phaser { */ private _game; /** + * Temporary click sorting stack + */ + private _stack; + /** + * A vector object representing the previous position of the Pointer. + * @property vector + * @type {Vec2} + **/ + private _oldPosition; + /** * You can disable all Input by setting Input.disabled = true. While set all new input related events will be ignored. * If you need to disable just one type of input, for example mouse, use Input.mouse.disabled = true instead * @type {Boolean} @@ -7535,6 +7773,13 @@ module Phaser { **/ public position: Vec2; /** + * A vector object representing the speed of the Pointer. Only really useful in single Pointer games, + * otherwise see the Pointer objects directly. + * @property vector + * @type {Vec2} + **/ + public speed: Vec2; + /** * A Circle object centered on the x/y screen coordinates of the Input. * Default size of 44px (Apples recommended "finger tip" size) but can be changed to anything * @property circle @@ -7735,12 +7980,13 @@ module Phaser { * Starts the Input Manager running * @method start **/ - public start(): void; + public boot(): void; /** * Updates the Input Manager. Called by the core Game loop. * @method update **/ public update(): void; + public addToStack(item): void; /** * Reset all of the Pointers and Input states * @method reset @@ -8080,11 +8326,11 @@ module Phaser { */ private loadComplete(); /** - * Game loop method will be called when it's booting. + * The bootLoop is called while the game is still booting (waiting for the DOM and resources to be available) */ private bootLoop(); /** - * Game loop method will be called when it's paused. + * The pausedLoop is called when the game is paused. */ private pausedLoop(); /** @@ -8112,7 +8358,7 @@ module Phaser { */ public switchState(state, clearWorld?: bool, clearCache?: bool): void; /** - * Nuke the whole game from orbit + * Nuke the entire game from orbit */ public destroy(): void; public paused : bool; @@ -8441,6 +8687,11 @@ module Phaser { */ public math: GameMath; /** + * Reference to the motion helper. + * @type {Motion} + */ + public motion: Motion; + /** * Reference to the sound manager. * @type {SoundManager} */ diff --git a/build/phaser.js b/build/phaser.js index fc10aa62..ddf43b67 100644 --- a/build/phaser.js +++ b/build/phaser.js @@ -1276,6 +1276,26 @@ var Phaser; function Group(game, maxSize) { if (typeof maxSize === "undefined") { maxSize = 0; } /** + * Helper for sort. + */ + this._sortIndex = ''; + /** + * This keeps track of the z value of any game object added to this Group + */ + this._zCounter = 0; + /** + * The unique Group ID + */ + this.ID = -1; + /** + * The z value of this Group (within its parent Group, if any) + */ + this.z = -1; + /** + * The Group this Group is a child of (if any). + */ + this.group = null; + /** * You can set a globalCompositeOperation that will be applied before the render method is called on this Groups children. * This is useful if you wish to apply an effect like 'lighten' to a whole group of children as it saves doing it one-by-one. * If this value is set it will call a canvas context save and restore before and after the render pass. @@ -1298,9 +1318,16 @@ var Phaser; this._marker = 0; this._sortIndex = null; this.cameraBlacklist = []; + this.ID = this.game.world.getNextGroupID(); } Group.ASCENDING = -1; Group.DESCENDING = 1; + Group.prototype.getNextZIndex = /** + * Gets the next z index value for children of this Group + */ + function () { + return this._zCounter++; + }; Group.prototype.destroy = /** * Override this function to handle any deleting or "shutdown" type operations you might need, * such as removing traditional Flash children like Basic objects. @@ -1400,7 +1427,7 @@ var Phaser; configurable: true }); Group.prototype.add = /** - * Adds a new Basic subclass (Basic, GameObject, Sprite, etc) to the group. + * Adds a new Game Object to the group. * Group will try to replace a null member of the array first. * Failing that, Group will add it to the end of the member array, * assuming there is room for it, and doubling the size of the array if necessary. @@ -1409,19 +1436,21 @@ var Phaser; * the object will NOT be added to the group!

* * @param {Basic} Object The object you want to add to the group. - * @return {Basic} The same Basic object that was passed in. + * @return {Basic} The same object that was passed in. */ function (object) { - //Don't bother adding an object twice. - if(this.members.indexOf(Object) >= 0) { + // Is this object already in another Group? + // You can't add a Group to itself or an object to the same Group twice + if(object.group && (object.group.ID == this.ID || (object.type == Phaser.Types.GROUP && object.ID == this.ID))) { return object; } - //First, look for a null entry where we can add the object. + // First, look for a null entry where we can add the object. this._i = 0; this._length = this.members.length; while(this._i < this._length) { if(this.members[this._i] == null) { this.members[this._i] = object; + this.setObjectIDs(object); if(this._i >= this.length) { this.length = this._i + 1; } @@ -1429,7 +1458,7 @@ var Phaser; } this._i++; } - //Failing that, expand the array (if we can) and add the object. + // Failing that, expand the array (if we can) and add the object. if(this._maxSize > 0) { if(this.members.length >= this._maxSize) { return object; @@ -1441,12 +1470,45 @@ var Phaser; } else { this.members.length *= 2; } - //If we made it this far, then we successfully grew the group, - //and we can go ahead and add the object at the first open slot. + // If we made it this far, then we successfully grew the group, + // and we can go ahead and add the object at the first open slot. this.members[this._i] = object; this.length = this._i + 1; + this.setObjectIDs(object); return object; }; + Group.prototype.addNewSprite = /** + * Create a new Sprite within this Group at the specified position. + * + * @param x {number} X position of the new sprite. + * @param y {number} Y position of the new sprite. + * @param [key] {string} The image key as defined in the Game.Cache to use as the texture for this sprite + * @param [bodyType] {number} The physics body type of the object (defaults to BODY_DISABLED) + * @returns {Sprite} The newly created sprite object. + */ + function (x, y, key, bodyType) { + if (typeof key === "undefined") { key = ''; } + if (typeof bodyType === "undefined") { bodyType = Phaser.Types.BODY_DISABLED; } + return this.add(new Phaser.Sprite(this.game, x, y, key, bodyType)); + }; + Group.prototype.setObjectIDs = /** + * Sets all of the game object properties needed to exist within this Group. + */ + function (object, zIndex) { + if (typeof zIndex === "undefined") { zIndex = -1; } + // If the object is already in another Group, inform that Group it has left + if(object.group !== null) { + object.group.remove(object); + } + object.group = this; + if(zIndex == -1) { + zIndex = this.getNextZIndex(); + } + object.z = zIndex; + if(object['events']) { + object['events'].onAddedToGroup.dispatch(object, this, object.z); + } + }; Group.prototype.recycle = /** * Recycling is designed to help you reuse game objects without always re-allocating or "newing" them. * @@ -1499,7 +1561,7 @@ var Phaser; Group.prototype.remove = /** * Removes an object from the group. * - * @param {Basic} object The Basic you want to remove. + * @param {Basic} object The Game Object you want to remove. * @param {boolean} splice Whether the object should be cut from the array entirely or not. * * @return {Basic} The removed object. @@ -1516,10 +1578,15 @@ var Phaser; } else { this.members[this._i] = null; } + if(object['events']) { + object['events'].onRemovedFromGroup.dispatch(object, this); + } + object.group = null; + object.z = -1; return object; }; Group.prototype.replace = /** - * Replaces an existing Basic with a new one. + * Replaces an existing game object in this Group with a new one. * * @param {Basic} oldObject The object you want to replace. * @param {Basic} newObject The new object you want to use instead. @@ -1531,9 +1598,25 @@ var Phaser; if(this._i < 0 || (this._i >= this.members.length)) { return null; } + this.setObjectIDs(newObject, this.members[this._i].z); + // Null the old object + this.remove(this.members[this._i]); this.members[this._i] = newObject; return newObject; }; + Group.prototype.swap = function (child1, child2, sort) { + if (typeof sort === "undefined") { sort = true; } + if(child1.group.ID != this.ID || child2.group.ID != this.ID || child1 === child2) { + return false; + } + var tempZ = child1.z; + child1.z = child2.z; + child2.z = tempZ; + if(sort) { + this.sort(); + } + return true; + }; Group.prototype.sort = /** * Call this function to sort the group according to a particular value and order. * For example, to sort game objects for Zelda-style overlaps you might call @@ -1545,11 +1628,30 @@ var Phaser; * @param {number} order A Group constant that defines the sort order. Possible values are Group.ASCENDING and Group.DESCENDING. Default value is Group.ASCENDING. */ function (index, order) { - if (typeof index === "undefined") { index = "y"; } + if (typeof index === "undefined") { index = 'z'; } if (typeof order === "undefined") { order = Group.ASCENDING; } + var _this = this; this._sortIndex = index; this._sortOrder = order; - this.members.sort(this.sortHandler); + this.members.sort(function (a, b) { + return _this.sortHandler(a, b); + }); + }; + Group.prototype.sortHandler = /** + * Helper function for the sort process. + * + * @param {Basic} Obj1 The first object being sorted. + * @param {Basic} Obj2 The second object being sorted. + * + * @return {number} An integer value: -1 (Obj1 before Obj2), 0 (same), or 1 (Obj1 after Obj2). + */ + function (obj1, obj2) { + if(obj1[this._sortIndex] < obj2[this._sortIndex]) { + return this._sortOrder; + } else if(obj1[this._sortIndex] > obj2[this._sortIndex]) { + return -this._sortOrder; + } + return 0; }; Group.prototype.setAll = /** * Go through and set the specified variable to the specified value on all members of the group. @@ -1791,22 +1893,6 @@ var Phaser; } } }; - Group.prototype.sortHandler = /** - * Helper function for the sort process. - * - * @param {Basic} Obj1 The first object being sorted. - * @param {Basic} Obj2 The second object being sorted. - * - * @return {number} An integer value: -1 (Obj1 before Obj2), 0 (same), or 1 (Obj1 after Obj2). - */ - function (obj1, obj2) { - if(obj1[this._sortIndex] < obj2[this._sortIndex]) { - return this._sortOrder; - } else if(obj1[this._sortIndex] > obj2[this._sortIndex]) { - return -this._sortOrder; - } - return 0; - }; return Group; })(); Phaser.Group = Group; @@ -5328,6 +5414,11 @@ var Phaser; */ (function (Components) { var Texture = (function () { + /** + * Creates a new Sprite Texture component + * @param parent The Sprite using this Texture to render + * @param key An optional Game.Cache key to load an image from + */ function Texture(parent, key) { if (typeof key === "undefined") { key = ''; } /** @@ -5340,7 +5431,7 @@ var Phaser; */ this.dynamicTexture = null; /** - * The status of the texture image. + * The load status of the texture image. * @type {boolean} */ this.loaded = false; @@ -5365,7 +5456,7 @@ var Phaser; * @type {boolean} */ this.isDynamic = false; - this._game = parent.game; + this.game = parent.game; this._sprite = parent; this.canvas = parent.game.stage.canvas; this.context = parent.game.stage.context; @@ -5401,7 +5492,6 @@ var Phaser; * The graphic can be SpriteSheet or Texture Atlas. If you need to use a DynamicTexture see loadDynamicTexture. * @param key {string} Key of the graphic you want to load for this sprite. * @param clearAnimations {boolean} If this Sprite has a set of animation data already loaded you can choose to keep or clear it with this boolean - * @return {Sprite} Sprite instance itself. */ function (key, clearAnimations, updateBody) { if (typeof clearAnimations === "undefined") { clearAnimations = true; } @@ -5409,9 +5499,9 @@ var Phaser; if(clearAnimations && this._sprite.animations.frameData !== null) { this._sprite.animations.destroy(); } - if(this._game.cache.getImage(key) !== null) { - this.setTo(this._game.cache.getImage(key), null); - if(this._game.cache.isSpriteSheet(key)) { + if(this.game.cache.getImage(key) !== null) { + this.setTo(this.game.cache.getImage(key), null); + if(this.game.cache.isSpriteSheet(key)) { this._sprite.animations.loadFrameData(this._sprite.game.cache.getFrameData(key)); } else { this._sprite.frameBounds.width = this.width; @@ -5426,7 +5516,6 @@ var Phaser; Texture.prototype.loadDynamicTexture = /** * Load a DynamicTexture as its texture. * @param texture {DynamicTexture} The texture object to be used by this sprite. - * @return {Sprite} Sprite instance itself. */ function (texture) { if(this._sprite.animations.frameData !== null) { @@ -5485,8 +5574,47 @@ var Phaser; */ (function (Components) { var Input = (function () { + /** + * Sprite Input component constructor + * @param parent The Sprite using this Input component + */ function Input(parent) { + this.dragPixelPerfect = false; + this.allowHorizontalDrag = true; + this.allowVerticalDrag = true; + this.snapOnDrag = false; + this.snapOnRelease = false; + /** + * Is this sprite being dragged by the mouse or not? + * @default false + */ + this.isDragged = false; + /** + * Is this sprite allowed to be dragged by the mouse? true = yes, false = no + * @default false + */ + this.draggable = false; + /** + * An FlxRect region of the game world within which the sprite is restricted during mouse drag + * @default null + */ + this.boundsRect = null; + /** + * An FlxSprite the bounds of which this sprite is restricted during mouse drag + * @default null + */ + this.boundsSprite = null; + /** + * The x coordinate of the Input pointer, relative to the top-left of the parent Sprite. + * This value is only set with the pointer is over this Sprite. + * @type {number} + */ this.x = 0; + /** + * The y coordinate of the Input pointer, relative to the top-left of the parent Sprite + * This value is only set with the pointer is over this Sprite. + * @type {number} + */ this.y = 0; /** * If the Pointer is touching the touchscreen, or the mouse button is held down, isDown is set to true @@ -5537,6 +5665,9 @@ var Phaser; if(this.enabled == false) { return; } + if(this.draggable && this.isDragged) { + this.updateDrag(); + } if(this.game.input.x != this.oldX || this.game.input.y != this.oldY) { this.oldX = this.game.input.x; this.oldY = this.game.input.y; @@ -5565,16 +5696,55 @@ var Phaser; } } }; - Input.prototype.justOver = function (delay) { + Input.prototype.updateDrag = /** + * Updates the Mouse Drag on this Sprite. + */ + function () { + if(this.isUp) { + this.stopDrag(); + return; + } + if(this.allowHorizontalDrag) { + this._sprite.x = this.game.input.x - this.dragOffsetX; + } + if(this.allowVerticalDrag) { + this._sprite.y = this.game.input.y - this.dragOffsetY; + } + if(this.boundsRect) { + this.checkBoundsRect(); + } + if(this.boundsSprite) { + this.checkBoundsSprite(); + } + if(this.snapOnDrag) { + this._sprite.x = Math.floor(this._sprite.x / this.snapX) * this.snapX; + this._sprite.y = Math.floor(this._sprite.y / this.snapY) * this.snapY; + } + }; + Input.prototype.justOver = /** + * Returns true if the pointer has entered the Sprite within the specified delay time (defaults to 500ms, half a second) + * @param delay The time below which the pointer is considered as just over. + * @returns {boolean} + */ + function (delay) { if (typeof delay === "undefined") { delay = 500; } return (this.isOver && this.duration < delay); }; - Input.prototype.justOut = function (delay) { + Input.prototype.justOut = /** + * Returns true if the pointer has left the Sprite within the specified delay time (defaults to 500ms, half a second) + * @param delay The time below which the pointer is considered as just out. + * @returns {boolean} + */ + function (delay) { if (typeof delay === "undefined") { delay = 500; } return (this.isOut && (this.game.time.now - this.timeOut < delay)); }; Object.defineProperty(Input.prototype, "duration", { - get: function () { + get: /** + * If the pointer is currently over this Sprite this returns how long it has been there for in milliseconds. + * @returns {number} The number of milliseconds the pointer has been over the Sprite, or -1 if not over. + */ + function () { if(this.isOver) { return this.game.time.now - this.timeOver; } @@ -5583,6 +5753,135 @@ var Phaser; enumerable: true, configurable: true }); + Input.prototype.enableDrag = /** + * Make this Sprite draggable by the mouse. You can also optionally set mouseStartDragCallback and mouseStopDragCallback + * + * @param lockCenter If false the Sprite will drag from where you click it. If true it will center itself to the tip of the mouse pointer. + * @param pixelPerfect If true it will use a pixel perfect test to see if you clicked the Sprite. False uses the bounding box. + * @param alphaThreshold If using pixel perfect collision this specifies the alpha level from 0 to 255 above which a collision is processed (default 255) + * @param boundsRect If you want to restrict the drag of this sprite to a specific FlxRect, pass the FlxRect here, otherwise it's free to drag anywhere + * @param boundsSprite If you want to restrict the drag of this sprite to within the bounding box of another sprite, pass it here + */ + function (lockCenter, pixelPerfect, alphaThreshold, boundsRect, boundsSprite) { + if (typeof lockCenter === "undefined") { lockCenter = false; } + if (typeof pixelPerfect === "undefined") { pixelPerfect = false; } + if (typeof alphaThreshold === "undefined") { alphaThreshold = 255; } + if (typeof boundsRect === "undefined") { boundsRect = null; } + if (typeof boundsSprite === "undefined") { boundsSprite = null; } + this.draggable = true; + this.dragFromPoint = lockCenter; + this.dragPixelPerfect = pixelPerfect; + this.dragPixelPerfectAlpha = alphaThreshold; + if(boundsRect) { + this.boundsRect = boundsRect; + } + if(boundsSprite) { + this.boundsSprite = boundsSprite; + } + }; + Input.prototype.disableDrag = /** + * Stops this sprite from being able to be dragged. If it is currently the target of an active drag it will be stopped immediately. Also disables any set callbacks. + */ + function () { + if(this.isDragged) { + //FlxMouseControl.dragTarget = null; + //FlxMouseControl.isDragging = false; + } + this.isDragged = false; + this.draggable = false; + //mouseStartDragCallback = null; + //mouseStopDragCallback = null; + }; + Input.prototype.setDragLock = /** + * Restricts this sprite to drag movement only on the given axis. Note: If both are set to false the sprite will never move! + * + * @param allowHorizontal To enable the sprite to be dragged horizontally set to true, otherwise false + * @param allowVertical To enable the sprite to be dragged vertically set to true, otherwise false + */ + function (allowHorizontal, allowVertical) { + if (typeof allowHorizontal === "undefined") { allowHorizontal = true; } + if (typeof allowVertical === "undefined") { allowVertical = true; } + this.allowHorizontalDrag = allowHorizontal; + this.allowVerticalDrag = allowVertical; + }; + Input.prototype.enableSnap = /** + * Make this Sprite snap to the given grid either during drag or when it's released. + * For example 16x16 as the snapX and snapY would make the sprite snap to every 16 pixels. + * + * @param snapX The width of the grid cell in pixels + * @param snapY The height of the grid cell in pixels + * @param onDrag If true the sprite will snap to the grid while being dragged + * @param onRelease If true the sprite will snap to the grid when released + */ + function (snapX, snapY, onDrag, onRelease) { + if (typeof onDrag === "undefined") { onDrag = true; } + if (typeof onRelease === "undefined") { onRelease = false; } + this.snapOnDrag = onDrag; + this.snapOnRelease = onRelease; + this.snapX = snapX; + this.snapY = snapY; + }; + Input.prototype.disableSnap = /** + * Stops the sprite from snapping to a grid during drag or release. + */ + function () { + this.snapOnDrag = false; + this.snapOnRelease = false; + }; + Input.prototype.startDrag = /** + * Called by FlxMouseControl when Mouse Drag starts on this Sprite. Should not usually be called directly. + */ + function () { + this.isDragged = true; + if(this.dragFromPoint == false) { + this.dragOffsetX = this.game.input.x - this._sprite.x; + this.dragOffsetY = this.game.input.y - this._sprite.y; + } else { + // Move the sprite to the middle of the mouse + this.dragOffsetX = this._sprite.frameBounds.halfWidth; + this.dragOffsetY = this._sprite.frameBounds.halfHeight; + } + }; + Input.prototype.checkBoundsRect = /** + * Bounds Rect check for the sprite drag + */ + function () { + if(this._sprite.x < this.boundsRect.left) { + this._sprite.x = this.boundsRect.x; + } else if((this._sprite.x + this._sprite.width) > this.boundsRect.right) { + this._sprite.x = this.boundsRect.right - this._sprite.width; + } + if(this._sprite.y < this.boundsRect.top) { + this._sprite.y = this.boundsRect.top; + } else if((this._sprite.y + this._sprite.height) > this.boundsRect.bottom) { + this._sprite.y = this.boundsRect.bottom - this._sprite.height; + } + }; + Input.prototype.checkBoundsSprite = /** + * Parent Sprite Bounds check for the sprite drag + */ + function () { + if(this._sprite.x < this.boundsSprite.x) { + this._sprite.x = this.boundsSprite.x; + } else if((this._sprite.x + this._sprite.width) > (this.boundsSprite.x + this.boundsSprite.width)) { + this._sprite.x = (this.boundsSprite.x + this.boundsSprite.width) - this._sprite.width; + } + if(this._sprite.y < this.boundsSprite.y) { + this._sprite.y = this.boundsSprite.y; + } else if((this._sprite.y + this._sprite.height) > (this.boundsSprite.y + this.boundsSprite.height)) { + this._sprite.y = (this.boundsSprite.y + this.boundsSprite.height) - this._sprite.height; + } + }; + Input.prototype.stopDrag = /** + * Called by FlxMouseControl when Mouse Drag is stopped on this Sprite. Should not usually be called directly. + */ + function () { + this.isDragged = false; + if(this.snapOnRelease) { + this._sprite.x = Math.floor(this._sprite.x / this.snapX) * this.snapX; + this._sprite.y = Math.floor(this._sprite.y / this.snapY) * this.snapY; + } + }; Input.prototype.renderDebugInfo = /** * Render debug infos. (including name, bounds info, position and some other properties) * @param x {number} X position of the debug info to be rendered. @@ -5593,7 +5892,7 @@ var Phaser; if (typeof color === "undefined") { color = 'rgb(255,255,255)'; } this._sprite.texture.context.font = '14px Courier'; this._sprite.texture.context.fillStyle = color; - this._sprite.texture.context.fillText('Input: (' + this._sprite.frameBounds.width + ' x ' + this._sprite.frameBounds.height + ')', x, y); + this._sprite.texture.context.fillText('Sprite Input: (' + this._sprite.frameBounds.width + ' x ' + this._sprite.frameBounds.height + ')', x, y); this._sprite.texture.context.fillText('x: ' + this.x.toFixed(1) + ' y: ' + this.y.toFixed(1), x, y + 14); this._sprite.texture.context.fillText('over: ' + this.isOver + ' duration: ' + this.duration.toFixed(0), x, y + 28); this._sprite.texture.context.fillText('just over: ' + this.justOver() + ' just out: ' + this.justOut(), x, y + 42); @@ -5614,9 +5913,17 @@ var Phaser; */ (function (Components) { var Events = (function () { + /** + * The Events component is a collection of events fired by the parent Sprite and its other components. + * @param parent The Sprite using this Input component + */ function Events(parent) { this.game = parent.game; this._sprite = parent; + this.onAddedToGroup = new Phaser.Signal(); + this.onRemovedFromGroup = new Phaser.Signal(); + this.onKilled = new Phaser.Signal(); + this.onRevived = new Phaser.Signal(); this.onInputOver = new Phaser.Signal(); this.onInputOut = new Phaser.Signal(); this.onInputDown = new Phaser.Signal(); @@ -6181,8 +6488,8 @@ var Phaser; this.scrollFactor = new Phaser.Vec2(1, 1); this.x = x; this.y = y; - this.z = 0// not used yet - ; + this.z = -1; + this.group = null; // If a texture has been given the body will be set to that size, otherwise 16x16 this.body = new Phaser.Physics.Body(this, bodyType); this.animations = new Phaser.Components.AnimationManager(this); @@ -6334,9 +6641,14 @@ var Phaser; * like to animate an effect or whatever, you should override this, * setting only alive to false, and leaving exists true. */ - function () { + function (removeFromGroup) { + if (typeof removeFromGroup === "undefined") { removeFromGroup = false; } this.alive = false; this.exists = false; + if(removeFromGroup && this.group) { + this.group.remove(this); + } + this.events.onKilled.dispatch(this); }; Sprite.prototype.revive = /** * Handy for bringing game objects "back to life". Just sets alive and exists back to true. @@ -6345,6 +6657,7 @@ var Phaser; function () { this.alive = true; this.exists = true; + this.events.onRevived.dispatch(this); }; return Sprite; })(); @@ -6595,7 +6908,7 @@ var Phaser; this._stageX = x; this._stageY = y; this.scaledX = x; - this.scaledY = x; + this.scaledY = y; this.fx = new Phaser.CameraFX(this._game, this); // The view into the world canvas we wish to render this.worldView = new Phaser.Rectangle(0, 0, width, height); @@ -6622,10 +6935,7 @@ var Phaser; * @param object {Sprite/Group} The object to check. */ function (object) { - if(object['cameraBlacklist'] && object['cameraBlacklist'].length > 0 && object['cameraBlacklist'].indexOf(this.ID) == -1) { - return true; - } - return false; + return (object['cameraBlacklist'] && object['cameraBlacklist'].length > 0 && object['cameraBlacklist'].indexOf(this.ID) == -1); }; Camera.prototype.show = /** * Un-hides an object previously hidden to this Camera. @@ -8270,7 +8580,7 @@ var Phaser; if (typeof speedY === "undefined") { speedY = 0; } if(x > this.width || y > this.height || x < 0 || y < 0 || (x + width) > this.width || (y + height) > this.height) { throw Error('Invalid ScrollRegion defined. Cannot be larger than parent ScrollZone'); - return; + return null; } this.currentRegion = new Phaser.ScrollRegion(x, y, width, height, speedX, speedY); this.regions.push(this.currentRegion); @@ -8995,7 +9305,7 @@ var Phaser; this.generateTiles(tileQuantity); }; Tilemap.prototype.parseTiledJSON = /** - * Parset JSON map data and generate tiles. + * Parse JSON map data and generate tiles. * @param data {string} JSON map data. * @param key {string} Asset key for tileset image. */ @@ -9132,14 +9442,19 @@ var Phaser; if (typeof layer === "undefined") { layer = 0; } return this.tiles[this.layers[layer].getTileFromWorldXY(x, y)]; }; - Tilemap.prototype.getTileFromInputXY = function (layer) { + Tilemap.prototype.getTileFromInputXY = /** + * Gets the tile underneath the Input.x/y position + * @param layer The layer to check, defaults to 0 + * @returns {Tile} + */ + function (layer) { if (typeof layer === "undefined") { layer = 0; } return this.tiles[this.layers[layer].getTileFromWorldXY(this.game.input.getWorldX(), this.game.input.getWorldY())]; }; Tilemap.prototype.getTileOverlaps = /** * Get tiles overlaps the given object. * @param object {GameObject} Tiles you want to get that overlaps this. - * @return {array} Array with tiles informations. (Each contains x, y and the tile.) + * @return {array} Array with tiles information. (Each contains x, y and the tile.) */ function (object) { return this.currentLayer.getTileOverlaps(object); @@ -10156,8 +10471,6 @@ var Phaser; this.aspectRatio = width / height; this.scaleMode = Phaser.StageScaleMode.NO_SCALE; this.scale = new Phaser.StageScaleMode(this._game); - this._bootScreen = new Phaser.BootScreen(this._game); - this._pauseScreen = new Phaser.PauseScreen(this._game, width, height); document.addEventListener('visibilitychange', function (event) { return _this.visibilityChange(event); }, false); @@ -10171,6 +10484,13 @@ var Phaser; return _this.visibilityChange(event); }; } + Stage.prototype.boot = /** + * Stage boot + */ + function () { + this._bootScreen = new Phaser.BootScreen(this._game); + this._pauseScreen = new Phaser.PauseScreen(this._game, this.width, this.height); + }; Stage.prototype.update = /** * Update stage for rendering. This will handle scaling, clearing * and PauseScreen/BootScreen updating and rendering. @@ -11678,12 +11998,25 @@ var Phaser; * @param height {number} Height of the world bound. */ function World(game, width, height) { + /** + * Object container stores every object created with `create*` methods. + * @type {Group} + */ + this._groupCounter = 0; this._game = game; this.cameras = new Phaser.CameraManager(this._game, 0, 0, width, height); - this.group = new Phaser.Group(this._game, 0); this.bounds = new Phaser.Rectangle(0, 0, width, height); - this.physics = new Phaser.Physics.PhysicsManager(this._game, width, height); } + World.prototype.getNextGroupID = function () { + return this._groupCounter++; + }; + World.prototype.boot = /** + * Called one by Game during the boot process. + */ + function () { + this.group = new Phaser.Group(this._game, 0); + this.physics = new Phaser.Physics.PhysicsManager(this._game, this.width, this.height); + }; World.prototype.update = /** * This is called automatically every frame, and is where main logic happens. */ @@ -13846,6 +14179,12 @@ var Phaser; (function (Phaser) { var Input = (function () { function Input(game) { + /** + * A vector object representing the previous position of the Pointer. + * @property vector + * @type {Vec2} + **/ + this._oldPosition = null; /** * You can disable all Input by setting Input.disabled = true. While set all new input related events will be ignored. * If you need to disable just one type of input, for example mouse, use Input.mouse.disabled = true instead @@ -13863,6 +14202,13 @@ var Phaser; **/ this.position = null; /** + * A vector object representing the speed of the Pointer. Only really useful in single Pointer games, + * otherwise see the Pointer objects directly. + * @property vector + * @type {Vec2} + **/ + this.speed = null; + /** * A Circle object centered on the x/y screen coordinates of the Input. * Default size of 44px (Apples recommended "finger tip" size) but can be changed to anything * @property circle @@ -13984,6 +14330,7 @@ var Phaser; **/ this.pointer10 = null; this._game = game; + this._stack = []; this.mousePointer = new Phaser.Pointer(this._game, 0); this.pointer1 = new Phaser.Pointer(this._game, 1); this.pointer2 = new Phaser.Pointer(this._game, 2); @@ -13999,7 +14346,9 @@ var Phaser; this.onUp = new Phaser.Signal(); this.onTap = new Phaser.Signal(); this.onHold = new Phaser.Signal(); + this.speed = new Phaser.Vec2(); this.position = new Phaser.Vec2(); + this._oldPosition = new Phaser.Vec2(); this.circle = new Phaser.Circle(0, 0, 44); this.currentPointers = 0; } @@ -14067,7 +14416,7 @@ var Phaser; return this['pointer' + next]; } }; - Input.prototype.start = /** + Input.prototype.boot = /** * Starts the Input Manager running * @method start **/ @@ -14083,6 +14432,9 @@ var Phaser; * @method update **/ function () { + this.speed.x = this.position.x - this._oldPosition.x; + this.speed.y = this.position.y - this._oldPosition.y; + this._oldPosition.copyFrom(this.position); this.mousePointer.update(); this.pointer1.update(); this.pointer2.update(); @@ -14105,6 +14457,9 @@ var Phaser; this.pointer10.update(); } }; + Input.prototype.addToStack = function (item) { + this._stack.push(item); + }; Input.prototype.reset = /** * Reset all of the Pointers and Input states * @method reset @@ -14852,9 +15207,11 @@ var Phaser; (Date.now() * Math.random()).toString() ]); this.setRenderer(Phaser.Types.RENDERER_CANVAS); + this.world.boot(); + this.stage.boot(); + this.input.boot(); this.framerate = 60; this.isBooted = true; - this.input.start(); // Display the default game screen? if(this.onInitCallback == null && this.onCreateCallback == null && this.onUpdateCallback == null && this.onRenderCallback == null && this._pendingState == null) { this._raf = new Phaser.RequestAnimationFrame(this, this.bootLoop); @@ -14889,7 +15246,7 @@ var Phaser; this._loadComplete = true; }; Game.prototype.bootLoop = /** - * Game loop method will be called when it's booting. + * The bootLoop is called while the game is still booting (waiting for the DOM and resources to be available) */ function () { this.tweens.update(); @@ -14897,7 +15254,7 @@ var Phaser; this.stage.update(); }; Game.prototype.pausedLoop = /** - * Game loop method will be called when it's paused. + * The pausedLoop is called when the game is paused. */ function () { this.tweens.update(); @@ -15036,7 +15393,7 @@ var Phaser; } }; Game.prototype.destroy = /** - * Nuke the whole game from orbit + * Nuke the entire game from orbit */ function () { this.callbackContext = null; @@ -15574,11 +15931,10 @@ var Phaser; this.add = game.add; this.camera = game.camera; this.cache = game.cache; - //this.collision = game.collision; this.input = game.input; this.loader = game.loader; this.math = game.math; - //this.motion = game.motion; + this.motion = game.motion; this.sound = game.sound; this.stage = game.stage; this.time = game.time;