Problems with Source code, game compiles but insta crashes

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
Repl4y
Posts: 2
Joined: Tue Mar 08, 2016 12:14 pm

Problems with Source code, game compiles but insta crashes

Post by Repl4y »

Hey, I'm doing a school project in which I try to recreate a 3D FPS shooter like the original Doom or Quake.

The Problem
I've been having problems where if an IF statement is allowed to run inside a Spawner class, it instantly crashes my game. I have no idea what's happening, as it's copied from Ben Wright on YouTube, who has a series of tutorials in which he explains his code (https://www.youtube.com/playlist?list=P ... FMqe_THGh9). I made sure the code is exactly as it should be many times, but I'm still getting the same crash. I discovered that if I just don't allow the statement to run, then the game runs fine, however, without the enemies spawning. I tried to contact him but it's of no help.

The Source Code

Code: Select all

#include <iostream>
#include <irrlicht.h>
#include <vector>
#include <string>
 
//NAMESPACES
using namespace std;
using namespace irr;
using namespace scene;
using namespace core;
using namespace video;
using namespace io;
using namespace gui;
 
//INITIALISATION
IrrlichtDevice *device;
IVideoDriver *driver;
ISceneManager *smgr;
 
class MyEventReceiver : public IEventReceiver
{
public:
    //Struct to record info on the mouse state
    struct SMouseState
    {
        core::position2di Position;
        bool LeftButtonDown;
        SMouseState() : LeftButtonDown(false) { }
    } MouseState;
 
    virtual bool OnEvent(const SEvent& event)
    {
        //Remember whether each key is up or down
        if (event.EventType == irr::EET_KEY_INPUT_EVENT)
        {
            KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
 
        return false;
        }
 
        //Remember the mouse state
        if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) {
            switch(event.MouseInput.Event) {
            case EMIE_LMOUSE_PRESSED_DOWN:
                MouseState.LeftButtonDown = true;
                break;
 
            case EMIE_LMOUSE_LEFT_UP:
                MouseState.LeftButtonDown = false;
                break;
 
            case EMIE_MOUSE_MOVED:
                MouseState.Position.X = event.MouseInput.X;
                MouseState.Position.Y = event.MouseInput.Y;
                break;
 
            default:
                break;
            }
 
        return false;
        }
    return 0;
    }
 
 
    const SMouseState & GetMouseState(void) const
    {
        return MouseState;
    }
 
    //Used to check whether each 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];
};
 
class Enemy {
    IAnimatedMeshSceneNode *enemy;
    int state;
 
    public:
    Enemy(core::vector3df pos) {
        IAnimatedMesh *mesh = smgr->getMesh("media/sydney.md2");
        enemy = smgr->addAnimatedMeshSceneNode(mesh);
        state = 0;
 
        if (enemy) {
            enemy->setMaterialFlag(EMF_LIGHTING, false);
            enemy->setMD2Animation(EMAT_RUN);
            enemy->setScale(core::vector3df(2,2,2));
            enemy->setPosition(pos);
            enemy->setMaterialTexture(0, driver->getTexture("media/sydney.bmp"));
        }
    }
 
    void update(core::vector3df target) {
        core::vector3df playerPos = target;
        core::vector3df enemyPos = enemy->getPosition();
        core::vector3df vec = playerPos - enemyPos;
        core::vector3df rot = enemy->getRotation();
 
        //Set up the enemy rotation
        rot.Y = atan(vec.Z/vec.X) + (180.0f / core::PI);
        if ((playerPos.X - enemyPos.X) > 0) {
            rot.Y = 90 - rot.Y;
        }
        else if ((playerPos.X - enemyPos.X) < 0) {
            rot.Y = -90 - rot.Y;
        }
        rot.Y -= 90;
        enemy->setRotation(rot);
 
        //Force the enemy to keep following the player
        if (enemyPos.getDistanceFrom(playerPos) < 50) {
            if (state == 0) {
                state = 1;
                enemy->setMD2Animation(EMAT_ATTACK);
            }
        }
        else if (state != 0) {
            state = 0;
            enemy->setMD2Animation(EMAT_RUN);
        }
        //Update the animation state
        if (state == 0) {
            scene::ISceneNodeAnimator *enemy_anim = smgr->createFlyStraightAnimator(enemy->getPosition(), target, 3500, true);
            enemy->addAnimator(enemy_anim);
            enemy_anim->drop();
        }
        else {
            scene::ISceneNodeAnimator *enemy_anim = smgr->createFlyStraightAnimator(enemy->getPosition(), enemy->getPosition(), 3500, true);
            enemy->addAnimator(enemy_anim);
            enemy_anim->drop();
        }
    }
};
 
class Spawner {
    int inc;
    int count;
    std::vector<Enemy*> enemies;
    core::vector3df pos;
 
    public:
        Spawner(core::vector3df pos) : pos(pos), count(0), inc(0)
        {
        }
 
        void update(core::vector3df player)
        {
            //Check to spawn a new enemy
            if (inc++ % 100 == 0 && count < 10) //crashes game without '&& count > 0'
            {
                Enemy *new_enemy = new Enemy(pos);
                enemies.push_back(new_enemy);
                count++;
            }
 
            for (int i = 0; i < enemies.size(); i++)
            {
                enemies[i]->update(player);
            }
        }
};
 
//ENUMERATORS
enum
{
    ID_IsNotPickable = 0,
    IDFlag_IsPickable = 1 << 0,
    IDFlag_IsHighlightable = 1 << 1
};
 
int main()
{
    //INITIALISE PATHS FOR HEARTS
    io::path HeartDepPath = "media/HeartDepleted.png";
    io::path HeartFullPath = "media/HeartFull.png";
 
    MyEventReceiver receiver;
 
    //INITIAL DEVICES AND MANAGERS
    IrrlichtDevice *device = createDevice(video::EDT_OPENGL, core::dimension2d<u32>(1080,720), 16, false, false, false, &receiver);
    device->setWindowCaption(L"DOOMENSTEIN");
    video::IVideoDriver* driver = device->getVideoDriver();
    scene::ISceneManager* smgr = device->getSceneManager();
 
    //ABORT IF SOMETHING GOES WRONG
    if (!device)
    {
        return EXIT_FAILURE;
    }
 
    //FILE ARCHIVE FOR MAP
    device->getFileSystem()->addFileArchive("media/map-20kdm2.pk3");
    scene::IAnimatedMesh* mesh = smgr->getMesh("20kdm2.bsp");
    scene::IMeshSceneNode* node = NULL;
 
    //CROSSHAIRS BILLBOARD
    device->getGUIEnvironment()->addImage(driver->getTexture("media/crosshairs.png"),
                                          core::position2d<s32>(508,328));
    //GUI
    device->getGUIEnvironment()->addImage(driver->getTexture("media/GUI.png"),
                                          core::position2d<s32>(0,0));
 
    //HEARTS
    device->getGUIEnvironment()->addImage(driver->getTexture(HeartFullPath), core::position2d<s32>(60,597));
    device->getGUIEnvironment()->addImage(driver->getTexture(HeartDepPath), core::position2d<s32>(210,597));
    device->getGUIEnvironment()->addImage(driver->getTexture(HeartDepPath), core::position2d<s32>(360,597));
 
    //DISPLAY GUIENV
    gui::IGUIStaticText* diagnostics = device->getGUIEnvironment()->addStaticText(
        L"", core::rect<s32>(10, 10, 400, 20));
    diagnostics->setOverrideColor(video::SColor(255, 255, 255, 0));
 
    //OCTREE AND SELECTOR FOR MAP
    if (mesh)
    {
        node = smgr->addOctreeSceneNode(mesh->getMesh(0), 0 , IDFlag_IsPickable);
    }
 
    scene::ITriangleSelector* selector = NULL;
 
    if (node)
    {
        node->setPosition(core::vector3df(-1350, -130, -1400));
        selector = smgr->createOctreeTriangleSelector(node->getMesh(), node, 128);
        node->setTriangleSelector(selector);
        //not done yet so no drop
    }
 
    //CREATES CAMERA AND KEYMAP
    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, 50.f, 0.4f, -1, keyMap, 5, false, 1.99f, false);
    camera->setPosition(core::vector3df(50,50,-60));
    camera->setTarget(core::vector3df(-70,50,-60));
    camera->setFarValue(10000.0f);
 
    device->getCursorControl()->setVisible(false);
 
    //IF A SELECTOR EXISTS:
    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();
    }
 
    Spawner spawner(core::vector3df(60,50,-40));
 
    //RUN LOOP
    while (device->run())
    {
        //EXTRA CONTROLS
        if(receiver.IsKeyDown(irr::KEY_ESCAPE))
        {
            driver->endScene();
            device->drop();
            return EXIT_SUCCESS;
        }
        if(receiver.IsKeyDown(irr::KEY_LBUTTON)) //SET UP SO I DON'T FORGET THE SHOOT BUTTON
        {
        }
 
        driver->beginScene(true, true, video::SColor(255,113,113,113));
        smgr->drawAll();
        device->getGUIEnvironment()->drawAll();
        driver->endScene();
        spawner.update(camera->getPosition());
    }
    device->drop();
    return EXIT_SUCCESS;
}
The specific class I'm having trouble with is:

