Irrlicht 3D Engine
 
Loading...
Searching...
No Matches
Tutorial 4: Movement

This Tutorial shows how to move and animate SceneNodes. The basic concept of SceneNodeAnimators is shown as well as manual movement of nodes using the keyboard. We'll demonstrate framerate independent movement, which means moving by an amount dependent on the duration of the last run of the Irrlicht loop.

Example 19.MouseAndJoystick shows how to handle those kinds of input.

As always, I include the header files, use the irr namespace, and tell the linker to link with the .lib file.

#ifdef _MSC_VER
// We'll also define this to stop MSVC complaining about sprintf().
#define _CRT_SECURE_NO_WARNINGS
#pragma comment(lib, "Irrlicht.lib")
#endif
#include <irrlicht.h>
#include "driverChoice.h"
using namespace irr;
Main header file of the irrlicht, the only file needed to include.
Everything in the Irrlicht Engine can be found in this namespace.
Definition aabbox3d.h:13

To receive events like mouse and keyboard input, or GUI events like "the OK button has been clicked", we need an object which is derived from the irr::IEventReceiver object. There is only one method to override: irr::IEventReceiver::OnEvent(). This method will be called by the engine once when an event happens. What we really want to know is whether a key is being held down, and so we will remember the current state of each key.

class MyEventReceiver : public IEventReceiver
{
public:
// This is the one method that we have to implement
virtual bool OnEvent(const SEvent& event)
{
// Remember whether each key is down or up
KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
return false;
}
// This is used to check whether a key is being held down
virtual bool IsKeyDown(EKEY_CODE keyCode) const
{
return KeyIsDown[keyCode];
}
MyEventReceiver()
{
for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
KeyIsDown[i] = false;
}
private:
// We use this array to store the current state of each key
bool KeyIsDown[KEY_KEY_CODES_COUNT];
};
Interface of an object which can receive events.
unsigned int u32
32 bit unsigned variable.
Definition irrTypes.h:58
EKEY_CODE
Definition Keycodes.h:12
@ KEY_KEY_CODES_COUNT
Definition Keycodes.h:168
@ EET_KEY_INPUT_EVENT
A key input event.
SEvents hold information about an event. See irr::IEventReceiver for details on event handling.
EEVENT_TYPE EventType

The event receiver for keeping the pressed keys is ready, the actual responses will be made inside the render loop, right before drawing the scene. So lets just create an irr::IrrlichtDevice and the scene node we want to move. We also create some other additional scene nodes, to show that there are also some different possibilities to move and animate scene nodes.

