I am trying to get a car to move in OpenGL in a semi-realistic manner, with the car going roughly in the direction of it's front tires (which can turn, just like real car tires).
I don't know how to make the car go towards the tires. I am taking input from the keyboard (key down and key up) using SDL2 like this:
case SDLK_LEFT:
car.setTireRotationSpeed(-TIRE_ROTATION_SPEED);
break;
case SDLK_RIGHT:
car.setTireRotationSpeed(TIRE_ROTATION_SPEED);
break;
case SDLK_UP:
car.setCarForwardSpeed(CAR_MOVEMENT_SPEED);
break;
case SDLK_DOWN:
car.setCarForwardSpeed(-CAR_MOVEMENT_SPEED);
break;
And similarly for key release:
case SDLK_LEFT:
if (car.getTireRotationSpeed() == -TIRE_ROTATION_SPEED)
car.setTireRotationSpeed(0);
break;
case SDLK_RIGHT:
if (car.getTireRotationSpeed() == TIRE_ROTATION_SPEED)
car.setTireRotationSpeed(0);
break;
case SDLK_UP:
if (car.getCarForwardSpeed() == CAR_MOVEMENT_SPEED)
car.setCarForwardSpeed(0);
break;
case SDLK_DOWN:
if (car.getCarForwardSpeed() == -CAR_MOVEMENT_SPEED)
car.setCarForwardSpeed(0);
break;
This sets variables in the car
that are used every frame to move the car/rotate the tires on the car (since the chasis and the tires are separately rendered) in a smooth fashion.
I am using glm-math (i.e. glm:vec3
, glm::mat4
, etc) to accomplish the transformations, and I am using shaders to draw and texture.
Every time the whole car is rendered, it goes through the following steps:
void Car::Render(Shader & shader, Camera & camera)
{
incrementFrontTireRotation(getTireRotationSpeed());
move();
chasisTexture.Bind(0);
shader.Update(getChasisTransform(), camera);
chasisMesh.Draw();
tireTexture.Bind(0);
shader.Update(getTireTransform(Car::FRONT_RIGHT), camera);
tireMesh.Draw();
//Repeated for the other tires
}
Each of the shader.Update()
calls takes a custom Transform
class, which is simply a wrapper for the glm::translate
, glm::rotate
, and glm::scale
and returns the glm::mat4
that represents the model's matrix transformations, based on the transformations I specify.
The incrementFrontTireRotation
method and the move
method are where I am having trouble.
void Car::incrementFrontTireRotation(float amount)
{
if (amount > 0)
{
if (frontTireRotation.y + amount <= MAX_FRONT_TIRE_TURNED)
frontTireRotation.y += amount;
else
frontTireRotation.y = MAX_FRONT_TIRE_TURNED;
}
else if (amount < 0)
{
if (frontTireRotation.y + amount >= -MAX_FRONT_TIRE_TURNED)
frontTireRotation.y += amount;
else
frontTireRotation.y = -MAX_FRONT_TIRE_TURNED;
}
if (carForwardSpeed != 0)
carRotation += glm::vec3(0, -amount, 0);
}
frontTireRotation
is a glm::vec3
that is used for calculating the rotation of the front tires, since they can turn side to side. carRotation
is intended to be the rotation of the car.
void Car::move()
{
forward = glm::vec3(glm::rotate(-tireRotation, glm::vec3(0, 1.0f, 0)) * glm::vec4(forward, 1.0f));
position += forward * carForwardSpeed;
}
My intent is to try to get the car to move in the same direction in which the front tires are facing. I am trying to do this with the forward
vector, but it is having some weird results. The car does indeed turn, but after too much turning the car no longer moves the way the tires are pointing.
I know this is a lot. Let me know if I need to clarify anything.
The tires of a car should be aligned such that their axes meet at a common pivot like so:
As you see, the two front tires must have a different angle. So the easiest way to handle turns is to consider a virtual central wheel whose angle you control via a steering variable.
This steering then lets you calculate the radius of the turn circle on which the car must move. This will depend on the length of the car. Given this radius, you can calculate the pivot point. Then, when updating the car's location and rotation, all you need to do is rotate the entire car about the pivot. The rotation angle should be chosen in a way that the resulting circle segment is equal to the way the car should travel in a given frame time (i.e. angle * r = velocity * t
, angle
in radians).
Some care must be taken when no or very little steering happens. In this case, the turn radius becomes infinite and you cannot do a reasonable rotation. Switching to a simple translation in this case can solve this problem.