Урок 7. Столкновения
Автор: WiZzArD
В этой обучающей программе я покажу вам, как использовать столкновения в моём движке Irrlicht. Я опишу 3 метода проверки на столкновения : автомотическое обноружение столкновений для перемещения по 3-х мерным мирам, взбирание по ступенькам и ручной способ.
Чтобы начать наш урок, придётся немного подготовиться. Нам необходимо взять обучающую программу №2, где я рассказывал вам как использовать карты quake 3.
Мы будем использовать уровень, для того чтобы научиться управлять столкновениями. Кроме того мы немного усложним наш уровень, поместив на него 3 новые модели. Следующий код запускает Irrlicht и загружает quake 3 уровень. Я не буду рассказывать как мы это делаем так как уже рассказал вам об этом во 2-ом уроке.
#include <irrlicht.h>
#include <iostream>
using namespace irr;
#pragma comment(lib, "Irrlicht.lib")
int main()
{
// let user select driver type
video::E_DRIVER_TYPE driverType;
printf("Please select the driver you want for this example:\n"\
" (a) Direct3D 9.0c\n (b) Direct3D 8.1\n (c) OpenGL 1.5\n"\
" (d) Software Renderer\n (e) Apfelbaum Software Renderer\n"\
" (f) NullDevice\n (otherKey) exit\n\n");
char i;
std::cin >> i;
switch(i)
{
case 'a': driverType = video::EDT_DIRECT3D9;break;
case 'b': driverType = video::EDT_DIRECT3D8;break;
case 'c': driverType = video::EDT_OPENGL; break;
case 'd': driverType = video::EDT_SOFTWARE; break;
case 'e': driverType = video::EDT_SOFTWARE2;break;
case 'f': driverType = video::EDT_NULL; break;
default: return 0;
}
// create device
IrrlichtDevice *device = createDevice(driverType,
core::dimension2d<s32>(640, 480), 16, false);
if (device == 0)
return 1; // could not create selected driver.
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
device->getFileSystem()->addZipFileArchive
("../../media/map-20kdm2.pk3");
scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
scene::ISceneNode* q3node = 0;
if (q3levelmesh)
q3node = smgr->addOctTreeSceneNode(q3levelmesh->getMesh(0));
Пока неплохо, мы загрузили quke3 уровень, как показанно в обучающей программе №2. Но теперь всё будет несколько иначе. Мы создаём triangle selector.
Triangle selector - это класс, который может принести треугольники от узлов сцены для того, чтобы делать различные вещи с ними, например как в нашем случае обнаружения столкновений. Есть различные виды triangle selector, и все могут быть созданы с ISceneManager. В этом примере мы создаём OctTreeTriangleSelector.
Это очень полезно при использоании огромных mesh'ей как в quake3 уровне.
scene::ITriangleSelector* selector = 0;
if (q3node)
{
q3node->setPosition(core::vector3df(-1370,-130,-1400));
selector = smgr->createOctTreeTriangleSelector(
q3levelmesh->getMesh(0), q3node, 128);
q3node->setTriangleSelector(selector);
selector->drop();
}
Мы добавили камеру от первого лица в сцену для того, чтобы было возможно перемещение по нашему загруженному quke3 уровню как это было в уроке №2, но на этот раз мы добавили специальную камеру проверки на столкновение : Collision Response animator.
После того, как Collision Response animator связон с камерой, мы можем больше ничего не делать для проверки на столкновение, потому что движок сделает это за нас. Таким образом, выполнять проверку на столкновение в Irrlicht'e достаточно легко.
Теперь мы рассмотрим все параметры функции createCollisionResponseAnimator (). Первый параметр - TriangleSelector, определяет, столкновения в мире. Второй параметр - узел сцены, который присоединяется к объекту объектом занимается обнаружением столкновения, в нашем случае, это является камерой. Третье определяет, насколько большой объект, это - радиус эллипсоида. Испытайте это и измените радиус на меньшие значения, камера будет не в в состоянии придвинуться поближе к стенам. Следующий параметр - направление и скорость серьезности. Вы могли оставить этот параметр в значении (0,0,0) . И последняее значение - Без этого параметра , эллипсоид, с которым сделано обнаружение столкновения, был бы вокруг камеры, и камера будет в середине эллипсоида.
scene::ICameraSceneNode* camera =
camera = smgr->addCameraSceneNodeFPS(0,100.0f,300.0f);
camera->setPosition(core::vector3df(-100,50,-150));
scene::ISceneNodeAnimator* anim =
smgr->createCollisionResponseAnimator(
selector, camera, core::vector3df(30,50,30),
core::vector3df(0,-3,0),
core::vector3df(0,50,0));
camera->addAnimator(anim);
anim->drop();
Поскольку проверка на столкновения не является сложным в irrlicht, я опишу, как сделать два других способа проверки на столкновение. Но перед этим, я подготовлю сцену. Мне нужны 3 модели, которые я затем смогу осветить, и конечно надо избавиться от курсора :).
// disable mouse cursor
device->getCursorControl()->setVisible(false);
// add billboard
scene::IBillboardSceneNode * bill = smgr->addBillboardSceneNode();
bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR );
bill->setMaterialTexture(0, driver->getTexture(
"../../media/particle.bmp"));
bill->setMaterialFlag(video::EMF_LIGHTING, false);
bill->setSize(core::dimension2d<f32>(20.0f, 20.0f));
// add 3 animated faeries.
video::SMaterial material;
material.Texture1 = driver->getTexture(
"../../media/faerie2.bmp");
material.Lighting = true;
scene::IAnimatedMeshSceneNode* node = 0;
scene::IAnimatedMesh* faerie = smgr->getMesh(
"../../media/faerie.md2");
if (faerie)
{
node = smgr->addAnimatedMeshSceneNode(faerie);
node->setPosition(core::vector3df(-70,0,-90));
node->setMD2Animation(scene::EMAT_RUN);
node->getMaterial(0) = material;
node = smgr->addAnimatedMeshSceneNode(faerie);
node->setPosition(core::vector3df(-70,0,-30));
node->setMD2Animation(scene::EMAT_SALUTE);
node->getMaterial(0) = material;
node = smgr->addAnimatedMeshSceneNode(faerie);
node->setPosition(core::vector3df(-70,0,-60));
node->setMD2Animation(scene::EMAT_JUMP);
node->getMaterial(0) = material;
}
material.Texture1 = 0;
material.Lighting = false;
// Add a light
smgr->addLightSceneNode(0, core::vector3df(-60,100,400),
video::SColorf(1.0f,1.0f,1.0f,1.0f),
600.0f);
scene::ISceneNode* selectedSceneNode = 0;
scene::ISceneNode* lastSelectedSceneNode = 0;
int lastFPS = -1;
while(device->run())
if (device->isWindowActive())
{
driver->beginScene(true, true, 0);
smgr->drawAll();
После того, как мы нарисовали сцену : smgr-> drawAll (), мы сделаем первый выбор: Нам необходимо знать, куда направленна камера. Кроме того, нам нужна точная точка quake3 уровня, на которую мы смотрим. Для этого, мы создаем 3-ью линию, начинающуюся в позиции камеры.
core::line3d<f32> line;
line.start = camera->getPosition();
line.end = line.start +
(camera->getTarget() - line.start).normalize() * 1000.0f;
core::vector3df intersection;
core::triangle3df tri;
if (smgr->getSceneCollisionManager()->getCollisionPoint(
line, selector, intersection, tri))
{
bill->setPosition(intersection);
driver->setTransform(video::ETS_WORLD, core::matrix4());
driver->setMaterial(material);
driver->draw3DTriangle(tri, video::SColor(0,255,0,0));
}
Есть ещё один способ проверки на столкновения в движке Irrlicht.
selectedSceneNode = smgr->getSceneCollisionManager()->
getSceneNodeFromCameraBB(camera);
if (lastSelectedSceneNode)
lastSelectedSceneNode->setMaterialFlag(
video::EMF_LIGHTING, true);
if (selectedSceneNode == q3node ||
selectedSceneNode == bill)
selectedSceneNode = 0;
if (selectedSceneNode)
selectedSceneNode->setMaterialFlag(
video::EMF_LIGHTING, false);
lastSelectedSceneNode = selectedSceneNode;
Вот и всё :-) Осталось выполнить последние штрихи.
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"Collision detection example - Irrlicht Engine [";
str += driver->getName();
str += "] FPS:";
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
device->drop();
return 0;
}
Перевёл : WizZarD
7 ноября 2007