int main()
{
// ask user for driver
video::E_DRIVER_TYPE driverType=driverChoiceConsole();
if (driverType==video::EDT_COUNT)
return 1;
// create device
MyEventReceiver receiver;
IrrlichtDevice* device = createDevice(driverType,
core::dimension2d<u32>(640, 480), 16, false, false, false, &receiver);
if (device == 0)
return 1; // could not create selected driver.
video::IVideoDriver* driver = device->getVideoDriver();
The Irrlicht device. You can create it with createDevice() or createDeviceEx().
virtual scene::ISceneManager * getSceneManager()=0
Provides access to the scene manager.
virtual video::IVideoDriver * getVideoDriver()=0
Provides access to the video driver for drawing 3d and 2d geometry.
Axis aligned bounding box in 3d dimensional space.
Definition aabbox3d.h:22
The Scene Manager manages scene nodes, mesh recources, cameras and all the other stuff.
Interface to driver which is able to perform 2d and 3d graphics functions.
E_DRIVER_TYPE
An enum for all types of drivers the Irrlicht Engine supports.

Create the node which will be moved with the WSAD keys. We create a sphere node, which is a built-in geometry primitive. We place the node at (0,0,30) and assign a texture to it to let it look a little bit more interesting. Because we have no dynamic lights in this scene we disable lighting for each model (otherwise the models would be black).

if (node)
{
node->setPosition(core::vector3df(0,0,30));
node->setMaterialTexture(0, driver->getTexture("../../media/wall.bmp"));
node->setMaterialFlag(video::EMF_LIGHTING, false);
}
virtual IMeshSceneNode * addSphereSceneNode(f32 radius=5.0f, s32 polyCount=16, ISceneNode *parent=0, s32 id=-1, const core::vector3df &position=core::vector3df(0, 0, 0), const core::vector3df &rotation=core::vector3df(0, 0, 0), const core::vector3df &scale=core::vector3df(1.0f, 1.0f, 1.0f))=0
Adds a sphere scene node of the given radius and detail.
Scene node interface.
Definition ISceneNode.h:41
void setMaterialTexture(u32 textureLayer, video::ITexture *texture)
Sets the texture of the specified layer in all materials of this scene node to the new texture.
Definition ISceneNode.h:436
virtual void setPosition(const core::vector3df &newpos)
Sets the position of the node relative to its parent.
Definition ISceneNode.h:507
void setMaterialFlag(video::E_MATERIAL_FLAG flag, bool newvalue)
Sets all material flags at once to a new value.
Definition ISceneNode.h:425
virtual ITexture * getTexture(const io::path &filename)=0
Get access to a named texture.

Now we create another node, movable using a scene node animator. Scene node animators modify scene nodes and can be attached to any scene node like mesh scene nodes, billboards, lights and even camera scene nodes. Scene node animators are not only able to modify the position of a scene node, they can also animate the textures of an object for example. We create a cube scene node and attach a 'fly circle' scene node animator to it, letting this node fly around our sphere scene node.

if (n)
{
n->setMaterialTexture(0, driver->getTexture("../../media/t351sml.jpg"));
n->setMaterialFlag(video::EMF_LIGHTING, false);
if (anim)
{
n->addAnimator(anim);
anim->drop();
}
}
bool drop() const
Drops the object. Decrements the reference counter by one.
virtual IMeshSceneNode * addCubeSceneNode(f32 size=10.0f, ISceneNode *parent=0, s32 id=-1, const core::vector3df &position=core::vector3df(0, 0, 0), const core::vector3df &rotation=core::vector3df(0, 0, 0), const core::vector3df &scale=core::vector3df(1.0f, 1.0f, 1.0f))=0
Adds a cube scene node.
virtual ISceneNodeAnimator * createFlyCircleAnimator(const core::vector3df &center=core::vector3df(0.f, 0.f, 0.f), f32 radius=100.f, f32 speed=0.001f, const core::vector3df &direction=core::vector3df(0.f, 1.f, 0.f), f32 startPosition=0.f, f32 radiusEllipsoid=0.f)=0
Creates a fly circle animator, which lets the attached scene node fly around a center.
Animates a scene node. Can animate position, rotation, material, and so on.
virtual void addAnimator(ISceneNodeAnimator *animator)
Adds an animator which should animate this node.
Definition ISceneNode.h:348

The last scene node we add to show possibilities of scene node animators is a b3d model, which uses a 'fly straight' animator to run between to points.

smgr->addAnimatedMeshSceneNode(smgr->getMesh("../../media/ninja.b3d"));
if (anms)
{
core::vector3df(-100,0,60), 3500, true);
if (anim)
{
anms->addAnimator(anim);
anim->drop();
}
Scene node capable of displaying an animated mesh and its shadow.
virtual IAnimatedMesh * getMesh(const io::path &filename)=0
Get pointer to an animateable mesh. Loads the file if not loaded already.
virtual IAnimatedMeshSceneNode * addAnimatedMeshSceneNode(IAnimatedMesh *mesh, ISceneNode *parent=0, s32 id=-1, const core::vector3df &position=core::vector3df(0, 0, 0), const core::vector3df &rotation=core::vector3df(0, 0, 0), const core::vector3df &scale=core::vector3df(1.0f, 1.0f, 1.0f), bool alsoAddIfMeshPointerZero=false)=0
Adds a scene node for rendering an animated mesh model.
virtual ISceneNodeAnimator * createFlyStraightAnimator(const core::vector3df &startPoint, const core::vector3df &endPoint, u32 timeForWay, bool loop=false, bool pingpong=false)=0
Creates a fly straight animator, which lets the attached scene node fly or move along a line between ...

To make the model look right we disable lighting, set the frames between which the animation should loop, rotate the model around 180 degrees, and adjust the animation speed and the texture. To set the right animation (frames and speed), we would also be able to just call "anms->setMD2Animation(scene::EMAT_RUN)" for the 'run' animation instead of "setFrameLoop" and "setAnimationSpeed", but this only works with MD2 animations, and so you know how to start other animations. But a good advice is to not use hardcoded frame-numbers...

anms->setMaterialFlag(video::EMF_LIGHTING, false);
anms->setFrameLoop(0, 13);
anms->setAnimationSpeed(15);
// anms->setMD2Animation(scene::EMAT_RUN);
anms->setScale(core::vector3df(2.f,2.f,2.f));
anms->setRotation(core::vector3df(0,-90,0));
// anms->setMaterialTexture(0, driver->getTexture("../../media/sydney.bmp"));
}
virtual void setAnimationSpeed(f32 framesPerSecond)=0
Sets the speed with which the animation is played.
virtual bool setFrameLoop(s32 begin, s32 end)=0
Sets the frame numbers between the animation is looped.
virtual void setScale(const core::vector3df &scale)
Sets the relative scale of the scene node.
Definition ISceneNode.h:468
virtual void setRotation(const core::vector3df &rotation)
Sets the rotation of the node relative to its parent.
Definition ISceneNode.h:488

To be able to look at and move around in this scene, we create a first person shooter style camera and make the mouse cursor invisible.

