Moving towards a point over time

Understanding the language, error messages, etc.

Moving towards a point over time

Postby DFX2KX » Mon Sep 01, 2014 6:31 am

Anyone have a good way of doing this? If I have an x,y point to start from, and an x,y where I want to go, what sort of method works best for moving towards it over time?

Working on a new game, and have hit a wall here.

I'd show code, but I don't even know where to begin on this one. (GameMaker's builtin move logic has spoiled me)

Edit: After a few more hours of it. I give up.
DFX2KX
 
Posts: 250
Joined: Mon Apr 14, 2014 3:48 am

Re: Moving towards a point over time

Postby Myndale » Mon Sep 01, 2014 8:34 am

There are lots of ways to do this, which method you use depends on how you want the motion to occur. The most simple case is you have a point at <Sx, Sy> and you want to move at constant velocity towards <Tx, Ty>. I'm being very lazy here and posting C# code but it's close enough to C that you shouldn't have any problems:

Code: Select all
const int Sx=3, Sy=4, Tx=60, Ty=42;
const int numFrames = 10;   // e.g. 10 frames

for (int frameNum = 0; frameNum <= numFrames; frameNum++)
{
   float currentX = Sx + (float)frameNum * (Tx - Sx) / numFrames;
   float currentY = Sy + (float)frameNum * (Ty - Sy) / numFrames;
   Console.WriteLine("{0}: {1} {2}", frameNum, currentX, currentY);
}


What's effectively happening is that the terms "i * (Tx - Sx) / numFrames" and "i * (Ty - Sy) / numFrames" represent velocity (not exactly from a mathematical perspective but conceptually close enough). Another way of looking at it is you're adding velocity to position every frame, which is probably close to what you'd do in an actual game:

Code: Select all
const int Sx = 3, Sy = 4, Tx = 60, Ty = 42;
const int numFrames = 10;   // e.g. 10 frames
const float velocityX = (float)(Tx - Sx) / numFrames;
const float velocityY = (float)(Ty - Sy) / numFrames;

float currentX = Sx, currentY = Sy;
for (int frameNum = 0; frameNum <= numFrames; frameNum++)
{
   Console.WriteLine("{0}: {1} {2}", frameNum, currentX, currentY);

   // add velocity to position to calculate position at next frame
   currentX += velocityX;
   currentY += velocityY;
}


If you want to do anything more complicated than linear interpolation then you need to start implementing something that at least approximates the standard equations of motion. There's a complete wiki page on them at http://en.wikipedia.org/wiki/Equations_of_motion but the ones for accelerated motion that I consistently came back to time and time again during my years in game development were these 3:

  • v = u + a*t
  • s = u*t + 0.5*a*t^2
  • v^2 = u^2 + 2*a*s

...where u = initial velocity, v = final velocity, s = distance, a = acceleration and t = time. (Incidentally these are in fact the same equation expressed 3 different ways: the second is the differential of the first while the third can be derived at via regular substitution). With these equations you can generate most of the non-circular motion in all but some very specialized applications e.g. accelerate up to a maximum velocity, cruise for just the right time then decelerate back down again to arrive at your exact target point.

Let me know specifically what you need to achieve and I'll elaborate further.
Myndale
 
Posts: 507
Joined: Sat Mar 01, 2014 1:25 am

Re: Moving towards a point over time

Postby DFX2KX » Mon Sep 01, 2014 10:06 am

Thanks Myndale. At least it was explained int a way that I could at least read. I kept coming across vectors, which never made much sense to me.

Basically, I'm looking for some sort of logic to control the player missiles in a 'missile command' clone They move in straight lines, at a set speed. If I could come up with some basic logic that would control the movement both ways, that would be awesome. (I know the that the player's missiles always go from the ground up, and viseversa for the AI missiles)

This is the best I've managed so far:
2014-09-01_04-51-00_734.jpg
2014-09-01_04-51-00_734.jpg (34.88 KiB) Viewed 3616 times

So yeah. The line drawing part works, just not the movement. An unrelated note. Finally learned out to use structs in functions, this has made lifea bit easier.

Edit:life, not live, ugh.
DFX2KX
 
Posts: 250
Joined: Mon Apr 14, 2014 3:48 am

Re: Moving towards a point over time

Postby Myndale » Tue Sep 02, 2014 12:38 am

I'd start by packing all the variables for a single projectile into a struct. You don't absolutely need all these values, but they might come in handy later:

Code: Select all
struct projectile
{
   int start_x, start_y;
   int target_x, target_y;
   float current_x, current_y;
   float velocity_x, velocity_y;
   int num_frames;
};

void init_projectile(projectile & proj, int start_x, int start_y, int target_x, int target_y, int num_frames)
{
   proj.start_x = proj.current_x = start_x;
   proj.start_y = proj.current_y = start_y;
   proj.target_x = target_x;
   proj.target_y = target_y;
   proj.velocity_x = (float)(target_x - start_x) / num_frames;
   proj.velocity_y = (float)(target_y - start_y) / num_frames;
   proj.num_frames = num_frames;
}

void update_projectile(projectile & proj)
{
   proj.current_x += proj.velocity_x;
   proj.current_y += proj.velocity_y;
}


Call update_projectile once per frame and it will update the current position, so all you have to do is draw a line between <start-x, start_y> and <current_x, current_y>.

That will be enough to get started, but there are two problems with this approach. The first is that you'll get very small round-off errors building up over time. This isn't a problem for most games, but if you want to do it 100% perfect then you can get rid of those errors by passing in the frame_number (which should be relative to the first frame that the projectile was fired on):

Code: Select all
void update_projectile(projectile & proj, int frame_num)
{
   proj.current_x = proj.start_x + (float)frame_num * (proj.target_x - proj.start_x) / proj.num_frames;
   proj.current_y = proj.start_y + (float)frame_num * (proj.target_y - proj.start_y) / proj.num_frames;
}


The second problem is that your lines will "wobble" across multiple frames. This is the result of aliasing and is due to the fact that your floating-point variable end-points are constantly changing and being "snapped" to the nearest integer. A very simple solution to this is to replace the Gamebuino drawLine function with one that also accepts a cutoff value for y:

Code: Select all
void drawProjectile(int8_t x0, int8_t y0, int8_t x1, int8_t y1, float cutoff) {


And then further on in the function replace this:

Code: Select all
        if (steep) {
            drawPixel(y0, x0);
        } else {
            drawPixel(x0, y0);
        }


With this:

Code: Select all
        if (steep) {
            if (x0 > cutoff) drawPixel(y0, x0);
        } else {
            if (y0 > cutoff) drawPixel(x0, y0);
        }


And then to use this function you pass in the projectile's start and target points along with the current y:

drawProjectile(proj.start_x, proj.start_y, proj.target_x, proj.target_y, proj.current_y).

What this will effectively do is cause the function to always iterate over every pixel in the line using the same start/target endpoints, but any pixels above the current_y won't actually be drawn on screen. This will give the illusion that the end-point is moving whilst also eliminating the jitter.
Myndale
 
Posts: 507
Joined: Sat Mar 01, 2014 1:25 am

Re: Moving towards a point over time

Postby DFX2KX » Tue Sep 02, 2014 1:49 am

Thanks so much Myndale, taht makes complete sense to me. It's a lot simpler then what I'd been trying to make work!
DFX2KX
 
Posts: 250
Joined: Mon Apr 14, 2014 3:48 am


Return to Programming Questions

Who is online

Users browsing this forum: No registered users and 65 guests