Flex, creationComplete and Rotation - actionscript-3

Firstly I'm no expert with Flex so the following might be 101 gotchas.
My first problem is that I'm attempting to rotate a group of objects by 90 degrees that are contained within a canvas, each object is uniform in size containing text and an image. My first problem is that rotating the objects causes them to resize to the bounds of itself. For example if the object is 200 x 250 with an image stretching from 0 to 250 pixels, after rotation the contents would have resized to 200 pixels meaning the image is now 200 pixels. I can subsequently resize the object back up to the appropriate dimensions but feels awkward and clumsy. Is there a better way of performing this rotation where it rotates the object without the being restricted to the bounds of the object?
Secondly, I ideally need this code to run within the creationComplete event. When it does, the positioning / centering of the object is ignore and is different when rendered on the screen (for example, if I run the following code in my click event it rotates perfectly on its center point; through the creationComplete it offsets the object like the center point doesn't exist)?
Any pointers?
My rotation code...
<mx:Script>
<![CDATA[
// Capture the init for the application
private function InitialRotate( e:Event ):void{
var offsetWidth:Number = (this.width / 2);
var offsetHeight:Number = this.y + (this.height / 2);
// Rotates on the center (but resizes)
var tempMatrix:Matrix = this.transform.matrix;
tempMatrix.translate(-offsetWidth,-offsetHeight);
tempMatrix.rotate(90 * (Math.PI / 180));
tempMatrix.translate(+offsetWidth,+offsetHeight);
this.transform.matrix = tempMatrix;
// Adjusts the objects position and width to size appropriately
this.y -= 5.5;
this.width = 256;
}
]]>
</mx:Script>

I uses the rotate property of a spark rect class (recently.spark.primitives.supportClasses.GraphicElement.rotation) and it was as easy as
line.rotation=90;
Maybe this blog is helpful: http://blog.flexexamples.com/2007/09/14/rotating-images-using-the-matrix-class/

Related

setCenter() Method is not properly centering sprite texture on box2d fixture

The past few days I've been trying to figure out a display bug I don't understand. I've been working on a simple 2d platformer with box2d and orthogonal Tiled maps. So far so good, the physics work and using the b2d debug renderer I can assert proper player fixture and camera movement through the level.
Now next step I've tried to load textures to display sprites instead of debug shapes. This is where I stumble. I can load animations for my player body/fixture, but when I use the setCenter() method to center the texture on the fixture it is always out of center.
I've tried approaches via halving texture witdths and heights hoping to center the texture on the player fixture but I get the exact same off position rendering. I've played aorund with world/camera/screen unit coordinates but the misalignement persists.
I'm creating the player in my Player class with the following code.
First I define the player in box2d:
//define player's physical behaviour
public void definePlayer() {
//definitions to later use in a body
BodyDef bdef = new BodyDef();
bdef.position.set(120 / Constants.PPM, 60 / Constants.PPM);
bdef.type = BodyDef.BodyType.DynamicBody;
b2body = world.createBody(bdef);
//Define needed components of the player's main fixture
FixtureDef fdef = new FixtureDef();
PolygonShape shape = new PolygonShape();
shape.setAsBox(8 / Constants.PPM, 16 / Constants.PPM); //size of the player hitbox
//set the player's category bit
fdef.filter.categoryBits = Constants.PLAYER_BIT;
//set which category bits the player should collide with. If not mentioned here, no collision occurrs
fdef.filter.maskBits = Constants.GROUND_BIT |
Constants.GEM_BIT |
Constants.BRICK_BIT |
Constants.OBJECT_BIT |
Constants.ENEMY_BIT |
Constants.TREASURE_CHEST_BIT |
Constants.ENEMY_HEAD_BIT |
Constants.ITEM_BIT;
fdef.shape = shape;
b2body.createFixture(fdef).setUserData(this);
}
Then I call the texture Region to be drawn in the Player class constructor:
//define in box2d
definePlayer();
//set initial values for the player's location, width and height, initial animation.
setBounds(0, 0, 64 / Constants.PPM, 64 / Constants.PPM);
setRegion(playerStand.getKeyFrame(stateTimer, true));
And finally, I update() my player:
public void update(float delta) {
//center position of the sprite on its body
// setPosition(b2body.getPosition().x - getWidth() / 2, b2body.getPosition().y - getHeight() / 2);
setCenter(b2body.getPosition().x, b2body.getPosition().y);
setRegion(getFrame(delta));
//set all the boolean flags during update cycles approprietly. DO NOT manipulate b2bodies
//while the simulation happens! therefore, only set flags there, and call the appropriate
//methods outside the simulation step during update
checkForPitfall();
checkIfAttacking();
}
And my result is
this, facing right
and this, facing left
Update:
I've been trying to just run
setCenter(b2body.getPosition().x, b2body.getPosition().y);
as suggested, and I got the following result:
facing right and facing left.
The sprite texture flip code is as follows:
if((b2body.getLinearVelocity().x < 0 || !runningRight) && !region.isFlipX()) {
region.flip(true, false);
runningRight = false;
} else if ((b2body.getLinearVelocity().x > 0 || runningRight) && region.isFlipX()) {
region.flip(true, false);
runningRight = true;
}
I'm testing if either the boolean flag for facing right is set or the x-axis velocity of my player b2body has a positive/negative value and if my texture region is already flipped or not and then use libGDX's flip() accordingly. I should not be messing with fixture coords anywhere here, hence my confusion.
The coordinates of box2d fixtures are offsets from the position, the position isn't necessarily the center (although it could be depending on your shape definition offsets). So in your case i think the position is actually the lower left point of the box2d polygon shape.
In which case you don't need to adjust for width and height because sprites are also drawn from bottom left position. So all you need is ;
setPosition(b2body.getPosition().x , b2body.getPosition().y );
I'm guessing you flip the box2d body when the player looks left the position of the shape is now bottom right so the sprite offset of width/2 and height/2 is from the bottom right instead. So specifically when you are looking left you need an offset of
setPosition(b2body.getPosition().x - getWidth() , b2body.getPosition().y );
I think looking right will be fixed from this, but i don't know for sure how you handle looking left in terms of what you do to the body, but something is done because the offset changes entirely as shown in your capture. If you aren't doing some flipping you could add how you handle looking right to the question.
EDIT
It seems the answer was that the sprite wasn't centered in the sprite sheet and this additional space around the sprite caused the visual impression of being in the wrong place (see comments).

Increase width without moving image

I'm developing a drawing app, and I ran into a problem, and a possible solution would be to be able to resize horizontally (increase or decrease the width) of an image without altering it's pivot, but ending with a "directional resize", it means, that if I start dragging the right resize anchor, the image starts increasing it's width to the right, instead of always taking into consideration the pivot.
So, what I'm doing now is to increase the width and at the same time I move the image width/2, it works, however when I have to rotate the image.. everything starts to get broken, and even if I set the pivot in the middle of the sprite, since the image (that is contained inside the sprite) x is altered, it doesn't matter.
I've read some about matrices, but I'm not sure if this is possible.
Thanks.
There a few ways you can accomplish this. The first is to use a transform matrix.
Here is a function I use to rotate an object around a point in that fashion:
*You'll need to import fl.motion.MatrixTransformer, if not using flashPro you can get it here: https://code.google.com/p/artitem-as3/source/browse/trunk/ArtItem/src/fl/motion/MatrixTransformer.as?r=3
/**
* Rotates a displayObject around the passed local point.
* #param displayObj
* #param relativeDegrees - the relative amount of degrees to rotate the object
* #param point - a local point to rotate the object around, if null, center of bounding box will be used.
*/
public static function rotateAroundPoint(displayObj:DisplayObject, relativeDegrees:Number, point:Point = null):void {
var m:Matrix = displayObj.transform.matrix.clone();
if (!point) {
//default is center of bounding box
var r:Rectangle = displayObj.getBounds(displayObj);
point = new Point(r.x + (r.width * .5), r.y + (r.height * .5));
}
MatrixTransformer.rotateAroundInternalPoint(m, point.x, point.y, relativeDegrees);
displayObj.transform.matrix = m;
}
Another more lazy way to do this, is to use a dummy parent object and just rotate that:
var dummy:Sprite = new Sprite();
dummy.addChild(yourObjectToRotate);
//this effectively makes the anchor point of dummy in the center, so when you rotate it it rotate from the center.
yourObjectToRotate.x = -(yourObjectToRotate.width * .5);
yourObjectToRotate.y = -(yourObjectToRotate.height * .5);
dummy.rotation = 90;

Strange stability issues with Matrix scale/rotation in Actionscript

I have a Flash app where I am performing a scale and rotation operation about the center of _background:MovieClip (representing a page of a book). I have simple event listeners on the GESTURE_ROTATE and GESTURE_SCALE events of this MC which update some variables currentRotation and currentScaleX, currentScaleY. I then have the following code trigger on the ENTER_FRAME event of the app.
The problem I am encountering is upon rotating the MC beyond the limits of roughly 60 or -60 degrees, or scaling slightly and rotating, the MC begins to oscillate and finally spin wildly out of control and off the screen. I've tried several things to debug it, and even tried Math.flooring the currentRotationValue and rounding the values of currentScaleX/Y to the tenths place (Math.floor(currentScale * 10) / 10), but neither of these seems to remedy it. I'm a little stuck at this point and have tried researching as much as I can, but couldn't find anything. Any suggestions? Is there an issue with doing this operation on each frame perhaps?
private function renderPage(e:Event) {
var matrix:Matrix = new Matrix();
// Get dimension of current rectangle.
var rect:Rectangle = _background.getBounds(_background.parent);
// Calculate the center.
var centerX = rect.left + (rect.width/2);
var centerY = rect.top + (rect.height/2);
// Translating to the desired reference point.
matrix.translate(-centerX, -centerY);
matrix.rotate(currentRotation / 180) * Math.PI);
matrix.scale(currentScaleX, currentScaleY);
matrix.translate(centerX, centerY);
_background.transform.matrix = matrix;
}
I'm not certain what behaviour you're trying to produce, but I think the problem is that centerX and centerY define the middle of _background in _background.parent's coordinate space. You're then translating the matrix so that _background is rotated around the values centerX, centerY, but in _background's coordinate space.
Assuming you want _background to rotate around a point which remains static on screen, what you actually need to do is use two different Points:
matrix.translate(-_rotateAroundPoint.x, -_rotateAroundPoint.y);
matrix.rotate(currentRotation / 180) * Math.PI);
matrix.scale(currentScaleX, currentScaleY);
matrix.translate(_centerOnPoint.x, _centerOnPoint.y);
Where _rotateAroundPoint is the point around which _background should turn in it's own coordinate space, and _centerOnPoint is the point around which it should turn in its parent's coordinate space.
Both of those values only need to be recalculated when you want to pan _background, rather than every frame. For example:
private var _rotateAroundPoint:Point = new Point(_background.width * 0.5, _background.height * 0.5);
private var _centerOnPoint:Point = new Point(50, 50);
private function renderPage(e:Event) {
var matrix:Matrix = new Matrix();
matrix.translate(-_rotateAroundPoint.x, -_rotateAroundPoint.y);
matrix.rotate((currentRotation / 180) * Math.PI);
matrix.scale(currentScaleX, currentScaleY);
matrix.translate(_centerOnPoint.x, _centerOnPoint.y);
_background.transform.matrix = matrix;
}

