[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 1
  • 1
Форум » Игровые движки » IrrLicht Engine » Туториал №7: Столкновения
Туториал №7: Столкновения
MerGC_TeamДата: Четверг, 03.04.2014, 15:34 | Сообщение # 1
Веселый админ
Группа: Администраторы
Сообщений: 32
Статус: Оффлайн
В этом уроке описываются методы определения столкновений(коллизий) между объектами: автоматическое определение при перемещении по 3d миру (хождение по леснице, карабканье, скольжение), определение пересечения конкретного полигона модели с лучом направленным из выбранной камеры, хотя луч можно направить с любой точки пространства.

Как обычно, подключаем заголовочный файл IrrLicht, объявляем пространство имен 'irr'. После выбора драйвера и запуска движка, мы аналогично уроку №2, загрузим уровень из quake3 и 3 анимированные модельки - 4 объекта на которых будем тестировать пересечение с лучем конкретного их треугольника.

Код
#include < irrlicht.h >
#include "driverChoice.h"

using namespace irr;

enum
{
         // идентификатор для узлов сцены, которые будут реагировать
         // на вызовы метода getSceneNodeAndCollisionPointFromRay()
         ID_IsNotPickable = 0,

         // идентификатор для узлов сцены, которые не будут  
         // проходить проверку на столкновение с лучем
         IDFlag_IsPickable = 1 << 0,

         // идентификатор для узлов сцены, которы будут
         // подсвечиваться при столкновении с лучем
         IDFlag_IsHighlightable = 1 << 1
};

int main()
{
         // запрашиваем у пользователя драйвер (DirectX, OpenGL и т.д.)
         video::E_DRIVER_TYPE driverType=driverChoiceConsole();
         if (driverType==video::EDT_COUNT)  return 1;

         // создаем корневой объект(движок)
         IrrlichtDevice *device =  createDevice(driverType, core::dimension2d< u32 >(640, 480), 16, false);

         if (device == 0) return 1; // ошибка при создании движка, выходим.

         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::IMeshSceneNode* q3node = 0;

         // Добавляем уровень Quake, как принимающий тесты на столкновения, указав идентификатор IDFlag_IsPickable
         if (q3levelmesh)  q3node = smgr->addOctreeSceneNode(q3levelmesh->getMesh(0), 0, IDFlag_IsPickable);


Уровень загружен и теперь начинаются отличия от урока№2: Мы создаем селектор треугольников(triangle selector) - класс, который искать конкретный полигон в узлах(нодах). В этом уроке, мы создадим OctreeTriangleSelector, который оптимизирован для обработки большого числа треугольников, т.к. мы будем иметь дело с большим уровнем quake3. После создания селектора, прикрепим его у ноде q3node, в которую загружен уровень.

Код
scene::ITriangleSelector* selector = 0;

         if (q3node)
         {
                 q3node->setPosition(core::vector3df(-1350,-130,-1400));

                 selector = smgr->createOctreeTriangleSelector(q3node->getMesh(), q3node, 128);
                 q3node->setTriangleSelector(selector);
                 // т.к. селектор нам еще пригодится, то мы не будем здесь выполнять selector->drop()
         }


Добавляем FPS камеру на сцену так же как в уроке №2, но сейчас добавим еще к ней специальный аниматор: Аниматор определеня столкновения. Аниматор влияет на узел(ноду) сцены таким образом, что добавляет влияние на него гравитации и ощущение перепядствий (стен, лесниц и т.д.). В аниматоре настраивается с каким типом мира он имеет дело, каким типом ноды и как велика гравитация и т.п. Аниматор определения столкновений может быть прикреплен не только к камере, но и любому другому объекту на сцене.

Сейчас подробно остановлюсь на параметрах метода createCollisionResponseAnimator(). Первый параметр - это селектор треугольников(TriangleSelector), эдакий поисковик треугольников в базе данных сцены . Второй параметр - это узел(нода) на сцене, который является инициатором столкновений, в нашем случае это камера из которой мы будет направлять луч. Третий параметр - это радиус объекта инициатора(камеры), к примеру чем меньше радиус, тем ближе камера сможет подлететь к стене. Четвертый параметр определяет силу и направление гравитации, мы установим его в (0, -10, 0), т.е. направим в пол с силой 10г, предполагая что уровень мы мерим в метрах. Значение (0,0,0) отключит гравитацию. Пятый параметр - это смещение внутри элипсоида, радиус которого мы определили в третьем параметре. Этот параметр понадобится к примеру, если у нас модель человека, которую мы вписали в элипсоид, а луч для определения столкновений мы хотим напривить из глаз, которые географически будут расположены под его потолком (незнаю как по лучше сказать). Если не задать пятый параметр, то луч будет направляться из центра элипсоида.

Код
// зададим ускорение прыжка 3 единицы в секунду
         // относительно значения гравитации (0, -10, 0) в аниматоре определения столкновений.
         scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS(0, 100.0f, .3f, ID_IsNotPickable, 0, 0, true, 3.f);
         camera->setPosition(core::vector3df(50,50,-60));
         camera->setTarget(core::vector3df(-70,30,-60));

         if (selector)
         {
                 scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
                         selector, camera, core::vector3df(30,50,30),
                         core::vector3df(0,-10,0), core::vector3df(0,30,0));
                 selector->drop(); // вот теперь лично нам селектор больше не нужен, дропаем его
                 camera->addAnimator(anim);
                 anim->drop();  // аниматор столкновений тоже дропаем, оставляя на попечение камере
         }

         // Теперь добавим персонажей, которых будем "щупать" лучем камеры и источник света(билборд)
         // которым будем подсвечивать модель в которую уперся луч из камеры

         // спрячем стандартный указатель мыши
         device->getCursorControl()->setVisible(false);

         // Добавляем билборд, который будет подсвечивать место пересечения луча и объекта в который он уперся
         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->setMaterialFlag(video::EMF_ZBUFFER, false);
         bill->setSize(core::dimension2d< f32 >(20.0f, 20.0f));
         bill->setID(ID_IsNotPickable); // дадим билборду ID для объектов которые не реагируют на столкновения.


Добавляем модельки персонажей и слегка анимируем.

Код
scene::IAnimatedMeshSceneNode* node = 0;

         // моделька феи в формате MD2 с морф-анимацией
         node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/faerie.md2"), 0, IDFlag_IsPickable | IDFlag_IsHighlightable);
         node->setPosition(core::vector3df(-70,-15,-120)); // "поставим" ее на пол
         node->setScale(core::vector3df(2, 2, 2)); // масштабируем
         node->setMD2Animation(scene::EMAT_POINT);
         node->setAnimationSpeed(20.f);
         video::SMaterial material;
         material.setTexture(0, driver->getTexture("../../media/faerie2.bmp"));
         material.Lighting = true;
         material.NormalizeNormals = true;
         node->getMaterial(0) = material;

         // создаем селектор и прикрепляем к модельке феи
         selector = smgr->createTriangleSelector(node);
         node->setTriangleSelector(selector);
         selector->drop(); // селектор более не нужен - дропаем.

         // моделька дварфа в формате X со скелетной анимацией, но без текстур.
         node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/dwarf.x"), 0, IDFlag_IsPickable | IDFlag_IsHighlightable);
         node->setPosition(core::vector3df(-70,-66,0)); // "ставим" дварфа на пол
         node->setRotation(core::vector3df(0,-90,0)); // поворачиваем лицом к камере
         node->setAnimationSpeed(20.f);
         selector = smgr->createTriangleSelector(node);
         node->setTriangleSelector(selector);
         selector->drop();

         // моделька ниндзи в B3D формате со скелетной анимацией.
         node = smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/ninja.b3d"), 0, IDFlag_IsPickable | IDFlag_IsHighlightable);
         node->setScale(core::vector3df(10, 10, 10));
         node->setPosition(core::vector3df(-70,-66,-60));
         node->setRotation(core::vector3df(0,90,0));
         node->setAnimationSpeed(10.f);
         node->getMaterial(0).NormalizeNormals = true;
         // добавляем селектор, как и для предыдущих моделек.
         selector = smgr->createTriangleSelector(node);
         node->setTriangleSelector(selector);
         selector->drop();

         material.setTexture(0, 0);
         material.Lighting = false;

         // добавляем источник света, которым осветим все сразу модельки персонажей
         scene::ILightSceneNode * light = smgr->addLightSceneNode(0, core::vector3df(-60,100,400), video::SColorf(1.0f,1.0f,1.0f,1.0f), 600.0f);
         light->setID(ID_IsNotPickable); // помечаем источник света, как объект не реагирующий на столкновения.

         // определяем переменную, которая хранит указатель на подсвеченную модель и переменную для указателя на менеджер столкновений
         scene::ISceneNode* highlightedSceneNode = 0;
         scene::ISceneCollisionManager* collMan = smgr->getSceneCollisionManager();
         int lastFPS = -1;

         // рисуем треугольник, который прошел тест на столкновение, как полый контур(wireframe)
         material.Wireframe=true;

         while(device->run())
         if (device->isWindowActive())
         {
                 driver->beginScene(true, true, 0);
                 smgr->drawAll();

                 // включаем реакцию на обычное освещение (указатель *light) для подсвеченого узла(ноды), если таковой есть и "забываем" его(узел)
                 if (highlightedSceneNode)
                 {
                         highlightedSceneNode->setMaterialFlag(video::EMF_LIGHTING, true);
                         highlightedSceneNode = 0;
                 }

                 // Все пересещения в примере - это проверка на столкновение с лучем длинной 1000 ед.
                 // опущенного из камеры на сцену.  Вы можете легко модифицировать пример под проверку
                 // траектории пули или положения меча или определение 3d позицию клика указателя мыши
                 // посредством метода ISceneCollisionManager::getRayFromScreenCoordinates()
                 core::line3d ray;
                 ray.start = camera->getPosition();
                 ray.end = ray.start + (camera->getTarget() - ray.start).normalize() * 1000.0f;

                 // переменная под хранение точки пересещения
                 core::vector3df intersection;
                 // переменная под хранение треугольника с которым пересекся луч
                 core::triangle3df hitTriangle;

                 // Далее метод который делает всю работу по определению столкновения луча с объектами сцены
                 // Он находит ближайшую точку столкновения и возвращает узел(ноду), которому она принадлежит.
                 // IrrLicht предлагает и другие методы пересечения: луча с треугольником, луча с bounding box,  
                 // Irrlicht provides other types of selection, including ray/triangle selector,
                 // элипса с треугольником, плюс несколько методов хелперов(помошников).
                 // для подробностей поглядите методы класса ISceneCollisionManager
                 scene::ISceneNode * selectedSceneNode =
                         collMan->getSceneNodeAndCollisionPointFromRay(
                    ray,
                    intersection, // точка столкновения
                    hitTriangle, // полигон(треугольник) в котором точка столкновения
                    IDFlag_IsPickable, // определять столкновения только для нод с идентификатором IDFlag_IsPickable
                    0); // проверять относительно всей сцены (оставляем значение по умолчанию)

                 // Если луч столкнулся с чем нибудь, перемещаем указку(билборд) в точку столкновения
                 // и рисуем треугольник в котором она находится
                 if(selectedSceneNode)
                 {
                         bill->setPosition(intersection);

                         // мы должны сбросить трансформации перед отрисовкой.
                         driver->setTransform(video::ETS_WORLD, core::matrix4());
                         driver->setMaterial(material);
                         driver->draw3DTriangle(hitTriangle, video::SColor(0,255,0,0));

                         // Проверяем идентификатор узла(ноды) на который пришлось столкновение на тему
                         // подсветки. Если ID узла соответствует таковому, то запоминаем его и подсвечиваем.
                         if((selectedSceneNode->getID() & IDFlag_IsHighlightable) == IDFlag_IsHighlightable)
                         {
                    highlightedSceneNode = selectedSceneNode;

                    // В данном случае, мы отключаем обработку освещения(реакцию на *light) для узла, на который пришлось,
                    // столкновение тогда свет билборда, который подсвечивает точку столкновения, разольется по контуру(создаст ореол)
                    // (отключам реакцию на свет, чтобы осветить - каламбур, но вот так и есть).
                    highlightedSceneNode->setMaterialFlag(video::EMF_LIGHTING, false);
                         }
                 }

                 // обработка сцены закончена.
                 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;
}


Урок закончен. Компилируем, запускаем и вдохновленные новыми знаниями, бежим делать игру smile
 
Форум » Игровые движки » IrrLicht Engine » Туториал №7: Столкновения
  • Страница 1 из 1
  • 1
Поиск: