6

I've been coding a game in C++ using the SDL library. Today while changing the way my player character class works, I've come up against a very puzzling problem. The following code forms part of my logic allowing the player to fire bullets. The control variables, b_canFire and b_shouldFire (I plan to rename these to make more sense), are set elsewhere in the class to allow this function to execute when the user presses a key.

bool PlayerChar::DoFiring()
{
    if(b_canFire && b_shouldFire)
    {
        Fire(box.x + 22, box.y); // This fires a bullet
        b_canFire = false; // Does not work
        b_shouldFire = false; // Does not work

        return true;
    }
}

When I step through this code using my debugger, it becomes aparrent that the values of b_canFire and b_shouldFire are not being changed to false by the assignments inside the if statement. However, if I change the code to the following:

bool PlayerChar::DoFiring()
{
    if((b_canFire) && (b_shouldFire))
    {
        Fire(box.x + 22, box.y); // This fires a bullet
        b_canFire = false; // Works
        b_shouldFire = false; // Works

        return true;
    }
}

or

bool PlayerChar::DoFiring()
{
    if(b_canFire == true && b_shouldFire == true)
    {
        Fire(box.x + 22, box.y); // This fires a bullet
        b_canFire = false; // Works
        b_shouldFire = false; // Works

        return true;
    }
}

Suddenly the assignments work. I have also tried replicating this situation in an empty test-project, as follows:

bool bC = true;
bool bD = true;

if(bC == true && bD == true)
{
    bC = false; // Works
    bD = false; // Works
}


bool bE = true;
bool bF = true;

if(bE && bF)
{
    bE = false; // Works
    bF = false; // Works
}

However, both of these examples assign the values exactly as they should. Clearly I am missing something here, but for the life of me, I can't see what it is. I have figured out how to fix the problem to make my code work, but it's really bothering me not knowing what is breaking the assignments in the first example, because everything I've learned about C++ so far is telling me they should work fine.

This is my first major project using the C++ language, and I am still learning, so any help or advice from more experienced programmers would be great.

Thanks!

EDIT:

As requested here is the entire class:

#include <list>
#include "SDL_mixer.h"
#include "hiGlobalVars.h"
#include "hiGlobalObjects.h"
#include "hiAssetManager.h"
#include "hiRendering.h"
#include "hiTimer.h"
#include "hiBullet.h"
#include "hiPlayerChar.h"
#include "hiDebugger.h"


using std::list;


PlayerChar::PlayerChar()
{
    moveSpeed = 6;
    moveDir = NONE;
    b_canFire = true;
    b_shouldFire = false;
    box.x = 0;
    box.y = 470;
    box.w = 38;
    box.h = 40;
}


PlayerChar::~PlayerChar()
{

}


void PlayerChar::SetPos(int x)
{
    box.x = x;
}


void PlayerChar::Draw()
{
    BlitSurface(box.x, box.y, assets.ss_playerchar_idle, ss_screen);
}


bool PlayerChar::DoFiring()
{
    if((b_canFire) && (b_shouldFire)) 
    {
        // Fire a bullet
        Fire(box.x + 22, box.y);
        b_canFire = false;
        b_shouldFire = false;

        return true; // fired a bullet
    }
    return false; // did not fire a bullet
}


void PlayerChar::Fire(int x, int y)
{
    // Create a new bullet at the correct location and add it to the global bullet list
    Bullet* bullet = new Bullet();
    bullet->SetPos(x, y);
    bullets.push_back(bullet);

    // Play bullet firing sound
    Mix_PlayChannel(-1, assets.mc_firebullet, 0);
}