Scaling tile grid

I'm working on my own tile bliting engine, this one is using hexagonal tiles - but I think it doesn't differ much from regular tiles.
I have huge x,y array of tiles and they have their x,y coordinates for rendering on canvas, I iterate only the ones that should be visible on canvas in current camera position.
So I'm stuck with scaling and cant resolve this on my own.
Here is my code for drawing tiles on canvas:
public function draw():Void{
clearCanvas(); //Clear canvas (bitmapData)
var _m:Matrix;
iterateTiles(function(_tile:HexTile):Void{ // loop every tile that is visible on screen
_m = new Matrix();
_m.translate(_tile.x + cameraPoint.x,_tile.y + cameraPoint.y);//Get pre calculated tile x,y and add camera x,y
_m.scale(matrixScale, matrixScale);
drawToCanvas(_tile,_m);//Send to draw tile on canvas using Matrix
},true);
}
This works nice and fast but only problem is it scales tiles from left top corner (like regular scale would work)
Before scale
After scale
My question is how to transform tiles to always scale from center. So if tile 10:10 is in center of screen before scaling, then it should
stay there after scaling.
Sorry, I misunderstood the question, but I think I've got it now:
// Scale the distance from the original point to the center of the canvas
var xDistance:Number = ((_tile.x + cameraPoint.x) - xCenter) * matrixScale;
var yDistance:Number = ((_tile.y + cameraPoint.y) - yCenter) * matrixScale;
// Add the distances to the center of the canvas. This is where you want the tile
// to appear.
var x:Number = xCenter + xDistance;
var y:Number = yCenter + yDistance;
// Because the coordinate is going to be scaled, you need to increase it first.
x = (1 / matrixScale) * x;
y = (1 / matrixScale) * y;
_m.translate(x, y);
I have not tested this, I've just drawn it out on graph paper. Let me know if it works.

