Jump to content

View more

Image of the Day

Adding some finishing touches...
Follow us for more
#screenshotsaturday #indiedev... by #MakeGoodGames https://t.co/Otbwywbm3a
IOTD | Top Screenshots

The latest, straight to your Inbox.

Subscribe to GameDev.net Direct to receive the latest updates and exclusive content.


Sign up now

Continous Movement for SDL 2 Joysticks?

4: Adsense
  • You cannot reply to this topic
31 replies to this topic

#21 Alberth   Members   

9192
Like
2Likes
Like

Posted 25 April 2017 - 10:29 PM

double vs float shouldn't make much difference for you at all. More likely, you're moving variables a bit in memory, which by chance have a different initial value or so, making it seem to work.

 

Did you try switching on compiler warnings? These are normally off by default, if you turn them on, the compiler tends to warn for for real errors (at least some of them).

 

Otherwise, it's a matter of debugging. Not displaying a sprite can be due to

1) The sprite doesn't get loaded, or is completely transparent.

2) x or y position of plotting it to the screen is off-screen

3) x or y size is 0 or negative.

Number 1 is somewhat unlikely, as in that case, you  wouldn't see the sprite ever, and it works in some cases for you.

Number 2 and 3 can be checked by inspecting the values you write in the destination rectangle of the sdl call to plot the sprite. Just print the value to the screen, and verify that they are in a correct range.



#22 windghost91   Members   

143
Like
0Likes
Like

Posted 19 May 2017 - 08:15 PM

What is now the position of the player? You seem to have both pPosX/pPosY and playerBase.x/playerBase.y .

Unless there is some additional step somewhere, use one of them for the player position. Delete the other set, it's just confusing the issue.

So, I am trying to get things to work that way now. My solution looked something like this:

void World1::movePlayer()
{
    playerBase.x+= static_cast<int>(pVelX);
    if (pPosX < 0 || (pPosX + playerBase.w > window_width))
        playerBase.x-= static_cast<int>(pVelX);
        
    playerBase.y+= static_cast<int>(pVelY);
    if (pPosY < 0 || (pPosY + playerBase.h > window_height))
        playerBase.y-= static_cast<int>(pVelY);
} 

However, the character position does not update on the screen. It stays at its original position when it is loaded. Why could this be, you think?


Edited by windghost91, 19 May 2017 - 08:16 PM.


#23 Alberth   Members   

9192
Like
2Likes
Like

Posted 19 May 2017 - 10:38 PM

However, the character position does not update on the screen. It stays at its original position when it is loaded. Why could this be, you think?

There is something wrong in your program.

 

The trouble with computers is that they do what you wrote in the program. In particular, that may be different from what you think you wrote.

In other words, your ideas of how the program works may be different from what you actually told the computer to do.

In this case, you even have proof of that. It is "not working".

 

The next step is to find out why, and that starts with finding where. If you programmed exactly what you thought you programmed, then the computer would do what you thought it would do. It doesn't, so there is a point in the program where it doesn't behave like you think. Values of variables don't change as you think.

The process of finding that point is called "debugging". A simple way to get a general picture is to add 'print' statements. (It's called 'print-debugging' for obvious reasons :) )

Here, your player position is not changing. So add a 'print' statement to output the player position each frame. Being very old-school, I still use printf-style coding here, eg

printf("playerBaseXY=%3.1f, %3.1f\n", playerBase.x, playerBase.y);

It needs a "#include <cstdio>" near the top if you don't have that already. Then compile, and run the program.

 

Now as the program runs, you get a stream of lines from the program as well. Check the general value. Is it what you expect? Try to move the player. Does the output change as you expect?

If the answer is "yes", then you know that where you added the "printf", the code works like you think it should. The problem is thus in a next step in the computation. Find the code that uses the working variable (playerBase in my example), to compute some new variable that is related to player positioning, and repeat the trick. ("find the code" usually works best by searching for the variable "playerBase" in this example, which will simply return all spots in the code where the variable is either assigned, or it is used to derive other values.)

If the answer is "no", then you concluded that "playerBase" value is messed up. The problem must thus be earlier. Somewhere in the code, you use other data that causes playerBase to change (or rather, not to change, probably). Here, it looks like pVelX and pVelY would be one cause. If playerBase is not working, are those variables working as expected? Repeat the trick, and check the output.

In this way, you can sort-of "walk" backward or forward through all the steps that do some computation step in deciding the player position. I added a unique string prefix to the printf, so you can have several of these lines active at the same time, and still understand all the numbers that are produced.

On the other hand, it quickly becomes too much, so comment or remove such statements after you found they are not of interest in finding the problem.

 

A second form of debugging is to use a tool called "debugger". Some IDEs have one built-in, with other systems it is supplied separately. I fully recommend playing with that too, as it's a very powerful tool, where you can walk through code step-by-step. This is not simple to do in above 'printf-debugging'.

The general process with a debugger is to find a line in the code where the interesting stuff starts, and you set a breakpoint. Then run the program in the debugger. When the program passes the breakpoint, it stops, and you get control what to do next. You can print variables, change variables, execute a line of code, run again until it hits the next breakpoint, etc etc.

In your case, you'd set a breakpoint where you read player-movement from the input. Then you can step through the code, seeing each statement being run or skipped (if the "if" around it doesn't hold), after each assignment you can print the values of the variables, and thus trace how a change at the input propagates to the displayed player position (or in your case, fails to propagate somewhere).



