Idiot in need of help with car game

Started by
8 comments, last by Zahlman 17 years ago
Hi all, Im in the process of beggining to write a 3d car game but having some troubles at the minute when it comes to the free movement of the car. Im terrible at maths and the physics really starts to annoy me after a while (i know, not ideal for creating a car game). At the moment everytime the user presses the left or right key i have a rotation variable which changes the heading of the car. When the user lets go, the rotation variable decreases for the car to straighten up. This is fine but i can only turn to the maximum degrees allowed by the rotation. Can anybody suggest me a way of how i can freely drive the car round? my aim at the moment is to jus have a car which turns realistically, can go in circles if i want etc. Any ideas cos im pulling my hair out here. Thanks in advance to anyone willing to help.
Advertisement
Quote:Original post by TigerSam
This is fine but i can only turn to the maximum degrees allowed by the rotation.
Can you clarify this a bit? Perhaps you could post the code where the car's orientation is updated.
Hi, heres an example from when i am trying to turn the car right. (45degs = full lock left, -45 = full lock right)

 if(keys[39]==true)// to turn right {	 if(CRotation > -45) // using 45 degrees as the maximum the wheel can turn	 {		CRotation--; 		if(CRotation != 0)		{		Cdirection = Vector3d(sin(CRotation*3.1412/180),0,cos(CRotation*3.1412/180));		}	 } }else if(CRotation < 0) {	 CRotation++; // Rotate wheel to be straight	 Cdirection = Vector3d(sin(CRotation*3.1412/180),0,cos(CRotation*3.1412/180)); } 



EDIT : Then after this i have

	float EngineForce = -horsePower; 	Vector3d Ftraction = Cdirection.Normalise()*EngineForce;	float CFdrag = 0.025;	Vector3d Fdrag = Cvelocity * Cvelocity.Magnitude() * -CFdrag;	float CFrr = 0.9;	Vector3d Ffriction = Cvelocity * -CFrr;	Vector3d Flong;	if(braking)	{		Vector3d Fbraking = Cdirection*1000;		Flong = Fbraking + Fdrag + Ffriction;	}	else	{		Flong = Ftraction + Fdrag + Ffriction;	}		braking = false;	Cacceleration = Flong.divideBy(MASS);	Cvelocity = Cvelocity + (Cacceleration * dt);	Cposition = Cposition + (Cvelocity * dt); 


My code probably isnt the best way of doing this but i havent been doing games for long, please criticise me, it can only make me better haha. Cheers
Quote:Original post by TigerSam
Hi, heres an example from when i am trying to turn the car right. (45degs = full lock left, -45 = full lock right)

 if(keys[39]==true)// to turn right {	 if(CRotation > -45) // using 45 degrees as the maximum the wheel can turn	 {		CRotation--; 		if(CRotation != 0)		{		Cdirection = Vector3d(sin(CRotation*3.1412/180),0,cos(CRotation*3.1412/180));		}	 } }else if(CRotation < 0) {	 CRotation++; // Rotate wheel to be straight	 Cdirection = Vector3d(sin(CRotation*3.1412/180),0,cos(CRotation*3.1412/180)); } 
Is Cdirection supposed to be the direction the car is moving? If so, then either I'm confused, or you appear to be confusing the steering wheel angle with the car angle...or something.

The above code makes sense for adjusting the car's steering, but it seems you'd then want to apply a relative adjustment to the car's orientation based on the value of CRotation, rather than computing the car's direction vector from CRotation itself.
Yes CDirection is the direct the car should be moving towards. Am i missing something here then?
Quote:Original post by TigerSam
Am i missing something here then?
Well, one of us is - it might be me :-)

To me it looks like CRotation represents, more or less, the rotation of the steering wheel (and by extension, the angle of the front wheels).

Note that this is different than the rotation of the car. CRotation is restricted to [-45,45], reflecting the constraints on the orientation of the front wheels, while the car rotation presumably has a full 360-degree range.