Coordinate overflow in AS3 when scrolling container object?

Scrolling graph. Currently I show a real-time graph by drawing each piece of data as it is generated. To scroll, I simply move the container object left to compensate and erase all the data left of the bounds.
Simple, fast, surprisingly memory efficient...but is there a concern of the coordinates overflowing as I keep adding contents to the right and scrolling the container left? Is there a limit on the x coordinate of an (empty) object? I'm afraid if I keep scrolling the container left indefinitely and writing to ever increasing x coordinates the program would misbehave eventually. Would this occur? If so, at what max coordinates? Or does AS3 handle this automatically?
I did a test, where I keep drawing an item to a container making it bigger and bigger, and moving it to the left. It stopped getting bigger at 105,000,000 pixels. After that the width reported 0 and the x property reported -107,374,182.4 and wouldn't move beyond that.
If you have bitmap data though, the FP10 limit is 8,191 pixels in width or height, and the total number of pixels cannot exceed 16,777,215 pixels.
Here was my code used to test:
var b:Sprite = new Sprite();
addChild(b);
b.x = stage.stageWidth * .5;
var t:Timer = new Timer(100);
t.addEventListener(TimerEvent.TIMER,tick);
t.start();
var moveAmt:Number = 50;
function tick(e:Event):void {
b.x -= moveAmount;
b.graphics.beginFill(Math.random() * 0xFFFFFF);
b.graphics.drawRect(b.width,0,moveAmount,stage.stageHeight);
b.graphics.endFill();
trace(b.width + " : " + b.x);
}