acceleration and maxium speed problem...

Started by
8 comments, last by shurcool 22 years, 9 months ago
i want to have a spaceship, that moves left when u press left, up when up is pressed, etc., but i want to have a maxium speed limit. right now i use x_pos += x_vel y_pos += y_vel x_vel += x_accel y_vel += y_accel if (left_key_pressed) { if (x_vel > -10.0f) {x_vel += time_passed / 10.0f} if (x_vel < -10.0f) {x_vel = 10.0f} } and same thing for other keys. but that makes the speed limit too sudden. i want your acceleration to *slowly* slow down to 0 as your speed becomes close to 10.0f. also, it has to work with negative and positive initial speeds. thank you for any help, shurcool.
Advertisement
i''m really sorry for the double-post >, please reply here, and delete the other post.
You essentially want a function with a negative slope, right?

y = m *x + b

m is negative (the slope), y is your new velocity, x is your original velocity, b is the starting velocity for this attenuation function. This will reduce your velocity steadily from b to zero - and beyond. It's your responsibility to check if y is zero and stop then.

Of course, that's total junk. A better solution is to use a simple parabolic of the form

y = a *x 2 + b

and confine the range of your x . This would ensure that your value ramped up smoothly to a maxium value b when x was zero, and then ramped down. a is just a constant multiplier. To restrict x to positive values (from 0 to c ), use

y = a *(x - c )2 + b

You want to position your b such that it was the speed at which your acceleration started to decrease. You could also use a sine function to achieve the effect. If you wanted a rapid initial acceleration, slowing to a plateau and then gently decreasing to zero, you'd need to aggregate functions (add them together). A few sine and cosines of carefully selected complements, summed up, would give you the desired effect. You could also bias (add a constant or variable to) the function for values of x less than a certain threshold.

Hope I've helped to not only provide a solution but also provoke thought on better ones.

**Edited for consistent typeface

Edited by - Oluseyi on July 3, 2001 7:35:20 PM
You can add a friction force, just like in the real life. After all, friction with the air, the water, etc. is the reason why cars, planes, etc. have maximum speeds. Here

#define MAX_VEL 10.0
#define MAX_ACCEL 1.0
#define FRICTION (MAX_ACCEL/MAX_VEL)

x_pos += x_vel
y_pos += y_vel
x_vel += x_accel - xvel * FRICTION // the faster the object moves
y_vel += y_accel - yvel * FRICTION // the greater the friction force


Note that the maximum speed is MAX_VEL : if you accel to the max and your speed reaches MAX_VEL, the speed cannot grow anymore :
x_vel += MAX_ACCEL - MAX_VEL * (MAX_ACCEL/MAX_VEL)

is equivalent to
x_vel += 0


If you try this, tell me if it works.

Edited by - Diodor on July 4, 2001 5:29:56 PM
Alternatively, use a logarithmic scale for your velocity. This gives a realistic looking acceleration and deceleration. Here's some (pseudo) code.

(In 1 dimension (easy to extrapolate to N dimensions))

Variables:

iIndex: a number that represents the sum of all left key strokes (which count for -1) and all right key strokes (which count for +1).

rVelocity: the 1-D speed of the object on a logarithmic scale

pseudo-code:

  int   iIndexfloat rVelocityif (KEY_PRESSED){   if (key==LEFT_KEY)   {      decrement iIndex    }   if (key==RIGHT_KEY)   {      increment iIndex   }   rVelocity = V_MAX * (1 - exp(-iIndex*iIndex))}   


where exp(x) is a function that returns the exponential e x.

Good luck,

Tim

Edited by - Timkin on July 3, 2001 12:09:55 AM
quote:Original post by Diodor
You can add a friction force, just like in the real life. After all, friction with the air, the water, etc. is the reason why cars, planes, etc. have maximum speeds. Here

#define MAX_VEL 10
#define MAX_ACCEL 1
#define FRICTION (MAX_ACCEL/MAX_VEL)

x_pos += x_vel
y_pos += y_vel
x_vel += x_accel - xvel * FRICTION // the faster the object moves
y_vel += y_accel - yvel * FRICTION // the greater the friction force



