My question is: is that approach basically right or how should it be done?
Note: I used the Qt library for some things. Sorry for not posting an URL, I don't have an FTP area or something similar.
Code: Select all
#include <irrlicht.h>
#include <qthread.h>
#include <qdatetime.h>
#include <qptrlist.h>
using namespace irr;
#pragma comment(lib, "Irrlicht.lib")
scene::ISceneManager* smgr;
video::IVideoDriver* driver;
IrrlichtDevice *device;
video::SMaterial material;
scene::ITriangleSelector* selector;
scene::IAnimatedMeshSceneNode* CreateFaerie()
{
// add animated faerie.
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;
}
material.Texture1 = 0;
material.Lighting = false;
return node;
}
void AnimateObj(scene::ISceneNode* pNode, bool bFixedRadius = false)
{
core::aabbox3df box = pNode->getBoundingBox();
core::vector3df radius = bFixedRadius ? core::vector3df(30,50,30) : box.MaxEdge - box.getCenter();
scene::ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
selector, pNode,
radius, // how big the object is (radius of an ellipsoid)
core::vector3df(0,-100,0), // direction and speed of gravity (0,0,0 to disable gravity)
100.0f, // acceleration value when falling down
core::vector3df(0,0,0)); // translation from the center of the ellipsoid
pNode->addAnimator(anim);
anim->drop();
}
class FaerieControl : public QThread
{
public:
FaerieControl() : QThread()
,m_bRunning(true), m_direction(0), m_bShouldRenderMove(false)
,m_id(0), m_jump(0),m_stepWidth(4)
{
static int id = 0;
m_id = id++;
static int x = -150;
x += 40;
m_pFaerie = CreateFaerie();
m_pFaerie->setPosition(core::vector3df(x,50,0));
m_oldPos.Z = 99;
m_pFaerie->setRotation(core::vector3df(0,90,0));
AnimateObj(m_pFaerie);
}
void doExit()
{
m_bRunning = false;
}
void move()
{
// really set to queued position
m_pFaerie->setPosition(m_newPos);
// reset queue flag
m_bShouldRenderMove = false;
}
bool shouldMove()
{
return m_bShouldRenderMove;
}
protected:
virtual void run()
{
msleep(500 + 17*(m_id+10)); // ensure different start times
// as long as the program isn't shutdown
while (m_bRunning) {
// wait 100 milliseconds until the next action of the faerie
msleep(100);
// if the queued new position is not already rendered, don't do further steps
if (m_bShouldRenderMove) {
continue;
}
bool bChangeDirection = false;
core::vector3df pos = m_pFaerie->getPosition();
// was she able to walk a step, or has her position not changed?
bool bStoppedSomehow = isWalkingStopped(pos);
if (bStoppedSomehow && !m_jump) {
// decide to try to jump over the obstacle that stopped her
// (that helps at stairs)
m_jump = 3;
}
else if (bStoppedSomehow) {
// if she already tried to jump but that didn't help, decide to change the direction
// (that helps at walls)
m_jump = 0;
bChangeDirection = true;
}
// sometimes randomly decide to change the direction
int timeMSec = QTime::currentTime().msec();
if ((timeMSec % 10) == 0) {
bChangeDirection = true;
}
// maybe turn to another direction
if (bChangeDirection) {
decideNewDirection();
turnToNewDirection();
}
// compute the new position where we'll be after the step
// and queue it for rendering
m_newPos = pos + computeNextStep();
m_bShouldRenderMove = true;
// store her old position for further checking if the queued step will be successful
m_oldPos = pos;
}
}
bool isWalkingStopped(core::vector3df pos)
{
bool bStopped;
switch (m_direction) {
case 0: bStopped = pos.Z >= m_oldPos.Z; break;
case 1: bStopped = pos.X >= m_oldPos.X; break;
case 2: bStopped = pos.Z <= m_oldPos.Z; break;
case 3: bStopped = pos.X <= m_oldPos.X; break;
}
return bStopped;
}
void decideNewDirection()
{
static unsigned int i = 0;
int oldDirection = m_direction;
do {
m_direction = i % 4;
i++;
}
while (oldDirection == m_direction);
}
void turnToNewDirection()
{
switch (m_direction) {
case 0: m_pFaerie->setRotation(core::vector3df(0,90,0)); break;
case 1: m_pFaerie->setRotation(core::vector3df(0,180,0)); break;
case 2: m_pFaerie->setRotation(core::vector3df(0,270,0)); break;
case 3: m_pFaerie->setRotation(core::vector3df(0,0,0)); break;
}
}
core::vector3df computeNextStep()
{
core::vector3df move;
switch (m_direction) {
case 0: move = core::vector3df(0,m_jump,-m_stepWidth); break;
case 1: move = core::vector3df(-m_stepWidth,m_jump,0); break;
case 2: move = core::vector3df(0,m_jump,m_stepWidth); break;
case 3: move = core::vector3df(m_stepWidth,m_jump,0); break;
}
return move;
}
scene::IAnimatedMeshSceneNode* m_pFaerie;
bool m_bRunning;
core::vector3df m_oldPos;
core::vector3df m_newPos;
int m_direction;
bool m_bShouldRenderMove;
int m_id;
int m_jump;
int m_stepWidth;
};
int main(int /*argc*/, char** /*argv*/)
{
device = createDevice(video::EDT_DIRECTX9, core::dimension2d<s32>(640, 480), 16, false);
driver = device->getVideoDriver();
smgr = device->getSceneManager();
device->getFileSystem()->addZipFileArchive("../../media/map-20kdm2.pk3");
scene::IAnimatedMesh* q3levelmesh = smgr->getMesh("20kdm2.bsp"); if (!q3levelmesh) return 1;
scene::IMesh* pMesh = q3levelmesh->getMesh(0);
scene::ISceneNode* q3node = smgr->addOctTreeSceneNode(pMesh); if (!q3node) return 1;
q3node->setPosition(core::vector3df(-1370,-130,-1400));
selector = smgr->createOctTreeTriangleSelector(pMesh, q3node, 128);
q3node->setTriangleSelector(selector);
selector->drop();
scene::ICameraSceneNode* camera = smgr->addCameraSceneNodeFPS(0, 100.0f, 300.0f);
camera->setPosition(core::vector3df(0,50,0));
camera->setRotation(core::vector3df(0,180,0));
AnimateObj(camera, true);
// create a number of faeries, each one an own thread doing its action every 100 milliseconds
QPtrList<FaerieControl> faeries;
for (int i = 0; i < 9; i++) {
FaerieControl* pFaerie = new FaerieControl; if (!pFaerie) return 1;
faeries.append(pFaerie);
pFaerie->start();
}
smgr->addLightSceneNode(0, core::vector3df(-60,100,400), video::SColorf(1.0f,1.0f,1.0f,1.0f), 600.0f); // Add a light
device->getCursorControl()->setVisible(false); // disable mouse cursor
// infinite rendering loop
while (device->run()) {
// check all faeries if they've queued a new step.
// if so, do the walk
QPtrListIterator<FaerieControl> it(faeries);
while (*it) {
FaerieControl* pFaerie = *it;
if (pFaerie->shouldMove()) {
pFaerie->move();
}
++it;
}
driver->beginScene(true, true, 0);
smgr->drawAll();
driver->endScene();
}
// exits all faerie threads
QPtrListIterator<FaerieControl> it(faeries);
while (*it) {
FaerieControl* pFaerie = *it;
pFaerie->doExit();
++it;
}
device->drop();
return 0;
}