How to make a mesh move like a Car?(Solved)

Started by
8 comments, last by Degra 17 years, 5 months ago
Hi guys i'm doing a 3D car demo (DX9/c++), i want to get the mesh to move like a car at the moment the mesh moves Forwards, Backwards and can rotate left and right. I want to be able to make the mesh Accelerate/Decelerate at the moment i have: -

void CCar::move(int direction, float anim_rate)
{
	switch (direction)
	{
	case FORWARD:
		forwardVector.x = -MOVE_RATE;

		position.x -= forwardVector.x * -sin(D3DXToRadian(forwardVector.y));
		position.z += forwardVector.x * cos(D3DXToRadian(forwardVector.y));
		break;

	case BACKWARD:
		forwardVector.x = MOVE_RATE;
		position.x -= forwardVector.x * -sin(D3DXToRadian(forwardVector.y));
		position.z += forwardVector.x * cos(D3DXToRadian(forwardVector.y));
		break;

	case LEFT:
		forwardVector.y -= 5.0f;
		break;

	case RIGHT:
		forwardVector.y += 5.0f;
		break;
	}
}



can anyone give me some help? [Edited by - Prog101 on November 10, 2006 4:07:56 AM]
Advertisement
If you want to make it by your own, then here on gamedev are articles about car physics or look for the article by "Racer" creator on google.

But my suggestion is: Just take a physics engine like ODE or Newton Game Dynamics.
I saw demonstrations of them both, simulating car movements and they were very good.
-----"Master! Apprentice! Heartborne, 7th Seeker Warrior! Disciple! In me the Wishmaster..." Wishmaster - Nightwish
proper car physics is not for the faint hearted.

Maybe you need a crash course in Newtonian physics, how things accelerate, decelerate, relate to mass and momentums, ...

Chris Hecker has some good starter articles. There are good one on Gamasutra, and here, in the article section.

http://www.d6.com/users/checker/dynamics.htm
http://www.gamasutra.com/resource_guide/20030121/kennedy_01.shtml
http://www.gamedev.net/reference/list.asp?categoryid=28

Everything is better with Metal.

This is all basic physics stuff... but still :

For movement of the car, there are 3 vectors you have to keep track of:
position, velocity and acceleration.

now, when updating,

velocity = velocity + acceleration * time;
position = position + velocity;

(I am assuming you have overloaded + and * operators in your vector class. If not, do the above for each of the three coordinates.)

That is about it. Maybe you are getting confused because of the "direction" of the car. If you change the direction of the car, you are changing the velocity.
Right so now i have the equations that you suggested updating in my UpdateCarPhysics function but how do i
implement this into my move functions to get the car to speed up with acceleration and decelerate when the forward is not pressed?

car.cpp
//**************** globals#define MOVE_RATE 4.0fCCar::CCar(void){	magnitude = 0.0f;	VelocityVector.x = MOVE_RATE;	turningAngle = 2.0f;}CCar::~CCar(void){ }bool CCar::create(LPDIRECT3DDEVICE9 device){	//Setup the cars physics 1st	init_carPhysics();	Model = new CModel();	return Model->loadModel(device, "./models/lambo.x");}void CCar::init_carPhysics(void){	accelerationVector.x = 0.0f;	accelerationVector.y = 0.0f;	accelerationVector.z = 0.0f;			positionVector.x = 0.0f;	positionVector.y = 0.0f;	positionVector.z = 0.0f;	VelocityVector.x = 0.0f;	VelocityVector.y = 0.0f;	VelocityVector.z = 1.0f;		set_LastVeloicty(VelocityVector);}void CCar::setPosition(D3DXVECTOR3 newPosition){	positionVector = newPosition;}void CCar::move(int direction){	switch (direction)	{	case FORWARD:		VelocityVector.x = -MOVE_RATE;		positionVector.x -= VelocityVector.x * -sin(D3DXToRadian(VelocityVector.y));		positionVector.z += VelocityVector.x * cos(D3DXToRadian(VelocityVector.y));				break;	case BACKWARD:		VelocityVector.x = MOVE_RATE;		positionVector.x -= VelocityVector.x * -sin(D3DXToRadian(VelocityVector.y));		positionVector.z += VelocityVector.x * cos(D3DXToRadian(VelocityVector.y));				break;	case BRAKE:		break;	case LEFT:				VelocityVector.y -= turningAngle;		break;	case RIGHT:		VelocityVector.y +=turningAngle;		break;	}}void CCar::updateCarPhysics(float dtime){	/*Calculate the cars speed by its magnitude (velocity = length of its vectors)	//Velocity is speed with direction	----------------------------------------------------------------------------------*/		// D3DXVec3Normalize(&normVelocity,&VelocityVector);	VelocityVector = VelocityVector + accelerationVector * dtime;	positionVector = positionVector + VelocityVector;	accelerationVector = (VelocityVector - get_LastVelocity()) / dtime;	magnitude = (sqrt)((VelocityVector.x * VelocityVector.x) + 						(VelocityVector.y * VelocityVector.y) + 						(VelocityVector.z * VelocityVector.z));	//speed = magnitude of the cars vectors	speed = magnitude;		set_LastVeloicty(VelocityVector);}D3DXVECTOR3 CCar::get_LastVelocity(){	return Last_VelocityVector;}void CCar::set_LastVeloicty(D3DXVECTOR3 Velocity){	Velocity = Last_VelocityVector;}void CCar::render(LPDIRECT3DDEVICE9 device){	D3DXMATRIX transMatrix;				// the translation matrix	D3DXMATRIX rotMatrix;				// the rotation matrix	D3DXMATRIX scaleMatrix;				// the scale matrix	// create the translation matrix	D3DXMatrixTranslation(&transMatrix, positionVector.x, positionVector.y, positionVector.z);	// create the rotation matrix for the object	D3DXMatrixRotationY(&rotMatrix,D3DXToRadian(VelocityVector.y));	// Scale the Car by the size amount	D3DXMatrixScaling(&scaleMatrix, size, size, size);	// Multiply the translation matrix by the rotation matrix	// The resulting matrix is stored in the transMatrix	D3DXMatrixMultiply(&transMatrix, &rotMatrix, &transMatrix);	// Multiply the translation matrix by the scale	D3DXMatrixMultiply(&transMatrix, &scaleMatrix, &transMatrix);	// Transform the object into world space	device->SetTransform(D3DTS_WORLD, &transMatrix);		// render the model for this car	Model->render(device);}void CCar::setSize(float carSize){	size = carSize;}
You can dump the CCar::move(int direction) function now.

For accelerating the car, all you need to do is set accelerationVector to some value based on various keys.

For example,
When forward key is pressed, you would want to set the acceleration as {0,0,10} which would accelerate the car 10 units in the z direction.
When no key is pressed, you want the car to slow down due to friction. For this, just set the acceleration as {0,0,-1} (This should not happen when the car is at rest btw).
Similarly, when brakes are pressed set acceleration as {0,0,-20}

Note that here I am not considering the car's direction. I am assuming it always travels straight in the +ve Z direction.

To account for car's direction, all you have to do is keep track of normalized car's direction which would change whenever you press left/right keys.
Now, whenever you want to get the acceleration vector based on the direction, use a acceleration constant and multiply the direction vector with it. This will give the acceleration vector required for that particular direction.

The constants will have to be decided by you. Like, 10 units forward acceleration speed, -1 units friction, -20 units braking speed etc.

For example, in above example, for going forward the direction vector is {0,0,1} and acceleration constant is 10 units. So multiplying I get {0,0,10}. Now, if my car's direction was +ve X direction, the direction vector would be {1,0,0} and the acceleration vector would accordingly be {10,0,0}

So just keep calling updateCarPhysics() each frame and set acceleration appropriately whenever state changes.
Thanks Zymph, i have now aded the changes which you said i should do and kept the UpdatePhysics function as it was. I have added a constant float angle and add the rotaion of the vector in my car_getkeys function. At present the car does not turn and disapers of the screen.

