From c5f6817c4ca07bcc7e1d65bfcdb1b397b1c09833 Mon Sep 17 00:00:00 2001 From: Richard Davey Date: Tue, 11 Jun 2013 21:30:15 +0100 Subject: [PATCH] Working sprite bounds / vertices regardless of scale or rotation --- Phaser/components/Transform.ts | 207 +++++++++++++++--- Phaser/gameobjects/Sprite.ts | 8 +- Phaser/renderers/CanvasRenderer.ts | 4 +- Phaser/utils/SpriteUtils.ts | 132 ++++++++--- README.md | 1 + Tests/Tests.csproj | 12 + Tests/assets/tests/200x100corners.png | Bin 0 -> 357 bytes Tests/assets/tests/200x100corners2.png | Bin 0 -> 500 bytes Tests/assets/tests/320x200.png | Bin 0 -> 708 bytes Tests/assets/tests/320x200g.png | Bin 0 -> 713 bytes Tests/misc/point1.js | 97 +++++++++ Tests/misc/point1.ts | 123 +++++++++++ Tests/misc/point2.js | 56 +++++ Tests/misc/point2.ts | 76 +++++++ Tests/misc/point3.js | 57 +++++ Tests/misc/point3.ts | 77 +++++++ Tests/phaser.js | 290 ++++++++++++++++++++----- Tests/sprites/origin 5.js | 207 ++++++++++++++++-- Tests/sprites/origin 5.ts | 225 +++++++++++++++++-- build/phaser.d.ts | 59 ++++- build/phaser.js | 290 ++++++++++++++++++++----- 21 files changed, 1718 insertions(+), 203 deletions(-) create mode 100644 Tests/assets/tests/200x100corners.png create mode 100644 Tests/assets/tests/200x100corners2.png create mode 100644 Tests/assets/tests/320x200.png create mode 100644 Tests/assets/tests/320x200g.png create mode 100644 Tests/misc/point1.js create mode 100644 Tests/misc/point1.ts create mode 100644 Tests/misc/point2.js create mode 100644 Tests/misc/point2.ts create mode 100644 Tests/misc/point3.js create mode 100644 Tests/misc/point3.ts diff --git a/Phaser/components/Transform.ts b/Phaser/components/Transform.ts index 206a3fbe..bc9830da 100644 --- a/Phaser/components/Transform.ts +++ b/Phaser/components/Transform.ts @@ -27,44 +27,135 @@ module Phaser.Components { } - private _sin: number; - private _cos: number; + private _rotation: number; + + private _cachedSin: number; + private _cachedCos: number; + private _cachedRotation: number; + private _cachedScaleX: number; + private _cachedScaleY: number; + private _cachedAngle: number; + private _cachedAngleToCenter: number; + private _cachedDistance: number; + private _cachedWidth: number; + private _cachedHeight: number; + private _cachedHalfWidth: number; + private _cachedHalfHeight: number; + private _cachedCosAngle: number; + private _cachedSinAngle: number; + private _cachedOffsetX: number; + private _cachedOffsetY: number; + private _cachedOriginX: number; + private _cachedOriginY: number; + private _cachedCenterX: number; + private _cachedCenterY: number; public local: Mat3; - public update() { + public setCache() { - // Scale & Skew + this._cachedHalfWidth = this.parent.width / 2; + this._cachedHalfHeight = this.parent.height / 2; + this._cachedOffsetX = this.origin.x * this.parent.width; + this._cachedOffsetY = this.origin.y * this.parent.height; + this._cachedAngleToCenter = Math.atan2(this.halfHeight - this._cachedOffsetY, this.halfWidth - this._cachedOffsetX); + this._cachedDistance = Math.sqrt(((this._cachedOffsetX - this._cachedHalfWidth) * (this._cachedOffsetX - this._cachedHalfWidth)) + ((this._cachedOffsetY - this._cachedHalfHeight) * (this._cachedOffsetY - this._cachedHalfHeight))); + this._cachedWidth = this.parent.width; + this._cachedHeight = this.parent.height; + this._cachedOriginX = this.origin.x; + this._cachedOriginY = this.origin.y; + this._cachedSin = Math.sin((this.rotation + this.rotationOffset) * GameMath.DEG_TO_RAD); + this._cachedCos = Math.cos((this.rotation + this.rotationOffset) * GameMath.DEG_TO_RAD); + this._cachedCosAngle = Math.cos((this.rotation + this.rotationOffset) * GameMath.DEG_TO_RAD + this._cachedAngleToCenter); + this._cachedSinAngle = Math.sin((this.rotation + this.rotationOffset) * GameMath.DEG_TO_RAD + this._cachedAngleToCenter); + this._cachedRotation = this.rotation; - this._sin = 0; - this._cos = 1; - - if (this.parent.texture.renderRotation) + if (this.parent.texture && this.parent.texture.renderRotation) { - this._sin = GameMath.sinA[this.rotation + this.rotationOffset]; - this._cos = GameMath.cosA[this.rotation + this.rotationOffset]; - } - - if (this.parent.texture.flippedX) - { - this.local.data[0] = this._cos * -this.scale.x; - this.local.data[3] = (this._sin * -this.scale.x) + this.skew.x; + this._cachedSin = Math.sin((this.rotation + this.rotationOffset) * GameMath.DEG_TO_RAD); + this._cachedCos = Math.cos((this.rotation + this.rotationOffset) * GameMath.DEG_TO_RAD); } else { - this.local.data[0] = this._cos * this.scale.x; - this.local.data[3] = (this._sin * this.scale.x) + this.skew.x; + this._cachedSin = 0; + this._cachedCos = 1; + } + + } + + public update() { + + // Check cache + var dirty: bool = false; + + // 1) Height or Width change (also triggered by a change in scale) or an Origin change + if (this.parent.width !== this._cachedWidth || this.parent.height !== this._cachedHeight || this.origin.x !== this._cachedOriginX|| this.origin.y !== this._cachedOriginY) + { + this._cachedHalfWidth = this.parent.width / 2; + this._cachedHalfHeight = this.parent.height / 2; + this._cachedOffsetX = this.origin.x * this.parent.width; + this._cachedOffsetY = this.origin.y * this.parent.height; + this._cachedAngleToCenter = Math.atan2(this.halfHeight - this._cachedOffsetY, this.halfWidth - this._cachedOffsetX); + this._cachedDistance = Math.sqrt(((this._cachedOffsetX - this._cachedHalfWidth) * (this._cachedOffsetX - this._cachedHalfWidth)) + ((this._cachedOffsetY - this._cachedHalfHeight) * (this._cachedOffsetY - this._cachedHalfHeight))); + // Store + this._cachedWidth = this.parent.width; + this._cachedHeight = this.parent.height; + this._cachedOriginX = this.origin.x; + this._cachedOriginY = this.origin.y; + dirty = true; + } + + // 2) Rotation change + if (this.rotation != this._cachedRotation) + { + this._cachedSin = Math.sin((this.rotation + this.rotationOffset) * GameMath.DEG_TO_RAD); + this._cachedCos = Math.cos((this.rotation + this.rotationOffset) * GameMath.DEG_TO_RAD); + this._cachedCosAngle = Math.cos((this.rotation + this.rotationOffset) * GameMath.DEG_TO_RAD + this._cachedAngleToCenter); + this._cachedSinAngle = Math.sin((this.rotation + this.rotationOffset) * GameMath.DEG_TO_RAD + this._cachedAngleToCenter); + + if (this.parent.texture.renderRotation) + { + this._cachedSin = Math.sin((this.rotation + this.rotationOffset) * GameMath.DEG_TO_RAD); + this._cachedCos = Math.cos((this.rotation + this.rotationOffset) * GameMath.DEG_TO_RAD); + } + else + { + this._cachedSin = 0; + this._cachedCos = 1; + } + + // Store + this._cachedRotation = this.rotation; + dirty = true; + } + + if (dirty) + { + this._cachedCenterX = this.parent.x + this._cachedDistance * this._cachedCosAngle; + this._cachedCenterY = this.parent.y + this._cachedDistance * this._cachedSinAngle; + } + + // Scale and Skew + if (this.parent.texture.flippedX) + { + this.local.data[0] = this._cachedCos * -this.scale.x; + this.local.data[3] = (this._cachedSin * -this.scale.x) + this.skew.x; + } + else + { + this.local.data[0] = this._cachedCos * this.scale.x; + this.local.data[3] = (this._cachedSin * this.scale.x) + this.skew.x; } if (this.parent.texture.flippedY) { - this.local.data[4] = this._cos * -this.scale.y; - this.local.data[1] = -(this._sin * -this.scale.y) + this.skew.y; + this.local.data[4] = this._cachedCos * -this.scale.y; + this.local.data[1] = -(this._cachedSin * -this.scale.y) + this.skew.y; } else { - this.local.data[4] = this._cos * this.scale.y; - this.local.data[1] = -(this._sin * this.scale.y) + this.skew.y; + this.local.data[4] = this._cachedCos * this.scale.y; + this.local.data[1] = -(this._cachedSin * this.scale.y) + this.skew.y; } // Translate @@ -99,7 +190,7 @@ module Phaser.Components { public scrollFactor: Phaser.Vec2; /** - * The origin is the point around which scale and rotation takes place and defaults to the center of the sprite. + * The origin is the point around which scale and rotation takes place and defaults to the top-left of the sprite. */ public origin: Phaser.Vec2; @@ -117,17 +208,75 @@ module Phaser.Components { public rotation: number = 0; /** - * The center of the Sprite after taking scaling into consideration - */ - public get centerX(): number { - return this.parent.width / 2; + * The distance from the center of the transform to the rotation origin. + */ + public get distance(): number { + return this._cachedDistance; + //return Math.sqrt(((this.offsetX - this.halfWidth) * (this.offsetX - this.halfWidth)) + ((this.offsetY - this.halfHeight) * (this.offsetY - this.halfHeight))); } /** - * The center of the Sprite after taking scaling into consideration + * The angle between the center of the transform to the rotation origin. + */ + public get angleToCenter(): number { + return this._cachedAngleToCenter; + //return Math.atan2(this.halfHeight - this.offsetY, this.halfWidth - this.offsetX); + } + + /** + * The offset on the X axis of the origin + */ + public get offsetX(): number { + return this._cachedOffsetX; + //return this.origin.x * this.parent.width; + } + + /** + * The offset on the Y axis of the origin + */ + public get offsetY(): number { + return this._cachedOffsetY; + //return this.origin.y * this.parent.height; + } + + /** + * Half the width of the parent sprite, taking into consideration scaling + */ + public get halfWidth(): number { + return this._cachedHalfWidth; + //return this.parent.width / 2; + } + + /** + * Half the height of the parent sprite, taking into consideration scaling + */ + public get halfHeight(): number { + return this._cachedHalfHeight; + //return this.parent.height / 2; + } + + /** + * The center of the Sprite in world coordinates, after taking scaling and rotation into consideration + */ + public get centerX(): number { + return this._cachedCenterX; + //return this.parent.x + this.distance * Math.cos((this.rotation * Math.PI / 180) + this.angleToCenter); + } + + /** + * The center of the Sprite in world coordinates, after taking scaling and rotation into consideration */ public get centerY(): number { - return this.parent.height / 2; + return this._cachedCenterY; + //return this.parent.y + this.distance * Math.sin((this.rotation * Math.PI / 180) + this.angleToCenter); + } + + public get sin(): number { + return this._cachedSin; + } + + public get cos(): number { + return this._cachedCos; } } diff --git a/Phaser/gameobjects/Sprite.ts b/Phaser/gameobjects/Sprite.ts index 7cd30f57..74d2176d 100644 --- a/Phaser/gameobjects/Sprite.ts +++ b/Phaser/gameobjects/Sprite.ts @@ -72,6 +72,8 @@ module Phaser { this.worldView = new Rectangle(x, y, this.width, this.height); this.cameraView = new Rectangle(x, y, this.width, this.height); + this.transform.setCache(); + } /** @@ -243,8 +245,10 @@ module Phaser { this.transform.update(); - this.worldView.x = this.x * this.transform.scrollFactor.x; - this.worldView.y = this.y * this.transform.scrollFactor.y; + this.worldView.x = (this.x * this.transform.scrollFactor.x) - (this.width * this.transform.origin.x); + this.worldView.y = (this.y * this.transform.scrollFactor.y) - (this.height * this.transform.origin.y); + //this.worldView.x = this.x * this.transform.scrollFactor.x; + //this.worldView.y = this.y * this.transform.scrollFactor.y; this.worldView.width = this.width; this.worldView.height = this.height; diff --git a/Phaser/renderers/CanvasRenderer.ts b/Phaser/renderers/CanvasRenderer.ts index 8535068c..c3558af4 100644 --- a/Phaser/renderers/CanvasRenderer.ts +++ b/Phaser/renderers/CanvasRenderer.ts @@ -221,8 +221,6 @@ module Phaser { return true; } - Phaser.SpriteUtils.updateCameraView(camera, sprite); - return RectangleUtils.intersects(sprite.cameraView, camera.screenView); } @@ -447,7 +445,7 @@ module Phaser { if (sprite.transform.scale.x == 0 || sprite.transform.scale.y == 0 || sprite.texture.alpha < 0.1 || this.inCamera(camera, sprite) == false) { - //return false; + return false; } sprite.renderOrderID = this._count; diff --git a/Phaser/utils/SpriteUtils.ts b/Phaser/utils/SpriteUtils.ts index 29b844d4..1a3e4875 100644 --- a/Phaser/utils/SpriteUtils.ts +++ b/Phaser/utils/SpriteUtils.ts @@ -38,43 +38,39 @@ module Phaser { else { // If the sprite is rotated around its center we can use this quicker method: - - // Work out bounding box - SpriteUtils._sin = Phaser.GameMath.sinA[sprite.rotation]; - SpriteUtils._cos = Phaser.GameMath.cosA[sprite.rotation]; - - if (SpriteUtils._sin < 0) + if (sprite.transform.origin.x == 0.5 && sprite.transform.origin.y == 0.5) { - SpriteUtils._sin = -SpriteUtils._sin; - } + SpriteUtils._sin = Math.sin((sprite.rotation + sprite.transform.rotationOffset) * GameMath.DEG_TO_RAD); + SpriteUtils._cos = Math.cos((sprite.rotation + sprite.transform.rotationOffset) * GameMath.DEG_TO_RAD); - if (SpriteUtils._cos < 0) - { - SpriteUtils._cos = -SpriteUtils._cos; - } + if (SpriteUtils._sin < 0) + { + SpriteUtils._sin = -SpriteUtils._sin; + } - sprite.cameraView.width = Math.round(sprite.height * SpriteUtils._sin + sprite.width * SpriteUtils._cos); - sprite.cameraView.height = Math.round(sprite.height * SpriteUtils._cos + sprite.width * SpriteUtils._sin); + if (SpriteUtils._cos < 0) + { + SpriteUtils._cos = -SpriteUtils._cos; + } - // if origin isn't 0.5 we need to work out the difference to apply to the x/y - if (sprite.transform.origin.equals(0.5)) - { - // Easy out + sprite.cameraView.width = Math.round(sprite.height * SpriteUtils._sin + sprite.width * SpriteUtils._cos); + sprite.cameraView.height = Math.round(sprite.height * SpriteUtils._cos + sprite.width * SpriteUtils._sin); sprite.cameraView.x = Math.round(sprite.x - (camera.worldView.x * sprite.transform.scrollFactor.x) - (sprite.cameraView.width * sprite.transform.origin.x)); sprite.cameraView.y = Math.round(sprite.y - (camera.worldView.y * sprite.transform.scrollFactor.y) - (sprite.cameraView.height * sprite.transform.origin.y)); } else { - //var ax = sprite.cameraView.width * sprite.transform.origin.x; - //var ay = sprite.cameraView.height * sprite.transform.origin.y; - //var bx = sprite.cameraView.width * 0.5; - //var by = sprite.cameraView.height * 0.5; - //var c = sprite.game.math.distanceBetween(ax, ay, bx, by) / 2; - //console.log('actual x', ax, 'actual y', ay, 'cx', bx, 'cy', by); - sprite.cameraView.x = Math.round(sprite.x - (camera.worldView.x * sprite.transform.scrollFactor.x) - (sprite.cameraView.width * sprite.transform.origin.x)); - sprite.cameraView.y = Math.round(sprite.y - (camera.worldView.y * sprite.transform.scrollFactor.y) - (sprite.cameraView.height * sprite.transform.origin.y)); + + /* + // Useful to get the maximum AABB size of any given rect + + If you want a single box that covers all angles, just take the half-diagonal of your existing box as the radius of a circle. + The new box has to contain this circle, so it should be a square with side-length equal to twice the radius + (equiv. the diagonal of the original AABB) and with the same center as the original. + */ + } } @@ -91,6 +87,90 @@ module Phaser { } + static getCornersAsPoints(sprite: Sprite): Phaser.Point[] { + + var out: Phaser.Point[] = []; + + var sin = Math.sin((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + var cos = Math.cos((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + + // Upper Left + out.push(new Point(sprite.x + (sprite.width / 2) * cos - (sprite.height / 2) * sin, sprite.y + (sprite.height / 2) * cos + (sprite.width / 2) * sin)); + + // Upper Right + out.push(new Point(sprite.x - (sprite.width / 2) * cos - (sprite.height / 2) * sin, sprite.y + (sprite.height / 2) * cos - (sprite.width / 2) * sin)); + + // Bottom Left + out.push(new Point(sprite.x + (sprite.width / 2) * cos + (sprite.height / 2) * sin, sprite.y - (sprite.height / 2) * cos + (sprite.width / 2) * sin)); + + // Bottom Right + out.push(new Point(sprite.x - (sprite.width / 2) * cos + (sprite.height / 2) * sin, sprite.y - (sprite.height / 2) * cos - (sprite.width / 2) * sin)); + + return out; + + } + + static getCornersAsPoints2(sprite: Sprite): Phaser.Point[] { + + var out: Phaser.Point[] = []; + + var sin = Math.sin((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + var cos = Math.cos((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + + // This = the center point + var cx = sprite.x + (sprite.width / 2) * cos - (sprite.height / 2) * sin; + var cy = sprite.y + (sprite.height / 2) * cos + (sprite.width / 2) * sin; + + // Upper Left + out.push(new Point(cx + (sprite.width / 2) * cos - (sprite.height / 2) * sin, cy + (sprite.height / 2) * cos + (sprite.width / 2) * sin)); + + // Upper Right + out.push(new Point(cx - (sprite.width / 2) * cos - (sprite.height / 2) * sin, cy + (sprite.height / 2) * cos - (sprite.width / 2) * sin)); + + // Bottom Left + out.push(new Point(cx + (sprite.width / 2) * cos + (sprite.height / 2) * sin, cy - (sprite.height / 2) * cos + (sprite.width / 2) * sin)); + + // Bottom Right + out.push(new Point(cx - (sprite.width / 2) * cos + (sprite.height / 2) * sin, cy - (sprite.height / 2) * cos - (sprite.width / 2) * sin)); + + return out; + + } + + static getCornersAsPoints3(sprite: Sprite): Phaser.Point[] { + + var out: Phaser.Point[] = []; + + var sin: number = sprite.transform.sin; + var cos: number = sprite.transform.cos; + + //var sin = Math.sin((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + //var cos = Math.cos((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + + // This = the center point + //var cx = sprite.x + sprite.transform.distance * Math.cos((sprite.transform.rotation * Math.PI / 180) + sprite.transform.angleToCenter); + //var cy = sprite.y + sprite.transform.distance * Math.sin((sprite.transform.rotation * Math.PI / 180) + sprite.transform.angleToCenter); + + var cx: number = sprite.transform.centerX; + var cy: number = sprite.transform.centerY; + + // Upper Left + out.push(new Point(cx + sprite.transform.halfWidth * cos - sprite.transform.halfHeight * sin, cy + sprite.transform.halfHeight * cos + sprite.transform.halfWidth * sin)); + + // Upper Right + out.push(new Point(cx - sprite.transform.halfWidth * cos - sprite.transform.halfHeight * sin, cy + sprite.transform.halfHeight * cos - sprite.transform.halfWidth * sin)); + + // Bottom Left + out.push(new Point(cx + sprite.transform.halfWidth * cos + sprite.transform.halfHeight * sin, cy - sprite.transform.halfHeight * cos + sprite.transform.halfWidth * sin)); + + // Bottom Right + out.push(new Point(cx - sprite.transform.halfWidth * cos + sprite.transform.halfHeight * sin, cy - sprite.transform.halfHeight * cos - sprite.transform.halfWidth * sin)); + + return out; + + } + + static getAsPoints(sprite: Sprite): Phaser.Point[] { var out: Phaser.Point[] = []; diff --git a/README.md b/README.md index 8c7c8478..3abe1513 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ TODO: * Swap to using time based motion (like the tweens) rather than delta timer - it just doesn't work well on slow phones * Pointer.getWorldX(camera) needs to take camera scale into consideration * Add a 'click to bring to top' demo (+ Group feature?) +* If stage.clear set to false and game pauses, when it unpauses you still see the pause arrow - resolve this * Add clip support + shape options to Texture Component. diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj index e26a5666..405475d7 100644 --- a/Tests/Tests.csproj +++ b/Tests/Tests.csproj @@ -128,6 +128,18 @@ world drag.ts + + + point1.ts + + + + point2.ts + + + + point3.ts + sprite test 1.ts diff --git a/Tests/assets/tests/200x100corners.png b/Tests/assets/tests/200x100corners.png new file mode 100644 index 0000000000000000000000000000000000000000..6cb5cda733f1b8025c9078aed99880e2b0626278 GIT binary patch literal 357 zcmeAS@N?(olHy`uVBq!ia0vp^CxAGGgAGU?ZmZ`8Qj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS=07#P_-T^vIyZoRqc$kkxL<9g7g>c6)8!n~Fny&SP;7L@#d zIB`wKYi6s*b;e%mA0|Y$i$1u^bL4A-t3rc=gMxy900##P3lkG#V}k<&5EBw&0INh% zg(B3_Z}hz1A>&bphBHTttD4%4;|2%aPJJwJglR#t48U}fi7AzZCsS=07#OE{x;TbZ+Nw+`R>B`J2u+0{y&OYQYLq-`1taL2U5cX7CI;j zEIiQV(88q6B*?{dv+St82+JnZDT%`W0yaNCoUBoJqsI0CSCGK+v^5$WE@1TJ!DDXL zMh+;-f{H@qTr>(cIkYr^P#>?Wj?5yAmUv_qP2GGfpNx30R{c%64hK%;X<8jV?{C ztLj`L)GHWL-P`2gpr9Ziz`?-+!YoWojExPrg&-O25OBZH@_pUXO@geCxZpPEPj literal 0 HcmV?d00001 diff --git a/Tests/assets/tests/320x200.png b/Tests/assets/tests/320x200.png new file mode 100644 index 0000000000000000000000000000000000000000..b88b121387dd5cbe513590164f56662164f3ea70 GIT binary patch literal 708 zcmeAS@N?(olHy`uVBq!ia0y~yU~~YoPjIjS$(bw`tAUhciEBhjaDG}zd16s2gJVj5 zQmTSyZen_BP-c4PQb83-K!hs;muZ1%j>wi9-9slm}77-?o z4h1f`bq7Fl4Gi8t**Gj1!AuGXh;3jiDIzG+4Dl4iCkX#kvlU#sbcVsDc>8%yfdW=G w0fz>KPkYuK>YrXaXBlJJr?>UTqZ=3*+P7%7PL8zw3`~{`p00i_>zopr0Msyv@c;k- literal 0 HcmV?d00001 diff --git a/Tests/assets/tests/320x200g.png b/Tests/assets/tests/320x200g.png new file mode 100644 index 0000000000000000000000000000000000000000..dcdd505e8c035ccb0bc94c0e0e9b48d97fd088be GIT binary patch literal 713 zcmeAS@N?(olHy`uVBq!ia0y~yU~~YoPjIjS$(bw`tAUhciEBhjaDG}zd16s2gJVj5 zQmTSyZen_BP-6tPJ-s$jQN`LaZpN7ZUWE=)NlycC&;cT*SNj$HqgId9~P`$F8aYgJiqSonr%~ZxfoNd zlJ8XgVc`!?U|?hsaA06eiM#h7M1wiF3EVP7Xhu?k#3XDf9>pYSez1*U+S?mz86$l2 z=L;)5;Nk%WO?O8+$8(D{x~Z!f(~7?Cf2@3f;q+R~?t?cinSp7O!PC{xWt~$(6993U BkN5xp literal 0 HcmV?d00001 diff --git a/Tests/misc/point1.js b/Tests/misc/point1.js new file mode 100644 index 00000000..5e4d49de --- /dev/null +++ b/Tests/misc/point1.js @@ -0,0 +1,97 @@ +/// +(function () { + var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + function init() { + game.load.image('box2', 'assets/tests/320x200.png'); + game.load.image('box1', 'assets/sprites/oz_pov_melting_disk.png'); + game.load.image('box', 'assets/sprites/bunny.png'); + game.load.start(); + } + var sprite; + var rotate = false; + function create() { + game.stage.backgroundColor = 'rgb(0,0,0)'; + game.stage.disablePauseScreen = true; + sprite = game.add.sprite(game.stage.centerX, game.stage.centerY, 'box'); + //sprite.transform.scale.setTo(0.5, 0.5); + sprite.transform.origin.setTo(0.3, 0.3); + game.input.onTap.add(rotateIt, this); + game.add.tween(sprite.transform.scale).to({ + x: 0.5, + y: 0.5 + }, 2000, Phaser.Easing.Linear.None, true, 0, true); + points = [ + new Phaser.Point(), + new Phaser.Point(), + new Phaser.Point(), + new Phaser.Point() + ]; + } + function rotateIt() { + if(rotate == false) { + rotate = true; + } else { + rotate = false; + } + } + function update() { + if(rotate) { + sprite.rotation++; + } + } + var points; + function render() { + /* + points = Phaser.SpriteUtils.getCornersAsPoints3(sprite); + + game.stage.context.fillStyle = 'rgb(255,255,0)'; + game.stage.context.fillRect(points[0].x, points[0].y, 2, 2); + game.stage.context.fillRect(points[1].x, points[1].y, 2, 2); + game.stage.context.fillRect(points[2].x, points[2].y, 2, 2); + game.stage.context.fillRect(points[3].x, points[3].y, 2, 2); + */ + var originX = sprite.transform.origin.x * sprite.width; + var originY = sprite.transform.origin.y * sprite.height; + var centerX = 0.5 * sprite.width; + var centerY = 0.5 * sprite.height; + var distance = Math.sqrt(((originX - centerX) * (originX - centerX)) + ((originY - centerY) * (originY - centerY))); + var originAngle = Math.atan2(centerY - originY, centerX - originX); + var sin = Math.sin((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + var cos = Math.cos((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + //var px = sprite.x + distance * Math.cos((sprite.transform.rotation * Math.PI / 180) + originAngle); + //var py = sprite.y + distance * Math.sin((sprite.transform.rotation * Math.PI / 180) + originAngle); + // WORKS + //var px = sprite.x + sprite.transform.distance * Math.cos((sprite.transform.rotation * Math.PI / 180) + sprite.transform.angleToCenter); + //var py = sprite.y + sprite.transform.distance * Math.sin((sprite.transform.rotation * Math.PI / 180) + sprite.transform.angleToCenter); + // Upper Left + //points[0].setTo(px + (sprite.width / 2) * cos - (sprite.height / 2) * sin, py + (sprite.height / 2) * cos + (sprite.width / 2) * sin); + // Upper Right + //points[1].setTo(px - (sprite.width / 2) * cos - (sprite.height / 2) * sin, py + (sprite.height / 2) * cos - (sprite.width / 2) * sin); + // Bottom Left + //points[2].setTo(px + (sprite.width / 2) * cos + (sprite.height / 2) * sin, py - (sprite.height / 2) * cos + (sprite.width / 2) * sin); + // Bottom Right + //points[3].setTo(px - (sprite.width / 2) * cos + (sprite.height / 2) * sin, py - (sprite.height / 2) * cos - (sprite.width / 2) * sin); + points = Phaser.SpriteUtils.getCornersAsPoints3(sprite); + game.stage.context.save(); + game.stage.context.fillStyle = 'rgb(0,255,255)'; + //game.stage.context.fillRect(px, py, 2, 2); + game.stage.context.fillText('rect width: ' + originX + ' height: ' + originY, 32, 32); + game.stage.context.fillText('center x: ' + centerX + ' centerY: ' + centerY, 32, 52); + game.stage.context.fillText('angle: ' + sprite.rotation, 32, 72); + game.stage.context.fillText('point of rotation x: ' + sprite.transform.origin.x + ' y: ' + sprite.transform.origin.y, 32, 92); + game.stage.context.fillText('x: ' + sprite.x + ' y: ' + sprite.y, sprite.x + 4, sprite.y); + game.stage.context.fillRect(points[0].x, points[0].y, 2, 2); + game.stage.context.fillRect(points[1].x, points[1].y, 2, 2); + game.stage.context.fillRect(points[2].x, points[2].y, 2, 2); + game.stage.context.fillRect(points[3].x, points[3].y, 2, 2); + game.stage.context.restore(); + game.stage.context.save(); + game.stage.context.fillStyle = 'rgba(255,255,255,0.1)'; + game.stage.context.beginPath(); + game.stage.context.moveTo(sprite.x, sprite.y); + game.stage.context.arc(sprite.x, sprite.y, distance, 0, Math.PI * 2); + game.stage.context.closePath(); + game.stage.context.fill(); + game.stage.context.restore(); + } +})(); diff --git a/Tests/misc/point1.ts b/Tests/misc/point1.ts new file mode 100644 index 00000000..55bf59b9 --- /dev/null +++ b/Tests/misc/point1.ts @@ -0,0 +1,123 @@ +/// + +(function () { + + var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + + game.load.image('box2', 'assets/tests/320x200.png'); + game.load.image('box1', 'assets/sprites/oz_pov_melting_disk.png'); + game.load.image('box', 'assets/sprites/bunny.png'); + game.load.start(); + + } + + var sprite: Phaser.Sprite; + var rotate: bool = false; + + function create() { + + game.stage.backgroundColor = 'rgb(0,0,0)'; + game.stage.disablePauseScreen = true; + + sprite = game.add.sprite(game.stage.centerX, game.stage.centerY, 'box'); + + //sprite.transform.scale.setTo(0.5, 0.5); + + sprite.transform.origin.setTo(0.3, 0.3); + + game.input.onTap.add(rotateIt, this); + + game.add.tween(sprite.transform.scale).to({ x: 0.5, y: 0.5 }, 2000, Phaser.Easing.Linear.None, true, 0, true); + + points = [new Phaser.Point, new Phaser.Point, new Phaser.Point, new Phaser.Point]; + + } + + function rotateIt() { + if (rotate == false) { rotate = true; } else { rotate = false; } + } + + function update() { + + if (rotate) + { + sprite.rotation++; + } + + } + + var points: Phaser.Point[]; + + function render() { + + /* + points = Phaser.SpriteUtils.getCornersAsPoints3(sprite); + + game.stage.context.fillStyle = 'rgb(255,255,0)'; + game.stage.context.fillRect(points[0].x, points[0].y, 2, 2); + game.stage.context.fillRect(points[1].x, points[1].y, 2, 2); + game.stage.context.fillRect(points[2].x, points[2].y, 2, 2); + game.stage.context.fillRect(points[3].x, points[3].y, 2, 2); + */ + + var originX: number = sprite.transform.origin.x * sprite.width; + var originY: number = sprite.transform.origin.y * sprite.height; + var centerX: number = 0.5 * sprite.width; + var centerY: number = 0.5 * sprite.height; + var distance: number = Math.sqrt(((originX - centerX) * (originX - centerX)) + ((originY - centerY) * (originY - centerY))); + var originAngle: number = Math.atan2(centerY - originY, centerX - originX); + + var sin = Math.sin((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + var cos = Math.cos((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + + //var px = sprite.x + distance * Math.cos((sprite.transform.rotation * Math.PI / 180) + originAngle); + //var py = sprite.y + distance * Math.sin((sprite.transform.rotation * Math.PI / 180) + originAngle); + + // WORKS + //var px = sprite.x + sprite.transform.distance * Math.cos((sprite.transform.rotation * Math.PI / 180) + sprite.transform.angleToCenter); + //var py = sprite.y + sprite.transform.distance * Math.sin((sprite.transform.rotation * Math.PI / 180) + sprite.transform.angleToCenter); + + // Upper Left + //points[0].setTo(px + (sprite.width / 2) * cos - (sprite.height / 2) * sin, py + (sprite.height / 2) * cos + (sprite.width / 2) * sin); + + // Upper Right + //points[1].setTo(px - (sprite.width / 2) * cos - (sprite.height / 2) * sin, py + (sprite.height / 2) * cos - (sprite.width / 2) * sin); + + // Bottom Left + //points[2].setTo(px + (sprite.width / 2) * cos + (sprite.height / 2) * sin, py - (sprite.height / 2) * cos + (sprite.width / 2) * sin); + + // Bottom Right + //points[3].setTo(px - (sprite.width / 2) * cos + (sprite.height / 2) * sin, py - (sprite.height / 2) * cos - (sprite.width / 2) * sin); + + points = Phaser.SpriteUtils.getCornersAsPoints3(sprite); + + game.stage.context.save(); + game.stage.context.fillStyle = 'rgb(0,255,255)'; + //game.stage.context.fillRect(px, py, 2, 2); + game.stage.context.fillText('rect width: ' + originX + ' height: ' + originY, 32, 32); + game.stage.context.fillText('center x: ' + centerX + ' centerY: ' + centerY, 32, 52); + game.stage.context.fillText('angle: ' + sprite.rotation , 32, 72); + game.stage.context.fillText('point of rotation x: ' + sprite.transform.origin.x + ' y: ' + sprite.transform.origin.y, 32, 92); + game.stage.context.fillText('x: ' + sprite.x + ' y: ' + sprite.y, sprite.x + 4, sprite.y); + + game.stage.context.fillRect(points[0].x, points[0].y, 2, 2); + game.stage.context.fillRect(points[1].x, points[1].y, 2, 2); + game.stage.context.fillRect(points[2].x, points[2].y, 2, 2); + game.stage.context.fillRect(points[3].x, points[3].y, 2, 2); + + game.stage.context.restore(); + + game.stage.context.save(); + game.stage.context.fillStyle = 'rgba(255,255,255,0.1)'; + game.stage.context.beginPath(); + game.stage.context.moveTo(sprite.x, sprite.y); + game.stage.context.arc(sprite.x, sprite.y, distance, 0, Math.PI * 2); + game.stage.context.closePath(); + game.stage.context.fill(); + game.stage.context.restore(); + + } + +})(); diff --git a/Tests/misc/point2.js b/Tests/misc/point2.js new file mode 100644 index 00000000..2b16e544 --- /dev/null +++ b/Tests/misc/point2.js @@ -0,0 +1,56 @@ +/// +(function () { + var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + function init() { + game.load.image('box', 'assets/tests/320x200.png'); + game.load.start(); + } + var sprite; + var rotate = false; + function create() { + game.stage.backgroundColor = 'rgb(0,0,0)'; + game.stage.disablePauseScreen = true; + sprite = game.add.sprite(game.stage.centerX, game.stage.centerY, 'box'); + sprite.transform.origin.setTo(0, 0); + game.input.onTap.add(rotateIt, this); + } + function rotateIt() { + if(rotate == false) { + rotate = true; + } else { + rotate = false; + } + } + function update() { + if(rotate) { + sprite.rotation++; + } + } + function render() { + var originX = sprite.transform.origin.x * sprite.width; + var originY = sprite.transform.origin.y * sprite.height; + var centerX = 0.5 * sprite.width; + var centerY = 0.5 * sprite.height; + var distanceX = originX - centerX; + var distanceY = originY - centerY; + var distance = Math.sqrt(((originX - centerX) * (originX - centerX)) + ((originY - centerY) * (originY - centerY))); + var px = sprite.x + distance * Math.cos(sprite.transform.rotation + 45 * Math.PI / 180); + var py = sprite.y + distance * Math.sin(sprite.transform.rotation + 45 * Math.PI / 180); + game.stage.context.save(); + game.stage.context.fillStyle = 'rgb(255,255,0)'; + game.stage.context.fillText('rect width: ' + originX + ' height: ' + originY, 32, 32); + game.stage.context.fillText('center x: ' + centerX + ' centerY: ' + centerY, 32, 52); + game.stage.context.fillText('angle: ' + sprite.rotation, 32, 72); + game.stage.context.fillText('point of rotation x: ' + sprite.transform.origin.x + ' y: ' + sprite.transform.origin.y, 32, 92); + game.stage.context.fillText('x: ' + sprite.x + ' y: ' + sprite.y, sprite.x + 4, sprite.y); + game.stage.context.restore(); + game.stage.context.save(); + game.stage.context.fillStyle = 'rgba(255,255,255,0.1)'; + game.stage.context.beginPath(); + game.stage.context.moveTo(sprite.x, sprite.y); + game.stage.context.arc(sprite.x, sprite.y, distance, 0, Math.PI * 2); + game.stage.context.closePath(); + game.stage.context.fill(); + game.stage.context.restore(); + } +})(); diff --git a/Tests/misc/point2.ts b/Tests/misc/point2.ts new file mode 100644 index 00000000..ba3161e4 --- /dev/null +++ b/Tests/misc/point2.ts @@ -0,0 +1,76 @@ +/// + +(function () { + + var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + + game.load.image('box', 'assets/tests/320x200.png'); + game.load.start(); + + } + + var sprite: Phaser.Sprite; + var rotate: bool = false; + + function create() { + + game.stage.backgroundColor = 'rgb(0,0,0)'; + game.stage.disablePauseScreen = true; + + sprite = game.add.sprite(game.stage.centerX, game.stage.centerY, 'box'); + + sprite.transform.origin.setTo(0, 0); + + game.input.onTap.add(rotateIt, this); + + } + + function rotateIt() { + if (rotate == false) { rotate = true; } else { rotate = false; } + } + + function update() { + + if (rotate) + { + sprite.rotation++; + } + + } + + function render() { + + var originX: number = sprite.transform.origin.x * sprite.width; + var originY: number = sprite.transform.origin.y * sprite.height; + var centerX: number = 0.5 * sprite.width; + var centerY: number = 0.5 * sprite.height; + var distanceX: number = originX - centerX; + var distanceY: number = originY - centerY; + var distance: number = Math.sqrt(((originX - centerX) * (originX - centerX)) + ((originY - centerY) * (originY - centerY))); + + var px = sprite.x + distance * Math.cos(sprite.transform.rotation + 45 * Math.PI / 180); + var py = sprite.y + distance * Math.sin(sprite.transform.rotation + 45 * Math.PI / 180); + + game.stage.context.save(); + game.stage.context.fillStyle = 'rgb(255,255,0)'; + game.stage.context.fillText('rect width: ' + originX + ' height: ' + originY, 32, 32); + game.stage.context.fillText('center x: ' + centerX + ' centerY: ' + centerY, 32, 52); + game.stage.context.fillText('angle: ' + sprite.rotation , 32, 72); + game.stage.context.fillText('point of rotation x: ' + sprite.transform.origin.x + ' y: ' + sprite.transform.origin.y, 32, 92); + game.stage.context.fillText('x: ' + sprite.x + ' y: ' + sprite.y, sprite.x + 4, sprite.y); + game.stage.context.restore(); + + game.stage.context.save(); + game.stage.context.fillStyle = 'rgba(255,255,255,0.1)'; + game.stage.context.beginPath(); + game.stage.context.moveTo(sprite.x, sprite.y); + game.stage.context.arc(sprite.x, sprite.y, distance, 0, Math.PI * 2); + game.stage.context.closePath(); + game.stage.context.fill(); + game.stage.context.restore(); + + } + +})(); diff --git a/Tests/misc/point3.js b/Tests/misc/point3.js new file mode 100644 index 00000000..5a0ce273 --- /dev/null +++ b/Tests/misc/point3.js @@ -0,0 +1,57 @@ +/// +(function () { + var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + function init() { + game.load.image('box', 'assets/tests/320x200.png'); + game.load.start(); + } + var sprite; + var rotate = false; + function create() { + game.stage.backgroundColor = 'rgb(0,0,0)'; + game.stage.disablePauseScreen = true; + sprite = game.add.sprite(game.stage.centerX, game.stage.centerY, 'box'); + sprite.transform.origin.setTo(0.3, 0.8); + game.input.onTap.add(rotateIt, this); + } + function rotateIt() { + if(rotate == false) { + rotate = true; + } else { + rotate = false; + } + } + function update() { + if(rotate) { + sprite.rotation++; + } + } + function render() { + var originX = sprite.transform.origin.x * sprite.width; + var originY = sprite.transform.origin.y * sprite.height; + var centerX = 0.5 * sprite.width; + var centerY = 0.5 * sprite.height; + var distanceX = originX - centerX; + var distanceY = originY - centerY; + var distance = Math.sqrt(((originX - centerX) * (originX - centerX)) + ((originY - centerY) * (originY - centerY))); + var px = sprite.x + distance * Math.cos(sprite.transform.rotation + 45 * Math.PI / 180); + var py = sprite.y + distance * Math.sin(sprite.transform.rotation + 45 * Math.PI / 180); + game.stage.context.save(); + game.stage.context.fillStyle = 'rgb(255,255,0)'; + game.stage.context.fillText('rect width: ' + originX + ' height: ' + originY, 32, 32); + game.stage.context.fillText('center x: ' + centerX + ' centerY: ' + centerY, 32, 52); + game.stage.context.fillText('angle: ' + sprite.rotation, 32, 72); + game.stage.context.fillText('point of rotation x: ' + sprite.transform.origin.x + ' y: ' + sprite.transform.origin.y, 32, 92); + game.stage.context.fillText('sprite x: ' + sprite.x + ' sprite y: ' + sprite.y, 32, 112); + game.stage.context.fillRect(sprite.x, sprite.y, 2, 2); + game.stage.context.restore(); + game.stage.context.save(); + game.stage.context.fillStyle = 'rgba(255,255,255,0.1)'; + game.stage.context.beginPath(); + game.stage.context.moveTo(sprite.x, sprite.y); + game.stage.context.arc(sprite.x, sprite.y, distance, 0, Math.PI * 2); + game.stage.context.closePath(); + game.stage.context.fill(); + game.stage.context.restore(); + } +})(); diff --git a/Tests/misc/point3.ts b/Tests/misc/point3.ts new file mode 100644 index 00000000..388acec8 --- /dev/null +++ b/Tests/misc/point3.ts @@ -0,0 +1,77 @@ +/// + +(function () { + + var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); + + function init() { + + game.load.image('box', 'assets/tests/320x200.png'); + game.load.start(); + + } + + var sprite: Phaser.Sprite; + var rotate: bool = false; + + function create() { + + game.stage.backgroundColor = 'rgb(0,0,0)'; + game.stage.disablePauseScreen = true; + + sprite = game.add.sprite(game.stage.centerX, game.stage.centerY, 'box'); + + sprite.transform.origin.setTo(0.3, 0.8); + + game.input.onTap.add(rotateIt, this); + + } + + function rotateIt() { + if (rotate == false) { rotate = true; } else { rotate = false; } + } + + function update() { + + if (rotate) + { + sprite.rotation++; + } + + } + + function render() { + + var originX: number = sprite.transform.origin.x * sprite.width; + var originY: number = sprite.transform.origin.y * sprite.height; + var centerX: number = 0.5 * sprite.width; + var centerY: number = 0.5 * sprite.height; + var distanceX: number = originX - centerX; + var distanceY: number = originY - centerY; + var distance: number = Math.sqrt(((originX - centerX) * (originX - centerX)) + ((originY - centerY) * (originY - centerY))); + + var px = sprite.x + distance * Math.cos(sprite.transform.rotation + 45 * Math.PI / 180); + var py = sprite.y + distance * Math.sin(sprite.transform.rotation + 45 * Math.PI / 180); + + game.stage.context.save(); + game.stage.context.fillStyle = 'rgb(255,255,0)'; + game.stage.context.fillText('rect width: ' + originX + ' height: ' + originY, 32, 32); + game.stage.context.fillText('center x: ' + centerX + ' centerY: ' + centerY, 32, 52); + game.stage.context.fillText('angle: ' + sprite.rotation , 32, 72); + game.stage.context.fillText('point of rotation x: ' + sprite.transform.origin.x + ' y: ' + sprite.transform.origin.y, 32, 92); + game.stage.context.fillText('sprite x: ' + sprite.x + ' sprite y: ' + sprite.y, 32, 112); + game.stage.context.fillRect(sprite.x, sprite.y, 2, 2); + game.stage.context.restore(); + + game.stage.context.save(); + game.stage.context.fillStyle = 'rgba(255,255,255,0.1)'; + game.stage.context.beginPath(); + game.stage.context.moveTo(sprite.x, sprite.y); + game.stage.context.arc(sprite.x, sprite.y, distance, 0, Math.PI * 2); + game.stage.context.closePath(); + game.stage.context.fill(); + game.stage.context.restore(); + + } + +})(); diff --git a/Tests/phaser.js b/Tests/phaser.js index 3cd97fca..279c2dfc 100644 --- a/Tests/phaser.js +++ b/Tests/phaser.js @@ -3083,48 +3083,186 @@ var Phaser; this.scale = new Phaser.Vec2(1, 1); this.skew = new Phaser.Vec2(); } - Transform.prototype.update = function () { - // Scale & Skew - this._sin = 0; - this._cos = 1; - if(this.parent.texture.renderRotation) { - this._sin = Phaser.GameMath.sinA[this.rotation + this.rotationOffset]; - this._cos = Phaser.GameMath.cosA[this.rotation + this.rotationOffset]; - } - if(this.parent.texture.flippedX) { - this.local.data[0] = this._cos * -this.scale.x; - this.local.data[3] = (this._sin * -this.scale.x) + this.skew.x; + Transform.prototype.setCache = function () { + this._cachedHalfWidth = this.parent.width / 2; + this._cachedHalfHeight = this.parent.height / 2; + this._cachedOffsetX = this.origin.x * this.parent.width; + this._cachedOffsetY = this.origin.y * this.parent.height; + this._cachedAngleToCenter = Math.atan2(this.halfHeight - this._cachedOffsetY, this.halfWidth - this._cachedOffsetX); + this._cachedDistance = Math.sqrt(((this._cachedOffsetX - this._cachedHalfWidth) * (this._cachedOffsetX - this._cachedHalfWidth)) + ((this._cachedOffsetY - this._cachedHalfHeight) * (this._cachedOffsetY - this._cachedHalfHeight))); + this._cachedWidth = this.parent.width; + this._cachedHeight = this.parent.height; + this._cachedOriginX = this.origin.x; + this._cachedOriginY = this.origin.y; + this._cachedSin = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + this._cachedCos = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + this._cachedCosAngle = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._cachedAngleToCenter); + this._cachedSinAngle = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._cachedAngleToCenter); + this._cachedRotation = this.rotation; + if(this.parent.texture && this.parent.texture.renderRotation) { + this._cachedSin = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + this._cachedCos = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); } else { - this.local.data[0] = this._cos * this.scale.x; - this.local.data[3] = (this._sin * this.scale.x) + this.skew.x; + this._cachedSin = 0; + this._cachedCos = 1; + } + }; + Transform.prototype.update = function () { + // Check cache + var dirty = false; + // 1) Height or Width change (also triggered by a change in scale) or an Origin change + if(this.parent.width !== this._cachedWidth || this.parent.height !== this._cachedHeight || this.origin.x !== this._cachedOriginX || this.origin.y !== this._cachedOriginY) { + this._cachedHalfWidth = this.parent.width / 2; + this._cachedHalfHeight = this.parent.height / 2; + this._cachedOffsetX = this.origin.x * this.parent.width; + this._cachedOffsetY = this.origin.y * this.parent.height; + this._cachedAngleToCenter = Math.atan2(this.halfHeight - this._cachedOffsetY, this.halfWidth - this._cachedOffsetX); + this._cachedDistance = Math.sqrt(((this._cachedOffsetX - this._cachedHalfWidth) * (this._cachedOffsetX - this._cachedHalfWidth)) + ((this._cachedOffsetY - this._cachedHalfHeight) * (this._cachedOffsetY - this._cachedHalfHeight))); + // Store + this._cachedWidth = this.parent.width; + this._cachedHeight = this.parent.height; + this._cachedOriginX = this.origin.x; + this._cachedOriginY = this.origin.y; + dirty = true; + } + // 2) Rotation change + if(this.rotation != this._cachedRotation) { + this._cachedSin = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + this._cachedCos = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + this._cachedCosAngle = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._cachedAngleToCenter); + this._cachedSinAngle = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._cachedAngleToCenter); + if(this.parent.texture.renderRotation) { + this._cachedSin = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + this._cachedCos = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + } else { + this._cachedSin = 0; + this._cachedCos = 1; + } + // Store + this._cachedRotation = this.rotation; + dirty = true; + } + if(dirty) { + this._cachedCenterX = this.parent.x + this._cachedDistance * this._cachedCosAngle; + this._cachedCenterY = this.parent.y + this._cachedDistance * this._cachedSinAngle; + } + // Scale and Skew + if(this.parent.texture.flippedX) { + this.local.data[0] = this._cachedCos * -this.scale.x; + this.local.data[3] = (this._cachedSin * -this.scale.x) + this.skew.x; + } else { + this.local.data[0] = this._cachedCos * this.scale.x; + this.local.data[3] = (this._cachedSin * this.scale.x) + this.skew.x; } if(this.parent.texture.flippedY) { - this.local.data[4] = this._cos * -this.scale.y; - this.local.data[1] = -(this._sin * -this.scale.y) + this.skew.y; + this.local.data[4] = this._cachedCos * -this.scale.y; + this.local.data[1] = -(this._cachedSin * -this.scale.y) + this.skew.y; } else { - this.local.data[4] = this._cos * this.scale.y; - this.local.data[1] = -(this._sin * this.scale.y) + this.skew.y; + this.local.data[4] = this._cachedCos * this.scale.y; + this.local.data[1] = -(this._cachedSin * this.scale.y) + this.skew.y; } // Translate this.local.data[2] = this.parent.x; this.local.data[5] = this.parent.y; }; - Object.defineProperty(Transform.prototype, "centerX", { + Object.defineProperty(Transform.prototype, "distance", { get: /** - * The center of the Sprite after taking scaling into consideration + * The distance from the center of the transform to the rotation origin. */ function () { - return this.parent.width / 2; - }, + return this._cachedDistance; + //return Math.sqrt(((this.offsetX - this.halfWidth) * (this.offsetX - this.halfWidth)) + ((this.offsetY - this.halfHeight) * (this.offsetY - this.halfHeight))); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "angleToCenter", { + get: /** + * The angle between the center of the transform to the rotation origin. + */ + function () { + return this._cachedAngleToCenter; + //return Math.atan2(this.halfHeight - this.offsetY, this.halfWidth - this.offsetX); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "offsetX", { + get: /** + * The offset on the X axis of the origin + */ + function () { + return this._cachedOffsetX; + //return this.origin.x * this.parent.width; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "offsetY", { + get: /** + * The offset on the Y axis of the origin + */ + function () { + return this._cachedOffsetY; + //return this.origin.y * this.parent.height; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "halfWidth", { + get: /** + * Half the width of the parent sprite, taking into consideration scaling + */ + function () { + return this._cachedHalfWidth; + //return this.parent.width / 2; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "halfHeight", { + get: /** + * Half the height of the parent sprite, taking into consideration scaling + */ + function () { + return this._cachedHalfHeight; + //return this.parent.height / 2; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "centerX", { + get: /** + * The center of the Sprite in world coordinates, after taking scaling and rotation into consideration + */ + function () { + return this._cachedCenterX; + //return this.parent.x + this.distance * Math.cos((this.rotation * Math.PI / 180) + this.angleToCenter); + }, enumerable: true, configurable: true }); Object.defineProperty(Transform.prototype, "centerY", { get: /** - * The center of the Sprite after taking scaling into consideration + * The center of the Sprite in world coordinates, after taking scaling and rotation into consideration */ function () { - return this.parent.height / 2; + return this._cachedCenterY; + //return this.parent.y + this.distance * Math.sin((this.rotation * Math.PI / 180) + this.angleToCenter); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "sin", { + get: function () { + return this._cachedSin; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "cos", { + get: function () { + return this._cachedCos; }, enumerable: true, configurable: true @@ -4319,6 +4457,7 @@ var Phaser; this.body = new Phaser.Physics.Body(this, bodyType); this.worldView = new Phaser.Rectangle(x, y, this.width, this.height); this.cameraView = new Phaser.Rectangle(x, y, this.width, this.height); + this.transform.setCache(); } Object.defineProperty(Sprite.prototype, "rotation", { get: /** @@ -4394,8 +4533,10 @@ var Phaser; */ function () { this.transform.update(); - this.worldView.x = this.x * this.transform.scrollFactor.x; - this.worldView.y = this.y * this.transform.scrollFactor.y; + this.worldView.x = (this.x * this.transform.scrollFactor.x) - (this.width * this.transform.origin.x); + this.worldView.y = (this.y * this.transform.scrollFactor.y) - (this.height * this.transform.origin.y); + //this.worldView.x = this.x * this.transform.scrollFactor.x; + //this.worldView.y = this.y * this.transform.scrollFactor.y; this.worldView.width = this.width; this.worldView.height = this.height; if(this.modified == false && (!this.transform.scale.equals(1) || !this.transform.skew.equals(0) || this.transform.rotation != 0 || this.transform.rotationOffset != 0 || this.texture.flippedX || this.texture.flippedY)) { @@ -4514,32 +4655,28 @@ var Phaser; sprite.cameraView.height = sprite.height; } else { // If the sprite is rotated around its center we can use this quicker method: - // Work out bounding box - SpriteUtils._sin = Phaser.GameMath.sinA[sprite.rotation]; - SpriteUtils._cos = Phaser.GameMath.cosA[sprite.rotation]; - if(SpriteUtils._sin < 0) { - SpriteUtils._sin = -SpriteUtils._sin; - } - if(SpriteUtils._cos < 0) { - SpriteUtils._cos = -SpriteUtils._cos; - } - sprite.cameraView.width = Math.round(sprite.height * SpriteUtils._sin + sprite.width * SpriteUtils._cos); - sprite.cameraView.height = Math.round(sprite.height * SpriteUtils._cos + sprite.width * SpriteUtils._sin); - // if origin isn't 0.5 we need to work out the difference to apply to the x/y - if(sprite.transform.origin.equals(0.5)) { - // Easy out + if(sprite.transform.origin.x == 0.5 && sprite.transform.origin.y == 0.5) { + SpriteUtils._sin = Math.sin((sprite.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + SpriteUtils._cos = Math.cos((sprite.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + if(SpriteUtils._sin < 0) { + SpriteUtils._sin = -SpriteUtils._sin; + } + if(SpriteUtils._cos < 0) { + SpriteUtils._cos = -SpriteUtils._cos; + } + sprite.cameraView.width = Math.round(sprite.height * SpriteUtils._sin + sprite.width * SpriteUtils._cos); + sprite.cameraView.height = Math.round(sprite.height * SpriteUtils._cos + sprite.width * SpriteUtils._sin); sprite.cameraView.x = Math.round(sprite.x - (camera.worldView.x * sprite.transform.scrollFactor.x) - (sprite.cameraView.width * sprite.transform.origin.x)); sprite.cameraView.y = Math.round(sprite.y - (camera.worldView.y * sprite.transform.scrollFactor.y) - (sprite.cameraView.height * sprite.transform.origin.y)); } else { - //var ax = sprite.cameraView.width * sprite.transform.origin.x; - //var ay = sprite.cameraView.height * sprite.transform.origin.y; - //var bx = sprite.cameraView.width * 0.5; - //var by = sprite.cameraView.height * 0.5; - //var c = sprite.game.math.distanceBetween(ax, ay, bx, by) / 2; - //console.log('actual x', ax, 'actual y', ay, 'cx', bx, 'cy', by); - sprite.cameraView.x = Math.round(sprite.x - (camera.worldView.x * sprite.transform.scrollFactor.x) - (sprite.cameraView.width * sprite.transform.origin.x)); - sprite.cameraView.y = Math.round(sprite.y - (camera.worldView.y * sprite.transform.scrollFactor.y) - (sprite.cameraView.height * sprite.transform.origin.y)); - } + /* + // Useful to get the maximum AABB size of any given rect + + If you want a single box that covers all angles, just take the half-diagonal of your existing box as the radius of a circle. + The new box has to contain this circle, so it should be a square with side-length equal to twice the radius + (equiv. the diagonal of the original AABB) and with the same center as the original. + */ + } } if(sprite.animations.currentFrame !== null && sprite.animations.currentFrame.trimmed) { //sprite.cameraView.x += sprite.animations.currentFrame.spriteSourceSizeX; @@ -4549,6 +4686,58 @@ var Phaser; } return sprite.cameraView; }; + SpriteUtils.getCornersAsPoints = function getCornersAsPoints(sprite) { + var out = []; + var sin = Math.sin((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + var cos = Math.cos((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + // Upper Left + out.push(new Phaser.Point(sprite.x + (sprite.width / 2) * cos - (sprite.height / 2) * sin, sprite.y + (sprite.height / 2) * cos + (sprite.width / 2) * sin)); + // Upper Right + out.push(new Phaser.Point(sprite.x - (sprite.width / 2) * cos - (sprite.height / 2) * sin, sprite.y + (sprite.height / 2) * cos - (sprite.width / 2) * sin)); + // Bottom Left + out.push(new Phaser.Point(sprite.x + (sprite.width / 2) * cos + (sprite.height / 2) * sin, sprite.y - (sprite.height / 2) * cos + (sprite.width / 2) * sin)); + // Bottom Right + out.push(new Phaser.Point(sprite.x - (sprite.width / 2) * cos + (sprite.height / 2) * sin, sprite.y - (sprite.height / 2) * cos - (sprite.width / 2) * sin)); + return out; + }; + SpriteUtils.getCornersAsPoints2 = function getCornersAsPoints2(sprite) { + var out = []; + var sin = Math.sin((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + var cos = Math.cos((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + // This = the center point + var cx = sprite.x + (sprite.width / 2) * cos - (sprite.height / 2) * sin; + var cy = sprite.y + (sprite.height / 2) * cos + (sprite.width / 2) * sin; + // Upper Left + out.push(new Phaser.Point(cx + (sprite.width / 2) * cos - (sprite.height / 2) * sin, cy + (sprite.height / 2) * cos + (sprite.width / 2) * sin)); + // Upper Right + out.push(new Phaser.Point(cx - (sprite.width / 2) * cos - (sprite.height / 2) * sin, cy + (sprite.height / 2) * cos - (sprite.width / 2) * sin)); + // Bottom Left + out.push(new Phaser.Point(cx + (sprite.width / 2) * cos + (sprite.height / 2) * sin, cy - (sprite.height / 2) * cos + (sprite.width / 2) * sin)); + // Bottom Right + out.push(new Phaser.Point(cx - (sprite.width / 2) * cos + (sprite.height / 2) * sin, cy - (sprite.height / 2) * cos - (sprite.width / 2) * sin)); + return out; + }; + SpriteUtils.getCornersAsPoints3 = function getCornersAsPoints3(sprite) { + var out = []; + var sin = sprite.transform.sin; + var cos = sprite.transform.cos; + //var sin = Math.sin((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + //var cos = Math.cos((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + // This = the center point + //var cx = sprite.x + sprite.transform.distance * Math.cos((sprite.transform.rotation * Math.PI / 180) + sprite.transform.angleToCenter); + //var cy = sprite.y + sprite.transform.distance * Math.sin((sprite.transform.rotation * Math.PI / 180) + sprite.transform.angleToCenter); + var cx = sprite.transform.centerX; + var cy = sprite.transform.centerY; + // Upper Left + out.push(new Phaser.Point(cx + sprite.transform.halfWidth * cos - sprite.transform.halfHeight * sin, cy + sprite.transform.halfHeight * cos + sprite.transform.halfWidth * sin)); + // Upper Right + out.push(new Phaser.Point(cx - sprite.transform.halfWidth * cos - sprite.transform.halfHeight * sin, cy + sprite.transform.halfHeight * cos - sprite.transform.halfWidth * sin)); + // Bottom Left + out.push(new Phaser.Point(cx + sprite.transform.halfWidth * cos + sprite.transform.halfHeight * sin, cy - sprite.transform.halfHeight * cos + sprite.transform.halfWidth * sin)); + // Bottom Right + out.push(new Phaser.Point(cx - sprite.transform.halfWidth * cos + sprite.transform.halfHeight * sin, cy - sprite.transform.halfHeight * cos - sprite.transform.halfWidth * sin)); + return out; + }; SpriteUtils.getAsPoints = function getAsPoints(sprite) { var out = []; // top left @@ -16411,7 +16600,6 @@ var Phaser; if(sprite.transform.scrollFactor.equals(0)) { return true; } - Phaser.SpriteUtils.updateCameraView(camera, sprite); return Phaser.RectangleUtils.intersects(sprite.cameraView, camera.screenView); }; CanvasRenderer.prototype.inScreen = function (camera) { @@ -16579,8 +16767,8 @@ var Phaser; function (camera, sprite) { Phaser.SpriteUtils.updateCameraView(camera, sprite); if(sprite.transform.scale.x == 0 || sprite.transform.scale.y == 0 || sprite.texture.alpha < 0.1 || this.inCamera(camera, sprite) == false) { - //return false; - } + return false; + } sprite.renderOrderID = this._count; this._count++; // Reset our temp vars diff --git a/Tests/sprites/origin 5.js b/Tests/sprites/origin 5.js index cc88a6b4..e260ca76 100644 --- a/Tests/sprites/origin 5.js +++ b/Tests/sprites/origin 5.js @@ -3,17 +3,30 @@ var game = new Phaser.Game(this, 'game', 800, 600, init, create, update, render); function init() { // Using Phasers asset loader we load up a PNG from the assets folder - game.load.image('fuji', 'assets/pics/atari_fujilogo.png'); + game.load.image('disk', 'assets/sprites/oz_pov_melting_disk.png'); + game.load.image('fuji', 'assets/tests/200x100corners.png'); + game.load.image('fuji2', 'assets/tests/200x100corners2.png'); + game.load.image('fuji3', 'assets/tests/320x200.png'); + game.load.image('fuji4', 'assets/tests/320x200g.png'); game.load.start(); } var fuji; - var wn; - var hn; + var fuji2; + var fuji3; function create() { - game.stage.backgroundColor = 'rgb(0,0,100)'; - game.world.setSize(2000, 1200, true); + game.stage.backgroundColor = 'rgb(0,0,0)'; + //game.world.setSize(2000, 1200, true); // The sprite is 320 x 200 pixels in size positioned in the middle of the stage - fuji = game.add.sprite(game.stage.centerX, game.stage.centerY, 'fuji'); + //fuji = game.add.sprite(game.stage.centerX, game.stage.centerY, 'fuji4'); + fuji2 = game.add.sprite(game.stage.centerX, game.stage.centerY, 'fuji3'); + //fuji2 = game.add.sprite(game.stage.centerX, game.stage.centerY, 'fuji2'); + //fuji3 = game.add.sprite(game.stage.centerX, game.stage.centerY, 'fuji2'); + //fuji.visible = false; + //fuji2.visible = false; + //fuji.texture.alpha = 0.6; + //fuji2.texture.alpha = 0.6; + //fuji = game.add.sprite(0, 0, 'fuji'); + //fuji2 = game.add.sprite(0, 0, 'fuji'); //fuji.transform.scale.setTo(1.5, 1.5); //fuji.transform.scale.setTo(1.5, 1.5); //fuji.transform.skew.setTo(0.1, 0.1); @@ -24,13 +37,20 @@ //fuji.transform.scale.setTo(2, 2); // This sets the origin to the center //fuji.transform.origin.setTo(0.5, 0.5); + //fuji.transform.origin.setTo(0, 0); + fuji2.transform.origin.setTo(1, 1); + //fuji3.transform.origin.setTo(1, 1); game.input.onTap.add(rotateIt, this); - } + //game.stage.clear = false; + } function rotateIt() { - fuji.rotation += 20; - } + //fuji.rotation += 10; + fuji2.rotation += 10; + //fuji3.rotation += 20; + } function update() { - fuji.rotation += 1; + //fuji.rotation++; + //fuji2.rotation++; if(game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { game.camera.x -= 4; } else if(game.input.keyboard.isDown(Phaser.Keyboard.RIGHT)) { @@ -41,20 +61,169 @@ } else if(game.input.keyboard.isDown(Phaser.Keyboard.DOWN)) { game.camera.y += 4; } - //Phaser.SpriteUtils.updateCameraDisplay(game.camera, fuji); - } + } + var points; + var points2; function render() { + // This = the center point + //var cx = fuji2.x + (fuji2.width / 2) * cos - (fuji2.height / 2) * sin; + //var cy = fuji2.y + (fuji2.height / 2) * cos + (fuji2.width / 2) * sin; + // This gives me the top-left of an origin 1,1 + //var cx = fuji2.x + (-fuji2.width / fuji2.transform.origin.x) * cos - (-fuji2.height / fuji2.transform.origin.y) * sin; + //var cy = fuji2.y + (-fuji2.height / fuji2.transform.origin.y) * cos + (-fuji2.width / fuji2.transform.origin.x) * sin; + // This gives me the center point of an origin 1,1 + //var cx = fuji2.x + (-(fuji2.width * fuji2.transform.origin.x) / 2) * cos - (-(fuji2.height * fuji2.transform.origin.y) / 2) * sin; + //var cy = fuji2.y + (-(fuji2.height * fuji2.transform.origin.y) / 2) * cos + (-(fuji2.width * fuji2.transform.origin.x) / 2) * sin; + //var dx = 0.5 * fuji2.transform.origin.x; + //var dy = 0.5 * fuji2.transform.origin.y; + //dx = 1; + //dy = 1; + //console.log(fuji2.width * 0); + //var cx = fuji2.x + (-(fuji2.width * fuji2.transform.origin.x) / dx) * cos - (-(fuji2.height * fuji2.transform.origin.y) / dy) * sin; + //var cy = fuji2.y + (-(fuji2.height * fuji2.transform.origin.y) / dy) * cos + (-(fuji2.width * fuji2.transform.origin.x) / dx) * sin; + // This gives me the center point of an origin 0,0 + //var cx = fuji2.x + ((fuji2.width * fuji2.transform.origin.x) / 2) * cos - ((fuji2.height * fuji2.transform.origin.y) / 2) * sin; + //var cy = fuji2.y + ((fuji2.height * fuji2.transform.origin.y) / 2) * cos + ((fuji2.width * fuji2.transform.origin.x) / 2) * sin; + // center points + //game.stage.context.fillStyle = 'rgb(255,255,0)'; + //game.stage.context.fillRect(fuji.x, fuji.y, 4, 4); + //game.stage.context.fillRect(fuji2.x, fuji2.y, 4, 4); + //game.stage.context.fillRect(cx, cy, 4, 4); + var sin = Math.sin((fuji2.transform.rotation + fuji2.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + var cos = Math.cos((fuji2.transform.rotation + fuji2.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + var originX = fuji2.transform.origin.x * fuji2.width; + var originY = fuji2.transform.origin.y * fuji2.height; + var centerX = 0.5 * fuji2.width; + var centerY = 0.5 * fuji2.height; + var distanceX = originX - centerX; + var distanceY = originY - centerY; + var distance = Math.sqrt(((originX - centerX) * (originX - centerX)) + ((originY - centerY) * (originY - centerY))); + var px = fuji2.x + distance * Math.cos(fuji2.transform.rotation + 45 * Math.PI / 180); + var py = fuji2.y + distance * Math.sin(fuji2.transform.rotation + 45 * Math.PI / 180); + game.stage.context.save(); + game.stage.context.fillStyle = 'rgb(255,255,0)'; + game.stage.context.fillText('rect width: ' + originX + ' height: ' + originY, 32, 32); + game.stage.context.fillText('center x: ' + centerX + ' centerY: ' + centerY, 32, 52); + game.stage.context.fillText('angle: ' + fuji2.rotation, 32, 72); + game.stage.context.fillText('point of rotation x: ' + fuji2.transform.origin.x + ' y: ' + fuji2.transform.origin.y, 32, 92); + game.stage.context.fillText('x: ' + fuji2.x + ' y: ' + fuji2.y, fuji2.x + 4, fuji2.y); + game.stage.context.restore(); + game.stage.context.save(); + game.stage.context.fillStyle = 'rgba(255,255,255,0.1)'; + game.stage.context.arc(fuji2.x, fuji2.y, distance, 0, Math.PI * 2); + game.stage.context.fill(); + game.stage.context.restore(); + //points = Phaser.SpriteUtils.getCornersAsPoints(fuji); + //game.stage.context.fillStyle = 'rgb(255,255,255)'; + //game.stage.context.fillRect(points[0].x, points[0].y, 2, 2); + //game.stage.context.fillRect(points[1].x, points[1].y, 2, 2); + //game.stage.context.fillRect(points[2].x, points[2].y, 2, 2); + //game.stage.context.fillRect(points[3].x, points[3].y, 2, 2); + //points2 = Phaser.SpriteUtils.getCornersAsPoints2(fuji2); + //game.stage.context.fillStyle = 'rgb(255,0,0)'; + //game.stage.context.fillRect(points2[0].x, points2[0].y, 2, 2); + //game.stage.context.fillRect(points2[1].x, points2[1].y, 2, 2); + //game.stage.context.fillRect(points2[2].x, points2[2].y, 2, 2); + //game.stage.context.fillRect(points2[3].x, points2[3].y, 2, 2); + /* //game.stage.context.fillStyle = 'rgb(255,255,255)'; //game.stage.context.fillRect(fuji.x, fuji.y, 2, 2); - game.stage.context.fillStyle = 'rgb(255,0,0)'; - game.stage.context.fillRect(fuji.x, fuji.y, 2, 2); + + //var sin = Math.sin(game.math.degreesToRadians(fuji.rotation)); + //var cos = Math.cos(game.math.degreesToRadians(fuji.rotation)); + + //var fx = fuji.x + (fuji.width * fuji.transform.origin.x); + //var fy = fuji.y + (fuji.height * fuji.transform.origin.y); + + // center x/y + //var cx = fuji.width * 0.5; + //var cy = fuji.height * 0.5; + + //var ax = fuji.width * fuji.transform.origin.x; + //var ay = fuji.height * fuji.transform.origin.y; + + //var dx = cx - ax; + //var dy = cy - ay; + + //var fx = fuji.x - dx; + //var fy = fuji.y - dy; + + /* + var ox = fuji.x + (fuji.width * fuji.transform.origin.x); + var oy = fuji.y + (fuji.height * fuji.transform.origin.y); + var cx = fuji.x + (fuji.width * 0.5); + var cy = fuji.y + (fuji.height * 0.5); + + var dx = ox - cx; + var dy = oy - cy; + + game.stage.context.fillText('dx: ' + dx + ' dy: ' + dy, 300, 100); + + var fx = fuji.x + dx; + var fy = fuji.y + dy; + + //game.stage.context.fillStyle = 'rgb(255,0,255)'; + //game.stage.context.fillRect(cx, cy, 20, 20); + + //UL = x + ( Width / 2 ) * cos A - ( Height / 2 ) * sin A + //ul.x = fuji.x + (fuji.width / 2) * cos - (fuji.height / 2) * sin; + ul.x = fx + (fuji.width / 2) * cos - (fuji.height / 2) * sin; + + //UL = y + ( Height / 2 ) * cos A + ( Width / 2 ) * sin A + //ul.y = fuji.y + (fuji.height / 2) * cos + (fuji.width / 2) * sin; + ul.y = fy + (fuji.height / 2) * cos + (fuji.width / 2) * sin; + + //UR = x - ( Width / 2 ) * cos A - ( Height / 2 ) * sin A + //ur.x = fuji.x - (fuji.width / 2) * cos - (fuji.height / 2) * sin; + ur.x = fx - (fuji.width / 2) * cos - (fuji.height / 2) * sin; + + //UR = y + ( Height / 2 ) * cos A - ( Width / 2 ) * sin A + //ur.y = fuji.y + (fuji.height / 2) * cos - (fuji.width / 2) * sin; + ur.y = fy + (fuji.height / 2) * cos - (fuji.width / 2) * sin; + + //BL = x + ( Width / 2 ) * cos A + ( Height / 2 ) * sin A + //bl.x = fuji.x + (fuji.width / 2) * cos + (fuji.height / 2) * sin; + bl.x = fx + (fuji.width / 2) * cos + (fuji.height / 2) * sin; + + //BL = y - ( Height / 2 ) * cos A + ( Width / 2 ) * sin A + //bl.y = fuji.y - (fuji.height / 2) * cos + (fuji.width / 2) * sin; + bl.y = fy - (fuji.height / 2) * cos + (fuji.width / 2) * sin; + + //BR = x - ( Width / 2 ) * cos A + ( Height / 2 ) * sin A + //br.x = fuji.x - (fuji.width / 2) * cos + (fuji.height / 2) * sin; + br.x = fx - (fuji.width / 2) * cos + (fuji.height / 2) * sin; + + //BR = y - ( Height / 2 ) * cos A - ( Width / 2 ) * sin A + //br.y = fuji.y - (fuji.height / 2) * cos - (fuji.width / 2) * sin; + br.y = fy - (fuji.height / 2) * cos - (fuji.width / 2) * sin; + game.stage.context.fillStyle = 'rgb(255,255,0)'; - game.stage.context.fillRect(fuji.cameraView.x + fuji.cameraView.halfWidth, fuji.cameraView.y + fuji.cameraView.halfHeight, 2, 2); - game.stage.context.strokeStyle = 'rgb(255,255,0)'; - game.stage.context.strokeRect(fuji.cameraView.x, fuji.cameraView.y, fuji.cameraView.width, fuji.cameraView.height); + game.stage.context.fillRect(ul.x, ul.y, 2, 2); + game.stage.context.fillRect(ur.x, ur.y, 2, 2); + game.stage.context.fillRect(bl.x, bl.y, 2, 2); + game.stage.context.fillRect(br.x, br.y, 2, 2); + + + + //game.stage.context.fillRect(fuji.x - fuji.width / 2, fuji.y - fuji.width / 2, 2, 2); + + //game.stage.context.fillStyle = 'rgb(255,255,0)'; + //game.stage.context.fillRect(fuji2.x, fuji2.y, 2, 2); + + //game.stage.context.fillStyle = 'rgb(255,255,0)'; + //game.stage.context.fillRect(fuji.cameraView.x + fuji.cameraView.halfWidth, fuji.cameraView.y + fuji.cameraView.halfHeight, 2, 2); + + //game.stage.context.strokeStyle = 'rgb(255,255,0)'; + //game.stage.context.strokeRect(fuji.cameraView.x, fuji.cameraView.y, fuji.cameraView.width, fuji.cameraView.height); + //game.stage.context.strokeStyle = 'rgb(0,255,0)'; //game.stage.context.strokeRect(fuji.x, fuji.y, wn, hn); + //game.camera.renderDebugInfo(32, 32); - Phaser.DebugUtils.renderSpriteInfo(fuji, 32, 32); - } + + //Phaser.DebugUtils.renderSpriteInfo(fuji, 32, 32); + */ + //game.stage.context.strokeStyle = 'rgb(255,255,0)'; + //game.stage.context.strokeRect(fuji.cameraView.x, fuji.cameraView.y, fuji.cameraView.width, fuji.cameraView.height); + } })(); diff --git a/Tests/sprites/origin 5.ts b/Tests/sprites/origin 5.ts index 750f6ea0..3efd276b 100644 --- a/Tests/sprites/origin 5.ts +++ b/Tests/sprites/origin 5.ts @@ -7,23 +7,39 @@ function init() { // Using Phasers asset loader we load up a PNG from the assets folder - game.load.image('fuji', 'assets/pics/atari_fujilogo.png'); + game.load.image('disk', 'assets/sprites/oz_pov_melting_disk.png'); + game.load.image('fuji', 'assets/tests/200x100corners.png'); + game.load.image('fuji2', 'assets/tests/200x100corners2.png'); + game.load.image('fuji3', 'assets/tests/320x200.png'); + game.load.image('fuji4', 'assets/tests/320x200g.png'); game.load.start(); } var fuji: Phaser.Sprite; - var wn: number; - var hn: number; - + var fuji2: Phaser.Sprite; + var fuji3: Phaser.Sprite; function create() { - game.stage.backgroundColor = 'rgb(0,0,100)'; - game.world.setSize(2000, 1200, true); + game.stage.backgroundColor = 'rgb(0,0,0)'; + //game.world.setSize(2000, 1200, true); // The sprite is 320 x 200 pixels in size positioned in the middle of the stage - fuji = game.add.sprite(game.stage.centerX, game.stage.centerY, 'fuji'); + + //fuji = game.add.sprite(game.stage.centerX, game.stage.centerY, 'fuji4'); + fuji2 = game.add.sprite(game.stage.centerX, game.stage.centerY, 'fuji3'); + //fuji2 = game.add.sprite(game.stage.centerX, game.stage.centerY, 'fuji2'); + //fuji3 = game.add.sprite(game.stage.centerX, game.stage.centerY, 'fuji2'); + + //fuji.visible = false; + //fuji2.visible = false; + + //fuji.texture.alpha = 0.6; + //fuji2.texture.alpha = 0.6; + + //fuji = game.add.sprite(0, 0, 'fuji'); + //fuji2 = game.add.sprite(0, 0, 'fuji'); //fuji.transform.scale.setTo(1.5, 1.5); //fuji.transform.scale.setTo(1.5, 1.5); @@ -39,17 +55,27 @@ // This sets the origin to the center //fuji.transform.origin.setTo(0.5, 0.5); + //fuji.transform.origin.setTo(0, 0); + fuji2.transform.origin.setTo(1, 1); + + //fuji3.transform.origin.setTo(1, 1); + game.input.onTap.add(rotateIt, this); + //game.stage.clear = false; + } function rotateIt() { - fuji.rotation += 20; + //fuji.rotation += 10; + fuji2.rotation += 10; + //fuji3.rotation += 20; } function update() { - fuji.rotation += 1; + //fuji.rotation++; + //fuji2.rotation++; if (game.input.keyboard.isDown(Phaser.Keyboard.LEFT)) { @@ -69,30 +95,195 @@ game.camera.y += 4; } - //Phaser.SpriteUtils.updateCameraDisplay(game.camera, fuji); - } + var points: Phaser.Point[]; + var points2: Phaser.Point[]; + function render() { + // This = the center point + //var cx = fuji2.x + (fuji2.width / 2) * cos - (fuji2.height / 2) * sin; + //var cy = fuji2.y + (fuji2.height / 2) * cos + (fuji2.width / 2) * sin; + + // This gives me the top-left of an origin 1,1 + //var cx = fuji2.x + (-fuji2.width / fuji2.transform.origin.x) * cos - (-fuji2.height / fuji2.transform.origin.y) * sin; + //var cy = fuji2.y + (-fuji2.height / fuji2.transform.origin.y) * cos + (-fuji2.width / fuji2.transform.origin.x) * sin; + + // This gives me the center point of an origin 1,1 + //var cx = fuji2.x + (-(fuji2.width * fuji2.transform.origin.x) / 2) * cos - (-(fuji2.height * fuji2.transform.origin.y) / 2) * sin; + //var cy = fuji2.y + (-(fuji2.height * fuji2.transform.origin.y) / 2) * cos + (-(fuji2.width * fuji2.transform.origin.x) / 2) * sin; + + //var dx = 0.5 * fuji2.transform.origin.x; + //var dy = 0.5 * fuji2.transform.origin.y; + + //dx = 1; + //dy = 1; + + //console.log(fuji2.width * 0); + + //var cx = fuji2.x + (-(fuji2.width * fuji2.transform.origin.x) / dx) * cos - (-(fuji2.height * fuji2.transform.origin.y) / dy) * sin; + //var cy = fuji2.y + (-(fuji2.height * fuji2.transform.origin.y) / dy) * cos + (-(fuji2.width * fuji2.transform.origin.x) / dx) * sin; + + // This gives me the center point of an origin 0,0 + //var cx = fuji2.x + ((fuji2.width * fuji2.transform.origin.x) / 2) * cos - ((fuji2.height * fuji2.transform.origin.y) / 2) * sin; + //var cy = fuji2.y + ((fuji2.height * fuji2.transform.origin.y) / 2) * cos + ((fuji2.width * fuji2.transform.origin.x) / 2) * sin; + + // center points + //game.stage.context.fillStyle = 'rgb(255,255,0)'; + //game.stage.context.fillRect(fuji.x, fuji.y, 4, 4); + //game.stage.context.fillRect(fuji2.x, fuji2.y, 4, 4); + //game.stage.context.fillRect(cx, cy, 4, 4); + + + var sin = Math.sin((fuji2.transform.rotation + fuji2.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + var cos = Math.cos((fuji2.transform.rotation + fuji2.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + + var originX: number = fuji2.transform.origin.x * fuji2.width; + var originY: number = fuji2.transform.origin.y * fuji2.height; + var centerX: number = 0.5 * fuji2.width; + var centerY: number = 0.5 * fuji2.height; + var distanceX: number = originX - centerX; + var distanceY: number = originY - centerY; + var distance: number = Math.sqrt(((originX - centerX) * (originX - centerX)) + ((originY - centerY) * (originY - centerY))); + + var px = fuji2.x + distance * Math.cos(fuji2.transform.rotation + 45 * Math.PI / 180); + var py = fuji2.y + distance * Math.sin(fuji2.transform.rotation + 45 * Math.PI / 180); + + game.stage.context.save(); + game.stage.context.fillStyle = 'rgb(255,255,0)'; + game.stage.context.fillText('rect width: ' + originX + ' height: ' + originY, 32, 32); + game.stage.context.fillText('center x: ' + centerX + ' centerY: ' + centerY, 32, 52); + game.stage.context.fillText('angle: ' + fuji2.rotation , 32, 72); + game.stage.context.fillText('point of rotation x: ' + fuji2.transform.origin.x + ' y: ' + fuji2.transform.origin.y, 32, 92); + game.stage.context.fillText('x: ' + fuji2.x + ' y: ' + fuji2.y, fuji2.x + 4, fuji2.y); + game.stage.context.restore(); + + game.stage.context.save(); + game.stage.context.fillStyle = 'rgba(255,255,255,0.1)'; + game.stage.context.arc(fuji2.x, fuji2.y, distance, 0, Math.PI * 2); + game.stage.context.fill(); + game.stage.context.restore(); + + //points = Phaser.SpriteUtils.getCornersAsPoints(fuji); + + //game.stage.context.fillStyle = 'rgb(255,255,255)'; + //game.stage.context.fillRect(points[0].x, points[0].y, 2, 2); + //game.stage.context.fillRect(points[1].x, points[1].y, 2, 2); + //game.stage.context.fillRect(points[2].x, points[2].y, 2, 2); + //game.stage.context.fillRect(points[3].x, points[3].y, 2, 2); + + + //points2 = Phaser.SpriteUtils.getCornersAsPoints2(fuji2); + + //game.stage.context.fillStyle = 'rgb(255,0,0)'; + //game.stage.context.fillRect(points2[0].x, points2[0].y, 2, 2); + //game.stage.context.fillRect(points2[1].x, points2[1].y, 2, 2); + //game.stage.context.fillRect(points2[2].x, points2[2].y, 2, 2); + //game.stage.context.fillRect(points2[3].x, points2[3].y, 2, 2); + + + /* //game.stage.context.fillStyle = 'rgb(255,255,255)'; //game.stage.context.fillRect(fuji.x, fuji.y, 2, 2); - game.stage.context.fillStyle = 'rgb(255,0,0)'; - game.stage.context.fillRect(fuji.x, fuji.y, 2, 2); + //var sin = Math.sin(game.math.degreesToRadians(fuji.rotation)); + //var cos = Math.cos(game.math.degreesToRadians(fuji.rotation)); + + //var fx = fuji.x + (fuji.width * fuji.transform.origin.x); + //var fy = fuji.y + (fuji.height * fuji.transform.origin.y); + + // center x/y + //var cx = fuji.width * 0.5; + //var cy = fuji.height * 0.5; + + //var ax = fuji.width * fuji.transform.origin.x; + //var ay = fuji.height * fuji.transform.origin.y; + + //var dx = cx - ax; + //var dy = cy - ay; + + //var fx = fuji.x - dx; + //var fy = fuji.y - dy; + + /* + var ox = fuji.x + (fuji.width * fuji.transform.origin.x); + var oy = fuji.y + (fuji.height * fuji.transform.origin.y); + var cx = fuji.x + (fuji.width * 0.5); + var cy = fuji.y + (fuji.height * 0.5); + + var dx = ox - cx; + var dy = oy - cy; + + game.stage.context.fillText('dx: ' + dx + ' dy: ' + dy, 300, 100); + + var fx = fuji.x + dx; + var fy = fuji.y + dy; + + //game.stage.context.fillStyle = 'rgb(255,0,255)'; + //game.stage.context.fillRect(cx, cy, 20, 20); + + //UL = x + ( Width / 2 ) * cos A - ( Height / 2 ) * sin A + //ul.x = fuji.x + (fuji.width / 2) * cos - (fuji.height / 2) * sin; + ul.x = fx + (fuji.width / 2) * cos - (fuji.height / 2) * sin; + + //UL = y + ( Height / 2 ) * cos A + ( Width / 2 ) * sin A + //ul.y = fuji.y + (fuji.height / 2) * cos + (fuji.width / 2) * sin; + ul.y = fy + (fuji.height / 2) * cos + (fuji.width / 2) * sin; + + //UR = x - ( Width / 2 ) * cos A - ( Height / 2 ) * sin A + //ur.x = fuji.x - (fuji.width / 2) * cos - (fuji.height / 2) * sin; + ur.x = fx - (fuji.width / 2) * cos - (fuji.height / 2) * sin; + + //UR = y + ( Height / 2 ) * cos A - ( Width / 2 ) * sin A + //ur.y = fuji.y + (fuji.height / 2) * cos - (fuji.width / 2) * sin; + ur.y = fy + (fuji.height / 2) * cos - (fuji.width / 2) * sin; + + //BL = x + ( Width / 2 ) * cos A + ( Height / 2 ) * sin A + //bl.x = fuji.x + (fuji.width / 2) * cos + (fuji.height / 2) * sin; + bl.x = fx + (fuji.width / 2) * cos + (fuji.height / 2) * sin; + + //BL = y - ( Height / 2 ) * cos A + ( Width / 2 ) * sin A + //bl.y = fuji.y - (fuji.height / 2) * cos + (fuji.width / 2) * sin; + bl.y = fy - (fuji.height / 2) * cos + (fuji.width / 2) * sin; + + //BR = x - ( Width / 2 ) * cos A + ( Height / 2 ) * sin A + //br.x = fuji.x - (fuji.width / 2) * cos + (fuji.height / 2) * sin; + br.x = fx - (fuji.width / 2) * cos + (fuji.height / 2) * sin; + + //BR = y - ( Height / 2 ) * cos A - ( Width / 2 ) * sin A + //br.y = fuji.y - (fuji.height / 2) * cos - (fuji.width / 2) * sin; + br.y = fy - (fuji.height / 2) * cos - (fuji.width / 2) * sin; game.stage.context.fillStyle = 'rgb(255,255,0)'; - game.stage.context.fillRect(fuji.cameraView.x + fuji.cameraView.halfWidth, fuji.cameraView.y + fuji.cameraView.halfHeight, 2, 2); + game.stage.context.fillRect(ul.x, ul.y, 2, 2); + game.stage.context.fillRect(ur.x, ur.y, 2, 2); + game.stage.context.fillRect(bl.x, bl.y, 2, 2); + game.stage.context.fillRect(br.x, br.y, 2, 2); - game.stage.context.strokeStyle = 'rgb(255,255,0)'; - game.stage.context.strokeRect(fuji.cameraView.x, fuji.cameraView.y, fuji.cameraView.width, fuji.cameraView.height); + + + //game.stage.context.fillRect(fuji.x - fuji.width / 2, fuji.y - fuji.width / 2, 2, 2); + + //game.stage.context.fillStyle = 'rgb(255,255,0)'; + //game.stage.context.fillRect(fuji2.x, fuji2.y, 2, 2); + + //game.stage.context.fillStyle = 'rgb(255,255,0)'; + //game.stage.context.fillRect(fuji.cameraView.x + fuji.cameraView.halfWidth, fuji.cameraView.y + fuji.cameraView.halfHeight, 2, 2); + + //game.stage.context.strokeStyle = 'rgb(255,255,0)'; + //game.stage.context.strokeRect(fuji.cameraView.x, fuji.cameraView.y, fuji.cameraView.width, fuji.cameraView.height); //game.stage.context.strokeStyle = 'rgb(0,255,0)'; //game.stage.context.strokeRect(fuji.x, fuji.y, wn, hn); //game.camera.renderDebugInfo(32, 32); - Phaser.DebugUtils.renderSpriteInfo(fuji, 32, 32); + //Phaser.DebugUtils.renderSpriteInfo(fuji, 32, 32); + */ + + //game.stage.context.strokeStyle = 'rgb(255,255,0)'; + //game.stage.context.strokeRect(fuji.cameraView.x, fuji.cameraView.y, fuji.cameraView.width, fuji.cameraView.height); } diff --git a/build/phaser.d.ts b/build/phaser.d.ts index 9244f4d4..47d1ea01 100644 --- a/build/phaser.d.ts +++ b/build/phaser.d.ts @@ -1896,9 +1896,29 @@ module Phaser.Components { * @param parent The Sprite using this transform */ constructor(parent); - private _sin; - private _cos; + private _rotation; + private _cachedSin; + private _cachedCos; + private _cachedRotation; + private _cachedScaleX; + private _cachedScaleY; + private _cachedAngle; + private _cachedAngleToCenter; + private _cachedDistance; + private _cachedWidth; + private _cachedHeight; + private _cachedHalfWidth; + private _cachedHalfHeight; + private _cachedCosAngle; + private _cachedSinAngle; + private _cachedOffsetX; + private _cachedOffsetY; + private _cachedOriginX; + private _cachedOriginY; + private _cachedCenterX; + private _cachedCenterY; public local: Mat3; + public setCache(): void; public update(): void; /** * Reference to Phaser.Game @@ -1921,7 +1941,7 @@ module Phaser.Components { */ public scrollFactor: Vec2; /** - * The origin is the point around which scale and rotation takes place and defaults to the center of the sprite. + * The origin is the point around which scale and rotation takes place and defaults to the top-left of the sprite. */ public origin: Vec2; /** @@ -1936,13 +1956,39 @@ module Phaser.Components { */ public rotation: number; /** - * The center of the Sprite after taking scaling into consideration + * The distance from the center of the transform to the rotation origin. + */ + public distance : number; + /** + * The angle between the center of the transform to the rotation origin. + */ + public angleToCenter : number; + /** + * The offset on the X axis of the origin + */ + public offsetX : number; + /** + * The offset on the Y axis of the origin + */ + public offsetY : number; + /** + * Half the width of the parent sprite, taking into consideration scaling + */ + public halfWidth : number; + /** + * Half the height of the parent sprite, taking into consideration scaling + */ + public halfHeight : number; + /** + * The center of the Sprite in world coordinates, after taking scaling and rotation into consideration */ public centerX : number; /** - * The center of the Sprite after taking scaling into consideration + * The center of the Sprite in world coordinates, after taking scaling and rotation into consideration */ public centerY : number; + public sin : number; + public cos : number; } } /** @@ -2669,6 +2715,9 @@ module Phaser { * @return {Rectangle} A reference to the Sprite.cameraView property */ static updateCameraView(camera: Camera, sprite: Sprite): Rectangle; + static getCornersAsPoints(sprite: Sprite): Point[]; + static getCornersAsPoints2(sprite: Sprite): Point[]; + static getCornersAsPoints3(sprite: Sprite): Point[]; static getAsPoints(sprite: Sprite): Point[]; /** * Checks to see if a point in 2D world space overlaps this GameObject. diff --git a/build/phaser.js b/build/phaser.js index 3cd97fca..279c2dfc 100644 --- a/build/phaser.js +++ b/build/phaser.js @@ -3083,48 +3083,186 @@ var Phaser; this.scale = new Phaser.Vec2(1, 1); this.skew = new Phaser.Vec2(); } - Transform.prototype.update = function () { - // Scale & Skew - this._sin = 0; - this._cos = 1; - if(this.parent.texture.renderRotation) { - this._sin = Phaser.GameMath.sinA[this.rotation + this.rotationOffset]; - this._cos = Phaser.GameMath.cosA[this.rotation + this.rotationOffset]; - } - if(this.parent.texture.flippedX) { - this.local.data[0] = this._cos * -this.scale.x; - this.local.data[3] = (this._sin * -this.scale.x) + this.skew.x; + Transform.prototype.setCache = function () { + this._cachedHalfWidth = this.parent.width / 2; + this._cachedHalfHeight = this.parent.height / 2; + this._cachedOffsetX = this.origin.x * this.parent.width; + this._cachedOffsetY = this.origin.y * this.parent.height; + this._cachedAngleToCenter = Math.atan2(this.halfHeight - this._cachedOffsetY, this.halfWidth - this._cachedOffsetX); + this._cachedDistance = Math.sqrt(((this._cachedOffsetX - this._cachedHalfWidth) * (this._cachedOffsetX - this._cachedHalfWidth)) + ((this._cachedOffsetY - this._cachedHalfHeight) * (this._cachedOffsetY - this._cachedHalfHeight))); + this._cachedWidth = this.parent.width; + this._cachedHeight = this.parent.height; + this._cachedOriginX = this.origin.x; + this._cachedOriginY = this.origin.y; + this._cachedSin = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + this._cachedCos = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + this._cachedCosAngle = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._cachedAngleToCenter); + this._cachedSinAngle = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._cachedAngleToCenter); + this._cachedRotation = this.rotation; + if(this.parent.texture && this.parent.texture.renderRotation) { + this._cachedSin = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + this._cachedCos = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); } else { - this.local.data[0] = this._cos * this.scale.x; - this.local.data[3] = (this._sin * this.scale.x) + this.skew.x; + this._cachedSin = 0; + this._cachedCos = 1; + } + }; + Transform.prototype.update = function () { + // Check cache + var dirty = false; + // 1) Height or Width change (also triggered by a change in scale) or an Origin change + if(this.parent.width !== this._cachedWidth || this.parent.height !== this._cachedHeight || this.origin.x !== this._cachedOriginX || this.origin.y !== this._cachedOriginY) { + this._cachedHalfWidth = this.parent.width / 2; + this._cachedHalfHeight = this.parent.height / 2; + this._cachedOffsetX = this.origin.x * this.parent.width; + this._cachedOffsetY = this.origin.y * this.parent.height; + this._cachedAngleToCenter = Math.atan2(this.halfHeight - this._cachedOffsetY, this.halfWidth - this._cachedOffsetX); + this._cachedDistance = Math.sqrt(((this._cachedOffsetX - this._cachedHalfWidth) * (this._cachedOffsetX - this._cachedHalfWidth)) + ((this._cachedOffsetY - this._cachedHalfHeight) * (this._cachedOffsetY - this._cachedHalfHeight))); + // Store + this._cachedWidth = this.parent.width; + this._cachedHeight = this.parent.height; + this._cachedOriginX = this.origin.x; + this._cachedOriginY = this.origin.y; + dirty = true; + } + // 2) Rotation change + if(this.rotation != this._cachedRotation) { + this._cachedSin = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + this._cachedCos = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + this._cachedCosAngle = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._cachedAngleToCenter); + this._cachedSinAngle = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD + this._cachedAngleToCenter); + if(this.parent.texture.renderRotation) { + this._cachedSin = Math.sin((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + this._cachedCos = Math.cos((this.rotation + this.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + } else { + this._cachedSin = 0; + this._cachedCos = 1; + } + // Store + this._cachedRotation = this.rotation; + dirty = true; + } + if(dirty) { + this._cachedCenterX = this.parent.x + this._cachedDistance * this._cachedCosAngle; + this._cachedCenterY = this.parent.y + this._cachedDistance * this._cachedSinAngle; + } + // Scale and Skew + if(this.parent.texture.flippedX) { + this.local.data[0] = this._cachedCos * -this.scale.x; + this.local.data[3] = (this._cachedSin * -this.scale.x) + this.skew.x; + } else { + this.local.data[0] = this._cachedCos * this.scale.x; + this.local.data[3] = (this._cachedSin * this.scale.x) + this.skew.x; } if(this.parent.texture.flippedY) { - this.local.data[4] = this._cos * -this.scale.y; - this.local.data[1] = -(this._sin * -this.scale.y) + this.skew.y; + this.local.data[4] = this._cachedCos * -this.scale.y; + this.local.data[1] = -(this._cachedSin * -this.scale.y) + this.skew.y; } else { - this.local.data[4] = this._cos * this.scale.y; - this.local.data[1] = -(this._sin * this.scale.y) + this.skew.y; + this.local.data[4] = this._cachedCos * this.scale.y; + this.local.data[1] = -(this._cachedSin * this.scale.y) + this.skew.y; } // Translate this.local.data[2] = this.parent.x; this.local.data[5] = this.parent.y; }; - Object.defineProperty(Transform.prototype, "centerX", { + Object.defineProperty(Transform.prototype, "distance", { get: /** - * The center of the Sprite after taking scaling into consideration + * The distance from the center of the transform to the rotation origin. */ function () { - return this.parent.width / 2; - }, + return this._cachedDistance; + //return Math.sqrt(((this.offsetX - this.halfWidth) * (this.offsetX - this.halfWidth)) + ((this.offsetY - this.halfHeight) * (this.offsetY - this.halfHeight))); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "angleToCenter", { + get: /** + * The angle between the center of the transform to the rotation origin. + */ + function () { + return this._cachedAngleToCenter; + //return Math.atan2(this.halfHeight - this.offsetY, this.halfWidth - this.offsetX); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "offsetX", { + get: /** + * The offset on the X axis of the origin + */ + function () { + return this._cachedOffsetX; + //return this.origin.x * this.parent.width; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "offsetY", { + get: /** + * The offset on the Y axis of the origin + */ + function () { + return this._cachedOffsetY; + //return this.origin.y * this.parent.height; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "halfWidth", { + get: /** + * Half the width of the parent sprite, taking into consideration scaling + */ + function () { + return this._cachedHalfWidth; + //return this.parent.width / 2; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "halfHeight", { + get: /** + * Half the height of the parent sprite, taking into consideration scaling + */ + function () { + return this._cachedHalfHeight; + //return this.parent.height / 2; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "centerX", { + get: /** + * The center of the Sprite in world coordinates, after taking scaling and rotation into consideration + */ + function () { + return this._cachedCenterX; + //return this.parent.x + this.distance * Math.cos((this.rotation * Math.PI / 180) + this.angleToCenter); + }, enumerable: true, configurable: true }); Object.defineProperty(Transform.prototype, "centerY", { get: /** - * The center of the Sprite after taking scaling into consideration + * The center of the Sprite in world coordinates, after taking scaling and rotation into consideration */ function () { - return this.parent.height / 2; + return this._cachedCenterY; + //return this.parent.y + this.distance * Math.sin((this.rotation * Math.PI / 180) + this.angleToCenter); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "sin", { + get: function () { + return this._cachedSin; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Transform.prototype, "cos", { + get: function () { + return this._cachedCos; }, enumerable: true, configurable: true @@ -4319,6 +4457,7 @@ var Phaser; this.body = new Phaser.Physics.Body(this, bodyType); this.worldView = new Phaser.Rectangle(x, y, this.width, this.height); this.cameraView = new Phaser.Rectangle(x, y, this.width, this.height); + this.transform.setCache(); } Object.defineProperty(Sprite.prototype, "rotation", { get: /** @@ -4394,8 +4533,10 @@ var Phaser; */ function () { this.transform.update(); - this.worldView.x = this.x * this.transform.scrollFactor.x; - this.worldView.y = this.y * this.transform.scrollFactor.y; + this.worldView.x = (this.x * this.transform.scrollFactor.x) - (this.width * this.transform.origin.x); + this.worldView.y = (this.y * this.transform.scrollFactor.y) - (this.height * this.transform.origin.y); + //this.worldView.x = this.x * this.transform.scrollFactor.x; + //this.worldView.y = this.y * this.transform.scrollFactor.y; this.worldView.width = this.width; this.worldView.height = this.height; if(this.modified == false && (!this.transform.scale.equals(1) || !this.transform.skew.equals(0) || this.transform.rotation != 0 || this.transform.rotationOffset != 0 || this.texture.flippedX || this.texture.flippedY)) { @@ -4514,32 +4655,28 @@ var Phaser; sprite.cameraView.height = sprite.height; } else { // If the sprite is rotated around its center we can use this quicker method: - // Work out bounding box - SpriteUtils._sin = Phaser.GameMath.sinA[sprite.rotation]; - SpriteUtils._cos = Phaser.GameMath.cosA[sprite.rotation]; - if(SpriteUtils._sin < 0) { - SpriteUtils._sin = -SpriteUtils._sin; - } - if(SpriteUtils._cos < 0) { - SpriteUtils._cos = -SpriteUtils._cos; - } - sprite.cameraView.width = Math.round(sprite.height * SpriteUtils._sin + sprite.width * SpriteUtils._cos); - sprite.cameraView.height = Math.round(sprite.height * SpriteUtils._cos + sprite.width * SpriteUtils._sin); - // if origin isn't 0.5 we need to work out the difference to apply to the x/y - if(sprite.transform.origin.equals(0.5)) { - // Easy out + if(sprite.transform.origin.x == 0.5 && sprite.transform.origin.y == 0.5) { + SpriteUtils._sin = Math.sin((sprite.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + SpriteUtils._cos = Math.cos((sprite.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + if(SpriteUtils._sin < 0) { + SpriteUtils._sin = -SpriteUtils._sin; + } + if(SpriteUtils._cos < 0) { + SpriteUtils._cos = -SpriteUtils._cos; + } + sprite.cameraView.width = Math.round(sprite.height * SpriteUtils._sin + sprite.width * SpriteUtils._cos); + sprite.cameraView.height = Math.round(sprite.height * SpriteUtils._cos + sprite.width * SpriteUtils._sin); sprite.cameraView.x = Math.round(sprite.x - (camera.worldView.x * sprite.transform.scrollFactor.x) - (sprite.cameraView.width * sprite.transform.origin.x)); sprite.cameraView.y = Math.round(sprite.y - (camera.worldView.y * sprite.transform.scrollFactor.y) - (sprite.cameraView.height * sprite.transform.origin.y)); } else { - //var ax = sprite.cameraView.width * sprite.transform.origin.x; - //var ay = sprite.cameraView.height * sprite.transform.origin.y; - //var bx = sprite.cameraView.width * 0.5; - //var by = sprite.cameraView.height * 0.5; - //var c = sprite.game.math.distanceBetween(ax, ay, bx, by) / 2; - //console.log('actual x', ax, 'actual y', ay, 'cx', bx, 'cy', by); - sprite.cameraView.x = Math.round(sprite.x - (camera.worldView.x * sprite.transform.scrollFactor.x) - (sprite.cameraView.width * sprite.transform.origin.x)); - sprite.cameraView.y = Math.round(sprite.y - (camera.worldView.y * sprite.transform.scrollFactor.y) - (sprite.cameraView.height * sprite.transform.origin.y)); - } + /* + // Useful to get the maximum AABB size of any given rect + + If you want a single box that covers all angles, just take the half-diagonal of your existing box as the radius of a circle. + The new box has to contain this circle, so it should be a square with side-length equal to twice the radius + (equiv. the diagonal of the original AABB) and with the same center as the original. + */ + } } if(sprite.animations.currentFrame !== null && sprite.animations.currentFrame.trimmed) { //sprite.cameraView.x += sprite.animations.currentFrame.spriteSourceSizeX; @@ -4549,6 +4686,58 @@ var Phaser; } return sprite.cameraView; }; + SpriteUtils.getCornersAsPoints = function getCornersAsPoints(sprite) { + var out = []; + var sin = Math.sin((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + var cos = Math.cos((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + // Upper Left + out.push(new Phaser.Point(sprite.x + (sprite.width / 2) * cos - (sprite.height / 2) * sin, sprite.y + (sprite.height / 2) * cos + (sprite.width / 2) * sin)); + // Upper Right + out.push(new Phaser.Point(sprite.x - (sprite.width / 2) * cos - (sprite.height / 2) * sin, sprite.y + (sprite.height / 2) * cos - (sprite.width / 2) * sin)); + // Bottom Left + out.push(new Phaser.Point(sprite.x + (sprite.width / 2) * cos + (sprite.height / 2) * sin, sprite.y - (sprite.height / 2) * cos + (sprite.width / 2) * sin)); + // Bottom Right + out.push(new Phaser.Point(sprite.x - (sprite.width / 2) * cos + (sprite.height / 2) * sin, sprite.y - (sprite.height / 2) * cos - (sprite.width / 2) * sin)); + return out; + }; + SpriteUtils.getCornersAsPoints2 = function getCornersAsPoints2(sprite) { + var out = []; + var sin = Math.sin((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + var cos = Math.cos((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + // This = the center point + var cx = sprite.x + (sprite.width / 2) * cos - (sprite.height / 2) * sin; + var cy = sprite.y + (sprite.height / 2) * cos + (sprite.width / 2) * sin; + // Upper Left + out.push(new Phaser.Point(cx + (sprite.width / 2) * cos - (sprite.height / 2) * sin, cy + (sprite.height / 2) * cos + (sprite.width / 2) * sin)); + // Upper Right + out.push(new Phaser.Point(cx - (sprite.width / 2) * cos - (sprite.height / 2) * sin, cy + (sprite.height / 2) * cos - (sprite.width / 2) * sin)); + // Bottom Left + out.push(new Phaser.Point(cx + (sprite.width / 2) * cos + (sprite.height / 2) * sin, cy - (sprite.height / 2) * cos + (sprite.width / 2) * sin)); + // Bottom Right + out.push(new Phaser.Point(cx - (sprite.width / 2) * cos + (sprite.height / 2) * sin, cy - (sprite.height / 2) * cos - (sprite.width / 2) * sin)); + return out; + }; + SpriteUtils.getCornersAsPoints3 = function getCornersAsPoints3(sprite) { + var out = []; + var sin = sprite.transform.sin; + var cos = sprite.transform.cos; + //var sin = Math.sin((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + //var cos = Math.cos((sprite.transform.rotation + sprite.transform.rotationOffset) * Phaser.GameMath.DEG_TO_RAD); + // This = the center point + //var cx = sprite.x + sprite.transform.distance * Math.cos((sprite.transform.rotation * Math.PI / 180) + sprite.transform.angleToCenter); + //var cy = sprite.y + sprite.transform.distance * Math.sin((sprite.transform.rotation * Math.PI / 180) + sprite.transform.angleToCenter); + var cx = sprite.transform.centerX; + var cy = sprite.transform.centerY; + // Upper Left + out.push(new Phaser.Point(cx + sprite.transform.halfWidth * cos - sprite.transform.halfHeight * sin, cy + sprite.transform.halfHeight * cos + sprite.transform.halfWidth * sin)); + // Upper Right + out.push(new Phaser.Point(cx - sprite.transform.halfWidth * cos - sprite.transform.halfHeight * sin, cy + sprite.transform.halfHeight * cos - sprite.transform.halfWidth * sin)); + // Bottom Left + out.push(new Phaser.Point(cx + sprite.transform.halfWidth * cos + sprite.transform.halfHeight * sin, cy - sprite.transform.halfHeight * cos + sprite.transform.halfWidth * sin)); + // Bottom Right + out.push(new Phaser.Point(cx - sprite.transform.halfWidth * cos + sprite.transform.halfHeight * sin, cy - sprite.transform.halfHeight * cos - sprite.transform.halfWidth * sin)); + return out; + }; SpriteUtils.getAsPoints = function getAsPoints(sprite) { var out = []; // top left @@ -16411,7 +16600,6 @@ var Phaser; if(sprite.transform.scrollFactor.equals(0)) { return true; } - Phaser.SpriteUtils.updateCameraView(camera, sprite); return Phaser.RectangleUtils.intersects(sprite.cameraView, camera.screenView); }; CanvasRenderer.prototype.inScreen = function (camera) { @@ -16579,8 +16767,8 @@ var Phaser; function (camera, sprite) { Phaser.SpriteUtils.updateCameraView(camera, sprite); if(sprite.transform.scale.x == 0 || sprite.transform.scale.y == 0 || sprite.texture.alpha < 0.1 || this.inCamera(camera, sprite) == false) { - //return false; - } + return false; + } sprite.renderOrderID = this._count; this._count++; // Reset our temp vars