This should work (except in C it defines FRICTION as 0) but it''s not realistic. Friction generally is proporitional to the square of the speed rather than the speed. So instead of

accn = thrust - vel * friction

it should be

accn = thrust - speed * speed * friction.

or, in 2D and using the same format as above,

x_pos += x_vel
y_pos += y_vel
x_vel += x_accel - xvel * speed * FRICTION
y_vel += y_accel - yvel * speed * FRICTION

with

speed = sqrt(xvel * xvel + yvel * yvel)
FRICTION = MAX_THRUST / (MAX_SPEED * MAX_SPEED)
John BlackburneProgrammer, The Pitbull Syndicate
thanks, guys, you''ve really helped a lot! i have changed my needs a little bit, because now i will not only have arrow keys moving the ship in 4 directions (4 more if you press 2 keys at once), but it will be able to rotate, so that you face different directions. i will figure out how to do that, since now i know how to figure out the friction. and all i needed was: friction = speed * speed * friction_constant (0.1 in my case). thanks a lot,
shurcool.
Neat stuff johnb, gotta write it down.
Some interesting suggestions here. This was my quick-and-dirty take:

You don''t say how you''re calculating x_accel. I''m assuming that you have it defined to be a constant value that depends on the button pressed, for example x_accel = 0 if no button pressed, x_accel = -1.0 if left pressed, x_accel = 1.0 if right pressed.

A quick-and-dirty approach would be to set x_accel as a function of how close to the speed limit you are. For example, you could say,

if (right_button_pressed)
{
x_accel = 1.0 * (speed_limit - fabs(x_vel))/speed_limit;
}

See how it works. If speed_limit is 10, then when x_vel is 0, x_accel is 1.0, and when x_vel is 10, x_accel is zero. And when x_vel is 9.0, x_accel is only 0.1 so you are accelerating slower when traveling at a speed close to the speed limit.

The function for left button pressed would be:

if (left_button_pressed)
{
x_accel = -1.0 * (speed_limit - fabs(x_vel))/speed_limit;
}

There are certainly more complex ways to do this, but I think this may do the trick for you.

If you make sure the acceleration is zero when you reach the speed limit, you may be able to get rid of the logic you have now that deals with x_vel < -10.0f, etc. (There seem to be some bugs in your logic anyway.)

Question: Are you setting your x_accel and initial x_vel so that the time_passed is built in? That is, the true equations of motion suggest that x_pos += x_vel * time_passed and x_vel += x_accel * time_passed, but you do not have time_passed in your equations.... No big deal for simple physics, as long as your accelerations are scaled to make things look good enough for your game.


Graham Rhodes
Senior Scientist
Applied Research Associates, Inc.
Graham Rhodes Moderator, Math & Physics forum @ gamedev.net
hey,
i am using time_passed in it, and that constant acceleration is dependant on keys pressed. i also made sure that when up and right arrow keys are pressed, user will not go faster than if he only pressed the up key, by making the x and y accelration variables equal to sin(45) (in degrees) times 0.001 (that''s what y_accel is equal to when up key is pressed). i have also tried the method grhodes_at_work suggested (the last one, "quick-and-dirty take"), but the air resistance way seems to work better and more realistic. i will post the source code of the physics part here, and you can download the exe at http://www16.brinkster.com/shurcool/ (but please note that i haven''t worked on graphics part yet, just the physics! so please don''t scream at my 7 lines and strigs of text! ). here''s the physics part source code, i think it works fine for my needs:

if (player[player_num].vel_x > 0.0f)
{
player[player_num].vel_x -= player[player_num].vel_x * player[player_num].vel_x / 20.0f;
}
if (player[player_num].vel_x < 0.0f)
{
player[player_num].vel_x += player[player_num].vel_x * player[player_num].vel_x / 20.0f;
}
if (player[player_num].vel_y > 0.0f)
{
player[player_num].vel_y -= player[player_num].vel_y * player[player_num].vel_y / 20.0f;
}
if (player[player_num].vel_y < 0.0f)
{
player[player_num].vel_y += player[player_num].vel_y * player[player_num].vel_y / 20.0f;
}
player[player_num].rot += mouse_move_x;
if (player[player_num].rot > 360.0f) {player[player_num].rot -= 360.0f;}
if (player[player_num].rot < 0.0f) {player[player_num].rot += 360.0f;}
if (suction)
{
player[player_num].vel_x += (0 - player[player_num].x_pos) / 12500.0f; // Suction Of The Vortex
player[player_num].vel_y += (0 - player[player_num].y_pos) / 12500.0f; // Suction Of The Vortex
}
if (PosValf((0 - player[player_num].x_pos) * (0 - player[player_num].x_pos) + (0 - player[player_num].y_pos) * (0 - player[player_num].y_pos)) < 25.0f)
{
// Die
print_inside = true;
}
player[player_num].x_pos += player[player_num].vel_x;
player[player_num].y_pos += player[player_num].vel_y;



