
-----------------------------------
Aziz
Fri Aug 25, 2006 8:26 pm

[Tutorial] Moving an Object Towards a Target
-----------------------------------
Moving an Object Towards a Target

Introduction

The object of this tutorial is to move an object (an (x,y) point) towards a target (another (x,y) point). This can include a ball moving towards another ball, a worm moving towards the cursor. If you have two points, you can make one move to the other (and with some creative thinking, make them both move towards each other, make one accelerate, etc!)

Let's get on with it

Okay, let's start with a simple example. We want to make a ball move around the screen. Okay, simple enough. The ball will have an x and y coordinate (we won't worry about radius). And for every animation loop, we'll move the ball a certain amount, we'll call this value 'speed'. So we move the ball like this.

x += speed
y += speed

But of course the ball won't always be moving 45 degrees, it could be moving horizontally or vertically. We replace 'speed' with two value, xSpeed and ySpeed.

x += xSpeed
y += ySpeed

Here's an example program:

 int x = 10
int y = 20

int xSpeed = 10
int ySpeed = 5

loop
    x += xSpeed
    y += ySpeed
end

That's all good. The ball will move across the screen at a shallow slope, increasing in x and lot more than y. That's all good.

Speed of an object

Okay, that's nice, but what if we want to know how fast our ball is moving? I don't mean how fast it's moving in the x or y direction, I mean, overall. When you're traveling in a direction between north and north-east at 50 km/h, you're still going 50km/h if you change to going north. So our ball, in the example above, isn't moving at 5 pixels per loop, or 10 pixels per loop.

What speed is it going? First, let's rename xSpeed and ySpeed. We'll call them dx and dy, or delta x and delta y. Because they're the x and y differences between the ball's current coordinate and the ball's next coordinate.

Okay, let's look at the dx and dy as vectors. If you remember grade 10 science, you remember that vectors have magnitude and direction. Well, dx has a magnitude of 10 and a direction in the +ve x direction, while dy has a magnitude of 5 and a direction in the +ve y direction.

Now, this is from grade 11 physics, so it might be new to you. Try to follow, and trust me. If you have any questions, I'm usually on the irc channel.

Let's make a vector diagram. B(10,20) is the ball's current coordinate. We'll draw dx going to the right, because we're adding that onto x. And we'll then draw dy going up, because we're adding that onto y. We can figure out where these get us, and that's our new point. Now, the vector labelled 'v' is the speed or velocity of the ball, and is drawn from B to the new position:

http://i7.photobucket.com/albums/y287/aziz88/image1a.jpg

From this diagram, we can see that v = dx + dy (a little bit of physics, that's call vector addition. When you add vectors a and b, you draw a and then draw b starting from the head of a). So we can arrange the vectors into a vector addition diagram:

http://i7.photobucket.com/albums/y287/aziz88/image1b.jpg

How this helps us you ask? Well, we want to know the length of 'v' here, because that's the ball's velocity. (Remember velocity is the change in distance of the change in time). How does this diagram help us? Look at it, it's a right triangle! And I know you passed grade 9 math, so we can easily use Pythagorean's theorem, a^2 + b^2 = c^2, where a and b are sides of a right triangle and c is the hypotenuse.

So in this case

v^2 = dx^2 + dy^2

or

v = sqrt(dx^2 + dy^2)

So, let's update are example to calculate and show the ball's speed. In the example we'll use speed instead of 'v' for sake of readability:

 int x = 10
int y = 10

int dx = 10
int dy = 5

int speed

loop
    x += dx
    y += dy
    
    speed = sqrt(dx^2 + dy^2)
    
    print("Speed = " + speed)
end

Our output should be:

11.18034

So we'll round that off, and say our ball is moving at 11.2 pixels per loop.

Now, off course, this is only the first step. We don't even know the problem.

The Problem

What's the problem I'm trying to address? Well, just this. Say you want you're object to move towards a point. You would find the difference in x and y between you're object and the target.

    int diffx = targetx - x
    int diffy = targety - y 

You'll move you're ball towards in the x direction and in the y direction. A simple selection structure:

    if diffx < dx
        x = targetx
    else if diffx > 0
        x += dx
    else if diffx < 0
        x -= dx
    end
    
    if diffy < dy
        y = targety
    else if diffy > 0
        y += dy
    else if diffy < 0
        y -= dy
    end

What we're doing here:

First, if the distance between this x/y and the target's x/y is less than the ball's movement speed in that direction, we'll simply move right to the target x/y (since we don't want to go over it). Otherwise, we'll move pos/neg x/y direction in order to get to that point. The object will move according to its own x and y speeds.

What's the problem with this? Well, look at what can happen:

http://i7.photobucket.com/albums/y287/aziz88/image1c.jpg

If the ball reaches the target's x position before the y position, or vice versa (which, if the target is not pre-calculated, will almost always happen), we get some weird motion. Now say you're trying to get a ball to move towards something. If you walk towards something, do you do some crazy weird path? No, you go straight towards it.