void PlayerChar::HandleInput(Uint8* keystates)
{
    if(keystates[SDLK_LEFT] && keystates[SDLK_RIGHT])// Both direction keys
        moveDir = NONE;
    if(keystates[SDLK_LEFT] && !keystates[SDLK_RIGHT]) // Left key and not right key
        moveDir = LEFT; 
    if(!keystates[SDLK_LEFT] && keystates[SDLK_RIGHT]) // Right key and not left key
        moveDir = RIGHT; 
    if(!keystates[SDLK_LEFT] && !keystates[SDLK_RIGHT]) // Neither direction key
        moveDir = NONE;

    if(keystates[SDLK_SPACE]) // Space bar
        b_shouldFire = true;

    if(!keystates[SDLK_SPACE]) // Allow another bullet to be fired after release
        b_canFire = true;
}


void PlayerChar::Move()
{
    if(moveDir == LEFT && box.x > 0) // If not off screen, move
        box.x -= moveSpeed;
    if(moveDir == RIGHT && box.x < (ss_screen->w - box.w)) 
        box.x += moveSpeed;
}

and the header:

#ifndef __hiPlayerChar__
#define __hiPlayerChar__


#include "SDL.h"
#include "SDL_mixer.h"
#include "hiGlobalVars.h"


enum MoveDir
{
    LEFT,
    RIGHT,
    NONE
};


class PlayerChar
{
private:
    Sint16 moveSpeed;
    MoveDir moveDir;
    bool b_canFire;
    bool b_shouldFire;

public:
    // Ctr & Dtr
    PlayerChar();
    ~PlayerChar();

    // Functions
    void SetPos(int x);
    bool DoFiring();
    void Fire(int x, int y);
    void Move();
    void Draw();
    void HandleInput(Uint8* keystates);

    // Size and position
    SDL_Rect box;
};


#endif
12
  • 2
    show us definitions of b_canFire, b_shouldFire Commented Oct 28, 2012 at 16:53
  • 1
    How do you detect that the assignment doesn't work? The problem may be in the missing return in case the conditional branch isn't taken: This has undefined behavior and subtle changes in the code can have intesting effects... Commented Oct 28, 2012 at 16:59
  • 2
    Can you reproduce the original problem if you change the code back? I suspect something silly like "running code that did not match latest source". Commented Oct 28, 2012 at 17:03
  • 1
    If you rerun code without re-compiling, this often happens. Usually (or at least in gdb) a debugger will warn you if you're debugging code that is newer/different than the source files that it's showing. Commented Oct 28, 2012 at 17:17
  • 1
    Well, you live and learn. This will certainly teach me to do a full rebuild of my projects when stuff isn't working properly! Thanks everyone, once again stack overflow has saved the day :) Commented Oct 28, 2012 at 17:23

1 Answer 1

2

It is missing last return instruction in your function:

bool PlayerChar::DoFiring()
{
    if(b_canFire && b_shouldFire)
    {
        Fire(box.x + 22, box.y); // This fires a bullet
        b_canFire = false; // Does not work
        b_shouldFire = false; // Does not work

        return true;
    }
    return false; // WAS MISSING HERE
}

Then your function returns whatever (UB) if any of your b_canFire, b_shouldFire are false.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks, I don't believe that was the problem however, as I took that out of the initial example I posted. I was stepping through line-by-line so it was aparrent that the problem appeared even before the end of the function was reached. Also the return value of the function was not conditional on the assignment of the booleans in question, only their initial values.
It is possible you aren't running the code you think you are running. Instead of relying on the debugger to tell you, create explicit side effects, like dumping debug strings with file name and line numbers that track the value of your variables. As in OutputDebugStringW( ReportValue( "b_canFire", b_canFire, FILE, LINE ) );, where ReportValue formats the arguments. The condition if (A && B) only differs from if ((A)&&(B)) in that the second will cause you to recompile... After checking this, start stripping out code that doesn't change the logic (like drawing the screen).
Still, it is sound advice. I'm just learning to use my debugger recently and apparently it can cause some pretty strange problems if you don't always return a value from a function.
Yakk, that does indeed seem to be the case. In future I'll know not to trust my debugger entirely!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.