Continous Movement for SDL 2 Joysticks?

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

Ok yeah, I keep doing that. You can't read my mind or anything. I have to say why. Thanks for the feedback.

It was not moving at first, but I was able to get it to work. Thank you for the reminder about the int problem, my mind just did not make the connection for some reason! It's a bit choppy looking as it is moving across the screen though.

I've actually gone by those SDL tutorials for a little while that you mentioned when talking about the GPU to render non integral types to the screen. I notice that alot of the graphics tutorials are further down than where I am now. Perhaps I can improve the choppiness in a little while, unless my mind is not thinking creatively enough and I know enough (I passed the 30th tutorial a little while ago).

Advertisement

I would recommend storing your positions, velocities, etc. as float. When it's time to actually draw stuff with SDL, use the float position coordinates, just cast them to int when SDL requires the arguments to be int.

Hello to all my stalkers.

I would recommend storing your positions, velocities, etc. as float. When it's time to actually draw stuff with SDL, use the float position coordinates, just cast them to int when SDL requires the arguments to be int.

Convert with static cast?

Convert with static cast?

Yes. I don't remember the syntax for drawing in SDL right now so this is just pseudo-code, but basically something like:


SDL_Draw(texture, static_cast<int>(posX), static_cast<int>(posY)); 

Hello to all my stalkers.

I am guessing that you are recommending float for memory purposes? It's half as large, so I like your suggestion since I know it's good to not use up memory if it is not needed. The screen will not be bigger in pixel size than what float can hold, so float is adequate in this case.

I just use SDL_RenderCopy and it passes in the whole rectangle as shown in the above code, so I cannot convert when rendering. I can convert before rendering during the move function, but I cannot pass arguments like that with the way that I have things coded. I just wonder why the rectangle on the screen disappeared when changing my position and velocity variables and my velocity constants to float. It all worked fine with double.

I am guessing that you are recommending float for memory purposes?

No, this was in relation to Alberth's last post edit regarding the type of playerBase.x and .y being an int. Sorry, I should have specified better.

You can cast when you're going to render -- just create an SDL_Rect before rendering, and plonk the values in at that point (casting from doubles/floats). Either create your own float/double rect struct/class, or store the values separately (which you update when moving the player, etc.), then convert it to something SDL understands for drawing only.

Doubles and floats should both work. I'm not sure you need the extra precision doubles offer, but I also don't think you need to worry about it taking twice the memory. If you're having memory issues, it won't be related to a few bytes here and there, but more large-scale problems. Use whichever you feel like, with doubles of course being more precise.

Hello to all my stalkers.

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?

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

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.

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.

This topic is closed to new replies.

Advertisement