Strafe Around a Point - actionscript-3

This seems like a simple trig question, but for whatever reason, things aren't working out.
I'm trying to simply have an object rotate around a given point when the user presses the A/D keys (strafing around the mouse in a circular motion, while still facing the mouse).
Here's the code I've tried so far (all Math functions take and return radians):
if (_inputRef.isKeyDown(GameData.KEY_LEFT))
{
x += 2 * Math.cos(Math.atan2(mouseY - y, mouseX - x) - Math.PI * 0.5);
y += 2 * Math.sin(Math.atan2(mouseY - y, mouseX - x) - Math.PI * 0.5);
}
else if (_inputRef.isKeyDown(GameData.KEY_RIGHT))
{
x += 2 * Math.cos(Math.atan2(mouseY - y, mouseX - x) + Math.PI * 0.5);
y += 2 * Math.sin(Math.atan2(mouseY - y, mouseX - x) + Math.PI * 0.5);
}
And a more elegant method which accomplishes the same thing:
if (_inputRef.isKeyDown(GameData.KEY_LEFT))
{
x += 2 * Math.sin(Math.atan2(mouseY - y, mouseX - x));
y -= 2 * Math.cos(Math.atan2(mouseY - y, mouseX - x));
}
else if (_inputRef.isKeyDown(GameData.KEY_RIGHT))
{
x -= 2 * Math.sin(Math.atan2(mouseY - y, mouseX - x));
y += 2 * Math.cos(Math.atan2(mouseY - y, mouseX - x));
}
Now, they both kind of work, the object rotates around the mouse while always facing the mouse, but given enough time of holding down the strafe button, it becomes increasingly apparent that the object is also rotating AWAY from the mouse, as if its being pushed away.
I have no idea why this is and how to fix it.
Any insight is appreciated!

I think your current approach would only work if you take 'infinitely small' steps. As it is now, each movement is perpendicular to the "to-mouse vector" and thus increases the distance between mouse and object.
A solution would be to calculate the new position while keeping the distance to the mouse unchanged, by rotating the position around the mouse:
// position relative to mouse
var position:Point = new Point(
x - mouseX,
y - mouseY);
var r:Number = position.length; // distance to mouse
// get rotation angle around mouse that moves
// us "SPEED" unit in world space
var a:Number = 0;
if (/* LEFT PRESSED */) a = getRotationAngle( SPEED, r);
if (/* RIGHT PRESSED */) a = getRotationAngle(-SPEED, r);
if (a > 0) {
// rotate position around mouse
var rotation:Matrix = new Matrix();
rotation.rotate(a);
position = rotation.transformPoint(position);
position.offset(mouseX, mouseY);
x = position.x;
y = position.y;
}
// elsewhere...
// speed is the distance to cover in world space, in a straight line.
// radius is the distance from the unit to the mouse, when rotating.
private static function getRotationAngle(speed:Number, radius:Number):Number {
return 2 * Math.asin(speed / (2 * radius));
}
The above uses a Matrix to rotate the (x, y) position around the mouse position. Ofcourse you can apply the same principle without using Matrix if so desired.
I had to do some trig to come up with the right equation for getting the correct angle. The angle depends on the radius of the movement arc, since a larger radius but constant angle would increase the movement distance (undesired behavior). My earlier solution (before edits) was to scale the angle by the radius, but that would still result in slightly more movement with larger radii.
The current approach ensures that radius and speed remain constant in all cases.

Related

LibGdx Issue of coordinates

I have issue in my LibGdx program. I gave 800 height and 480 width to my camera. I am drawing target under coordinates :
randomTargetX = new Random().nextInt((350 - 100) + 1) + 100;
randomTargetY = new Random().nextInt((600 - 300) + 1) + 300;
But after clicking on target my cannonball don't overlap target rectangle.
I am doing this in Touch:
if (Gdx.input.justTouched()) {
touchX = Gdx.input.getX();
touchY = Gdx.input.getY();
camera.unproject(touch.set(touchX, touchY, 0));
if (touch.y>200) {
isTouched = true;
rectangleCannonBall.x = (width / 2) - 50 / 2;
rectangleCannonBall.y = 0;
double angle = 180.0 / Math.PI * Math.atan2(rectangleCannonBall.x - touch.x, touch.y - rectangleCannonBall.y);
spriteCannon.setRotation((float) angle);
}
}
Doesn't work.
It's a cannonball game:
First i am setting camera.
Randomly showing targets inside range of coordinates.
On touch unprojecting camera with Vector3 new position.
On touch calculating target position with cannon position and getting angle to rotate cannon.
After rotating cannon I fire ball towards target.
Now when I do Rectanglar1.overlaps(rec2) , it doesn't work because of both rectangles have different points but by visible both overlaps each other.
When I check coordinates of Rectangle of Target and Touch its different.
The line:
camera.unproject(touch.set(touchX, touchY, 0));
Doesn't do anything.
Try with:
touch = camera.unproject(touch.set(touchX, touchY, 0));

AS3 Missile Logic

