[AS3]Rotate a Sprite around it's center - actionscript-3

I need some help with a really specific problem.
I've looked and found some answers, but nothiung as specific as what I need.
I'm using flashdevelop, and for now, I've a rectangle (it's a Sprite) following my mouse cursor (centered).
I'd like to apply a rotation on it when I scroll my mouse wheel, but I need the rotation to apply on it's center, and I need the object to stay centered on my mouse cursor at the same time.
And I also need to apply the rotation in radians, not in degree.
For now I got this :
var mod:Number;
if (e.delta <= 0) {
mod = -0.1;
}else {
mod = 0.1;
}
Could someone help me with it please ? After what I've seen it seems that I've to use matrix, and complex maths, but I've no idea how to do what I want to do with this.

You just need to keep the registration point of the sprite in center of the rectangle to rotate it about it's center. When you draw a sprite from (0,0) the registration point is top-left and not center. Here's how you make it center:
var rectangleSprite:Sprite = new Sprite();
rectangleSprite.graphics.lineStyle(3,0x00ff00);
rectangleSprite.graphics.beginFill(0x0000FF);
//please note that i am not drawing the sprite from (0,0),
//instead i start from (-width/2,-height/2)
rectangleSprite.graphics.drawRect(-150,-75,300,150);
rectangleSprite.graphics.endFill();
And for degree to radian conversion you just need multiply your value in radians by (180/Math.PI) before applying your rotation.
var rotationInDegrees = rotationInRadians*(180/Math.PI);

Related

My startDrag rectangle isn't where I set it to be

I've made a rectangle, a rectangular sprite and a circular sprite. The rectangular sprite has the exact same properties of my rectangle, and I set my circle to be positioned in the center of the stage, which is the middle of both of these rectangles.
After tracing the values of the rectangle, I can safely say that the rectangle is the same as the rect sprite. So then of course, if I wanted to drag the circle around the rectangle, it should stay within the bounds of it, right?
stop();
var isDragging:Boolean;
import flash.events.MouseEvent;
import flash.geom.Rectangle;
import flash.display.Sprite;
import flash.geom.Point;
var px:Number = boundingBox.x;
var py:Number = boundingBox.y;
var h:Number = boundingBox.height;
var w:Number = boundingBox.width;
var box:Rectangle = new Rectangle(px,py,w,h);
trace(box);
var blob:Sprite = new Sprite();
blob.graphics.beginFill(0x000000);
blob.graphics.lineStyle(1.2, 0xFFFFFF);
blob.graphics.drawCircle(stage.stageWidth/2, stage.stageHeight/2, 18);
blob.graphics.endFill();
addChild(blob);
var box2:Sprite = new Sprite();
box2.graphics.lineStyle(1, 0xFFCC00);
box2.graphics.drawRect(px,py,w,h);
addChild(box2);
blob.addEventListener(MouseEvent.MOUSE_DOWN, drag);
function drag(event:MouseEvent):void {
event.target.startDrag(false, box);
isDragging = true
}
Well that doesn't seem be the case. Maybe I'm just a noob but I have no idea what's going on.
When I run the scene, everything looks normal.
The orange/dotted line is two rectangles I made, one is a movie clip that I used to set the properties of the rectangle I made in AS3 (dotted) and the orange is the AS3 sprite.
The circle is in the middle of the stage and it all looks as exactly how I wanted to be programmed.
But as soon as I click on it, it teleports over here.
And when I try to drag it, it seems as if the box's top left corner has been translated to that point all the way down there, and it only drags around in the bottom-right quadrant of the stage, and even drags further off screen.
I'm not sure if the rectangle that I put into the startDrag function is somehow distorted from the actual rectangle.
I have no idea why this happens, because when I tried to do this using a movie clip that I added to the stage, everything worked fine. But as soon as I try to do it programmatically, everything goes tits up.
I'd appreciate some help on why this isn't working. Thanks for reading.
There's your problem:
blob.graphics.drawCircle(stage.stageWidth/2, stage.stageHeight/2, 18);
You draw this circle and only seem to care about the circle:
if I wanted to drag the circle around the rectangle, it should stay within the bounds of it, right?
No. Just because you added something to the .graphics doesn't mean As3 automagically knows that this is what should stay within the bounding Rectangle that's being passed to startDrag().
The thing that stays within the bounding Rectangle is the registration point of the Sprite. That registration point is not where you drew the circle. It is in fact still at its default position of 0/0 in your first image. I added a green star at the registration point of blob. I also added a red rectangle that connects the registration point and your point. Think of this as some jelly that both your circle and the registration point are stuck in. This should help illustrate that just because you drew a circle somewhere doesn't mean that you are only dealing with that circle:
But as soon as I click on it, it teleports over here.
As soon as you click, the movement limitation of startDrag() is applied. The registration point has to be in the Rectangle that you specified and that's exactly what happens. The whole red jelly blob is moved over so that the green star is in the Rectangle:
To solve this, first create your blob in a reasonable fashion, which means drawing the circle around the registration point at 0/0 and then moving the Sprite object around if you want to have the circle at another position. This way, there's no red jelly and the green star is right at the center of the circle. The visual helpers that I added become superfluous, because your perception of the situation now matches with how it actually is.
const RADIUS:Number = 18;
var blob:Sprite = new Sprite();
blob.graphics.beginFill(0x000000);
blob.graphics.lineStyle(1.2, 0xFFFFFF);
blob.graphics.drawCircle(0, 0, RADIUS);
blob.x = stage.stageWidth/2;
blob.y = stage.stageHeight/2;
blob.graphics.endFill();
addChild(blob);
This will keep the blob within the bounds for the most part or more precisely: its center point. If you want to keep the entire circle within the bounds, you'd have to subtract the circle from the bounding Rectangle Minkowski-style. Mink-what? yeah...err, that's a fancy way of saying: make the bounding Rectangle smaller by the radius, so that this smaller boundary can be applied to the center of the radius to ensure that the whole circle stays within the bigger original boundary.
In your case Rectangle's inflate() should do:
var px:Number = boundingBox.x;
var py:Number = boundingBox.y;
var h:Number = boundingBox.height;
var w:Number = boundingBox.width;
var box:Rectangle = new Rectangle(px,py,w,h);
box.inflate(-RADIUS, -RADIUS);
trace(box);

Scale, Position & Rotate Parent object to make child object take up entire stage

Using the first photo below, let's say:
The red outline is the stage bounds
The gray box is a Sprite on the stage.
The green box is a child of the gray box and has a rotation set.
both display object are anchored at the top-left corner (0,0).
I'd like to rotate, scale, and position the gray box, so the green box fills the stage bounds (the green box and stage have the same aspect ratio).
I can negate the rotation easily enough
parent.rotation = -child.rotation
But the scale and position are proving tricky (because of the rotation). I could use some assistance with the Math involved to calculate the scale and position.
This is what I had tried but didn't produce the results I expected:
gray.scaleX = stage.stageWidth / green.width;
gray.scaleY = gray.scaleX;
gray.x = -green.x;
gray.y = -green.y;
gray.rotation = -green.rotation;
I'm not terribly experienced with Transformation matrices but assume I will need to go that route.
Here is an .fla sample what I'm working with:
SampleFile
You can use this answer: https://stackoverflow.com/a/15789937/1627055 to get some basics. First, you are in need to rotate around the top left corner of the green rectangle, so you use green.x and green.y as center point coordinates. But in between you also need to scale the gray rectangle so that the green rectangle's dimensions get equal to stage. With uniform scaling you don't have to worry about distortion, because if a gray rectangle is scaled uniformly, then a green rectangle will remain a rectangle. If the green rectangle's aspect ratio will be different than what you want it to be, you'd better scale the green rectangle prior to performing this trick. So, you need to first transpose the matrix to offset the center point, then you need to add rotation and scale, then you need to transpose it away. Try this set of code:
var green:Sprite; // your green rect. The code is executed within gray rect
var gr:Number=green.rotation*Math.PI/180; // radians
var gs:Number=stage.stageWidth/green.width; // get scale ratio
var alreadyTurned:Boolean; // if we have already applied the rotation+scale
function turn():void {
if (alreadyTurned) return;
var mat:flash.geom.Matrix=this.transform.matrix;
mat.scale(gs,gs);
mat.translate(-gs*green.x,-gs*green.y);
mat.rotate(-1*gr);
this.transform.matrix=mat;
alreadyTurned=true;
}
Sorry, didn't have time to test, so errors might exist. If yes, try swapping scale, translate and rotate, you pretty much need this set of operations to make it work.
For posterity, here is what I ended up using. I create a sprite/movieClip inside the child (green) box and gave it an instance name of "innerObj" (making it the actually content).
var tmpRectangle:Rectangle = new Rectangle(greenChild.x, greenChild.y, greenChild.innerObj.width * greenChild.scaleX, greenChild.innerObj.height * greenChild.scaleY);
//temporary reset
grayParent.transform.matrix = new Matrix();
var gs:Number=stage.stageHeight/(tmpRectangle.height); // get scale ratio
var mat:Matrix=grayParent.transform.matrix;
mat.scale(gs,gs);
mat.translate(-gs * tmpRectangle.x, -gs * tmpRectangle.y);
mat.rotate( -greenChild.rotation * Math.PI / 180);
grayParent.transform.matrix = mat;
If the registration point of the green box is at one of it's corners (let's say top left), and in order to be displayed this way it has a rotation increased, then the solution is very simple: apply this rotation with negative sign to the parent (if it's 56, add -56 to parent's). This way the child will be with rotation 0 and parent -> -56;
But if there is no rotation applied to the green box, there is almost no solution to your problem, because of wrong registration point. There is no true way to actually determine if the box is somehow rotated or not. And this is why - imagine you have rotated the green box at 90 degrees, but changed it's registration point and thus it has no property for rotation. How could the script understand that this is not it's normal position, but it's flipped? Even if you get the bounds, you will see that it's a regular rectangle, but nobody know which side is it's regular positioned one.
So the short answer is - make the registration point properly, and use rotation in order to display it like in the first image. Then add negative rotation to the parent, and its all good :)
Edit:
I'm uploading an image so I can explain my idea better:
 