if ( key_pressed[''S''] && s_wu )
{
suction = !suction;
s_wu = false;
}
if ( !key_pressed[''S''] )
{
s_wu = true;
}

if ( (!key_pressed[VK_LEFT] || !key_pressed[VK_RIGHT]) && (!key_pressed[VK_UP] || !key_pressed[VK_DOWN]) )
{
if ( key_pressed[VK_UP] )
{
if ( key_pressed[VK_LEFT] )
{
player[player_num].vel_x -= (float)sin(player[player_num].rot / 57.2957795130823208767981548141052f - 0.785398163397448309615660845819876f) * 0.00157079632679489661923132169163975f;
player[player_num].vel_y -= (float)cos(player[player_num].rot / 57.2957795130823208767981548141052f - 0.785398163397448309615660845819876f) * 0.00157079632679489661923132169163975f;
}
else if ( key_pressed[VK_RIGHT] )
{
player[player_num].vel_x -= (float)sin(player[player_num].rot / 57.2957795130823208767981548141052f + 0.785398163397448309615660845819876f) * 0.00157079632679489661923132169163975f;
player[player_num].vel_y -= (float)cos(player[player_num].rot / 57.2957795130823208767981548141052f + 0.785398163397448309615660845819876f) * 0.00157079632679489661923132169163975f;
}
else
{
player[player_num].vel_x -= (float)sin(player[player_num].rot / 57.2957795130823208767981548141052f) * 0.002f;
player[player_num].vel_y -= (float)cos(player[player_num].rot / 57.2957795130823208767981548141052f) * 0.002f;
}
}
else if ( key_pressed[VK_DOWN] )
{
if ( key_pressed[VK_RIGHT] )
{
player[player_num].vel_x += (float)sin(player[player_num].rot / 57.2957795130823208767981548141052f - 0.785398163397448309615660845819876f) * 0.00157079632679489661923132169163975f;
player[player_num].vel_y += (float)cos(player[player_num].rot / 57.2957795130823208767981548141052f - 0.785398163397448309615660845819876f) * 0.00157079632679489661923132169163975f;
}
else if ( key_pressed[VK_LEFT] )
{
player[player_num].vel_x += (float)sin(player[player_num].rot / 57.2957795130823208767981548141052f + 0.785398163397448309615660845819876f) * 0.00157079632679489661923132169163975f;
player[player_num].vel_y += (float)cos(player[player_num].rot / 57.2957795130823208767981548141052f + 0.785398163397448309615660845819876f) * 0.00157079632679489661923132169163975f;
}
else
{
player[player_num].vel_x += (float)sin(player[player_num].rot / 57.2957795130823208767981548141052f) * 0.002f;
player[player_num].vel_y += (float)cos(player[player_num].rot / 57.2957795130823208767981548141052f) * 0.002f;
}
}
else if ( key_pressed[VK_RIGHT] )
{
player[player_num].vel_x -= (float)cos(player[player_num].rot / 57.2957795130823208767981548141052f) * 0.002f;
player[player_num].vel_y += (float)sin(player[player_num].rot / 57.2957795130823208767981548141052f) * 0.002f;
}
else if ( key_pressed[VK_LEFT] )
{
player[player_num].vel_x += (float)cos(player[player_num].rot / 57.2957795130823208767981548141052f) * 0.002f;
player[player_num].vel_y -= (float)sin(player[player_num].rot / 57.2957795130823208767981548141052f) * 0.002f;
}
}

This topic is closed to new replies.

Advertisement