I was wondering if someone could give me an example of how to code crouching and jumping into a FPS style game. I am new to both Irrlicht and C++, but I am eager to learn. In just a couple of hours I managed to be able to understand some of the basic concepts and wrote a console based menu for one of the tutorials.
I am now examining the demo that comes with the latest (0.8) release of Irrlicht and have started to create my own demo by learning from the code. I have been investigating ways of implementing these two features but have not yet found a concrete solution.
Jumping I see as being the easiest to implement as the createCollisionResponseAnimator function allows you to set the gravity and acceleration per second of the camera. One would simply have to give the camera a vertical speed.
Crouching on the other hand has me stumped. I have found that it is possible to set the radius of the elipsoid using:
virtual void irr::scene::ISceneNodeAnimatorCollisionResponse::setEllipsoidRadius ( const core::vector3df & radius ) [pure virtual]
However is it possible to do this after the animator has already been applied to the scene node using addAnimator. If so, does it remove the old animator as well?
Final question. Using SKeyMap like in the included demo, is it possible for the actions to be a call to a function, say for example the action of space to call jump().
Implementing Crouching And Jumping
-
- Posts: 13
- Joined: Mon Feb 28, 2005 4:12 pm
Another problem I have is with the movement of the camera. Using the default movement code for the FPS camera, when moving forward whilst looking up or backwards whilst looking down causes the camera to jump up and down. Would this just require a change to the vectors of the animation code so they are respond to the x and z axis and not the y.
The second post has been answered I think somewhere else, I think Bal got annoyed by it. I can't remember where the post is but search and you will find, need to have 2 conditions really, and inserting an AND will help too.
As for the jumping and crouching, I started trying to implement it using the engine, and its quite difficult compared to some of the other options that are available to you. I've just implemented Newton physics engine in to my game, and it takes around about 5 LOC to implement the jump functionality, also its more realistic with the Physics engine. However, other functionality does take time to implement.
In my opinion I would run before I could jump or crouch, sorry for the pun. But get most of the application made then implement the other nice features in to the application like jumping and crouching later on. Get used to handling the engine, then see how other things can be implemented in to the engine.
However, you could do it the other way and learn both a physics engine and the irrlicht engine at the same time.
As for the jumping and crouching, I started trying to implement it using the engine, and its quite difficult compared to some of the other options that are available to you. I've just implemented Newton physics engine in to my game, and it takes around about 5 LOC to implement the jump functionality, also its more realistic with the Physics engine. However, other functionality does take time to implement.
In my opinion I would run before I could jump or crouch, sorry for the pun. But get most of the application made then implement the other nice features in to the application like jumping and crouching later on. Get used to handling the engine, then see how other things can be implemented in to the engine.
However, you could do it the other way and learn both a physics engine and the irrlicht engine at the same time.
-
- Posts: 13
- Joined: Mon Feb 28, 2005 4:12 pm
The integration demo does quite a bit of work for you, the mercior tutorial, then theres quite a few threads on here about it. Its not overly complicated to understand, like any engine, at the start you don't know a lot but over time everything starts to become clear... If its only WIN32 there must be other options, like ODE, Tokamak, and the others that bal posted when I asked the other day. I think its in the offtopic thread...
-
- Posts: 13
- Joined: Mon Feb 28, 2005 4:12 pm
Ok, I've finished stripping down the demo code to remove the portals, models etc, but I now have a problem with the OnEvent reciever. The application crashes on running CGame::run().
CGame.cpp
I have managed to deduce that the problem is due to the lines as the application works fine when they are commented out:
Any suggestions as to why this could be happening. I am use Dev-Cpp 4.9.9.2 and the irrlicht.dll from the Dev-Cpp folder included in the SDK. The camera is still moveable when this is commented out using the KeyMap defined in the source. Is this code block optional or will the other events fail to work properly.
CGame.cpp
Code: Select all
#include "CGame.h"
#include <stdio.h>
CGame::CGame(bool f, bool s, bool a, bool v, int w, int h, video::E_DRIVER_TYPE d)
: fullscreen(f), shadows(s), additive(a), vsync(v), screenwidth(w), screenheight(h), driverType(d),
currentScene(-2), timeForThisScene(0), mapSelector(0), metaSelector(0), quakeLevelMesh(0), quakeLevelNode(0), skyboxNode(0)
{
}
CGame::~CGame()
{
if (mapSelector)
mapSelector->drop();
if (metaSelector)
metaSelector->drop();
}
void CGame::run()
{
device = createDevice(driverType,
core::dimension2d<s32>(screenwidth, screenheight), 32, fullscreen, shadows, vsync, this);;
device->getFileSystem()->addZipFileArchive("media/irrlicht.dat");
device->getFileSystem()->addZipFileArchive("maps/map-20kdm2.pk3");
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* smgr = device->getSceneManager();
gui::IGUIEnvironment* guienv = device->getGUIEnvironment();
device->setWindowCaption(L"Irrlicht Engine Demo");
wchar_t tmp[255];
while(device->run() && driver)
{
if (device->isWindowActive())
{
// load next scene if necessary
u32 now = device->getTimer()->getTime();
if (now - sceneStartTime > timeForThisScene && timeForThisScene!=-1)
switchToNextScene();
// draw everything
driver->beginScene(true, true, backColor);
smgr->drawAll();
guienv->drawAll();
driver->endScene();
// write statistics
swprintf(tmp, 255, L"%s fps:%d", driver->getName(), driver->getFPS());
statusText->setText(tmp);
}
}
device->drop();
}
bool CGame::OnEvent(SEvent event)
{
if (!device)
return false;
if (event.EventType == EET_KEY_INPUT_EVENT &&
event.KeyInput.Key == KEY_ESCAPE &&
event.KeyInput.PressedDown == false)
{
// user wants to quit.
if (currentScene < 3)
timeForThisScene = 0;
else
device->closeDevice();
}
else
if ((event.EventType == EET_KEY_INPUT_EVENT &&
event.KeyInput.Key == KEY_SPACE &&
event.KeyInput.PressedDown == false) ||
(event.EventType == EET_MOUSE_INPUT_EVENT &&
event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) &&
currentScene == 3)
{
// shoot
shoot();
}
else
#ifdef _DEBUG
if (event.EventType == irr::EET_MOUSE_INPUT_EVENT &&
event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
{
char tmp[255];
core::vector3df pos = device->getSceneManager()->getActiveCamera()->getAbsolutePosition();
sprintf(tmp, "points.push_back(core::vector3df(%ff, %ff, %ff)); // %d\n", pos.X, pos.Y, pos.Z,
sceneStartTime - device->getTimer()->getTime());
OutputDebugString(tmp);
return true;
}
else
#endif
/*if (device->getSceneManager()->getActiveCamera())
{
device->getSceneManager()->getActiveCamera()->OnEvent(event);
return true;
}
*/
return false;
}
void CGame::switchToNextScene()
{
currentScene++;
if (currentScene > 3)
currentScene = 1;
scene::ISceneManager* sm = device->getSceneManager();
scene::ISceneNodeAnimator* sa = 0;
scene::ICameraSceneNode* camera = 0;
camera = sm->getActiveCamera();
if (camera)
{
sm->setActiveCamera(0);
camera->remove();
}
switch(currentScene)
{
case -1: // loading screen
timeForThisScene = 0;
createLoadingScreen();
break;
case 0: // load scene
timeForThisScene = 0;
loadSceneData();
//currentScene += 2;
break;
case 1: // panorama camera
{
#if 0
camera = sm->addCameraSceneNodeFPS(0, 100, 500);
timeForThisScene = 14000000;
#else
currentScene += 1;
camera = sm->addCameraSceneNode(0, core::vector3df(0,0,0), core::vector3df(-586,708,52));
camera->setTarget(core::vector3df(0,400,0));
core::array<core::vector3df> points;
points.push_back(core::vector3df(-931.473755f, 138.300003f, 987.279114f)); // -49873
points.push_back(core::vector3df(-847.902222f, 136.757553f, 915.792725f)); // -50559
points.push_back(core::vector3df(-748.680420f, 152.254501f, 826.418945f)); // -51964
points.push_back(core::vector3df(-708.428406f, 213.569580f, 784.466675f)); // -53251
points.push_back(core::vector3df(-686.217651f, 288.141174f, 762.965576f)); // -54015
points.push_back(core::vector3df(-679.685059f, 365.095612f, 756.551453f)); // -54733
points.push_back(core::vector3df(-671.317871f, 447.360107f, 749.394592f)); // -55588
points.push_back(core::vector3df(-669.468445f, 583.335632f, 747.711853f)); // -56178
points.push_back(core::vector3df(-667.611267f, 727.313232f, 746.018250f)); // -56757
points.push_back(core::vector3df(-665.853210f, 862.791931f, 744.436096f)); // -57859
points.push_back(core::vector3df(-642.649597f, 1026.047607f, 724.259827f)); // -59705
points.push_back(core::vector3df(-517.793884f, 838.396790f, 490.326050f)); // -60983
points.push_back(core::vector3df(-474.387299f, 715.691467f, 344.639984f)); // -61629
points.push_back(core::vector3df(-444.600250f, 601.155701f, 180.938095f)); // -62319
points.push_back(core::vector3df(-414.808899f, 479.691406f, 4.866660f)); // -63048
points.push_back(core::vector3df(-410.418945f, 429.642242f, -134.332687f)); // -63757
points.push_back(core::vector3df(-399.837585f, 411.498383f, -349.350983f)); // -64418
points.push_back(core::vector3df(-390.756653f, 403.970093f, -524.454407f)); // -65005
points.push_back(core::vector3df(-334.864227f, 350.065491f, -732.397400f)); // -65701
points.push_back(core::vector3df(-195.253387f, 349.577209f, -812.475891f)); // -66335
points.push_back(core::vector3df(16.255573f, 363.743134f, -833.800415f)); // -67170
points.push_back(core::vector3df(234.940964f, 352.957825f, -820.150696f)); // -67939
points.push_back(core::vector3df(436.797668f, 349.236450f, -816.914185f)); // -68596
points.push_back(core::vector3df(575.236206f, 356.244812f, -719.788513f)); // -69166
points.push_back(core::vector3df(594.131042f, 387.173828f, -609.675598f)); // -69744
points.push_back(core::vector3df(617.615234f, 412.002899f, -326.174072f)); // -70640
points.push_back(core::vector3df(606.456848f, 403.221954f, -104.179291f)); // -71390
points.push_back(core::vector3df(610.958252f, 407.037750f, 117.209778f)); // -72085
points.push_back(core::vector3df(597.956909f, 395.167877f, 345.942200f)); // -72817
points.push_back(core::vector3df(587.383118f, 391.444519f, 566.098633f)); // -73477
points.push_back(core::vector3df(559.572449f, 371.991333f, 777.689453f)); // -74124
points.push_back(core::vector3df(423.753204f, 329.990051f, 925.859741f)); // -74941
points.push_back(core::vector3df(247.520050f, 252.818954f, 935.311829f)); // -75651
points.push_back(core::vector3df(114.756012f, 199.799759f, 805.014160f));
points.push_back(core::vector3df(96.783348f, 181.639481f, 648.188110f));
points.push_back(core::vector3df(97.865623f, 138.905975f, 484.812561f));
points.push_back(core::vector3df(99.612457f, 102.463669f, 347.603210f));
points.push_back(core::vector3df(99.612457f, 102.463669f, 347.603210f));
points.push_back(core::vector3df(99.612457f, 102.463669f, 347.603210f));
timeForThisScene = (points.size()-3)* 1000;
sa = sm->createFollowSplineAnimator(device->getTimer()->getTime(),
points);
camera->addAnimator(sa);
sa->drop();
#endif
inOutFader->fadeIn(7000);
break;
}
case 2: // down fly anim camera
camera = sm->addCameraSceneNode(0, core::vector3df(100,40,-80), core::vector3df(844,670,-885));
sa = sm->createFlyStraightAnimator( core::vector3df(94, 1002, 127),
core::vector3df(108, 15, -60), 10000, true);
camera->addAnimator(sa);
timeForThisScene = 9900;
sa->drop();
break;
case 3: // interactive, go around
{
timeForThisScene = (u32)-1;
SKeyMap keyMap[8];
keyMap[0].Action = EKA_MOVE_FORWARD;
keyMap[0].KeyCode = KEY_UP;
keyMap[1].Action = EKA_MOVE_FORWARD;
keyMap[1].KeyCode = KEY_KEY_W;
keyMap[2].Action = EKA_MOVE_BACKWARD;
keyMap[2].KeyCode = KEY_DOWN;
keyMap[3].Action = EKA_MOVE_BACKWARD;
keyMap[3].KeyCode = KEY_KEY_S;
keyMap[4].Action = EKA_STRAFE_LEFT;
keyMap[4].KeyCode = KEY_LEFT;
keyMap[5].Action = EKA_STRAFE_LEFT;
keyMap[5].KeyCode = KEY_KEY_A;
keyMap[6].Action = EKA_STRAFE_RIGHT;
keyMap[6].KeyCode = KEY_RIGHT;
keyMap[7].Action = EKA_STRAFE_RIGHT;
keyMap[7].KeyCode = KEY_KEY_D;
camera = sm->addCameraSceneNodeFPS(0, 100.0f, 300.0f, -1, keyMap, 8);
camera->setPosition(core::vector3df(108,140,-140));
scene::ISceneNodeAnimatorCollisionResponse* collider =
sm->createCollisionResponseAnimator(
metaSelector, camera, core::vector3df(30,50,30),
core::vector3df(0, quakeLevelMesh ? -3.0f : 0.0f,0),
core::vector3df(0,40,0), 0.0005f);
camera->addAnimator(collider);
collider->drop();
}
break;
}
sceneStartTime = device->getTimer()->getTime();
// if we've got a new created camera, we call OnPostRender to let all animators
// set the right position of the camera, otherwise the camera would
// be at a wrong position in the first frame
if (device->getSceneManager()->getActiveCamera())
device->getSceneManager()->getActiveCamera()->OnPostRender(sceneStartTime);
}
void CGame::loadSceneData()
{
// load quake level
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* sm = device->getSceneManager();
quakeLevelMesh = sm->getMesh("20kdm2.bsp");
if (quakeLevelMesh)
{
quakeLevelNode = sm->addOctTreeSceneNode(quakeLevelMesh->getMesh(0));
if (quakeLevelNode)
{
quakeLevelNode->setPosition(core::vector3df(-1300,-70,-1249));
quakeLevelNode->setVisible(true);
// create map triangle selector
mapSelector = sm->createOctTreeTriangleSelector(quakeLevelMesh->getMesh(0),
quakeLevelNode, 128);
// set additive blending if wanted
if (additive)
quakeLevelNode->setMaterialType(video::EMT_LIGHTMAP_ADD);
}
}
// create sky box
//driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
skyboxNode = sm->addSkyBoxSceneNode(
driver->getTexture("media/skyboxes/irrlicht2_up.jpg"),
driver->getTexture("media/skyboxes/irrlicht2_dn.jpg"),
driver->getTexture("media/skyboxes/irrlicht2_lf.jpg"),
driver->getTexture("media/skyboxes/irrlicht2_rt.jpg"),
driver->getTexture("media/skyboxes/irrlicht2_ft.jpg"),
driver->getTexture("media/skyboxes/irrlicht2_bk.jpg"));
//driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
// create meta triangle selector with all triangles selectors in it.
metaSelector = sm->createMetaTriangleSelector();
metaSelector->addTriangleSelector(mapSelector);
// set background color
backColor.set(0,0,0,0);
}
void CGame::createLoadingScreen()
{
core::dimension2d<int> size = device->getVideoDriver()->getScreenSize();
device->getCursorControl()->setVisible(false);
// setup loading screen
backColor.set(255,90,90,156);
// create in fader
inOutFader = device->getGUIEnvironment()->addInOutFader();
inOutFader->setColor(backColor);
// irrlicht logo
gui::IGUIImage* img = device->getGUIEnvironment()->addImage(
core::rect<int>(10,10,98,41));
img->setImage(
device->getVideoDriver()->getTexture("media/irrlichtlogoaligned.jpg"));
// loading text
const int lwidth = 120;
const int lheight = 15;
core::rect<int> pos(10, size.Height-lheight-10, 10+lwidth, size.Height-10);
device->getGUIEnvironment()->addImage(pos);
statusText = device->getGUIEnvironment()->addStaticText(L"Loading...", pos, true);
statusText->setOverrideColor(video::SColor(255,205,200,200));
// load bigger font
device->getGUIEnvironment()->getSkin()->setFont(
device->getGUIEnvironment()->getFont("media/fonts/fonthaettenschweiler.bmp"));
// set new font color
device->getGUIEnvironment()->getSkin()->setColor(gui::EGDC_BUTTON_TEXT,
video::SColor(255,100,100,100));
}
void CGame::shoot()
{
scene::ISceneManager* sm = device->getSceneManager();
scene::ICameraSceneNode* camera = sm->getActiveCamera();
if (!camera || !mapSelector)
return;
}
Code: Select all
if (device->getSceneManager()->getActiveCamera())
{
device->getSceneManager()->getActiveCamera()->OnEvent(event);
return true;
}