In real life the relationship between steering and how the car turns is obviously somewhat complex (differentials and all that), but for the sake of argument we can reduce it to a simple linear relationship. In other words, you would simply add the value of CRotation, multiplied by some factor, to the current car angle, and then wrap the car angle to the range [0,360]. The multiplying factor presumably will incorporate a time step, as well as a constant that maps steering-wheel position to turn rate.

After all that, I'll say again that I may be misreading your code, in which case the above is probably entirely irrelevant :|
jky, thanks for your help, i think you have a greater understanding of my situation than me haha. Your explanation is correct, CRotation does represent the steering. I kinda understand what you have explained as to what the next step should be, i jus maybe need to get a clearer picture in my head

Quote:In other words, you would simply add the value of CRotation, multiplied by some factor, to the current car angle, and then wrap the car angle to the range [0,360]. The multiplying factor presumably will incorporate a time step, as well as a constant that maps steering-wheel position to turn rate.


Could i pick your brains again and ask you to show an example of this? Im not quite sure how u mean incorporating a time step

Cheers once again
Quote:Original post by TigerSam
Could i pick your brains again and ask you to show an example of this? Im not quite sure how u mean incorporating a time step
Sure. Don't have time to write up a complete code sample at the moment, but here are a few more ideas to consider.

By 'time step' I'm referring to the interval over which a single game update occurs. Although a fixed time step is often to be preferred, it's common to base the time step on the frame rate; that is, the time step is simply the amount of time that's passed since the last update (as determined indirectly by how long it takes to render a frame).

Actually, I see a 'dt' variable in your physics calculations. That's the time step.

So back to the steering wheel. Personally I'd use a normalized range for the steering rather than the range [-45,45]. For example, -1 would be 'all the way to the left', while 1 would be 'all the way to the right'.

CRotation would obviously have to be a float instead of an integer value. To update CRotation, instead of using an increment or decrement operator, you'd write something like:
if (left_key) {    CRotation -= steering_wheel_rot_speed * dt;    CRotation = std::max(CRotation, -1.f);}
Where steering_wheel_rot_speed represents how fast the steering wheel can be turned (a value of 1 would mean that it would take 1 second to turn the steering wheel all the way to the left).

You would then update the car angle like this:
car_angle += CRotation * max_turn_angle * dt;// Wrap CRotation to the range [0,360] here (assuming degrees)
When the steering wheel is turned all the way to the left or right, the car will turn by exactly max_turn_angle degrees/radians per second.

Now again, this probably isn't the 'proper' way to simulate a car's turning behavior, but I'm just trying to help you out with the specific problem you described (as I understand it at least).

Also, I can't guarantee that I got all of the above right (or any of it, for that matter :).
I have a car physics resource download on my website for people exactly like you.

http://www.kjmsoftware.co.uk/games.htm

40 odd mb download.

It contains tutorials, demo's, source code, phsyics papers et all.

Hope it helps.

KJM
1) Like jyk said. Just as there are concepts of linear displacement, velocity and acceleration, the same apply to angles. The car's heading is its "angular displacement" (normally just "angle"). The speed at which the car rotates is its angular velocity, and that's represented by the position of the steering wheel. We can have an angular acceleration of +/- 1 in some unit (according to how often that update is called; this probably isn't time-independent right now, and should get the dt applied to it) and that represents the movement of the steering wheel.

2) Your value for pi is wrong. (Also, the library can define it for you.) But anyway, why not just store the angle in radians?

3) What the hell are all those Cs and Fs? I know you're used to them from Physics class, but we use those because words don't make good algebraic symbols (compared to letters with words as subscripts). In programming, though, words are *fundamentally* our "algebraic symbols", i.e. our variable names. Similarly, in game programming, we normally forget about the mass of the vehicle, because it's constant for our purposes. Instead of adding forces (all of which are scaled by the same mass), we add the accelerations.

4) There's nothing special about the case of zero rotation.

5) There's no reason to calculate the direction in advance; it's a duplicate of the car angle. In the response to keys, we should only be setting stuff like position of the steering wheel and desired acceleration (i.e. position of foot on gas pedal ;) ). In the physics update, we do the real calculation. (Also, the CDirection you were calculating is always a unit vector, so it doesn't need to be normalized.) Similarly, 'braking' is a flag that is controlled by the user input, so it shouldn't get changed by the updating code, but by the key-processing code.

6) Don't write 'keys[39]', if the idea is that the indices into keys represent the character pressed. Use the corresponding character as the array index. Also, don't compare to boolean literals. It adds no explanatory value ("if it is raining" means the exact same thing as "if it is true that it is raining"), is awkward (see previous example) and introduces another place where you could make a mistake (i.e. putting = instead of ==).

7) Avoid doing the same stuff in both branches of an if/else. It leaves the reader wondering if there are any other cases to worry about o_O In our case, we really have an acceleration provided internally by the car, which is *always* added to friction and drag acceleration vectors. All the changes is how that acceleration is calculated, so we factor that out (and actually do it ahead of time, as shown way below).

8) Use 'immediate' mathematical operators (like +=) where they are provided.

So we might for example move the wheel like this:

// Assuming C++ for the M_PI constant, which would be in <cmath>.// C# presumably has it as Math.PI or something.const double RADIANS_PER_DEGREE = M_PI / 180;const double WHEEL_LOCK_POSITION = 45 * RADIANS_PER_DEGREE;double WheelMotion = dt * STEERING_WHEEL_TURN_RATE;if (keys['\'']) {  WheelPosition -= WheelMotion;  if (WheelPosition < -WHEEL_LOCK_POSITION) {    WheelPosition = -WHEEL_LOCK_POSITION;  }} else if (keys['a']) { // turn left - I'm totally guessing ;)  WheelPosition += WheelMotion;  if (WheelPosition > WHEEL_LOCK_POSITION) {    WheelPosition = WHEEL_LOCK_POSITION;  }} else {  // Move the wheel back towards the center  if (WheelPosition > 0) { WheelPosition -= WheelMotion; }  else { WheelPosition += WheelMotion; }}


This has some problems: in particular, if you move the wheel and then let it go and leave it a while, the wheel might oscillate between just slightly left of centre and just slightly right of centre. (This would be because of the WheelMotion being greater than the distance in either direction.) Also, it might not feel terribly realistic. You might try an exponential motion instead - i.e., setting a target point according to the input, and moving the wheel by some *fraction* of the distance to the target, instead of a constant amount.

void moveTowards(double& result, double target, double factor) {  result += (target - result) * factor;}const double RADIANS_PER_DEGREE = M_PI / 180;const double WHEEL_LOCK_POSITION = 45 * RADIANS_PER_DEGREE;double WheelMotion = dt * STEERING_WHEEL_TURN_RATE;if (keys['\'']) {  moveTowards(WheelPosition, -45, WheelMotion);} else if (keys['a']) { // turn left - I'm totally guessing ;)  moveTowards(WheelPosition, 45, WheelMotion);} else {  moveTowards(WheelPosition, 0, WheelMotion);}


In any event, we can then update the physics something like:

// Here I have capitalized things that would be data members of a class,// and used an initial lowercase letter for things that are either calculated// locally or accepted as a parameter.CarAngle += WheelPosition * dt * STEERING_RATIO; // in radians!Vector3d direction(sin(CarAngle), 0, cos(CarAngle));Vector3d driveAcceleration = direction * (Braking ? BRAKE_ACCELERATION : calculateDriveAcceleration(RPM, Gear, Velocity));const float DRAG_COEFFICIENT = -0.025;Vector3d drag = Velocity * Velocity.Magnitude() * DRAG_COEFFICIENT;const float FRICTION_COEFFICIENT = 0.9;Vector3d friction = Velocity * -FRICTION_COEFFICIENT;Vector3d acceleration = driveAccleration + drag + friction;Position += Velocity * dt + acceleration * dt * dt / 2;Velocity += acceleration * dt;

This topic is closed to new replies.

Advertisement