Code: Select all

class Spawner {
    int inc;
    int count;
    std::vector<Enemy*> enemies;
    core::vector3df pos;
 
    public:
        Spawner(core::vector3df pos) : pos(pos), count(0), inc(0)
        {
        }
 
        void update(core::vector3df player)
        {
            //Check to spawn a new enemy
            if (inc++ % 100 == 0 && count < 10) //crashes game without 'count > 0'
            {
                Enemy *new_enemy = new Enemy(pos);
                enemies.push_back(new_enemy);
                count++;
            }
 
            for (int i = 0; i < enemies.size(); i++)
            {
                enemies[i]->update(player);
            }
        }
};
I use this on the line before the start of the main loop

Code: Select all

Spawner spawner(core::vector3df(60,50,-40));
and then in the main loop

Code: Select all

spawner.update(camera->getPosition());
to spawn the enemies.

We suspect it might be a runtime error? If anyone has any idea how to fix it, it would be greatly appreciated as my deadline's getting closer and closer...
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Problems with Source code, game compiles but insta crash

Post by CuteAlien »

Hint 1: When you compile your project in debug in an IDE (like VisualStudio or Code::Blocks) and it crashes then you can look at the values of every variable while it crashes. Then you can see things like null-pointers quickly.
Hint 2: Initialize _all_ pointers always to nullptr (or 0 or NULL, whatever you prefer). Then you see invalid pointers quickly.

In this case - you have 2 smgr variables (and same for driver). One is global and one is inside the main functions. Those have the same name, but nothing to do with each other otherwise (meaning they refer to 2 different places in memory). You initialize the one in main but not the global one. So when you try to use the global one in "Enemy" constructor it will crash because it's uninitialized. Thought it should crash earlier than you described...

I would just get rid of the global variables - you don't need them. In case you need access to such a variable in a class just pass it to that class. For example pass a ISceneManager* and IDriver* to the Enemy constructor, a scene::ISceneManager* to the Enemy::update and scene::ISceneManager* and video::IVideoDriver* to Spawner::update. If you don't want to pass it on each update you can also pass the variable once in the constructor and save it internally in that class (matter of taste, some programmers prefer this, other prefer other style).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Repl4y
Posts: 2
Joined: Tue Mar 08, 2016 12:14 pm

Re: Problems with Source code, game compiles but insta crash

Post by Repl4y »

CuteAlien wrote:Hint 1: When you compile your project in debug in an IDE (like VisualStudio or Code::Blocks) and it crashes then you can look at the values of every variable while it crashes. Then you can see things like null-pointers quickly.
Hint 2: Initialize _all_ pointers always to nullptr (or 0 or NULL, whatever you prefer). Then you see invalid pointers quickly.

In this case - you have 2 smgr variables (and same for driver). One is global and one is inside the main functions. Those have the same name, but nothing to do with each other otherwise (meaning they refer to 2 different places in memory). You initialize the one in main but not the global one. So when you try to use the global one in "Enemy" constructor it will crash because it's uninitialized. Thought it should crash earlier than you described...

I would just get rid of the global variables - you don't need them. In case you need access to such a variable in a class just pass it to that class. For example pass a ISceneManager* and IDriver* to the Enemy constructor, a scene::ISceneManager* to the Enemy::update and scene::ISceneManager* and video::IVideoDriver* to Spawner::update. If you don't want to pass it on each update you can also pass the variable once in the constructor and save it internally in that class (matter of taste, some programmers prefer this, other prefer other style).
Thank you so much! It worked, I didn't realise this would be a problem/ that I was even doing that. Also, thank you for the hints/ tips, they're actually very helpful!

P.S. I also really like your profile photo, nice :D
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Problems with Source code, game compiles but insta crash

Post by CuteAlien »

Repl4y wrote:P.S. I also really like your profile photo, nice :D
Hehe, thanks :-)
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Post Reply