As you can see, I've created a green object inside the grey one, and the graphics INSIDE are rotated. The green object itself, has rotation of 0, and origin point - top left.
#Vesper - I don't think that the matrix will fix anything in this situation (remember that the green object has rotation of 0).
Otherwise I agree, that the matrix will do a pretty job, but there are many ways to do it :)

How to get a sprite to rotate to face the mouse position, using HTML5 canvas?

I'm trying to follow this tutorial to create a homing missile in Flash, except I'd like to adapt it to use HTML5 canvas. I'm struggling with getting the sprite to face the direction of the mouse; it rotates but doesn't always face the mouse! I've put up a fiddle so you can see what's going on.
http://jsfiddle.net/Mr4Tz/
Any help would be greatly appreciated.
The angle value passed to the HTML5 rotate method should be in radians, but you are passing degrees instead.
Based on your example, this should work:
var targetX = mouse.x - missile.x
, targetY = mouse.y - missile.y
, rotation = Math.atan2(targetY, targetX);
context.rotate(rotation);

Reset/lock html5 canvas position to center on player when panning

I cannot figure out how to do this. I was translating the character and background at the same time, but if there's any hiccup, the character position slides out of the viewable area of the canvas, and I need the canvas translation to be based off the position of the player (hero.x, hero.y).
Currently I have
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 640;
canvas.height = 480;
//then in my update function
if (38 in keysDown && hero.y > 0){ //UP KEY PRESSED KEY
ctx.translate(0,12); //translate background position +12y
hero.y -= hero.speed * modifier; //move player up on the background image
else if (40 in keysDown && hero.y < 3750-64){ //DOWN KEY PRESSED
ctx.translate(0, -12);
hero.y += hero.speed * modifier;
}
}
That moves the player and the canvas but not guaranteed together...if it freezes at all, the player is off center or even off screen.
The viewable canvas area is 640x480, but the background image you can navigate on is 5,000 x 3750.
On the web browser, when it doesn't freeze, it works how I want, moving the player and background at the same pace as the character.
However, that same rate on the phone puts the player much faster than the screen translates which means the player walks right out of the viewable area even though it still moves the background.
If I do ctx.translate(hero.x, hero.y) and use the hero.x, hero.y coordinates of the player, or some variation of it minus an offset, it moves the background BY that measurement each time I press the key instead of moving it TO that position.
How can I make everything conditional on the players position to move both the player and background, but together, or automatically adjust next update() to center on the player....?????
How can I make everything conditional on the players position to move both the player and background, but together, or automatically adjust next update() to center on the player
Well, the easy way would be to actually always draw the player in the center! In other words, never ever change or translate his or her coordinates. Instead worrying about translating everything else in relation to that.
Since you want the player to always be in the center, you should always draw the player at the center of the canvas (640/2 x 480/2 for you)
Then you'll want to do is keep a canvas offset for X and Y and draw everything (Background, etc) using that offset, then reset the transformation and draw the hero in the plain old center.
So your draw function might look something like this:
function draw() {
ctx.clearRect(0,0,500,500);
// Save the default transformation matrix
ctx.save();
// Translate by the background's offset
ctx.translate(xoffset, yoffset);
// Draw the background (and maybe other things) translated
ctx.fillStyle = backgroundGradient;
ctx.fillRect(0,0,500,500);
// We restore to the default transformation
// This will draw the hero not translated at all
// That means we can always draw the hero in the center!
ctx.restore();
// hero is a black rectangle
ctx.fillRect(240, 240, 20, 20);
}
Here is a live example that works with mouse down and up. There is a big "sun" for the background that moves, while the "stays" rectangle stays still because it is literally always drawn in the same place:
http://jsfiddle.net/3CfFE/

