 A Flash Developer Resource Site

# Thread: Moving ball between two points... atan2?

1. ## Moving ball between two points... atan2?

Hey guys. I'm getting really frustrated with this. I created a little flash document to help me understand how to use Math in Flash to move an object to a target X and Y at a constant speed, and I can't seem to get ANYTHING to work. This is what I have so far... first, I've attached an image of the app I'm creating:
picture basically, I just put in the number of the point (1-4) and have the ball move to that point. Everything works except for the trajectory of the ball. I've got the angles right because when I set the rotation of the ball equal to the angle that I get, the black dot lines up with whichever blue line applies. The only problem is, the speed is messed up. Here's what I need help with:

PHP Code:
``` atanX = (targetX - startX);atanY = (targetY - startY);angle = Math.atan2(atanY, atanX)angle = Math.round(angle/Math.PI * 180);var speed:Number = 4;xSpeed = Math.cos(angle)*speed; //these are the two lines ySpeed = Math.sin(angle)*speed; //that must be wrong        guy_mc.rotation = angle;this.addEventListener(Event.ENTER_FRAME, moveGuy);function moveGuy(e:Event):void{     guy_mc.x += xSpeed;     guy_mc.y += ySpeed;}  ```
Someone please look at this and tell me what I'm doing wrong.  Reply With Quote

2. Is it moving along the lines really slowly?

If so, maybe you should work out the length of the line and make the speed proportional to it.  Reply With Quote

3. No, it's just moving in completely wrong directions.

The following lines:
PHP Code:
``` var speed:Number = 4;xSpeed = Math.cos(angle)*speed; //these are the two linesySpeed = Math.sin(angle)*speed; //that must be wrong  ```
are supposed to calculate the percentage of speed that should be applied to X movement and the percentage of speed that should be applied to Y movement. It seems that I'm just getting the wrong numbers in those areas.  Reply With Quote

4. Take out the line ...

angle = Math.round(angle/Math.PI * 180);

I don't think its required with atan2 ...  Reply With Quote

5. Actually, it converts radians to degrees. I know all that part works, because the angle turns out correct.

In fact, I've got an update. Right now I'm using the same code except for the following changes:
PHP Code:
``` //xSpeed = Math.sin(angle)*speed;  //I'm not using these        //ySpeed = Math.cos(angle)*speed; //two lines anymore        xSpeed = (atanX/(atanX + atanY))*speed;        ySpeed = (atanY/(atanX + atanY))*speed;  ```
The last two lines calculate the total of the two numbers that make up the slope (we'll call it X = atanX + atanY) and then find out what percentage of X is comprised of atanX and what percentage is comprised of atanY. Then, I use those percentages to calculate the percentage of X movement and Y movement. It's working, but it's not completely accurate. My guy is always off by a couple of pixels in each direction. I'm going to attach the file so anyone who wants to can see it. I don't know what 'save and compact' means, but that's what I had to do to get the file size low enough...

Anyway, it's sort of working, but it's not very precise and there MUST be another way that's easier to do. Anyone got any more ideas?  Reply With Quote

6. Originally Posted by Taidaishar
Actually, it converts radians to degrees. I know all that part works, because the angle turns out correct.
I know that ... ;-) its just that initially you said ... Originally Posted by Taidaishar
No, it's just moving in completely wrong directions
Best of luck to you ..  Reply With Quote

7. Originally Posted by TechNoiZ
I know that ... ;-) its just that initially you said ...

Best of luck to you ..
Yeah, and it is moving in the wrong direction, but the angles that I'm getting from the Atan2 function are correct. When I plug those angles into the ball as the rotation, the ball rotates to face the correct direction, but the x and y speeds weren't working as they should and so it faced the correct direction and then just went the completely wrong direction. Anyway, thanks for your suggestions and help. Anyone else out there looking in this forum?  Reply With Quote

8. Using cos and sin gets buggy due to how the signs are twiddled. You will get better results, both in accuracy and performance by using vectors. Here's some quick psuedo-code as an example:
PHP Code:
``` endX = point.x; endY = point.y; vX = endX - ball._x;  // x vector vY = endY - ball._y;  // y vector dist = Math.sqrt(vX * vX + vY * vY);  // distance from start to finish nX = vX / dist;  // normalized x vector nY = vY / dist;  // normalized y vector vx = nX * speed;  // x motion vector (adjust speed to suit your needs) vy = nY * speed;  // y motion vector (same as above) ball.onEnterFrame = function(){     if(this._x != endX){         this._x += vx;         this._y += vy;     } };  ```  Reply With Quote

9. Originally Posted by JerryScript
Using cos and sin gets buggy due to how the signs are twiddled. You will get better results, both in accuracy and performance by using vectors. Here's some quick psuedo-code as an example:
PHP Code:
``` endX = point.x; endY = point.y; vX = endX - ball._x;  // x vector vY = endY - ball._y;  // y vector dist = Math.sqrt(vX * vX + vY * vY);  // distance from start to finish nX = vX / dist;  // normalized x vector nY = vY / dist;  // normalized y vector vx = nX * speed;  // x motion vector (adjust speed to suit your needs) vy = nY * speed;  // y motion vector (same as above) ball.onEnterFrame = function(){     if(this._x != endX){         this._x += vx;         this._y += vy;     } };  ```
Beautiful. It works a lot better than mine did. Thank you SOOO much. I still have a question though if you're available:
I have it set up so the ball stops when it gets to the point... like this:

