Mr Tea
Mr Tea

Reputation: 127

SDL2 Character movement a to b

enter image description here enter image description here

I am making a 2D point and click adventure game for free public use on GitHub. I've got it working quite well however one thing that has got me stumped is how to make the character move from A to B without holding the mouse. As it currently is, the character will move around the scene fine if you hold the mouse button down. This is OK but what I was trying to achieve was to make it work so you click the mouse and the character continues to move until it reaches the point where you clicked.

I've tried using a loop but that doesn't seem to work. I know when I click the button it registers at least 16 loops of the game loop by the time I release my finger. Any advice on ways I can make this work? This is an open project and happy for "anyone's" input on making it better. I'm not trying to write a AAA title or anything, It's a learning project of mine which has been very fun. You can see the full code here

if (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(SDL_BUTTON_LEFT)) {
        //Free up memory for dialog texture and sprite texture. Prevents memory leak!   TRUST ME!
       //Note needs some tweaking. If you remove this your RAM will rocket!                 
        SDL_DestroyTexture(Textures::spriteTexture);
        SDL_DestroyTexture(ftexture); //VERY VERRRRY IMPORTANT (DON'T REMOVE)
        Textures::spriteTexture = SDL_CreateTextureFromSurface(renderer, Textures::spriteDown1); 
        fsurface = TTF_RenderText_Solid(font, "", fcolor);
                              
        Uint8 buttons = SDL_GetMouseState(&x, &y);
        gd = gdSprite.x;
        gy = gdSprite.y;

        //Show coordinates in console for object placement.
        cout << "Current Mouse click coordinates are:"  << endl;
        cout << "x = " << x << endl;
        cout << "y = " << y << endl;
        cout << "Current Player X Position is: " << gdSprite.x << endl;
        cout << "Current Player Y Position is: " << gdSprite.y << endl;
        cout << "Current Scene is: " << SceneBackground << endl;
    
        if(mouseHold == 0){
            mouseHold = 1;
            ftexture = SDL_CreateTextureFromSurface(renderer, fsurface);               
            **gdSprite.x = player.walk(x, y, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture, ftexture, dialogmTexture);**
          
        
        //The following 2 statements will prevent the player from traversing diaginally which causes animation issues. Took ages to get this right!
        **if(y < gdSprite.y && x < gdSprite.x +75 && x >gdSprite.x)
            gdSprite.y = player.walky(x, y, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture,ftexture, dialogmTexture);
        
        if (y > gdSprite.y && x < gdSprite.x +75 && x > gdSprite.x)
            gdSprite.y = player.walky(x, y, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture,ftexture, dialogmTexture);**
     
        }

I have just tried moving the the mouseHold if statement outside the left click routine and the player still moves when I click the button, but to move to the destination, I still have to hold the left button. I tried adding a while loop but the player stays still and the loop is infinite. If I ignore the mouseHold value, then the player will follow me as a drag the pointer around the screen however, the ram sky rockets and crashes the game.

if (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(SDL_BUTTON_LEFT)) {

        mouseHold = 0;
        //Free up memory for dialog texture and sprite texture. Prevents memory leak!   TRUST ME!
       //Note needs some tweaking. If you remove this your RAM will rocket!                 
        SDL_DestroyTexture(Textures::spriteTexture);
        SDL_DestroyTexture(ftexture); //VERY VERRRRY IMPORTANT (DON'T REMOVE)
        Textures::spriteTexture = SDL_CreateTextureFromSurface(renderer, Textures::spriteDown1); 
        fsurface = TTF_RenderText_Solid(font, "", fcolor);
                              
        Uint8 buttons = SDL_GetMouseState(&x, &y);
        gd = gdSprite.x;
        gy = gdSprite.y;

        //Show coordinates in console for object placement.
        cout << "Current Mouse click coordinates are:"  << endl;
        cout << "x = " << x << endl;
        cout << "y = " << y << endl;
        cout << "Current Player X Position is: " << gdSprite.x << endl;
        cout << "Current Player Y Position is: " << gdSprite.y << endl;
        cout << "Current Scene is: " << SceneBackground << endl;
    
        /*
        if(mouseHold == 0){
            mouseHold = 1;
            ftexture = SDL_CreateTextureFromSurface(renderer, fsurface);               
            gdSprite.x = player.walk(x, y, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture, ftexture, dialogmTexture);
          
        
        //The following 2 statements will prevent the player from traversing diaginally which causes animation issues. Took ages to get this right!
        if(y < gdSprite.y && x < gdSprite.x +75 && x >gdSprite.x)
            gdSprite.y = player.walky(x, y, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture,ftexture, dialogmTexture);
        
        if (y > gdSprite.y && x < gdSprite.x +75 && x > gdSprite.x)
            gdSprite.y = player.walky(x, y, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture,ftexture, dialogmTexture);
     
        }
        _sleep(1);  //This makes the animation of the character look a bit more realistic and less like she's on skates.
       */

        //Get interaction message.         
        interactionMessage = pob.ObjectInteraction( x, y, gd, gy);
        if (interactionMessage != "") {
            SDL_DestroyTexture(Textures::spriteTexture);
            Textures::spriteTexture = SDL_CreateTextureFromSurface(renderer, Textures::spriteDown1);
        }

        std::string menuMessage;
        //Clicking objects on the scene.
  
        //Get object pickup message.
        gameObject = mob.PickUp(x, y, gd, gy, mInteraction, Textures::spriteTexture, renderer, Textures::spriteDown1, "");

        //Get object use message.
        useMessage = mob.Use(x, y, gd, gy, mInteraction, Textures::spriteTexture, renderer, Textures::spriteDown1, "");
             
        openMessage = mob.Open(x, y, gd, gy, mInteraction, Textures::spriteTexture, renderer, Textures::spriteDown1, "");

        //These messages are displayed to help tell the story.
        gameMessage = pi.DisplayPlayerMessages();
        if (gameMessage != "") {
            interactionMessage = gameMessage;
            PlayerInteraction::playerMessage = 100;               
        }     

        //Check which objects are picked up.
        if (gameObject != "") {            
            std::string object;
            object = pob.DestroyObjects(gameObject);
            objectToDestroy.append(object);
            std::cout << objectToDestroy << std::endl;
            //Added the following 2 lines to try and prevent the sprite from disappearing sometimes.
            SDL_DestroyTexture(Textures::spriteTexture);
            Textures::spriteTexture = SDL_CreateTextureFromSurface(renderer, Textures::spriteDown1);
        }
       
      
        if (interactionMessage != ""){
            messageHolder = 1;
            pi.InteractionControllerObject(interactionMessage, gameObject);
           
        }
        else if (useMessage != ""){
            messageHolder = 1;
            pi.InteractionControllerUse(useMessage, gameObject);                               
        }
        else if (openMessage != "") {
            messageHolder = 1;
            pi.InteractionControllerUse(openMessage, gameObject);
        }
        else
            SDL_DestroyTexture(ftexture);            
    }

    gd = gdSprite.x;
    gy = gdSprite.y;

    if (mouseHold == 0) {
        mouseHold = 1;
        ftexture = SDL_CreateTextureFromSurface(renderer, fsurface);
        while(x > gdSprite.x){
        gdSprite.x = player.walk(x, y, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture, ftexture, dialogmTexture);
        }

        //The following 2 statements will prevent the player from traversing diaginally which causes animation issues. Took ages to get this right!
        if (y < gdSprite.y && x < gdSprite.x + 75 && x >gdSprite.x)
            gdSprite.y = player.walky(x, y, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture, ftexture, dialogmTexture);

        if (y > gdSprite.y && x < gdSprite.x + 75 && x > gdSprite.x)
            gdSprite.y = player.walky(x, y, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture, ftexture, dialogmTexture);

    }

I tried putting a loop in the walking class and if I click the destination, the player teleports to that location and doesn't walk there. I think this is because the sprite is rendered later in the main game loop.

    //Set the coordinates for walking on the given Scene background.
if (Scene1::SceneBackground == "1") {

    if (gd <= x - 60 && y > 320 && y < 575) {

        while(gd <=x){

        if (position == 0) {

            SDL_DestroyTexture(spriteTexture);
            spriteTexture = SDL_CreateTextureFromSurface(Scene1::renderer, spriteRight1a);
            gd += 3.5; //Speed of sprite movement.
        }
        if (position == 1) {

            SDL_DestroyTexture(spriteTexture);
            spriteTexture = SDL_CreateTextureFromSurface(Scene1::renderer, spriteRight2a);
            gd += 3.5;
        }
        if (position == 2) {

            SDL_DestroyTexture(spriteTexture);
            spriteTexture = SDL_CreateTextureFromSurface(Scene1::renderer, spriteRight3a);
            gd += 3.5;
            //position = 0; //This achieves the animation effect.
        }
        if (position == 3) {

            SDL_DestroyTexture(spriteTexture);
            spriteTexture = SDL_CreateTextureFromSurface(Scene1::renderer, spriteRight4a);
            gd += 3.5;
            position = 0; //This achieves the animation effect.
        }

        if (gd == screenWidth - 50) {

            SDL_DestroyTexture(spriteTexture);
            spriteTexture = SDL_CreateTextureFromSurface(Scene1::renderer, spriteLeft1a);
            gd -= 3.5;
        }

        position++;
        }
    }

Upvotes: 1

Views: 565

Answers (2)

Mr Tea
Mr Tea

Reputation: 127

I got it working by doing the following code: I created 2 new x and y variables called wx and wy so that they don't get confused with the hover x and y values. The reason the player was following the mouse was because x and y were being updated on hover for scene objects.

I need to change some other code in the left click routine to stop the memory overflowing but the code below has sorted out the movement problem:

   if (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(SDL_BUTTON_LEFT)) {

        mouseHold = 0;
        //Free up memory for dialog texture and sprite texture. Prevents memory leak!   TRUST ME!
       //Note needs some tweaking. If you remove this your RAM will rocket!                 
        SDL_DestroyTexture(Textures::spriteTexture);
        SDL_DestroyTexture(ftexture); //VERY VERRRRY IMPORTANT (DON'T REMOVE)
        Textures::spriteTexture = SDL_CreateTextureFromSurface(renderer, Textures::spriteDown1); 
        fsurface = TTF_RenderText_Solid(font, "", fcolor);
                              
        Uint8 buttons = SDL_GetMouseState(&wx, &wy);
        gd = gdSprite.x;
        gy = gdSprite.y;

        //Show coordinates in console for object placement.
        cout << "Current Mouse click coordinates are:"  << endl;
        cout << "x = " << x << endl;
        cout << "y = " << y << endl;
        cout << "Current Player X Position is: " << gdSprite.x << endl;
        cout << "Current Player Y Position is: " << gdSprite.y << endl;
        cout << "Current Scene is: " << SceneBackground << endl;
    
        
        if (mouseHold == 0) {
            mouseHold = 1;
           
                std::cout << "In loop" << std::endl;
                std::cout << "gdsprite.x = " << gdSprite.x << std::endl;
                std::cout << "x =" << x << std::endl;

                ftexture = SDL_CreateTextureFromSurface(renderer, fsurface);


                /*
                gdSprite.x = player.walk(x, y, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture, ftexture, dialogmTexture);
                 

                //The following 2 statements will prevent the player from traversing diaginally which causes animation issues. Took ages to get this right!
                if (y < gdSprite.y && x < gdSprite.x + 75 && x >gdSprite.x)
                    gdSprite.y = player.walky(x, y, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture, ftexture, dialogmTexture);

                if (y > gdSprite.y && x < gdSprite.x + 75 && x > gdSprite.x)
                    gdSprite.y = player.walky(x, y, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture, ftexture, dialogmTexture);
          
               _sleep(1);  //This makes the animation of the character look a bit more realistic and less like she's on skates.
               */
        }

    
        //Get interaction message.         
        interactionMessage = pob.ObjectInteraction( x, y, gd, gy);
        if (interactionMessage != "") {
            SDL_DestroyTexture(Textures::spriteTexture);
            Textures::spriteTexture = SDL_CreateTextureFromSurface(renderer, Textures::spriteDown1);
        }

        std::string menuMessage;
        //Clicking objects on the scene.
  
        //Get object pickup message.
        gameObject = mob.PickUp(x, y, gd, gy, mInteraction, Textures::spriteTexture, renderer, Textures::spriteDown1, "");

        //Get object use message.
        useMessage = mob.Use(x, y, gd, gy, mInteraction, Textures::spriteTexture, renderer, Textures::spriteDown1, "");
             
        openMessage = mob.Open(x, y, gd, gy, mInteraction, Textures::spriteTexture, renderer, Textures::spriteDown1, "");

        //These messages are displayed to help tell the story.
        gameMessage = pi.DisplayPlayerMessages();
        if (gameMessage != "") {
            interactionMessage = gameMessage;
            PlayerInteraction::playerMessage = 100;               
        }     

        //Check which objects are picked up.
        if (gameObject != "") {            
            std::string object;
            object = pob.DestroyObjects(gameObject);
            objectToDestroy.append(object);
            std::cout << objectToDestroy << std::endl;
            //Added the following 2 lines to try and prevent the sprite from disappearing sometimes.
            SDL_DestroyTexture(Textures::spriteTexture);
            Textures::spriteTexture = SDL_CreateTextureFromSurface(renderer, Textures::spriteDown1);
        }
       
      
        if (interactionMessage != ""){
            messageHolder = 1;
            pi.InteractionControllerObject(interactionMessage, gameObject);
           
        }
        else if (useMessage != ""){
            messageHolder = 1;
            pi.InteractionControllerUse(useMessage, gameObject);                               
        }
        else if (openMessage != "") {
            messageHolder = 1;
            pi.InteractionControllerUse(openMessage, gameObject);
        }
        else
            SDL_DestroyTexture(ftexture);      

        
    }

    gd = gdSprite.x;
    gy = gdSprite.y;

    if (wx > gdSprite.x && mouseHold == 1 || wx < gdSprite.x && mouseHold == 1) {
        gdSprite.x = player.walk(wx, wy, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture, ftexture, dialogmTexture);
        _sleep(1);

    }
    
    if(wy < gdSprite.y && mouseHold == 1 || wy > gdSprite.y && mouseHold == 1){
    //The following 2 statements will prevent the player from traversing diaginally which causes animation issues. Took ages to get this right!
    if (y < gdSprite.y && wx < gdSprite.x + 75 && wx >gdSprite.x)
        gdSprite.y = player.walky(wx, wy, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture, ftexture, dialogmTexture);

    if (y > gdSprite.y && wx < gdSprite.x + 75 && wx > gdSprite.x)
        gdSprite.y = player.walky(wx, wy, gd, gy, WIDTH, HEIGHT, Textures::spriteTexture, ftexture, dialogmTexture);

    _sleep(1);  //This makes the animation of the character look a bit more realistic and less like she's on skates.
  
    }

Upvotes: 0

Morten Jensen
Morten Jensen

Reputation: 5936

  • If the player clicks somewhere, set a target.
  • While you're farther away from the target than some epsilon, move towards the target.
  • When the target is reached, clear the target -> stop moving.

EDIT: Or do like 0x5453 suggests in the comments. I think the idea is to set a target and move towards it, I'd try that out anyway :)

.

EDIT2:

If you

  • move what's inside if(mouseHold == 0) outside of the mouse-button check
  • change the logic to if(distance_to_target > threshold) or something similar
  • and THEN move towards the target

... you're pretty much there I think?

Upvotes: 1

Related Questions