23
\$\begingroup\$

I'm trying to create a simple car game with manual gear changes. However, I'm having a bit of trouble implementing the gear changes.

Here's my current code for the "car":

int gear = 1; // Current gear, initially the 1st
int gearCount = 5; // Total no. of gears

int speed = 0; // Speed (km/h), initially 0
int[] maxSpeedsPerGear = new int[]
{
    40,  // First gear max. speed at max. RPM
    70,  // Second gear max. speed at max. RPM
    100, // and so on
    130,
    170
}

int rpm = 0; // Current engine RPM
int maxRPM = 8500; // Max. RPM

public void update(float dt)
{
    if(rpm < maxRPM)
    {
        rpm += 65 / gear; // The higher the gear, the slower the RPM increases
    }

    speed = (int) ((float)rpm / (float)maxRPM) * (float)maxSpeedsPerGear[gear - 1]);

    if(isKeyPressed(Keys.SPACE))
    {
        if(gear < gearCount)
        {
            gear++; // Change the gear
            rpm -= 3600; // Drop the RPM by a fixed amount
            if(rpm < 1500) rpm = 1500; // Just a silly "lower limit" for RPM
        }
    }
}

However, this implementation doesn't really work. The first gear works fine, but the following gear changes cause the speed drop. By adding some debugging messages, I get these speed values when changing at the RPM limit:

Speed at gear 1 before change: 40
Speed after changing from gear 1 to gear 2: 41

Speed at gear 2 before change: 70
Speed after changing from gear 2 to gear 3: 59

Speed at gear 3 before change: 100
Speed after changing from gear 3 to gear 4: 76

Speed at gear 4 before change: 130
Speed after changing from gear 4 to gear 5: 100

As you can see, the speed after each change is slower before the change. How would you take into consideration the speed before the gear change so that the speed wouldn't drop when changing gears?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I am reminded of this excellent in-depth tutorial: Car Physics for Games. At about one thirds in the article starts talking about engine force transmission. \$\endgroup\$
    – Eric
    Oct 28, 2014 at 14:29

7 Answers 7

17
\$\begingroup\$

Calculate the new RPM based on the new gear and current speed of the car.

speed = (int) ((float)rpm / (float)maxRPM) * (float)maxSpeedsPerGear[gear - 1]);

so: instead of:

rpm -= 3600; // Drop the RPM by a fixed amount

use:

rpm = max(maxRPM,(float)maxRPM * (float)speed / (float)maxSpeedsPerGear[gear - 1]);

The speed will now be the same before and after the gear change, and you can accellerate/decelerate from there.

edit: added max(maxRPM, calc) as you want to limit it. Like in a car, this should result in a pretty sudden loss of speed

\$\endgroup\$
29
\$\begingroup\$

this is because there is no inertia in your speed computation. you just compute it as an absolute consequence of engine rpm and gear. but when you compute the new rpm after gear shift up, you empirically lower it by fixed 3600 rpm steps.

This is your mistake. the rpm dropdown is not fixed between gears. you can fix it by making a second array storing the exact number of rpm drop between each gear.

Second way you can fix it, is by using physically based calculations. You are doing a simulation, so you can do a numerical integration. Using time, dt, and euler integration, or Verlet integration. This sounds complex with names and all but actually is not.

Basically it would mean that you create a lookup table for engine torque at given rpms. Then you would take into account some air resistance increasing with the square of the speed. Then the simulation would calculate the next speed by reversing newtons second law, f=m a.
To find a=f/m, then Euler integration: speed=speed+a*dt. the m is about 1200 (typical car weight). f is the force derived from engine torque, reduced into the gearbox, and then converted to force using lever formula by considering the wheel's radius. (a vector cross product usually, but can be simplified by the multiplication of torque with radius. because netwton/meters multiplied meters = newtons.)

this way, rpm of engine is calculated backwards, as a function of the linear car speed.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ There is no exact number of RPM drop between each gear. It's a ratio, as @Baldrickk points out. And while it's a great idea to have the output of the transmission be torque rather than speed, a discussion of wind resistance and verlet integration is a little outside the scope of the question, no? \$\endgroup\$
    – Justin
    Oct 28, 2014 at 12:29
  • \$\begingroup\$ Yes. for a spot on answer to the question I would recommend Baldrickk's answer. I upvoted it. \$\endgroup\$
    – v.oddou
    Oct 29, 2014 at 1:41
5
\$\begingroup\$

Gears are used as a reduction mechanism.

Using a simplified transmission with just two ratios in the gearbox, one input gear (the engine) and one output gear (one of the ratios of the gearbox) we have two different reduction ratios.

So for an input gear with x teeth and an output gear of x/2 teeth, the speed of the output gear is twice the speed of the input gear ( a two to one ratio)