PHP Code:
``` if(ball.x >= targetX && ball.y >= targetY) {     ball.x = targetX     ball.y = targetY     removeEventListen....(Enterframe...blahblah) }  ```
I have different code to account for whether or not the points are above below to the left and right of the ball, I just posted one example for brevity's sake. Anyway, the ball seems to be a very little bit off on some of the trips it makes. Like, say it's going from x:100, y:100 to x:500, y:120 (almost a perfectly horizontal line). Well, it will pass the targetX at about y:115 or something and then snap back when it gets to the correct numbers. I tried changing the '&&' symbols to '||' to just test for ONE of the factors (x or y) and then, sometimes it will get to the right Y or X early and jump ahead to the point. Is there any way for it to be more exact, or is this the best I'm going to get?  Reply With Quote

10. I'm not sure why it's doing that, could you post the code you are using?

Rather than a bunch of different checks for each orientation, you could use a variable to keep track of the distance traveled, and stop motion when that distance is equal to or greater than the originally determined distance.  Reply With Quote

11. That's a very good idea, and if something in my code doesn't stick out to you as wrong, I may end up doing just that. Here's what I have... be gentle... I only just started AS 3 a little while ago:
PHP Code:
``` var a:Array = [{x:73, y:313},{x:503, y:333},{x:300, y:173},{x:490, y:80}]; var targetX:Number; var targetY:Number; var startX:Number; var startY:Number; var atanX:Number; var atanY:Number; var dist:Number; var nX:Number; var nY:Number; var xSpeed:Number; var ySpeed:Number; var speed:Number = 6; var angle:Number; var angleNum:Number; var increaseX:Boolean; var increaseY:Boolean; move_btn.addEventListener(MouseEvent.CLICK, startMove); reset_btn.addEventListener(MouseEvent.CLICK, reset); function startMove(e:MouseEvent):void {     angleNum = (Number(this.input_txt.text))-1;     targetX = a[angleNum].x;     targetY = a[angleNum].y;     startX = guy_mc.x;     startY = guy_mc.y;     if ((startX != targetX) && (startY != targetY))     {         atanX = (targetX - startX);         atanY = (targetY - startY);                  dist = Math.sqrt(atanX * atanX + atanY * atanY);                  angle = Math.atan2(atanY, atanX);         angle = angle/Math.PI * 180;                  nX = atanX/dist;         nY = atanY/dist;         xSpeed = nX*speed;         ySpeed = nY*speed;                  guy_mc.rotation = angle;         this.addEventListener(Event.ENTER_FRAME, moveGuy);     } } function reset(e:MouseEvent):void {     this.removeEventListener(Event.ENTER_FRAME, moveGuy);     guy_mc.x = 100;     guy_mc.y = 100; } function moveGuy(e:Event):void {     if (startX < targetX)     {         increaseX = true;     }     else     {         increaseX = false;     }     if (startY < targetY)     {         increaseY = true;     }     else     {         increaseY = false;     }     guy_mc.x += xSpeed;     guy_mc.y += ySpeed;     if (increaseX)     {         if (increaseY)         {             if (guy_mc.x >= targetX || guy_mc.y >= targetY)             {                 this.removeEventListener(Event.ENTER_FRAME, moveGuy);                 guy_mc.x = targetX;                 guy_mc.y = targetY;             }         }         else         {             if (guy_mc.x >= targetX || guy_mc.y <= targetY)             {                 this.removeEventListener(Event.ENTER_FRAME, moveGuy);                 guy_mc.x = targetX;                 guy_mc.y = targetY;             }         }     }     else     {         if (increaseY)         {             if (guy_mc.x <= targetX || guy_mc.y >= targetY)             {                 this.removeEventListener(Event.ENTER_FRAME, moveGuy);                 guy_mc.x = targetX;                 guy_mc.y = targetY;             }         }         else         {             if (guy_mc.x <= targetX || guy_mc.y <= targetY)             {                 this.removeEventListener(Event.ENTER_FRAME, moveGuy);                 guy_mc.x = targetX;                 guy_mc.y = targetY;             }         }     } }  ```  Reply With Quote

12. I don't see any problem with the vector code, though your variable names and leaving in the atan2 function threw me at first.