#24 Lactose   GDNet+   

11073
Like
1Likes
Like

Posted 20 May 2017 - 05:46 AM

It feels like this is the same problem as before, which we've tried to explain is due to you using SDL_Rect (which uses ints internally) to store and manage positions. Unless your velocities are really high, this will not work as you expect. If your velocities are less than 1 (e.g. 0.85f), casting them to int will be the same as adding 0.

You only want to deal with ints when it comes to rendering, not when it comes to storing and managing positions/velocities.

  1. Delete your playerBase variable.
  2. Use floats to store and update the positions. Either 2 floats (e.g. float positionX & float positionY), or create your own container (e.g. a Vector2 struct/class). I would recommend the 2 floats initially, then swapping to a Vector2 class when that works.
  3. Use floats to deal with movement speed (same as above). It seems like this is already the case.
  4. (You might also need to store width/height somewhere, storing those as ints is fine.)
  5. When updating movement, use these float values to compute the updated position. positionX += velocityX;
  6. When rendering, create a new SDL_Rect at that time, and populate it with static_cast<int> versions of the float positions you have. Pass that to the relevant SDL render call.

You might also want to check a int-casted version of the positions when checking whether you move out of the window or not.

 

Later on you'll probably also want to include delta time in the movement, but for now just sort out the float/int issues and get your player moving.


Hello to all my stalkers.

#25 windghost91   Members   

143
Like
0Likes
Like

Posted 20 May 2017 - 06:25 PM

Alberth, thanks for the debugging tips. I could definitely use more pointers with that.

Lactose, I like your suggestion of changing things at rendering time and not mixed with updating the positions. The code feels cleaner and more separate like it should (if that makes any sense).

 

Edit: Now the controller support does not work. The character does not move when I use the controller when the controller is connected.

//renders player to screen
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::movePlayer()
{
    pPosX += pVelX;
    if (pPosX < 0 || (pPosX + playerBase.w > window_width))
        pPosX -= pVelX;
    

    pPosY += pVelY;
    if (pPosY < 0 || (pPosY + playerBase.h > window_height))
        pPosY -= pVelY;
}

Success! As far as the keyboard movement goes, anyways (which I did before this topic). Now the controller does not move. It's like one thing works and something else pops out.

    //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;
            }
        }
    }

I printed out playerBase's coordinates to the console via cout, and they just stayed the same. I hope there is not something silly that I am missing.


Edited by windghost91, 20 May 2017 - 08:48 PM.


#26 Alberth   Members   

9192
Like
1Likes
Like

Posted 20 May 2017 - 10:16 PM

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?



#27 windghost91   Members   

143
Like
0Likes
Like

Posted 22 May 2017 - 09:10 PM

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);
    }

}

Edited by windghost91, 22 May 2017 - 09:16 PM.


#28 Alberth   Members   

9192
Like
2Likes
Like

Posted 22 May 2017 - 11:09 PM

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.


Edited by Alberth, 22 May 2017 - 11:11 PM.


#29 windghost91   Members   

143
Like
0Likes
Like

Posted 23 May 2017 - 11:48 PM

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).


Edited by windghost91, 23 May 2017 - 11:49 PM.


#30 Alberth   Members   

9192
Like
1Likes
Like

Posted Yesterday, 12:56 AM

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.



#31 windghost91   Members   

143
Like
0Likes
Like

Posted Today, 09:35 PM

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.


Edited by windghost91, Today, 09:53 PM.


#32 Alberth   Members   

9192
Like
0Likes
Like

Posted Today, 10:30 PM

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.