Continous Movement for SDL 2 Joysticks?

Started by
38 comments, last by Alberth 6 years, 10 months ago

I printed out playerBase's coordinates to the console via cout, and they just stayed the same.
So the variable is messed up, and the problem is earlier.

I assume that playerBase is assigned with the lines in "World1::renderPlayer" ? If so, is pPos[XY] changing? Is World1::renderPlayer executed? (having assignments in code that never runs also tends to not do much :) )

Murphy tends to do things you don't expect, like having more than one bug, or you changing a line that was crucial for some other part of the code, code that is never executed, or something else.

In this case, you seem to have both pPos[xy] variables and playerBase.[xy] variables. Can you explain how they are related?

Advertisement

pPosX and pPosY change in the movePlayer function. renderPlayer executes in the main loop after the movePlayer function. All the code executes.

I added pPosX and pPosY because I changed the velocity to a decimal and playerBase cannot be a decimal due to the values being pixels which are only whole numbers.

I'm not sure why you are asking those questions or if you are trying to hint something, but I think it is strange how only the controller part of the code does not work at this point, yet I see nothing wrong with the controller code. The keyboard movement with the arrow keys works fine, but trying to move with a controller does nothing and the rectangle does not move on the screen. The problem is still unclear.

Relevant code so far:


void World1::movePlayerEvent(SDL_Event& e) //function is called inside handlePlayerEvents
{
    //keyboard movement support
    if (e.type == SDL_KEYDOWN)
    {
        switch(e.key.keysym.sym)
        {
            case SDLK_UP:
                if (this->getPlayerRectPosY() == 0)
                    pVelY = PLAYER_VELOCITY_Y;
                pVelY = -PLAYER_VELOCITY_Y; break;
            case SDLK_DOWN:
                if (this->getPlayerRectPosY() == window_height - this->getPlayerRectH())
                    pVelY = -PLAYER_VELOCITY_Y;
                pVelY = PLAYER_VELOCITY_Y; break;
            case SDLK_LEFT:
                if (this->getPlayerRectPosX() == 0)
                    pVelX = PLAYER_VELOCITY_X;
                pVelX = -PLAYER_VELOCITY_X; break;
            case SDLK_RIGHT:
                if (this->getPlayerRectPosX() == window_width - this->getPlayerRectW())
                    pVelX = -PLAYER_VELOCITY_X;
                pVelX = PLAYER_VELOCITY_X; break;
            default:
                break;
        }
    }

    //keyboard movement support continued
    else if (e.type = SDL_KEYUP)
    {
        switch(e.key.keysym.sym)
        {
            case SDLK_UP: case SDLK_DOWN:
                pVelY = 0; break;
            case SDLK_LEFT: case SDLK_RIGHT:
                pVelX = 0; break;
            default:
                break;
        }
    }

    //controller movement support
    else if (e.type == SDL_JOYAXISMOTION)
    {
        if (e.jaxis.which == 0)//controller 0
        {
            if (e.jaxis.axis == 0)// x axis
            {
                if (e.jaxis.value < -CONTROLLER_DEAD_ZONE)
                    pVelX = -PLAYER_VELOCITY_X;
                else if (e.jaxis.value > CONTROLLER_DEAD_ZONE)
                    pVelX = PLAYER_VELOCITY_X;
                else
                    pVelX = 0;
            }

            else if (e.jaxis.axis == 1)//y axis
            {
                if (e.jaxis.value < -CONTROLLER_DEAD_ZONE)
                    pVelY = -PLAYER_VELOCITY_Y;
                else if (e.jaxis.value > CONTROLLER_DEAD_ZONE)
                    pVelY = PLAYER_VELOCITY_Y;
                else
                    pVelY = 0;
            }
        }
    }
}

//moves player
void World1::movePlayer()
{
    //update x and collision check
    pPosX += pVelX;
    if (pPosX < 0 || (pPosX + playerBase.w > window_width))
        pPosX -= pVelX;

    //update y and collision check
    pPosY += pVelY;
    if (pPosY < 0 || (pPosY + playerBase.h > window_height))
        pPosY -= pVelY;
}