So is i have followed the instructions but when i try to turn my car it disapears :( (so how have i implmented this wrongly, and be kind)


const float ACCELERATION_RATE;
const float BRAKING_RATE;
const float FRICTION_RATE;
const float STOP_RATE;
const float STEERING_RATE;
const float angle;
float fDot;

Car.cpp
bool CCar::create(LPDIRECT3DDEVICE9 device){	//Setup the cars physics 1st	car_initPhysics();	Model = new CModel();	return Model->loadModel(device, "./models/lambo.x");}void CCar::car_initPhysics(void){	magnitude = 0.0f;	accelerationVector.x = 0.0f;	accelerationVector.y = 0.0f;	accelerationVector.z = 0.0f;			positionVector.x = 0.0f;	positionVector.y = 0.0f;	positionVector.z = 0.0f;	VelocityVector.x = 0.0f;	VelocityVector.y = 0.0f;	VelocityVector.z = 0.0f;	directionVector.x = 0.0f;	directionVector.y = 0.0f;	directionVector.z = 1.0f;	referenceVector.x = 0.0f;	referenceVector.y = 0.0f;	referenceVector.z = 1.0f;	directionVectorTemp.x = 0.0f;	directionVectorTemp.y = 0.0f;	directionVectorTemp.z = 0.0f;	ACCELERATION_RATE	=  10.0;	BRAKING_RATE		= -20.0;	FRICTION_RATE		= -1.0;	STOP_RATE			=  0.0;	STEERING_RATE		=  5.0;}void CCar::car_setPosition(D3DXVECTOR3 newPosition){	positionVector = newPosition;}void CCar::car_updatePhysics(float dtime) { 	/*Update the VelocityVector*/	VelocityVector = VelocityVector + accelerationVector * dtime; 	/*Update the positionVector*/	positionVector = positionVector + VelocityVector;	/*Get the magnitude of the cars velocity*/	magnitude = (sqrt)((VelocityVector.x * VelocityVector.x) + 		(VelocityVector.y * VelocityVector.y) + 		(VelocityVector.z * VelocityVector.z));	/*speed = magnitude of the cars vectors*/	speed = magnitude;	/*Update the angle for the car to turn by getting the dot 	product between the direction and a reference*/	fDot = acos(D3DXVec3Dot(&directionVector,&referenceVector));}void CCar::render(LPDIRECT3DDEVICE9 device){	D3DXMATRIX transMatrix;				// the translation matrix	D3DXMATRIX rotMatrix;				// the rotation matrix	D3DXMATRIX scaleMatrix;				// the scale matrix	// create the translation matrix	D3DXMatrixTranslation(&transMatrix, positionVector.x, positionVector.y, positionVector.z);		// create the rotation matrix for the object	D3DXMatrixRotationY(&rotMatrix,D3DXToRadian(fDot));	// Scale the Car by the size amount	D3DXMatrixScaling(&scaleMatrix, size, size, size);	// Multiply the translation matrix by the rotation matrix	// The resulting matrix is stored in the transMatrix	D3DXMatrixMultiply(&transMatrix, &rotMatrix, &transMatrix);	// Multiply the translation matrix by the scale	D3DXMatrixMultiply(&transMatrix, &scaleMatrix, &transMatrix);	// Transform the object into world space	device->SetTransform(D3DTS_WORLD, &transMatrix);		// render the model for this car	Model->render(device);}void CCar::car_setSize(float carSize){	size = carSize;}



Game.cpp
==============
void Game::get_CarKeysvoid Game::get_CarKeys(){	g_pKeyboard->GetDeviceState(sizeof(KeyboardState),(LPVOID)&KeyboardState);		/* Set the key press's to false*/	pCar->AccelerationKeyPressed = false;	pCar->BreakingKeyPressed = false;	//Set The Keys to move the CAR	if ( KEYDOWN(KeyboardState, DIK_UP) ) // keys for moving Car Forward	{			pCar->AccelerationKeyPressed = true;			pCar->accelerationVector = pCar->directionVector * pCar->ACCELERATION_RATE;	}	if ( KEYDOWN(KeyboardState, DIK_LEFT) ) // keys for turning Car to the Left	{						pCar->directionVectorTemp.x = pCar->directionVector.x;		pCar->directionVectorTemp.z = pCar->directionVector.z;		//Rotate  the direction vector by an angle		pCar->directionVector.x = pCar->directionVectorTemp.x*cos(D3DXToRadian(angle))-pCar->directionVectorTemp.z*sin(D3DXToRadian(angle));		pCar->directionVector.z = pCar->directionVectorTemp.x*sin(D3DXToRadian(angle))+pCar->directionVectorTemp.z*cos(D3DXToRadian(angle));	}	if ( KEYDOWN(KeyboardState, DIK_RIGHT) ) // keys for turning Car to the Right		{						pCar->directionVectorTemp.x = pCar->directionVector.x;		pCar->directionVectorTemp.z = pCar->directionVector.z;		//Rotate the direction vector by an angle		pCar->directionVector.x = pCar->directionVectorTemp.x*cos(D3DXToRadian(-angle))-pCar->directionVectorTemp.z*sin(D3DXToRadian(-angle));		pCar->directionVector.z = pCar->directionVectorTemp.x*sin(D3DXToRadian(-angle))+pCar->directionVectorTemp.z*cos(D3DXToRadian(-angle));	}	if ( KEYDOWN(KeyboardState, DIK_SPACE) ) // keys for braking the car		{						pCar->BreakingKeyPressed = true;		pCar->accelerationVector = pCar->directionVector * pCar->BRAKING_RATE;	}	/*Set the friction rate on the car*/	if (pCar->AccelerationKeyPressed == false && pCar->speed != 0)	{		pCar->accelerationVector = pCar->directionVector * pCar->FRICTION_RATE;	}	pCar->VelocityVector = pCar->directionVector * pCar->speed;}



[Edited by - Prog101 on October 16, 2006 2:45:49 PM]
You have totally messed up the car_updatePhysics() function. The function you posted before was perfectly fine and didnt require any modifications. It is better if you switch back to the old version.

In the initPhysics() function, why is the velocity set to 1 in z direction? Velocity should be zero at rest!

The problem you are having is that you are assuming velocity and direction to be the same... but it is not the case! They are two different things and should be handled differently.

In your CCar class, create another vector called direction. And use this to find which direction the car is looking towards. This vector will always be a unit vector. This is what you will set as direction.z = 1 in initPhysics().

Now in Key handling function:

Whenever left or right key is pressed, this direction vector should change(rotate the vector around the y axis). Make sure you normalize this vector after changing it to ensure it remains a unit vector.
The velocity can now be changed to be along this direction vector too by calculating speed = magnitude(velocity) and velocity = direction*speed.
(This can be made more realistic by making the velocity change gradually to accomodate for change in direction so that the car doesnt turn immediately... but you can do this later after you learn more!)

Whenever up key is pressed, just set acceleration = direction * ACCELERATION_RATE;

Whenever down key is pressed, just set acceleration = direction * BRAKING_RATE;

Whenever neither up/down keys are pressed and speed != 0, acceleration = direction * FRICTION_RATE


Always keep in mind the distinction between a scalar(speed), vector(velocity) and unit vector(direction) to avoid problems.
Solved it here we go

CCar::CCar(void){	}CCar::~CCar(void){ }bool CCar::create(LPDIRECT3DDEVICE9 device){	//Setup the cars physics 1st	car_initPhysics();	Model = new CModel();	// Create a color for the text - in this case Blue	d3dBlue1 = D3DCOLOR_ARGB(255,0,0,255);	return Model->loadModel(device, "./models/lambo.x");}void CCar::car_initPhysics(void){	accelerationVector.x = 0.0f;	accelerationVector.y = 0.0f;	accelerationVector.z = 0.0f;		VelocityVector.x = 0.0f;	VelocityVector.y = 0.0f;	VelocityVector.z = 0.0f;	directionVector.x = 0.0f;	directionVector.y = 0.0f;	directionVector.z = 1.0f;		referenceVector.x = 0.0f;	referenceVector.y = 0.0f;	referenceVector.z = 1.0f;		ACCELERATION_RATE	=  5.0;	BRAKING_RATE		= -20.0;	FRICTION_RATE		= -1.0;	STOP_RATE			=  0.0;	STEERING_RATE		=  5.0;	MAX_SPEED			=  0.5f;}void CCar::car_setPosition(D3DXVECTOR3 newPosition){	positionVector = newPosition;}void CCar::getPosition(D3DXVECTOR3 &carPosition) {	carPosition = positionVector;} D3DXVECTOR3 CCar::car_getUpDirection() {	return directionVector;} void CCar::getCarDirection(D3DXVECTOR3 &carDirection) {	D3DXVECTOR3 tempCarDirection;	D3DXVec3Normalize(& tempCarDirection, & directionVector);		carDirection = tempCarDirection;}void CCar::car_updatePhysics(float dtime) { 	VelocityVector = VelocityVector + accelerationVector * dtime; /*Update the VelocityVector*/	positionVector = positionVector + VelocityVector;/*Update the positionVector*/	/*Get the magnitude of the cars velocity*/	magnitude = (sqrt)((VelocityVector.x * VelocityVector.x) + 		(VelocityVector.y * VelocityVector.y) + 		(VelocityVector.z * VelocityVector.z));		speed = magnitude;/*speed = magnitude of the cars vectors*/	/*Update the angle for the car to turn by getting the dot 	product between the direction and a reference in radians, and then clamp it to stop floating point errors*/	tempDot = D3DXVec3Dot(&directionVector,&referenceVector);	if (tempDot < -1){		tempDot = -1;}	else if (tempDot > 1){		tempDot = 1;}	fDot = acos(tempDot);	/* check if the directionVector has a -ve X, which means it is pointing towards -ve X	 in this case, the angle should be 360 - angle,360 in radians is 2*pi */	if(directionVector.x < 0) 	{		fDot = 2*D3DX_PI - fDot;	}	car_updateCar();}void CCar::car_updateCar(){	D3DXVECTOR3 directionVectorTemp;	if (bAccelerationKeyPressed == true)/*ACCELERATE THE CAR*/	{		if (speed <= MAX_SPEED) //Cap the speed			{					accelerationVector = directionVector * ACCELERATION_RATE;			}	}	if (bLeftKeyPressed == true)/*TURN CAR LEFT*/	{		if (speed >= 0.5f)/*only rotate if speed is above*/		{			directionVectorTemp.x = directionVector.x;			directionVectorTemp.z = directionVector.z;			//Rotate  the direction vector by an angle			directionVector.x = directionVectorTemp.x*cos(D3DXToRadian(STEERING_RATE))				- directionVectorTemp.z*sin(D3DXToRadian(STEERING_RATE));			directionVector.z = directionVectorTemp.x*sin(D3DXToRadian(STEERING_RATE))				+ directionVectorTemp.z*cos(D3DXToRadian(STEERING_RATE));		}	}	if (bRightKeyPressed == true)/*TURN CAR LEFT*/	{		if (speed >= 0.5f)/*only rotate if speed is above*/		{			directionVectorTemp.x = directionVector.x;			directionVectorTemp.z = directionVector.z;			//Rotate the direction vector by an angle			directionVector.x = directionVectorTemp.x*cos(D3DXToRadian(- STEERING_RATE))				- directionVectorTemp.z*sin(D3DXToRadian(- STEERING_RATE));			directionVector.z = directionVectorTemp.x*sin(D3DXToRadian(- STEERING_RATE))				+ directionVectorTemp.z*cos(D3DXToRadian(- STEERING_RATE));		}	}	if (bBreakingKeyPressed == true)/*BRAKE THE CAR*/	{				accelerationVector = directionVector * BRAKING_RATE;	}	if (bAccelerationKeyPressed == false && speed != 0)/*Set the friction rate on the car*/	{		accelerationVector = directionVector * FRICTION_RATE;	}	VelocityVector = directionVector * speed;}void CCar::render(LPDIRECT3DDEVICE9 device){	// Display the Speed	RECT rSpeed;	rSpeed.top = 100;	rSpeed.bottom = 600;	rSpeed.left = 5;	rSpeed.right = 640;	std::stringstream ss; ss <<"Speed "<<  speed/100;	dxMgr.pFont->DrawText(NULL, ss.str().c_str(), -1, &rSpeed, DT_TOP | DT_LEFT, d3dBlue1);	D3DXMATRIX transMatrix;				// the translation matrix	D3DXMATRIX rotMatrix;				// the rotation matrix	D3DXMATRIX scaleMatrix;				// the scale matrix	// create the translation matrix	D3DXMatrixTranslation(&transMatrix, positionVector.x, positionVector.y, positionVector.z);		// create the rotation matrix for the object	//D3DXMatrixRotationY(&rotMatrix,D3DXToRadian(rotation));	D3DXMatrixRotationY(&rotMatrix,fDot);		// Scale the Car by the size amount	D3DXMatrixScaling(&scaleMatrix, size, size, size);	// Multiply the translation matrix by the rotation matrix	// The resulting matrix is stored in the transMatrix	D3DXMatrixMultiply(&transMatrix, &rotMatrix, &transMatrix);	// Multiply the translation matrix by the scale	D3DXMatrixMultiply(&transMatrix, &scaleMatrix, &transMatrix);	// Transform the object into world space	device->SetTransform(D3DTS_WORLD, &transMatrix);		// render the model for this car	Model->render(device);}void CCar::car_setSize(float carSize){	size = carSize;}
Quote:Original post by Zymph
velocity = velocity + acceleration * time;
position = position + velocity;


A little niggle, but this is wrong.
What you should really do is this:
position = position + (velocity * time) + (acceleration * time * time / 2);
velocity = velocity + acceleration;

This is a bit hard to explain, but basically with your method, you are adding on too much velocity. You are adding on extra velocity during the time when the car is accelerating between 2 frames. This will make it appear that the car is moving further than it should given the acceleration.

What you should really do is work out the position taking into account the acceleration between time t-1 and t by using the equation s += ut + att/2 instead of s += (u + at).

I think that makes sense.

Degra

This topic is closed to new replies.

Advertisement