I remember about a year ago trying to work this out and messing it up completely. Well, I made another attempt about five minutes ago in AS3 and did it with no problems whatsoever, so I must have been on a bad day or something.
This is basically a tutorial on how to get your object to rotate around a custom reference point. I'll feel like a real idiot if something like this already exists in Flash, but lots of searching through the documentation yielded no results, so here's how to do it manually. This should be pretty much mandatory for any more complex physics engines.
In true AS3 style, here is is laid out in a class. Anyone uncomfortable with classes should still be able to extract the important information from this one:
package{
import flash.display.MovieClip;
import flash.geom.Point;
Just importing the necessary classes for this...
public class FreeObject extends MovieClip{
public function FreeObject(){
super();
}
The constructor function. Force of habit.
public function rotateAboutPoint(vx:Number, vy:Number, vr:Number = 1):void{
var radianangle:Number = vr * Math.PI / 180;
var nowangle:Number = Math.atan2(y - vy, x - vx);
var nowlength:Number = Point.distance(new Point(vx, vy), new Point(x, y));
var newangle:Number = nowangle + radianangle;
var newpos:Point = Point.polar(nowlength, newangle);
rotation += vr;
x = vx + newpos.x;
y = vy + newpos.y;
}
}
}
The actual function for doing the rotation. Let's take a look at this line - by - line:
public function rotateAboutPoint(vx:Number, vy:Number, vr:Number = 1):void{
Sets up a function with three parameters - the x co-ordinate to rotate around, the y co-ordinate to and the angle in degrees. You replace the vx an vy parameters with a Point object if you prefer.
var radianangle:Number = vr * Math.PI / 180;
var nowangle:Number = Math.atan2(y - vy, x - vx);
var nowlength:Number = Point.distance(new Point(vx, vy), new Point(x, y));
var newangle:Number = nowangle + radianangle;
var newpos:Point = Point.polar(nowlength, newangle);
Right! These are the variables we'll be using in our function.
radianangle is a representation of our degree - angle in radians, since Flash's geometric functions generally use radians as inputs and outputs.
nowangle is the current angle between the point we'll be rotating around and the object's actual reference, also in radians.
nowlength is the current distance between those two points. You could always use good ol' Pythagorean distance formula, but the Point class looks cooler :P
newangle is what the angle between the point we're rotating around and the clip will be once the rotation is complete. It's obtained simply by adding the original angle to our rotation's angle in radians.
newpos, finally, is a point representing where our object will end up once the rotation is complete. It's obtained by using a pretty cool (IMO) method of the point class, where a vector represented by a distance and an angle (radians) is converted into a vector represented by x and y values. In cases or rotation like this, converting between the two is important.
rotation += vr;
x = vx + newpos.x;
y = vy + newpos.y;
}
}
}
This adds our rotation value to the clip we're rotating. If you're doing something like a pendulum which doesn't require the actual object to rotate, you can skip this bit.
It also sets our clips new x and y values. Since the newpos point is based from the origin and not our centre of rotation, we need to add it to the vx and vy values representing the point we're rotating around.
Congratulation! You now have a class which you can assign to a DisplayObject, with this method:
rotateAboutPoint(point x, point y, angle)
Which you can access to rotate your object.
Want to see it in action? Name the file FreeObject, create a new Flash (AS3) document in the same folder and draw something. Convert it to a MovieClip, click on it in the library and in Linkage, select to export for ActionScript, type FreeObject as its base class and you should be good to go. You can set it as its main class, but then you'll only be able to link it to one object, so have the main class as something unique.
Give your object on the stage the instance name rotaty. Then just add this code to the main timeline:
import flash.events.MouseEvent;
import flash.events.Event;
stop();
var dwn:Boolean = false;
stage.addEventListener(Event.ENTER_FRAME, upd);
stage.addEventListener(MouseEvent.MOUSE_DOWN, ondown);
stage.addEventListener(MouseEvent.MOUSE_UP, onup);
function upd(ev:Event):void{
if(dwn){
rotaty.rotateAboutPoint(mouseX, mouseY, 2);
}
}
function onup(ev:MouseEvent):void{
dwn = false;
}
function ondown(ev:MouseEvent):void{
dwn = true;
}
And you should be good to go. You may want to draw something in the background, since the mouse events won't register if they're over an unfilled area.
Here it is in action. Click anywhere and your object will rotate around it.
As a function, this isn't that complex. You should be able to adapt it to work with different co-ordinate spaces and things. As far as theory goes, I like to think of it in terms of a circle. You have your point you're rotating around in the middle, and your object on the circumference. So long as you know the radius and the angle around the circle at which your object is located, you know all you need to in order to express its position.
That's all, folks! Have fun!