Difference between defining sprite's x, y coordinates, and painting object in some location

I have a task:
I need to place about 100 sprites on one canvas (with prepared grid on it). I need to place them as invisible (circles) stones, on the board, and make visible only on mouseover.
The problem I come across is following, I can't place those objects accurately into the nodes on the grid.
E.g.
if I define stones (it's just a sprite, as I said earlier) this way:
var stone:StoneSprite = new StoneSprite();
stone.x = this.x + 2*cellWidth;
stone.graphics.beginFill( 0x000000 );
stone.graphics.drawCircle(stone.x , this.y + cellWidth, cellWidth/3 );
stone.graphics.endFill();
rawChildren.addChild(stone);
They don't sit on the node...
See image:
http://img.skitch.com/20091014-kuhfyjeg1g5qmrbyxbcerp4aya.png
And if I do it this way:
var stone:StoneSprite = new StoneSprite();
stone.graphics.beginFill( 0x000000 );
  stone.graphics.drawCircle(this.x + 2*cellWidth , this.y + cellWidth, cellWidth/3 );
  stone.graphics.endFill();
rawChildren.addChild(stone);
The stone is displayed correctly in the grid node... See image 2:
http://img.skitch.com/20091014-f595tksjxramt98s7yfye591bh.png
So I wonder what is the difference between these 2 approaches.
Also, I think I need to pass correct coordinates to the stone class... In case I would like to change some properties of the stone object. E.g. visibility, or radius.
Could you please suggest, what's wrong in defining coordinates as stone.x, stone.y
How to fix the problem with incorrect positioning.
Would really appreciate ideas about the problem, I am trying to solve for so long :(
Assume x & y are 30 and cellWidth is 30.
First Example:
stone.x = 30 + 60; //90
drawCircle(90, 60, 10);
This means if you were to draw a rectangle around your circle, it would be at [170,50]. (x,y).
Second Example:
stone.x = 0;
drawCircle(90, 60, 10)
This means the rectangle around your circle is at [80,50];
In the first example, you are moving the sprite to position x==90. Then drawing a circle whose center is at x==90 inside the sprite. So relative to this, you're at x==180. But because a circle's x,y coords are the center, subtract 10 for the radius to get the boundary x position.
In the second example, the sprite defaults to position x==0 relative to this and you're drawing the circle inside the sprite at position x==90. (therefore it begins at x==80).
I am not sure what's causing the issue - might be some padding induced by the container - can't say without testing. But I believe that adding a Sprite (say board) to canvas.rawChildren and using it as the parent for the grid and stones would fix the issue.