void World1::renderPlayer(SDL_Renderer*& renderer)
{
    //prepare postions for rendering
    playerBase.x = static_cast<int>(pPosX);
    playerBase.y = static_cast<int>(pPosY);

    //render
    SDL_RenderClear(renderer); //clears screen
    SDL_RenderCopy(renderer, pTexture, nullptr, &playerBase);
    SDL_RenderPresent(renderer); //puts image on screen
}

void World1::mainLoop(SDL_Renderer*& renderer)
{
    SDL_Event e;

    quit = false;

    this->loadPlayer(renderer);

    while (!quit)
    {
        this->handlePlayerEvents(e, quit);
        this->movePlayer();
        this->renderPlayer(renderer);
    }

}
I'm not sure why you are asking those questions or if you are trying to hint something,

I am only trying to help you, I don't have more information.

I don't have your program in my head, so reasoning what might be wrong is difficult to say the least.

The best I can do is explain how to use debugging tactics to find the spot.

but I think it is strange how only the controller part of the code does not work at this point, yet I see nothing wrong with the controller code.

It's not strange, the problem is just very cleverly hiding. I agree that the code looks alright, but you have proof it fails, so "looks ok" is a false conclusion, even though currently, we both don't know why.

In cases like this, trust the result when you execute things. If it fails, something is wrong, no matter what your eyes or code seems to say. Bugs are often very small, just a wrong number, or a reversed test, or a missing semi-colon. Bugs have a lot of code lines to hide in, and our reading is very error-prone, especially for text you have read a few times already. (We read text mostly by the shape of the words, we don't really read all letters, for example.)

The debugging tactic however, will still work. Also, you have a nicely reproducible case (it fails every time), so it's simple enough to run tests to check some values.

Printf-style debugging would work like

- Verify playerBase.x and playerBase.y don't change (just before rendering). This will likely hold. While you're at it, you may want to print pPosX and pPosY too there, and they probably don't change either.

If this holds, the problem is not in SDL_RenderCopy, but earlier.

Assuming this is the case, print values of pPosX and pPosy in "movePlayer". Again, since they heavily depend on pVelX and pVelY, you may want to print those too.

I am not sure what the result will be, my guess is that pPosX and pPosY don't change, which would mean pVelX and pVelY are both 0

If that is the case, the move code is working alright (you want pPosX and pPosY to stay where they are if their velocities are 0), so the problem is earlier.

Following your code, this leads to the "movePlayerEvent" function, where pVelX and pVelY are decided. Ignore the keyboard stuff, as you know that works. The question is thus, what are the values of pVelX and pVelY, from the joystick code?

Assuming you find the pVelX and pVelY are both 0 there, it is likely the code takes the "else ... = 0;" path there (you can verify by adding a 'printf' line in that else block (don't forget the curly brackets!) ).

Assuming you find that, the problem is thus earlier. That leads to the values of e.jaxis.which, e.jaxis.axis, e.jaxis.value, CONTROLLER_DEAD_ZONE, and PLAYER_VELOCITY_X and PLAYER_VELOCITY_Y.

print them, and check that the right 'if' case is selected, and that a useful velocity is assigned.

"if" statements also decide the value of an assignment, and you can print them like any other variable, and verify correctness.

I hope you find the bug this way.

For the keyboard input, I had it print out "hello" for SDL_KEYDOWN and "hi" for SDL_KEYUP whenever I pushed down and released an arrow key to see if the code went into these else if branches in movePlayerEvent.

I did this after I tried the same approach with SDL_JOYAXISMOTION. I turned on the controller so that the "no controllers detected" code would not execute in my init function (completely outside the world class in main). So, I know that the controller turned on. The code is not going into the else if branch for JOYAXISMOTION. After pushing on the joystick, the following code, if statement and all, never executed:


else if (e.type == SDL_JOYAXISMOTION)
{
    cout << "JOYAXISMOTION: " << e.type;
//rest of code below  did not execute either
//...
}

It worked just fine with SDL_KEYDOWN and SDL_KEYUP, and I even found that else if (e.type == SDL_KEYUP) was constantly executing. This looked fishy. So, I commented out keyboard support entirely after finding out that SDL_JOYAXISMOTION was not executing. Guess what happened next? Controller supported worked! This is telling because I added SDL_KEYUP just recently after changing up the keyboard support. So, now I know that SDL_KEYUP is the problem and that it executes when no buttons on the keyboard are pressed.

I'm not sure why this affects SDL_JOYAXISMOTION, since it seems like control flow could just go to the next else if statement, which is the controller, even if SDL_KEYUP was constantly executing. Just commenting out SDL_KEYUP also caused it to not work after changing SDL_KEYDOWN to work with the newly implemented SDL_KEYUP (instead of having playerbase.x++ and playerBase.y++, I changed it to work with velocities instead just like the controller). Not sure how to change keyboard support to work with controller support now that I have changed keyboard support to work with velocity, but I will have to figure all this out more tomorrow. Alberth/anyone, feel free to post thoughts so far between now and tomorrow when I get back to this (later tonight really since it's past 12AM now where I live).


cout << "JOYAXISMOTION: " << e.type;

Be careful here, stdout is often line-based, which means output won't show up at your screen until you end the line by printing a \n or std::endl (iirc, it's a long time ago that I last used << for printing).

SDL_KEYUP shouldn't trigger for a joystick, I think, unless there is some joystick-to-keyboard mapping going on somewhere???

I don't have time to look at your code in more detail, maybe you should make a new test program that only detects events, and prints them. That gives room for testing how events work, without the added stuff of the game getting in the way.

It works fine without endl; (unless SDL_KEYUP is executing, then the whole controller part is skipped).

Yeah, it's really weird. If I comment out the SDL_KEYUP part, everything works fine (except for the character continuing to move as a result of SDL_KEYDOWN).

I tried moving the controller code to a different function to see if this constant execution was messing with the surrounding control flow, but the same problem still remains. SDL_KEYUP constantly executing makes the controller not work, even if each of the cases in the switch statement that keep the velocity at 0 do not execute.

I will go ahead and make a test program, though.

You can try printing e.type, to see which events are being fired.

Since these are internal numbers from the SDL2 library, you may need to consult the SDL2 include file (at my system it's /usr/include/SDL2/SDL_events.h ) to get the numeric values of the relevant events.

What does handlePlayerEvents() look like?

Hello to all my stalkers.

What does handlePlayerEvents() look like?

handlePlayerEvents:


//event handling
void World1::handlePlayerEvents(SDL_Event& e, bool& quit)
{

    while( SDL_PollEvent( &e ) != 0 )
    {
        //User requests quit
		if( e.type == SDL_QUIT )
		{
            quit = true;
		}

        //handles movement with collisions
        this->keyboardMovePlayerEvent(e);
        this->controllerMovePlayerEvent(e);
    }
}

goes inside main loop:


//main loop of program
void World1::mainLoop(SDL_Renderer*& renderer)
{
    quit = false;

    this->loadPlayer(renderer);

    while (!quit)
    {
        this->handlePlayerEvents(e, quit);
        this->movePlayer();
        this->renderPlayer(renderer);
    }

}

which goes inside game.cpp:


int main( int argc, char* argv[])
{
    static const int SCREEN_WIDTH = 1024;
    static const int SCREEN_HEIGHT = 768;

    SDL_Window* window = NULL; //make window
    SDL_Renderer* renderer = NULL;// make renderer
    SDL_Joystick* gameController = NULL; //make controller


    init(window, renderer, gameController, SCREEN_WIDTH, SCREEN_HEIGHT);

    //World Loader
    World1 world(SCREEN_WIDTH, SCREEN_HEIGHT);

    world.mainLoop(renderer);

    //exit SDL and end program
    exit(window, renderer, gameController);
    return 0;
}

else if (e.type = SDL_KEYUP)

This is always true.

Double equals, not single equals.

Bump up your warning level in your compiler/IDE.

Hello to all my stalkers.

This topic is closed to new replies.

Advertisement