1.说明:
这个例子介绍:
碰撞检测
在高低的3D世界移动.
需要做的事包括:
获得能碰撞的三角形集合,
创建一个用于碰撞检测的特殊动画,设置相关参数
绑定到摄像机节点上
碰撞检测
在原来的基础上进行修改,实现在地图中漫游,且有碰撞检测.
2.
2.1
场景节点声明为:
scene::IMeshSceneNode* q3node =0;
以使用相关方法
2.2
2.2.1创建三角形选择器
三角形选择器能收集来自场景节点的三角形用作其他用途,有多种三角形选择器,这里用八叉树优化过的.
创建一个八叉树优化过的三角形选择器
作用:用于碰撞检测
使用方法:
三角形选择器不会在被创建的时候自动与某个场景节点绑定,需要调用场景节点的方法进行设置.
创建,绑定.
ITriangleSelector* selector = sceneManager->createOctreeTriangleSelector(
yourMesh,//q3node->getMesh()
yourSceneNode//q3node
);
yourSceneNode->setTriangleSelector(selector);
2.2.2摄像机
函数:
virtual ICameraSceneNode*
addCameraSceneNodeFPS(
ISceneNode* parent = 0,
f32 rotateSpeed = 100.0f, f32 moveSpeed = 0.5f, s32 id=-1,
SKeyMap* keyMapArray=0, //可选的按键映射表
s32 keyMapSize=0,//表的大小
bool noVerticalMovement=false,//true摄像机只在水平面上移动,false在没有重力的情况下,摄像机可以自由移动
f32 jumpSpeed = 0.f,
bool invertMouse=false,//纵向鼠标反向操作
bool makeActive=true//是否激活摄像机
) = 0;
创建一个摄像机
添加WASD移动和跳跃键
struct SKeyMap
{
EKEY_ACTION Action;//对应的动作
EKEY_CODE KeyCode;//对应的按键
};
通过SKeyMap将动作和按键映射:
SKeyMap keyMap[5];
keyMap[0].Action = EKA_MOVE_FORWARD;
keyMap[0].KeyCode = KEY_KEY_W;
keyMap[1].Action = EKA_MOVE_BACKWARD;
keyMap[1].KeyCode = KEY_KEY_S;
keyMap[2].Action = EKA_STRAFE_LEFT;
keyMap[2].KeyCode = KEY_KEY_A;
keyMap[3].Action = EKA_STRAFE_RIGHT;
keyMap[3].KeyCode = KEY_KEY_D;
keyMap[4].Action = EKA_JUMP_UP;
keyMap[4].KeyCode = KEY_SPACE;
scene::ICameraSceneNode* camera =
smgr->addCameraSceneNodeFPS(
0, 100, .3, ID_IsNotPickable, keyMap, 5, true, 3.0);
设置位置
camera->setPosition(core::vector3df(50,50,-60));
设置look at 目标
camera->setTarget(core::vector3df(-70,30,-60));
为摄像机绑定动画
创建并为摄像机绑定一个用于自动碰撞检测和反应的特殊的场景节点动画
创建动画:
ISceneNodeAnimatorCollisionResponse* createCollisionResponseAnimator(
ITriangleSelector* world, //保存场景节点能碰撞到的所有三角形
ISceneNode* sceneNode,//添加要收到碰撞检测和重力作用的节点,这里是camra
const core::vector3df& ellipsoidRadius = core::vector3df(30,60,30),//碰撞检测的xyz分量的椭球半径范围
const core::vector3df& gravityPerSecond = core::vector3df(0,-9.8f,0),//重力加速度
const core::vector3df& ellipsoidTranslation = core::vector3df(0,0,0),//调整碰撞检测椭球中心位置,默认是地面
f32 slidingValue = 0.0005f
) ;
然后调用ISceneNode::addAnimator()绑定
比如:
scene::ISceneNodeAnimator* anim =
smgr->createCollisionResponseAnimator(
selector, camera, core::vector3df(30, 50, 30),
core::vector3df(0, -9.8, 0),
core::vector3df(0, 30, 0)
);
selector->drop();
camera->addAnimator(anim);
anim->drop();
现在就可以在地图里更合理的漫游了.
3.全部代码:
#include <iostream>
#include <irrlicht.h>
using namespace irr;
#ifdef _IRR_WINDOWS_
#pragma comment(lib, "irrlicht.lib")
//#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif
enum
{
//ID为ID_IsNotPickable的场景节点不能被getSceneNodeAndCollisionPointFromRay()拾取
ID_IsNotPickable = 0,
//这个标记表面能被拾取
IDFlag_IsPickable = 1 << 0,
//这个标记表面节点被拾取后高亮显示,
//这个例子中地图不能高亮,而角色能高亮
IDFlag_IsHighlightable = 1 << 1
};
int main(int argc, char** argv)
{
IrrlichtDevice *device =createDevice(video::EDT_OPENGL, core::dimension2d<u32>(720, 455), 32,false, true, false, 0);
if (!device)
return 1;
device->setWindowCaption(L"quake3 map");
video::IVideoDriver *driver = device->getVideoDriver();
scene::ISceneManager *smgr = device->getSceneManager();
gui::IGUIEnvironment *guiev = device->getGUIEnvironment();
//为了显示quake3 map,我们要加载它,而其被压缩在map-20kdm2.pk3中,
//所以先将压缩包加载到文件系统
//实际调用device->getFileArchive(filename,true,true,EFAT_ZIP);
device->getFileSystem()->addZipFileArchive("../media/map-20kdm2.pk3");
//将地图当作一帧动画加载
scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp");
scene::IMeshSceneNode* q3node = 0;//与例子2不一样,eg2是ISceneNode
if (q3levelmesh)
{
q3node = smgr->addOctreeSceneNode(q3levelmesh->getMesh(0), 0,IDFlag_IsPickable);
//node = smgr->addMeshSceneNode(mesh->getMesh(0));
}
scene::ITriangleSelector* selector = 0;
//通过irr::scene::ISceneNode 层的方法对node进行属性调整
//位置:irr::scene::ISceneNode::setPosition()
//旋转:irr::scene::ISceneNode::setRotation()
//缩放:irr::scene::ISceneNode::setScale()
if (q3node)
{
q3node->setPosition(core::vector3df(-1350, -130, -1400));
// node->setRotation(core::vector3df(0, 0, 0));
// node->setScale(core::vector3df(1, 1, 1));
selector = smgr->createOctreeTriangleSelector(
q3node->getMesh(),q3node, 128);
q3node->setTriangleSelector(selector);
}
SKeyMap keyMap[5];
keyMap[0].Action = EKA_MOVE_FORWARD;
keyMap[0].KeyCode = KEY_KEY_W;
keyMap[1].Action = EKA_MOVE_BACKWARD;
keyMap[1].KeyCode = KEY_KEY_S;
keyMap[2].Action = EKA_STRAFE_LEFT;
keyMap[2].KeyCode = KEY_KEY_A;
keyMap[3].Action = EKA_STRAFE_RIGHT;
keyMap[3].KeyCode = KEY_KEY_D;
keyMap[4].Action = EKA_JUMP_UP;
keyMap[4].KeyCode = KEY_SPACE;
//camera = sceneManager->addCameraSceneNodeFPS(0, 100, 500, -1, keyMap, 8);
scene::ICameraSceneNode* camera =
smgr->addCameraSceneNodeFPS(
0, 100, .3, ID_IsNotPickable, keyMap, 5, true, 3.0);
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, -9.8, 0),
core::vector3df(0, 30, 0)
);
selector->drop();
camera->addAnimator(anim);
anim->drop();
}
int lastFPS = -1;
while (device->run())
{
if (device->isWindowActive())
{
driver->beginScene(true, true, video::SColor(255, 100, 101, 140));
smgr->drawAll();
guiev->drawAll();
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw str = L"quake3 map [";
str += driver->getName();
str += "]FPS.",
str += fps;
device->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
else
//放置一直渲染占用过多cpu
device->yield();
}
device->drop();
return 0;
}