So how do we do this? Watch and be amazed! This is how we move an object. We move it by changing its x and its y. And that will give us a speed. So, if we want to move an object towards something, we can't predetermine it's x and y changes. We do need something, and we can set a speed. It's pretty simple to want your ball to move at about 30 pixels per loop, right? Well, it's a little more complicated to do it, but it makes sense. So we have 3 values for movement:  dx, dy, and speed.

http://i7.photobucket.com/albums/y287/aziz88/image1d.jpg

We can find any of the other values if we have the two others.

speed^2 = dx^2 + dy^2
dx ^2 = speed ^2 - dy^2 
dy ^2 = speed ^2 - dx ^2

dx and dy will form a triangle with speed because we want the ball to move at a constant speed, or rather, the speed we set it to.

But we only have one value, speed. So we'll turn to the idea of proportional triangles. If we have two similar triangles, their sides are proportional. Take the following diagram:

http://i7.photobucket.com/albums/y287/aziz88/image1e.jpg

The smaller triangle has sides a, c, and e. The large triangle has sides (a+b), (c+d), f

So, in this example, a/(a+b) = c/(c+d) = e/f

This is also true the other way around, (a+b)/a = (c+d)/c = f/e.

BE WARNED though, a/(a+b) != (c+d)/c. All the sides of one triangle have to be on the top of the fractions, while the sides of the triangles are on the bottom.

How do we apply this? Well, let's make a diagram with our movement triangle and the target. We want the ball to move towards the target, so we'll set it's speed in that direction.

http://i7.photobucket.com/albums/y287/aziz88/image1f.jpg

I've introduced some new values here: diffx and diffy are the x/y differences between the ball and the target. And 'dist' is the distance between the ball and the target, which can be easily found using Pythagoreans theorem with diffx and diffy.

We can use the rule above. Here's how it works:

 speed    dx      dy
----- = ----- = -----
dist    diffx   diffy 

Values we know (given)

 diffx -> targetx - x
 diffy -> targety - y
 dist -> Pythagoreans theorem using diffx and diffy
 speed -> We set it to some value


And what we don't know, (coincidently what we need in order to move the ball):


 dx
 dy


Now, our fractions, with # replacing our known values. Also, so you can read this better:

 ##   dx   dy
-- = -- = --
##   ##   ##

Split this up into to expressions:

 dx / diffx = speed / dist
dx = (speed / dist) * diffx

And in the same way:

 dy = (speed / dist) * diffy

So now we have a direct way to find dx and dy. And we're translating our ball by dx and dy, so thus we have a direct way to move our ball directly towards a target. And you can bet that sqrt(dx^2 + dy^2) will be exactly speed. Or, as close as possible. I recommend leaving dx and dy as a double/real type, and round when you change the x and y values. Also, leave 'dist' as a double/real, and you don't have to round it ever.

Here's an example program where the ball will follow the mouse cursor at 30px/loop, written in no particular language but easy to convert to any.

 //Start our ball at (10,10)
int x = 10
int y = 10

//The x and y offsets to move the ball
double dx
double dy

//Our ball will move 30 pixels per loop
int speed = 30

//Values for the target's x and y
int targetx
int targety

//The loop
loop
    //Set the target to the mouse's position
    targetx = mouseX
    targety = mouseY
    
    //Find the x and y differences between the ball and the target 
    int diffx = targetx - x
    int diffy = targety - y
    
    //Calculate the distance between the ball and the target
    double dist = sqrt(diffx^2 + diffy^2)
    
    //Calculate the x and y offsets to move the ball
    dx = (speed / dist) * diffx
    dy = (speed / dist) * diffy
    
    //If the ball moves past the target, keep it at the target
    if abs(diffx) < abs(dx)
        x = targetx
    end
    
    if abs(diffy) < abs(dy)
        y = targety
    end
    
    //Move the ball
    x += round(dx)
    y += round(dy)
    
    //Draw the ball
    drawball(x, y)
end

There you go! Try it, any questions or suggestions PLEASE post, or talk to me on the irc channel. Thanks for listening :)

-----------------------------------
Aziz
Sat Aug 26, 2006 12:56 am


-----------------------------------
Here's an example program in Turing

View.Set ("graphics:800;600,position:center;center,title:Chase the mouse,offscreenonly")

%Start our ball at (10,10)
var x : int := 10
var y : int := 10

%The x and y offsets to move the ball
var dx, dy : real

%Our ball will move 5 pixels per loop
var speed : int := 5

%Values for the target's x and y
var targetx, targety : int

%Just a button value, no use to us
var button : int

%The loop
loop
    %Set the target to the mouse's position
    Mouse.Where (targetx, targety, button)

    %Find the x and y differences between the ball and the target
    var diffx := targetx - x
    var diffy := targety - y

    %Calculate the distance between the ball and the target
    var dist := sqrt (diffx ** 2 + diffy ** 2)

    %If the ball is ontop of the mouse, set it there, otherwise calculate movement.
    %This prevents divide by zero errors.
    if dist 