I want to create a simple missile object, which moves at a set speed and rotates towards a specific target at a given rotation speed. However, I'm having trouble figuring out the math of it. This is my code so far:
private function enterFrame(e:Event):void {
// Rotate the missile towards the target.
var targetAngle:Number = getAngle(target.x, target.y, x, y);
if (targetAngle < 0) {
targetAngle += 360;
}
if (targetAngle - turnSpeed > rotation) {
rotation += turnSpeed;
} else if (targetAngle + turnSpeed < rotation) {
rotation -= turnSpeed;
} else {
rotation = targetAngle;
}
// Set the target point to move to based on angle and speed.
var newX:Number = x + Math.sin(degreesToRadians(rotation)) * speed;
var newY:Number = y + Math.cos(degreesToRadians(rotation)) * speed;
// Move to new location
x = newX;
y = newY;
}
private function getAngle (x1:Number, y1:Number, x2:Number, y2:Number):Number {
var dx:Number = x2 - x1;
var dy:Number = y2 - y1;
return (Math.atan2(dy,dx) * 180) / Math.PI;
}
private function degreesToRadians(degrees:Number):Number {
return degrees * Math.PI / 180;
}
I've been trying to debug it using trace and such, but I can't seem to figure out where the problem is, most likely because there are many problems and I can't tell if I've fixed one because the others are masking it. I suspect that the issue(s) lie somewhere in the rotation calculations, since I'm pretty sure that the movement part is working as it should, but I can't say for sure.
At any rate, whatever I do, the missiles always seem to fly off in random directions, sometimes tracking towards straight up, or straight down, or just looping around after nothing in particular.

find angle and velocity for a parabola that meet specific range

i'm a little ashamed to ask this, but i have tried a lot of different things and can't make it work.
i have a game that shots a bullet, i have made the code that calculates the parabola trajectory given a an angle and a velocity, but i'm trying to make the calculus needed to get the angle and velocity needed to reach X point (the user enemy tank) and i'm unable to make it work as i need.
my current code is:
var startingPointX:Number = globalCoord.x;
var startingPointY:Number = globalCoord.y;
var targetX:Number = tankPlayer.x;
var targetY:Number = tankPlayer.y;
//distance between user and enemy tank
var distanceTarget = Math.sqrt(( startingPointX - targetX ) * ( startingPointX - targetX ) + ( startingPointY - targetY ) * ( startingPointY - targetY ));
var fixedVel = (distanceTarget/10)*2;
var fixedG = bullet.g;
// launch angle
var o:Number = -(Math.asin((0.5 * Math.atan(fixedG * distanceTarget / fixedVel * fixedVel))) * 180 / Math.PI);
bullet.init(startingPointX, startingPointY, o, fixedVel);
and the functions in the bullet object that actually position the bullet in the parabola trajectory is:
public function init(x, y:Number, rot:Number, speed:Number) {
// set the start position
var initialMove:Number = 35.0;
this.x = x + initialMove * Math.cos(2 * Math.PI * rot / 360);
this.y = y + initialMove * Math.sin(2 * Math.PI * rot / 360);
this.rotation = rot;
//get speed
dx = speed * Math.cos(2 * Math.PI * rot / 360);
dy = speed * Math.sin(2 * Math.PI * rot / 360);
//animation
lastTime = getTimer();
addEventListener(Event.ENTER_FRAME,moveBullet);
}
public function moveBullet(event:Event)
{
//get the time passed
var timePassed:int = getTimer() - lastTime;
lastTime += timePassed;
//move bullet
dy += g * timePassed / 1000;
this.x += dx * timePassed / 1000;
this.y += dy * timePassed / 1000;
//bullet past the top of the screen
if (this.y < 0)
{
deleteBullet();
}
}
any help would be really useful, thanks ! :D
Regards,
Shadow.
If this is a ballistics problem in the sense that you project a particle from point A with velocity v at an angle theta and you want it to hit a point T where the y coordinates of A and T match (ie they lie on a plane perpendicular to the vector of gravitational force vector) then you can calculate the required angle and velocity from this equation (See your wiki link where this is defined):
R = (v * v * sin(2 * theta))/g
Here R is the distance travelled in the x direction from your start point A . The problem you are facing is you are trying to interpolate a parabola through just 2 points. There are an infinite amount of parabolas that will interpolate 2 points while the parabola through 3 points is unique. Essentially there are an infinite amount of choices for velocity and angle such that you can hit your target.
You will either need to fix the angle, or the velocity of the bullet in order to use the above equation to find the value you require. If not, you have an infinite number of parabolas that can hit your target.
The above assumes that air resistance is ignored.
EDIT : Thus if you know velocity v already you can get theta from simple rearrangement of the above :
( asin(g * R / (v * v)) ) / 2 = theta
Based on the suggestion from #mathematician1975 i resolved the code to this and works perfectly :D
var distanceTarget = startingPointX - targetX ;
var fixedVel = 100;
var fixedG = tmpB.g;
var o:Number = (0.5 * Math.atan((fixedG * distanceTarget / (fixedVel * fixedVel)))) * 180 / Math.PI;
//this is only necessary why the enemy tank is facing left
o -= 180;
what i made is:
set a fixed velocity as #mathematician1975 said, a lot bigger than before
the distance between starting and ending point is lineal and not using Pythagoras.
the -180 is just why the enemy tanks is facing left.
i hope someone would find it useful in the future :D
Regards,
Shadow.

