Working sprite bounds / vertices regardless of scale or rotation

This commit is contained in:
Richard Davey
2013-06-11 21:30:15 +01:00
parent 1f8c809f53
commit c5f6817c4c
21 changed files with 1718 additions and 203 deletions
+178 -29
View File
@@ -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;
}
}
+6 -2
View File
@@ -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;
+1 -3
View File
@@ -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;
+106 -26
View File
@@ -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[] = [];