Advertisement

Smooth Diagonal Movement

Started by August 05, 2000 09:02 AM
7 comments, last by Rappler 24 years, 4 months ago
In my soon to be completed arcade style shoot'em up, Ninja Pilot (don't ask) I've come across a problem in figuring out a way to get an entity smoothly from point A to point B. Say you have the player at point (30, 100) on the screen. An enemy that will fire at him is at (20, 25). The projectile needs to travel a smooth path from (20, 25) to (30, 100). Currently, I am calculating the slope between the two points and incrementing the position 1 pixel at a time every frame until the slope has been reached. The problem is this: If there are large differences in the slopes, like the X slope is 3 and the Y slope is 100, there is a movement of diagonal for 3 pixels then, since the x slope has been reached, in will move straight down for 97 pixels. Not smooth at all. It's hard to explain, I can post a small piece of code if needed. Now you say to yourself, "Why doesn't he just increment the position of the entity by the slope every frame, that would give smooth movement.". Why yes, it does, but if the slope works out to be 125, 53 or something else very high, the entities will move a huge distance in one frame, which is worse than what I described earlier. What I need to do is find a way to make sure the slopes are reduced, for example 10/100 == 1/10, but then primes get in the way, and the speed of the entity is still dependent on the slope. I am totally stumped and would greatly appreciate any help. I'll probably post the code in a moment since the scenario is hard to understand... EDIT: Hot damn! I'm a fanatic now Edited by - Rappler on 8/5/00 9:03:31 AM
"The Gates of Pearl have turned to gold, it seems you've lost your way."
This is what I am doing currently:

    if(Current->XSlopeCounter < Current->XSlope)					{						Current->WorldX += Current->XVel;						Current->BoundingBox.left += Current->XVel;						Current->BoundingBox.right += Current->XVel;						Current->XSlopeCounter++;					}					else						Current->XFull = 1;																			if(Current->YSlopeCounter < Current->YSlope)					{						Current->WorldY += Current->YVel;						Current->BoundingBox.top += Current->YVel;						Current->BoundingBox.bottom += Current->YVel;						Current->YSlopeCounter++;					}					else						Current->YFull = 1;										if(Current->XFull && Current->YFull)					{						Current->XSlopeCounter = 0;						Current->YSlopeCounter = 0;					}    


Hope that tag works...
"The Gates of Pearl have turned to gold, it seems you've lost your way."
Advertisement
there are two ways to implement this

1. Use bresenham line drawing algorithm
2. get the angle and use sin and cos to move at a constant rate.

I meantioned Bresenham''s Line algorithm because it uses integers only and therefor is slightly faster than a normal line drawing algorithm (i.e. a one that uses floats).

I can not remember the exact code for it, but I''m sure that ethier someone on here does, or it will be out on the web somewhere.

Another option would be to use floats to store the increase along the x and y axis as well as storing the x and y pos of the entity.
When I find my code in tons of trouble,Friends and colleages come to me,Speaking words of wisdom:"Write in C."My Web Site
for gods sake, use sin and cos (and put them in look-up tables).
And if you dont know how to use sin and cos (and even tan if ya know what you are doing) then get you a book on trigonometry.
Why put them in look-up tables ? sin and cos are VERY VERY VERY slow math functions.


"Now go away or I shall taunt you a second time"
- Monty Python and the Holy Grail
themGames Productions

Wouldn''t it be a lot easier to just take the diffrences between the X and Y positions (from the player and the enemy) and divide the bigger one with the smaller one. then every X/Y or Y/X (depends on which one is bigger) frame increase or decrease (depends on where u want it to go.) the smaller value.

    int i, j;int x, y;int smallermovement;x = player.position.x - enemy.position.x;y = player.position.y - enemy.position.y;if(x > y){    smallermovement = x / y;}else{    smallermovement = y / x;}for(i = 0; i < distanceinpixels; i++){     if(x > y)     {      x += movementx * speed;  // movementx can either be +1                        // -1 ( depending on if the player is on                        // the left or on the right of the enemy)     }     else     {      y += movementy * speed; //the same if the ydistanz is >     }          if(i % smallermovement != 0)     {        if(x > y)        {          x += movementx * speed;                 }        if(y > x)        {           y += movementy * speed;        }     }...}    

oh, i forgot: speed is the speed of the missle in pixels / frame.
would that work?
(It would give an aliased line)
With that u wouldn''t have to use the sin() and cos() funktions or Look up tables.

that''s how i''d do it.

hope that helped,

cya,

Phil

Ok, if you want to move 1 pixel at a time Bresenham''s pisses all over all others from a great height. Here''s the short version

dx = abs(x1-x0)
dy = abs(y1-y0)
xstep = 1 if x1 > x0 -1 otherwise
ystep = 1 if y1 > y0 -1 otherwise
vertical and horizontal lines are left as an excersise

if dx > dy then we want to move 1 pixel in the x direction every frame, so the only question is how often to move 1 pixel in the y direction. After some algebra we come up with this.

d = 2*dy-dx
n = 2*dy
p = 2*(dy-dx)

at each step

if(d<=0) {
d+=n;
} else {
y += ystep;
d+=p;
}
x += xstep;


dy > dx is left as an excersize

Your real problem is that your projectile''s speed is faster for diagonal movement, and dependant on your framerate. Lot''s of good games have used this, but it''s kind of out of style. You may want to go ahead and use float''s and a velocity vector.

float len = sqrt(dx*dx+dy*dy)
float velx = (x1-x0)/len
float vely = (y1-y0)/len

then your x is just time*speed*velx. y is also an excersize.

Advertisement
forget the path following stuff. calculate the angle needed to hit the desired point, then set the missiles x and y speed accordingly:

float angle = MyCalcAngleFunc(sourcePoint, destPoint); //in RADIANS
xvel = SPEED * cos(angle);
yvel = SPEED * sin(angle);

//then just update the missiles position every frame with:

xpos += xvel;
ypos += yvel;

since the trig functions are only done once (ie at creation time) there is no need to muck around with lookup tables etc.

i havent tested this, so if at first it dont succeed, tweak it
How would you calculate the angle needed? I can do everything I need to do on paper w/ a protractor, but with straight math, I''m lost

There''s probably a formula, but I sure don''t know it...
"The Gates of Pearl have turned to gold, it seems you've lost your way."
to get the angle from a point x0,y0 to a point x1,y1 the fastest way is arctan, but you only use sin and cos if you already have the angle. For example if you have a rotating gun. otherwise just calculate sin and cos yourself, remember SOHCAHTOA?

sin = (y1-y0)/sqrt((x1-x0)^2+(y1-y0)^2)
cos = (x1-x0)/sqrt((x1-x0)^2+(y1-y0)^2)



This topic is closed to new replies.

Advertisement