device->getCursorControl()->setVisible(false);
virtual gui::ICursorControl * getCursorControl()=0
Provides access to the cursor control.
virtual void setVisible(bool visible)=0
Changes the visible state of the mouse cursor.
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, f32 jumpSpeed=0.f, bool invertMouse=false, bool makeActive=true)=0
Adds a camera scene node with an animator which provides mouse and keyboard control appropriate for f...

Add a colorful irrlicht logo

driver->getTexture("../../media/irrlichtlogoalpha2.tga"),
L"", core::rect<s32>(10, 10, 400, 20));
diagnostics->setOverrideColor(video::SColor(255, 255, 255, 0));
virtual gui::IGUIEnvironment * getGUIEnvironment()=0
Provides access to the 2d user interface environment.
virtual IGUIImage * addImage(video::ITexture *image, core::position2d< s32 > pos, bool useAlphaChannel=true, IGUIElement *parent=0, s32 id=-1, const wchar_t *text=0)=0
Adds an image element.
virtual IGUIStaticText * addStaticText(const wchar_t *text, const core::rect< s32 > &rectangle, bool border=false, bool wordWrap=true, IGUIElement *parent=0, s32 id=-1, bool fillBackground=false)=0
Adds a static text.
Multi or single line text label.
virtual void setOverrideColor(video::SColor color)=0
Sets another color for the text.
Class representing a 32 bit ARGB color.
Definition SColor.h:202

We have done everything, so lets draw it. We also write the current frames per second and the name of the driver to the caption of the window.

int lastFPS = -1;
// In order to do framerate independent movement, we have to know
// how long it was since the last frame
u32 then = device->getTimer()->getTime();
// This is the movemen speed in units per second.
const f32 MOVEMENT_SPEED = 5.f;
while(device->run())
{
// Work out a frame delta time.
const u32 now = device->getTimer()->getTime();
const f32 frameDeltaTime = (f32)(now - then) / 1000.f; // Time in seconds
then = now;
virtual u32 getTime() const =0
Returns current virtual time in milliseconds.
virtual bool run()=0
Runs the device.
virtual ITimer * getTimer()=0
Provides access to the engine's timer.
float f32
32 bit floating point variable.
Definition irrTypes.h:104

Check if keys W, S, A or D are being held down, and move the sphere node around respectively.

core::vector3df nodePosition = node->getPosition();
if(receiver.IsKeyDown(irr::KEY_KEY_W))
nodePosition.Y += MOVEMENT_SPEED * frameDeltaTime;
else if(receiver.IsKeyDown(irr::KEY_KEY_S))
nodePosition.Y -= MOVEMENT_SPEED * frameDeltaTime;
if(receiver.IsKeyDown(irr::KEY_KEY_A))
nodePosition.X -= MOVEMENT_SPEED * frameDeltaTime;
else if(receiver.IsKeyDown(irr::KEY_KEY_D))
nodePosition.X += MOVEMENT_SPEED * frameDeltaTime;
node->setPosition(nodePosition);
driver->beginScene(true, true, video::SColor(255,113,113,133));
smgr->drawAll(); // draw the 3d scene
device->getGUIEnvironment()->drawAll(); // draw the gui environment (the logo)
driver->endScene();
int fps = driver->getFPS();
if (lastFPS != fps)
{
core::stringw tmp(L"Movement Example - Irrlicht Engine [");
tmp += driver->getName();
tmp += L"] fps: ";
tmp += fps;
device->setWindowCaption(tmp.c_str());
lastFPS = fps;
}
}
virtual void setWindowCaption(const wchar_t *text)=0
Sets the caption of the window.
T X
X coordinate of the vector.
Definition vector3d.h:408
T Y
Y coordinate of the vector.
Definition vector3d.h:411
virtual void drawAll()=0
Draws all gui elements by traversing the GUI environment starting at the root node.
virtual void drawAll()=0
Draws all the scene nodes.
virtual const core::vector3df & getPosition() const
Gets the position of the node relative to its parent.
Definition ISceneNode.h:498
virtual bool beginScene(bool backBuffer=true, bool zBuffer=true, SColor color=SColor(255, 0, 0, 0), const SExposedVideoData &videoData=SExposedVideoData(), core::rect< s32 > *sourceRect=0)=0
Applications must call this method before performing any rendering.
virtual s32 getFPS() const =0
Returns current frames per second value.
virtual const wchar_t * getName() const =0
Gets name of this video driver.
virtual bool endScene()=0
Presents the rendered image to the screen.
@ KEY_KEY_A
Definition Keycodes.h:66
@ KEY_KEY_D
Definition Keycodes.h:69
@ KEY_KEY_S
Definition Keycodes.h:84
@ KEY_KEY_W
Definition Keycodes.h:88

In the end, delete the Irrlicht device.

device->drop();
return 0;
}

That's it. Compile and play around with the program.