rpm2 = rpm1 * gearRatio

where:

gearRatio = teeth1 / teeth2

So instead of limiting each gear by hardcoded speed, we can limit it by ratio. You can then calculate the speed for a specific (rpmEngine, gear) pair, and, when the gear is changed, calculate the engine speed given the known speed and a new pair.

To simplify, using just an engine connected to two gears:

rpmEngine = 5000

gearRatio[1] = 2 #low gear:  one rotation of the engine results in 2 rotations output
gearRatio[2] = 3 #high gear: one rotation of the engine results in 3 rotations output

vehicleSpeed = rpmEngine * gearRatio[selectedGear]

so:

selectedGear = 1
vehicleSpeed = rpmEngine * gearRatio[selectedGear] #5000 * 2 = 10000 

when shifting to 2nd gear, 10000 is the speed, so plugging that in the same formula, we now have:

vehicleSpeed = 10000 #computed above
selectedGear = 2

thus our new rpm:

rpmEngine = vehicleSpeed / gearRatio[selectedGear] #10000 / 3 = 3333.3

That 10000 would then be further reduced by a differential (which can be abstracted as just another gear, look it up if needed, sorry, can post jut two links) and then by the wheel size to calculate ground speed in kilometers or miles per hour.

You would have to take into account the fact that shifting into a lower gear raises the engine rpm, so a simple approach is to check maxRPM and limit the rpm after the shift to your maximum rpm, thus reducing the vehicle speed.

So basically, each time a gear shift occurs, you compute the engineRPM from the vehicle speed, limit it by maxRPM, and then get back to "normal" where you update the rpm from the user input and compute speed based on that.

For a realistic simulation, you have to take into account at least engine torque (v.oddou's answer) and clutch slippage, which combined would have these effects: - when shifting up, assuming the shift is fast enough that the engine rpm does not fall, the speed will be raised while the engine rpm is lowered until they are balanced out - when shifting down, the vehicle speed will be lowered until the engine will be raised to the new rpm but this probably goes beyond the "simple" implementation.

\$\endgroup\$
4
\$\begingroup\$

Keep in mind that an engaged manual transmission is a two-way device. The engine can accelerate the vehicle, just as the vehicle (more specifically its momentum) can accelerate the engine.

This was a real problem in early manual transmissions. Downshifting would suddenly kick the engine into a higher rpm, throwing ignition cycles out of sync and possibly causing the engine to stall. This was offset by expert driving where the driver had to rev the engine to the correct speed before releasing the clutch to engage the transmission.

That was until the synchromesh was developed. It is a mechanism that prevents the transmission from engaging until the input and output speeds are in sync.

So, what I suggest is that you emulate the synchromesh and not engage the transmission until the engine rpm and the car speed match at their current levels.

\$\endgroup\$
2
\$\begingroup\$

The existing answer seems far too complex. For a game, RPM is just an indicator on the screen. The actual speed is the real variable. The gear ratio determines how you convert the engine speed to RPM's. Changing gears changes the ratio, but not the speed. Obviously, the RPM also changes as the inverse of the gear ratio.

Overrevving (downshifting past your 8500 RPM limit) is something that you'd implement separately, but it's a bad things in cars and you can let it be a bad thing in your game.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ The existing answer is exactly what most games I've seen do, even simple arcade games, because it's really not that complex. RPM on the screen might just be a number, but that approach gives you both the number (which you can tweak for visual indicators anyways), and the behavior to match those numbers \$\endgroup\$ Oct 27, 2014 at 14:29
2
\$\begingroup\$

As others have mentioned, vehicle speed should be the real value and RPM should be derived from that. Upshifting should cause an engine's rotational speed to decrease because the ratio of RPM per km/h will "instantly" change but the vehicle speed won't.

I would suggest, however, that engine torque should increase with RPM up to a certain limit and fall beyond that. The rate at which a vehicle accelerates should be proportional to torque divided by the gear ratio, minus air drag which is proportional to the square of speed. If consecutive gears have a 1:41:1 ratio, then optimal shifting for acceleration will occur at the point where torque in the lower gear has fallen to about 70% of what it would be in the next higher gear.

\$\endgroup\$
2
\$\begingroup\$

Building on @v.oddou, using

max(maxRPM, calc)

would cause the RPMS to max out instantly when the gears are shifted, not allowing for any smooth transition from gear to gear. The proper way would be to solve for RPM's using the speed variable as an equation.

speed = (int) ((float)rpm / (float)maxRPM) * (float)maxSpeedsPerGear[gear - 1]);

Solve for rpm

rpm = (maxRPM * speed) / maxSpeedsPerGear[gear - 1] ;

since gear is 1 higher than is was before, RPM's will be lower.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .