假车模拟(译文)
By robot-v1.0
本文链接 https://www.kyfws.com/games/irrlicht-car-simulation-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 12 分钟阅读 - 5539 个词 阅读量 0假车模拟(译文)
原文地址:https://www.codeproject.com/Articles/42678/Irrlicht-car-simulation
原文作者:Ihab ramadan
译文由本站 robot-v1.0 翻译
前言
A car simulation demo using the Irrlicht game engine and the Newton physics engine.
使用Irrlicht游戏引擎和Newton物理引擎的汽车模拟演示.
介绍(Introduction)
没有车?甚至没有驾照?您不需要它们;您所需要做的就是运行汽车仿真程序来驾驶最昂贵,最快速的汽车.我将向您介绍一个简单的汽车仿真程序,该程序使用最受欢迎的开源游戏引擎Irrlicht和出色的物理引擎Newton.(Don’t have a car? Don’t even have a driving license? You do not need them; all you need is to run a car simulation program to drive the most expensive and speedy cars. I will introduce you to a simple car simulation program using Irrlicht, the most popular Open Source game engine, and Newton, the great physics engine.) 要制作汽车模拟程序,您需要一个游戏引擎来渲染车身,车轮,轨道和其他任何图形项目,但是模拟的思想不是渲染图形项目,而是如何放置这些项目.在物理环境中模拟现实.为此,我们将使用物理引擎,如果它到达悬崖的尽头,它将使汽车掉落;如果您尝试通过急转弯,则将其翻转^等等.我在此模拟器中使用了牛顿物理引擎,为了使我们的工作更轻松,我还使用了IPhysics Irrlicht Newton包装器,它使Irrlicht和Newton之间的链接变得如此容易.(To make a car simulation program, you need a game engine to render the car body, the wheels, the track, and any other graphics items, but the idea behind the simulation is not to render the graphics items, but how to put these items in a physics environment to simulate reality. To do that, we will use a physics engine which will drop the car if it reaches the end of a cliff, or flip it if you try to pass a sharp turn … etc. I used the Newton physics engine in this simulator, and to make our task easier, I also used the IPhysics Irrlicht Newton wrapper, which will make the link between Irrlicht and Newton so easy.)
背景(Background)
Irrlicht引擎是用C ++编写和使用的开源高性能实时3D引擎.我使用了这个演示版本1.4.2;较早的版本无法呈现我的(The Irrlicht engine is an Open Source high performance real time 3D engine written and usable in C++. I used for this demo, the 1.4.2 version; the earlier versions couldn’t render my)**.3ds(.3ds)**建模很好. IPhysics库(一组用于同时使用Newton和Irrlicht的类)仅针对较早的版本发布,因此我不得不重新编译该库以使其与1.4.2版本兼容,并且我在新库中附加了代码样品.(models well. The IPhysics library (a set of classes for using Newton and Irrlicht together) was released only for the earlier versions, so I had to recompile this library to be compatible with the 1.4.2 version, and I have attached the new lib with the code sample.)
使用代码(Using the code)
使用Visual Studio 2005打开解决方案文件并运行项目.它将显示一条消息,告诉您找不到(Open the solution file with Visual Studio 2005 and run the project. It will show a message telling you that it couldn’t find the)**irrlicht.dll(irrlicht.dll)**文件.只是复制(file. Just copy)**irrlicht.dll(irrlicht.dll)**和(and)牛顿(newton.dll)(您可以在随附的示例可执行文件中找到它们)文件到((you will find them in the attached sample executable) files to the)**发布(release)**文件夹,然后再次运行.(folder, and run again.)
准备代码(Preparing the code)
在项目中包括以下头文件:(Include these header files needed in the project:)
#include <irrlicht.h>
#include <newton.h>
#include <IPhysics.h>
#include "EventReciever.h"
使用(Use the) #pragma comment
语句以加载lib文件(您也可以从项目设置中添加lib文件).(statement to load the lib files (you can also add the lib files from the project setting).)
#pragma comment (lib , "irrlicht.lib")
#pragma comment (lib , "newton.lib")
在Irrlicht引擎中,所有内容都可以在名称空间中找到.因此,如果您要使用一个类,则必须在该类的名称之前编写其名称空间,因此我们使用"(In the Irrlicht engine, everything can be found in a namespace. So if you want to use a class, you have to write its namespace before the name of the class, so we use the “) using namespace
“语句,以避免必须在每个类之前编写名称空间.(” statement to avoid having to write the namespaces before every class.)
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
违法基础(Irrlicht basics)
要加载网格,我们只需要从场景管理器中使用(To load a mesh, we only have to get the mesh from the Scene Manager with the) getMesh()
函数,将网格的路径传递给它,并添加一个(function, passing to it the path of the mesh, and add a) SceneNode
与显示网格(to display the mesh with) addAnimatedMeshSceneNode()
.(.)
我们禁用照明,因为此处没有动态照明,否则网格将完全变为黑色:(We disable lighting because we do not have a dynamic light in here, and the mesh would be totally black otherwise:)
/*
* Function to load the car
*/
IAnimatedMeshSceneNode* LoadCar(scene::ISceneManager* smgr )
{
char* carModel = "../media/Vehicle/impreza.3ds";
IAnimatedMesh* carMesh = smgr->getMesh(carModel);
IAnimatedMeshSceneNode* carSceneNode =
smgr->addAnimatedMeshSceneNode(carMesh);
carSceneNode->setMaterialFlag(video::EMF_LIGHTING, false);
return carSceneNode;
}
我们对车轮也一样.(We do the same for the car wheels.)
获取指向视频驱动程序,场景管理器和图形用户界面环境的指针,这样我们就不必总是写(Get a pointer to the Video Driver, the Scene Manager, and the graphical user interface environment, so that we do not always have to write) device->getVideoDriver()
要么(or) device->getSceneManager()
.(.)
注意:我使用Direct3D 9作为渲染器.您可以使用其他渲染器,例如Direct3D 8甚至OpenGL:(Note: I used Direct3D 9 as my renderer; you can use other renderers like Direct3D 8 or even OpenGL:)
// make direct3d9 our renderer
IrrlichtDevice* device = createDevice( EDT_DIRECT3D9);
if (device == 0)
return ;
// get the video driver
IVideoDriver* driver = device->getVideoDriver();
// get the scene manager
ISceneManager* smgr = device->getSceneManager();
游戏地图是<雷神之锤3>地图,其中包含(The map of the game is a Quake 3 map which is packed into)**.pk3(.pk3)文件,无非就是(files, which are nothing other than).压缩(.zip)文件.(files.)
通过调用(By calling the) addZipFileArchive
功能,我们可以读取该存档中的文件,就像它们直接存储在磁盘上一样.(function, we are able to read from the files in that archive as if they are directly stored on the disk.)
现在我们可以通过调用(Now we can load the mesh by calling) getMesh
(<雷神之锤3>的地图并不是真正的动画,它们只是一大堆静态几何图形,并附加了一些材料;我们的游戏地图存在于Irrlicht SDK中).的((Quake 3 maps are not really animated, they are only a huge chunk of static geometry with some materials attached; our game map exists in the Irrlicht SDK). The).bsp(.bsp)**是主要模型,其他文件是该模型所需的纹理或XML文件,因此我们按以下方式加载地图:(is the main model, and the other files are textures or XML files needed for this model, so we load the map as follows:)
//Load the game map
device->getFileSystem()->addZipFileArchive(pk3Map);
IAnimatedMesh* mapMesh = smgr->getMesh(mapName);
IAnimatedMeshSceneNode* gameMap = smgr->addAnimatedMeshSceneNode(mapMesh)
为了查看网格,我们将相机放置在3D空间中的位置:(To look at the mesh, we place a camera into the 3D space at the position:)
//ICameraSceneNode* camera = smgr->addCameraSceneNode();
camera->setPosition(vector3df(0, 30.0f, -30.0f));
要显示窗口消息,我们使用设备(To show a window message, we use the device) getGUIEnvironment
向场景添加消息的功能:(function to add a message to the scene:)
// tell the user a message , MessageText and Caption are declaired at the top of the file
device->getGUIEnvironment()->addMessageBox(MessageText.c_str(),Caption.c_str());
物理增强(The physics enhancements)
物理部分是最棘手的部分:如何使加载的汽车表现得像真实汽车一样.(The physics part is the most tricky part: how to make the loaded car behave as if it is real.)
Newton是Julio Jerez 2003创建的一种用于刚体实时仿真的高级物理引擎.在Newton和Irrlicht之间进行链接不是一件容易的事,因此我们使用IPhysics(一组用于轻松集成Newton和Irrlicht的接口类).(Newton is an advanced physics engine for real-time simulation of rigid bodies, created by Julio Jerez 2003. Linking between Newton and Irrlicht is not an easy task, so we use IPhysics (a set of interface classes for easily integrating Newton and Irrlicht).)
要使用IPhysics,请(To use IPhysics, make a) CPhysics
对象,然后调用(object, then call the) init
创建物理世界的功能:(function that creates the physics world:)
// init the physics world
CPhysics physics;
physics.init(device->getTimer());
创建汽车的方法与创建其他任何东西的方法相同.但是,有很多字段需要填写:(Creating a car follows the same approach as creating anything else. However, there are a lot of fields to fill out:)
carBodyOffset
:这是车身的偏移位置.例如,如果您希望车身是一个离地面5个单位的盒子,则应为(: This is the offset position for the car body. For example, if you want the car body to be a box that is 5 units above the ground, this should be)vector3df(0, 5.0f, 0)
.(.)carBodySize
:这是代表车身的立方体的大小.例如,如果您希望车身为10x5x8的盒子,则应为(: This is the size of the cube that will represent the car body. For example, if you want 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
:这是前轴到原点的距离(沿x轴).例如,如果您的汽车长10个单位,并且您希望车轮在任一端,则应将其设置为5.0f,以下字段也应如此.(: 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 car body.)tireMass
:轮胎质量.(: Mass of the tires.)tireWidth
:轮胎的宽度.(: Width of the tires.)tireRadius
:轮胎的半径.(: Radius of the tires.)carBodyNode
:指向代表车身的Irrlicht场景节点的指针.(: Pointer to the Irrlicht scene node representing the car body.)wheelNode_FL
:左前轮场景节点.(: Front left wheel scene node.)wheelNode_FR
:右前轮场景节点.(: Front right wheel scene node.)wheelNode_RL
:左后轮场景节点.(: Rear left wheel scene node.)wheelNode_RR
:右后轮场景节点.(: Rear right wheel scene node.)tireSuspensionShock
:此值直接传递给牛顿.来自Newton docs:“弹簧,质量,阻尼器系统的参数化阻尼常数.值1对应于临界阻尼系统."(: This value is passed straight to Newton. From the Newton docs: “Parameterized damping constant for a spring, mass, damper system. A value of one corresponds to a critically damped system.")tireSuspensionSpring
:此值直接传递给牛顿.来自Newton的文档:“弹簧,质量,阻尼器系统的参数化弹簧常数.值1对应于临界阻尼系统."(: This value is passed straight to Newton. From the Newton docs: “Parameterized 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.) 因此,我们将(So, we make the)SPhysicsCar
反对并填写以下值:(object and fill these values:)
// init the car parameter
SPhysicsCar carData ;
carData.carBodyOffset = vector3df(0, 0.0f, 0);
carData.carBodySize = vector3df(1.2f, 01.85f, 0.2f);
carData.carMass = 3000.0f;
carData.frontAxleOffset = 01.5f;
carData.rearAxleOffset = 01.1f;
carData.axleWidth = 01.7f;
carData.tireMass = 20.0f;
carData.tireRadius = 0.98f;
carData.tireWidth = 01.0f;
carData.maxSteerAngle = 0.6f;
carData.maxTorque = 2000.0f;
carData.maxBrakes = 50.0f;
carData.tireSuspensionLength = 0.20f;
carData.tireSuspensionSpring =
(carData.tireMass * 1.0f * 9.8f) / carData.tireSuspensionLength;
carData.tireSuspensionShock = sqrt(carData.tireSuspensionSpring) * 1.0f;
carData.carBodyNode = LoadCar(smgr);
carData.carBodyNode->setScale(vector3df(.943,.943,.943));
carData.tireNode_FL = LoadCarWheel(smgr ,driver);
carData.tireNode_FL->setScale(vector3df(carData.tireRadius,
carData.tireRadius, carData.tireWidth));
carData.tireNode_FR = LoadCarWheel(smgr ,driver);
carData.tireNode_FR->setScale(vector3df(carData.tireRadius,
carData.tireRadius, carData.tireWidth));
carData.tireNode_RL = LoadCarWheel(smgr ,driver);
carData.tireNode_RL->setScale(vector3df(carData.tireRadius,
carData.tireRadius, carData.tireWidth));
carData.tireNode_RR = LoadCarWheel(smgr ,driver);
carData.tireNode_RR->setScale(vector3df(carData.tireRadius,
carData.tireRadius, carData.tireWidth));
主程序主体(The main program body)
设置好之后,场景可以让我们绘制所有内容.我们在一个(After setting up, the scene lets us draw everything. We run the device in a) while()
循环,一切都在(loop, and everything is drawn between the) beginScene()
和(and) endScene()
呼叫.的(call. The) SetTarget
摄像头成员的功能是使摄像头跟随车身.使用可以在两种相机模式之间切换(camera member function is to make the camera follow the car body. Switch between the two camera modes using the) staticCamera
布尔值.通过调用来绘制所有初始化的图形项(boolean. Draw all initialized graphics items by calling the) drawAll()
ISceneManager
成员函数.通过调用以下命令绘制GUI内容(消息框)(member function. Draw the GUI stuff (the message box) by calling the) drawAll() GuiEnvironment
成员函数.更新物理环境,然后删除设备对象.(member function. Update the physics environment and then drop the device object.)
// the game main loop
while(device->run())
{
driver->beginScene(true, true, SColor(255,100,101,140));
//make the camera follow the car body
camera->setTarget(vector3df(carData.carBodyNode->getPosition().X,
carData.carBodyNode->getPosition().Y+3,
carData.carBodyNode->getPosition().Z ));
// switch between the two cameras
if(staticCamera)
camera->setPosition(vector3df(carData.carBodyNode->getPosition().X,
carData.carBodyNode->getPosition().Y+3,
carData.carBodyNode->getPosition().Z + 7));
smgr->drawAll();
device->getGUIEnvironment()->drawAll();
driver->endScene();
physics.update();
}
device->drop();
加球(Adding balls)
那我添加的球呢?演示中的球要感受真实感,因为球的运动使场景如此真实.通过IPhysics,您可以轻松地添加动态球体,如下所示:(What about the balls I added? The balls in the demo is to feel the reality, as the motion of the balls make the scene so real. IPhysics enables you to add dynamic spheres in an easy manner as follows:) 制作一个球体节点并为其添加沙滩球纹理:(Make a sphere node and add the beach ball texture to it:)
// make a sphere node
ISceneNode* dynamicShereNode = smgr->addSphereSceneNode(0.70f);
// make the sphere node take the ball shap by adding a beach ball texture
dynamicShereNode->setMaterialFlag(video::EMF_LIGHTING, false);
ITexture * ballTexture = driver->getTexture("../media/Balls/BeachBallColor.jpg");
dynamicShereNode->setMaterialTexture(0,ballTexture);
做一个(Make a) SPhysicsSphere
对象并初始化其成员,例如质量,x半径,y半径,z半径,然后将之前创建的球体节点添加到该节点成员.(object and initialize its members like mass, x radius, y radius, z radius, then add the sphere node created before to the node member.)
注意:(Note:) EBT_DYNAMIC
表示球是一个动态实体或可以移动.(means that the ball is a dynamic entity or can move around.)
// make a dynamic sphere
SPhysicsSphere dynamicSphere;
dynamicSphere.bodyType = EBT_DYNAMIC;
dynamicSphere.mass = 1.1;
dynamicSphere.radius_x = .70f;
dynamicSphere.radius_y = .70f;
dynamicSphere.radius_z = .70f;
dynamicSphere.node = dynamicShereNode;
添加(Add the) SPhysicsSphere
反对物理学世界:(object to the physics world:)
// add the ball to the physics world
IPhysicsEntity* dynamicSphereEntity = physics.addEntity(&dynamicSphere);
将球的位置设置在赛车上方:(Init the ball position just above the car:)
// set the ball position just above the car
dynamicSphereEntity->setPosition(vector3df(car->getPosition().X,
car->getPosition( ).Y+ 12, car->getPosition().Z));
事件处理(Event handling)
我们如何向前,向后,向左和向右移动汽车?(How do we move the car forward, backward, left, and right?)
的(The) CEventReciever
类为您完成任务.我们必须覆盖(class does the task for you. We have to overwrite the) OnEvent
虚函数来创建我们的自定义事件.该函数需要一个const(virtual function to make our custom events. This function takes a const) SEvent
对象作为参数,用于存储用户按下的键值,因此我们对此参数进行检查以了解用户按下了什么键.(object as a parameter, which stores the key value pressed by the user, so we do our checking on this parameter to know what key user has pressed.)
virtual bool OnEvent(const SEvent& event)
如果用户按下向上箭头键,我们将(If the user pressed the up arrow key, we set the) IPhysicsCar
将油门百分比降低为正值,如果他按下向下箭头键,则将(throttle percent by a positive value, and if he pressed the down arrow key, we set the) IPhysicsCar
将油门百分比降低一个负值.该值确定汽车的速度.(throttle percent by a negative value. This value determines the speed of the car.)
if(event.EventType == EET_KEY_INPUT_EVENT)
{
// if user press UP arrow
if(event.KeyInput.Key == KEY_UP)
{
if(!m_keys[KEY_UP])
{
m_keys[KEY_UP] = true;
// go forward
m_car->setThrottlePercent(1.0f);
}
else if(event.KeyInput.PressedDown == false)
{
m_keys[KEY_UP] = false;
}
}
// if user press Down arrow
if(event.KeyInput.Key == KEY_DOWN)
{
if(!m_keys[KEY_DOWN])
{
m_keys[KEY_DOWN] = true;
// go backword
m_car->setThrottlePercent(-100.0f);
}
else if(event.KeyInput.PressedDown == false)
{
m_keys[KEY_DOWN] = false;
}
}
对于向右键和向右键,我们设置了(For the right and arrow keys, we set the steering percent for the) IPhysicsCar
对象,而不是节流阀百分比,以相同的方式.如果用户同时按下两个相反的键,它将使汽车停下来.(object instead of the throttle percent, in the same manner. If the user presses two opposite keys at once, it will stop the car.)
if(!m_keys[KEY_LEFT] && !m_keys[KEY_RIGHT])
{
m_car->setSteeringPercent(0.0f);
}
if(!m_keys[KEY_UP] && !m_keys[KEY_DOWN])
{
m_car->setThrottlePercent(0.0f);
}
另外,我使用C键投球,使用V键翻转汽车,使用F2,F3键切换摄像头模式.(Also, I use the C key to drop a ball and the V key to flip the car, and F2, F3 keys to switch the camera mode.)
// if user press C button
if(event.KeyInput.Key == KEY_KEY_C)
{
// drop the sphere
dropDynSphere(m_smgr,m_driver,m_physics,m_car);
} // if user press V button
else if(event.KeyInput.Key == KEY_KEY_V)
{
// flib the car position
m_car->setPosition(vector3df(m_car->getPosition().X ,
m_car->getPosition().Y + 3 ,m_car->getPosition().Z));
m_car->setRotation(vector3df(m_car->getRotation().X +90,
m_car->getRotation().Y,m_car->getRotation().Z));
}
else if(event.KeyInput.Key == KEY_F3)
{
// active the static camera
staticCamera= true;
}
else if(event.KeyInput.Key == KEY_F2)
{
// active the dynamic camera
staticCamera = false;
}
兴趣点(Points of interest)
我是为那些对游戏编程热爱我的人写这篇文章的,因此,这篇文章对于具有游戏编程背景和某些C ++技能的开发人员将非常有用.(I wrote this article for those who share me love for game programming, so this article will be useful for developers who have a game programming background and some C++ skills.)
笔记(Notes)
如果尚未在PC上安装Visual Studio 2005,则必须安装VS2005可再发行组件包;您可以从下载软件包(You must install the VS2005 redistributable package if you have not installed Visual Studio 2005 on your PC; you can download the package from) 这里(here) .(.)
历史(History)
- 2009年9月27日-初始版本.(Sept. 27, 2009 - Initial release.)
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
VC7.0 VC7.1 VC8.0 C++ VC6 Dev 新闻 翻译