I'm pretty sure the issue is with your nested if/else checks. A better approach would be to check the current distance, and if it is less than the step distance, move to the target point.
PHP Code:
``` // in your vector code, add a variable to hold the step distance stepDist = Math.sqrt(xSpeed * xSpeed + ySpeed * ySpeed); // then use something like this function moveGuy(e:Event):void  {     var currentDist:Number = Math.sqrt(targetX - guy_mc.x * targetX - guy_mc.x + targetY - guy_mc.y * targetY - guy_mc.y);     if(currentDist < stepDist){          guy_mc.x = targetX;          guy_mc.y = targetY;          this.removeEventListener(Event.ENTER_FRAME, moveGuy);     }else{         guy_mc.x += xSpeed;         guy_mc.y += ySpeed;     } }  ```
To optimize for speed, calculate both stepDist and currentDist without the Math.sqrt (the check is valid squared or not, but Math.sqrt is slow):
PHP Code:
``` stepDist = (xSpeed * xSpeed + ySpeed * ySpeed); var currentDist:Number = (targetX - guy_mc.x * targetX - guy_mc.x + targetY - guy_mc.y * targetY - guy_mc.y);  ```  Reply With Quote

13. Come on people... It's take how many posts and only one person has spotted the problem?

Code:
```atanX = (targetX - startX);
atanY = (targetY - startY);

angle = Math.atan2(atanY, atanX)

var speed:Number = 4;

xSpeed = Math.cos(angle)*speed; //these are the two lines
ySpeed = Math.sin(angle)*speed; //that must be wrong - Because you are feeding them degrees when they want radians```
All math operations take radians while the movieclip.rotation takes degrees.

If you want to use vectors the best method would be to normalize the displacement from the object to the target and perform scalar multiplication by the speed.  Reply With Quote

14. Originally Posted by 691175002
If you want to use vectors the best method would be to normalize the displacement from the object to the target and perform scalar multiplication by the speed.
That's exactly what my sample code does, normalizes and multiplies by speed.

Perhaps you have the same problem seeing things as you say everyone else does?   Reply With Quote

15. I didn't bother reading the entire thread since the initial problem was clear.
IMO For something this simple trig is the best choice anyways since it is more clear what is going on, and you are going to do an atan2 anyways to rotate the object.

Having it stop once it reaches the destination is also quite straightforward.
Heres some sample code, copy paste it onto a movieclip in an as2.0 project:
Code:
```/* AS2 Code because its quicker to type up,
* easilly copy-pastable and all in one place.
*/
var nAngle:Number;
var nSpeed:Number = 3;
var nDistance:Number = 0;
var nTargetX:Number;
var nTargetY:Number;
}

onClipEvent (mouseDown) {
nTargetX = _root._xmouse;
nTargetY = _root._ymouse;

// Determine the angle (In radians) and the
// distance to the target.
nAngle = Math.atan2(nTargetY-_y, nTargetX-_x);
nDistance = Math.sqrt((_y-nTargetY)*(_y-nTargetY)+(_x-nTargetX)*(_x-nTargetX));
}

onClipEvent (enterFrame) {
//This line reduces the distance to the target by the speed
//Then checks to make sure that the disance is still greater than 0
//to avoid moving past the target.
if ((nDistance -= nSpeed) >= 0) {
// Keep the angle in radians for math
// Note that "Math.cos(nAngle) * nSpeed"
// could be precalculated in the mouseDown
// for speed.
_x += Math.cos(nAngle) * nSpeed;
_y += Math.sin(nAngle) * nSpeed;

// Convert to degrees for display
_rotation = nAngle * (180/Math.PI);
}
}```  Reply With Quote

16. Originally Posted by 691175002
Come on people... It's take how many posts and only one person has spotted the problem? Originally Posted by 691175002
I didn't bother reading the entire thread since the initial problem was clear.
Thanks for the variation on code, but the problem has already been fixed.  Reply With Quote

17. I believe an explanation of why his original code didn't work is just as important as an alternate solution.  Reply With Quote

18. I agree, I was just confused where your comments were coming from based on what you posted. First you say only one person found the problem after so many posts, indicating you had read the entire thread, then you stated you didn't read the entire thread. So I wasn't sure exactly what you had read, and therefor if you realized the problem was solved.  Reply With Quote

19. @Taidaishar
Hey! Your original code works [F8]. Only thing is that you have to use radians
instead of degrees for sin, and cos functions.
Code:
```attachMovie("guy_mc","guy_mc",100,{_x:500,_y:300});
attachMovie("target_mc","target_mc",200,{_x:100,_y:100});
var speed=10;
targetX=target_mc._x;
targetY=target_mc._y;
startX=guy_mc._x;
startY=guy_mc._y;
atanX = (targetX - startX);
atanY = (targetY - startY);
ang = Math.atan2(atanY, atanX);
angle = Math.round(ang/Math.PI * 180);
xSpeed = Math.cos(ang)*speed;
ySpeed = Math.sin(ang)*speed;
guy_mc.onEnterFrame=function(){
guy_mc._x+=xSpeed;
guy_mc._y+=ySpeed;
guy_mc._rotation=angle;
}
stop();```  Reply With Quote

#### Posting Permissions

• You may not post new threads
• You may not post replies
• You may not post attachments
• You may not edit your posts
•

 » Home » Movies » Tutorials » Submissions » Board » Links » Reviews » Feedback » Gallery » Fonts » The Lounge » Sound Loops » Sound FX » About FK » Sitemap 