Incrementing points along a line

I'm calculating increments from point A (top right) to point B (bottom left) with the following code. But as we get closer to point B, my increments get further and further off the expected path. The green line in the picture is the expected path of the white dot.
public function get target():Point { return _target; }
public function set target(p:Point):void
{
_target = p;
var dist:Number = distanceTwoPoints(x, _target.x, y, _target.y); //find the linear distance
//double the steps to get more accurate calculations. 2 steps are calculated each frame
var _stepT:Number = 2 * (dist * _speed); //_speed is in frames/pixel (something like 0.2)
if (_stepT < 1) //Make sure there's at least 1 step
_stepT = 1;
_stepTotal = int(_stepT); //ultimately, we cannot have half a step
xInc = (_target.x - x) / _stepT; //calculate the xIncrement based on the number of steps (distance / time)
yInc = (_target.y - y) / _stepT;
}
private function distanceTwoPoints(x1:Number, x2:Number, y1:Number, y2:Number):Number
{
var dx:Number = x1-x2;
var dy:Number = y1-y2;
return Math.sqrt(dx * dx + dy * dy);
}
Basically, I'm out of ideas. The only thing that seems to get the white dot to follow the green line exactly is to adjust the target's position like so:
distanceTwoPoints(x, _target.x + 2, y, _target.y + 1);
//...
xInc = (_target.x + 2 - x) / _stepT;
yInc = (_target.y + 1 - y) / _stepT;
However, this throws off other parts of the simulation where there is no angle between points, like coming into point A (top right). This makes me think the distance between the two points needs to be calculated as shorter than it actually is. Any ideas?
Flash has a great function that is really handy for this. Point.interpolate(pointA, pointB, number) It returns a point between points A and B. The third input (Number) is how close to pointA or pointB the resulting point should be, from 0 to 1. You'll have to calculate its value.
What interpolate does is basically a weighted average of the two input points, the number being the weight towards one point. If the number is 0.5, you'll get a point halfway between the two input points. 1 returns PointA, 0 returns PointB.
flash.geom.Point.interpolate() for details.
For other languages, or math in general, you can do it this way, no Trig required: point1, the origin, and point2 the end point. point3 is a point between point1 and point2. loc is a ratio from point1 to point2, how far down the line to go. loc = .25 would be a quarter of the way from point1 towards point2. point3.x = point1.x * (1 - loc) + point2.x * loc and point3.y = point1.y * (1 - loc) + point2.y * loc. This will even work for values outside of 0-1, such as a point on the line connecting point1 and point2 but not between them.

Zooming an object based on mouse position

I've got a large large Sprite, and I want to zoom in and out keeping mouse position as the pivot point, exactly how google maps or the photoshop zoom works, when you rotate the mouse wheel.
To archieve the zoom effect I tween the scaleX and scaleY properties, but the fixed point is (0,0), while i need to be the mouse position (that changes everytime, of course).
How can I change this?
thanks.
I just did a Google search for "zoom about an arbitrary point" (minus the quotes) and the first result looks promising.
You have to take the original offset out the scale and then reapply it at the new scale. I've done this myself but don't have the code to hand right now, so I'll see if I can dig it out and post it later.
The pseudo code for this is something like this (from memory):
float dir = UP ? 1 : -1;
float oldXscale = Xscale;
float oldYscale = Yscale;
Xscale += dir * increment;
Yscale += dir * increment;
newX = (oldX - Xoffset) / Xscale;
newY = (oldY - Yoffset) / Yscale;
Xoffset += (newX * oldXscale) - (newX * Xscale);
Yoffset += (newY * oldYscale) - (newY * Yscale);
Anything not declared is a "global"
Found out by searching the right words on google ...
This link explains the Affine Transformations http://gasi.ch/blog/zooming-in-flash-flex/
and so I can Tween the transformation Matrix:
var affineTransform:Matrix = board.transform.matrix;
affineTransform.translate( -mouseX, -mouseY );
affineTransform.scale( 0.8, 0.8 );
affineTransform.translate( mouseX, mouseY );
var originalMatrix:Matrix = board.transform.matrix;
TweenLite.to(originalMatrix, 0.7, {a:affineTransform.a, b:affineTransform.b, c:affineTransform.c, d:affineTransform.d, tx:affineTransform.tx, ty:affineTransform.ty, onUpdate:applyMatrix, onUpdateParams:[originalMatrix]});
:-)
private static function onMouseWheel(event:MouseEvent):void {
var zoomAmount:Number = 0.03;
if (event.delta < 0)
zoomAmount *= -1;
var x:int = largeLargeSprite.mouseX;
var y:int = largeLargeSprite.mouseY;
largeLargeSprite.scaleX += zoomAmount;
largeLargeSprite.scaleY += zoomAmount;
largeLargeSprite.x -= x * zoomAmount;
largeLargeSprite.y -= y * zoomAmount;
}