Car
Creating a car follows the same approach as creating anything else. However, there are a lot of fields to fill out. There are a few
important things to remember:
-
Always build your car with the positive y-axis pointing up, and the front of the car pointing in the direction of the negative x axis.
The reason for this is that otherwise a positive torque on the wheels makes the car go backwards, which is far more confusing than just
having to build the car this way around.
Field listing:
carBodyOffset
This is the offset position for the car body. For example, if you wanted the car body to be a box that is 5 units above the ground,
this should be vector3df(0, 5.0f, 0).
carBodySize
This is the size of the cube that will represent the car body. For example, if you wanted the car body to be a box that is 10x5x8,
this should be vector3df(10.0f, 5.0f, 8.0f).
carMass
The mass of the car body (not the wheels).
frontAxleOffset
This is the distance (along the x axis) that the front axle is from the origin. For example, if your car is 10 units long, and
you want the wheels to be at either end, this should be set to 5.0f, as should the field below.
rearAxleOffset
Same as above, but for the rear axle. Note that this should be a positive number.
axleWidth
The width of the axle determines how far apart the left and right wheels are. This should be about the width of the carbody.
tireMass
Mass of the tires.
tireWidth
Width of the tires.
tireRadius
Radius of the tires.
carBodyNode
Pointer to the Irrlicht scenenode representing the car body.
wheelNode_FL
Front Left wheel scenenode.
wheelNode_FR
Front Right wheel scenenode.
wheelNode_RL
Rear Left wheel scenenode.
wheelNode_RR
Rear Right wheel scenenode.
tireSuspensionShock
This value is passed straight to Newton. From the Newton docs: "parametrized damping constant for a spring, mass, damper system. A value of one corresponds to a critically damped system."
tireSuspensionSpring
This value is passed straight to Newton. From the Newton docs: "parametrized spring constant for a spring, mass, damper system. A value of one corresponds to a critically damped system."
tireSuspensionLength
This value is passed straight to Newton. From the Newton docs: "distance from the tire set position to the upper stop on the vehicle body frame. The total suspension length is twice that."
maxSteerAngle
The maximum angle the wheels are allowed to turn for steering.
maxTorque
The maximum torque for the wheels.
maxBrakes
The maximum brakes for the wheels.
The code:
We're going to create a car, and a small level (the one from the original tutorial!) as an example of how to use a mesh you've created
in a modelling program. You can move the camera with the mouse and arrow keys, and drive the car with w, a, s and d.
// start Irrlicht
IrrlichtDevice* device = createDevice(EDT_OPENGL);
// pointers to driver, scenemanager
IVideoDriver* driver = device->getVideoDriver();
ISceneManager* smgr = device->getSceneManager();
// add a light this time
smgr->addLightSceneNode(0, vector3df(0,1000.0f,0));
// create an FPS camera
ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS(0, 100.0f, 100.0f);
// move the camera up a bit so we can see the level
camera->setPosition(vector3df(0, 30.0f, -30.0f));
// start iphysics
CPhysics physics;
physics.init(device->getTimer());
// create the car data
SPhysicsCar carData;
carData.carBodyOffset = vector3df(0, 0.0f, 0);
carData.carBodySize = vector3df(1.2f, 0.5f, 0.2f);
carData.carMass = 500.0f;
carData.frontAxleOffset = 0.6f;
carData.rearAxleOffset = 0.6f;
carData.axleWidth = 0.8f;
carData.tireMass = 20.0f;
carData.tireRadius = 0.3f;
carData.tireWidth = 0.2f;
carData.maxSteerAngle = 0.6f;
carData.maxTorque = 1000.0f;
carData.maxBrakes = 50.0f;
carData.tireSuspensionLength = 0.2f;
carData.tireSuspensionSpring = (carData.tireMass * 1.0f * 9.8f) / carData.tireSuspensionLength;
carData.tireSuspensionShock = sqrt(carData.tireSuspensionSpring) * 1.0f;
carData.carBodyNode = smgr->addCubeSceneNode(1.0f);
carData.carBodyNode->setScale(carData.carBodySize);
carData.tireNode_FL = smgr->addSphereSceneNode(1.0f);
carData.tireNode_FL->setScale(vector3df(carData.tireRadius, carData.tireRadius, carData.tireWidth));
carData.tireNode_FR = smgr->addSphereSceneNode(1.0f);
carData.tireNode_FR->setScale(vector3df(carData.tireRadius, carData.tireRadius, carData.tireWidth));
carData.tireNode_RL = smgr->addSphereSceneNode(1.0f);
carData.tireNode_RL->setScale(vector3df(carData.tireRadius, carData.tireRadius, carData.tireWidth));
carData.tireNode_RR = smgr->addSphereSceneNode(1.0f);
carData.tireNode_RR->setScale(vector3df(carData.tireRadius, carData.tireRadius, carData.tireWidth));
IPhysicsCar* car = physics.addCar(&carData);
// create our custom event receiver, pass a pointer to the car
CEventReceiver_eg3 receiver(car);
device->setEventReceiver(&receiver);
IAnimatedMesh* levelmesh = smgr->getMesh("media/blockland2.obj");
IAnimatedMeshSceneNode* levelnode = smgr->addAnimatedMeshSceneNode(levelmesh);
SPhysicsStaticMesh level;
level.mesh = levelmesh;
level.meshnode = levelnode;
level.meshScale = vector3df(0.1f, 0.1f, 0.1f);
level.meshnode->setScale(level.meshScale);
IPhysicsEntity* levelEntity = physics.addEntity(&level);
while(device->run())
{
driver->beginScene(true, true, SColor(0,100,100,100));
smgr->drawAll();
driver->endScene();
physics.update();
}
Event receiver:
This time the event receiver we create will have a pointer
to the car, and can therefore control it when the keys are pressed.
class CEventReceiver_eg3 : public IEventReceiver
{
public:
CEventReceiver_eg3(IPhysicsCar* car)
{
m_car = car;
for(s32 i=0; i<KEY_KEY_CODES_COUNT; i++)
{
m_keys[i] = false;
}
}
~CEventReceiver_eg3()
{
}
bool OnEvent(SEvent event)
{
if(event.EventType == EET_KEY_INPUT_EVENT)
{
if(event.KeyInput.Key == KEY_KEY_W)
{
if(!m_keys[KEY_KEY_W])
{
m_keys[KEY_KEY_W] = true;
m_car->setThrottlePercent(100.0f);
}
else if(event.KeyInput.PressedDown == false)
{
m_keys[KEY_KEY_W] = false;
}
}
if(event.KeyInput.Key == KEY_KEY_S)
{
if(!m_keys[KEY_KEY_S])
{
m_keys[KEY_KEY_S] = true;
m_car->setThrottlePercent(-100.0f);
}
else if(event.KeyInput.PressedDown == false)
{
m_keys[KEY_KEY_S] = false;
}
}
if(event.KeyInput.Key == KEY_KEY_A)
{
if(!m_keys[KEY_KEY_A])
{
m_keys[KEY_KEY_A] = true;
m_car->setSteeringPercent(-100.0f);
}
else if(event.KeyInput.PressedDown == false)
{
m_keys[KEY_KEY_A] = false;
}
}
if(event.KeyInput.Key == KEY_KEY_D)
{
if(!m_keys[KEY_KEY_D])
{
m_keys[KEY_KEY_D] = true;
m_car->setSteeringPercent(100.0f);
}
else if(event.KeyInput.PressedDown == false)
{
m_keys[KEY_KEY_D] = false;
}
}
if(!m_keys[KEY_KEY_A] && !m_keys[KEY_KEY_D])
{
m_car->setSteeringPercent(0.0f);
}
if(!m_keys[KEY_KEY_W] && !m_keys[KEY_KEY_S])
{
m_car->setThrottlePercent(0.0f);
}
}
return false;
}
protected:
IPhysicsCar* m_car;
bool m_keys[KEY_KEY_CODES_COUNT];
};
|