Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Sprite animation with mouse control  (Read 2770 times)

0 Members and 1 Guest are viewing this topic.

frienzy

  • Newbie
  • *
  • Posts: 13
    • View Profile
    • Email
Sprite animation with mouse control
« on: March 12, 2021, 09:11:00 pm »
Hi!
I have a character sprite controlled by a mouse (first selected and then moved to the clicked location).
I can't figure out how to set the "sprite.setTextureRect" sprite location depending on what direction the character is going to be traveling.
This is what I have:

#include "player.h"

Player::Player(sf::String F, int X, int Y, float W, float H) {
       
        dir = 6;
        speed = 0; playerScore = 0; health = 100; dx = 0; dy = 0;
        life = true; isMove = false; isSelected = false;
        File = F;
        w = W;
        h = H;
        image.loadFromFile("" + File);
        texture.loadFromImage(image);
        sprite.setTexture(texture);
        x = X, y = Y;
        sprite.setTextureRect(sf::IntRect(0, 195, w, h));
        sf::Rect<float> size = sprite.getGlobalBounds();
        sprite.setOrigin(sf::Vector2f(size.width / 2, size.height -20));
}


void Player::update(float time) {
        switch (dir) {
        case 0: dx = -speed; dy = 0;  break;        //left
        case 1: dx = -speed; dy = -speed; break;  //up left
        case 2: dx = 0; dy = speed; break;        //down
        case 3: dx = speed; dy = speed; break;    //down right
        case 4: dx = speed; dy = 0; break;        //right
        case 5: dx = -speed; dy = speed; break;  //down left
        case 6: dx = 0; dy = -speed; break;       //up
        case 7: dx = speed; dy = -speed; break;    //down right
        }

        x += dx * time;
        y += dy * time;

        speed = 0;
        sprite.setPosition(x, y);

}
int main()
{
 
    sf::RenderWindow window(sf::VideoMode(1366, 768), "a2", Style::Fullscreen);
    //window.setFramerateLimit(20);
    view.reset(sf::FloatRect(0, 0, 1366, 768));
   
    const int level[] =
    {
        17, 17, 17, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        17, 17, 17, 1, 1, 1, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0,
        29, 29, 29, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3,
        29, 29, 29, 0, 2, 0, 3, 3, 3, 0, 1, 1, 1, 0, 0, 0,
        113, 113, 113, 0, 3, 3, 3, 0, 0, 0, 1, 1, 1, 2, 0, 0,
        113, 113, 113, 0, 3, 0, 2, 2, 0, 0, 1, 1, 1, 1, 2, 0,
        121, 121, 121, 0, 3, 0, 2, 2, 2, 0, 1, 1, 1, 1, 1, 1,
        121, 121, 121, 0, 3, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1,
    };

    TileMap map;
    if (!map.load("tiles.png", sf::Vector2u(32, 32), level, 16, 8))
        return -1;
   
    Player p ("pikeman_walk.png", 80, 16, 38, 55);     // x offset 3,  y offset 11.5f


    sf::Font font;
    font.loadFromFile("roboto.ttf");
    sf::Text text_fps("", font, 20);
    sf::String fps;

    sf::Clock clock;
    float timer = 0;
    int frames = 0;
   
    float currentFrame = 0;
    bool isMove = false;
    float dx = 0;
    float dy = 0;
    int tempX = 0;
    int tempY = 0;
    float distance = 0;

   

    while (window.isOpen())
    {
        float time = clock.getElapsedTime().asMicroseconds();
        timer += clock.getElapsedTime().asSeconds();
        frames++;
        clock.restart();
        time = time / 1000;

        if (timer >= 1.0f) {
            fps = std::to_string(frames);
            timer = 0;
            frames = 0;
        }

        sf::Vector2i pixelPos = sf::Mouse::getPosition(window);
        sf::Vector2f pos = window.mapPixelToCoords(pixelPos);

        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();

            if(event.type == sf::Event::MouseButtonPressed)
                if (event.key.code == sf::Mouse::Left) {
                    if (p.sprite.getGlobalBounds().contains(pos.x, pos.y)) {
                        p.isSelected = true;
                    }
                }
            if(p.isSelected)
                if(event.type == sf::Event::MouseButtonPressed)
                    if (event.key.code == sf::Mouse::Left) {
                        p.isMove = true;
                        tempX = pos.x;
                        tempY = pos.y;
                        float dx = pos.x - p.x;
                        float dy = pos.y - p.y;
                        float rotation = (atan2(dy, dx)) * 180 / 3.14159265;
                        //p.sprite.setRotation(rotation);   <-- sprite rotation
                       
                        if (p.dir == 0) {                          // ??? this is where I'm messing up ????
                            p.speed = 0.05;
                            currentFrame += 0.01 * time;
                            if (currentFrame > 10) currentFrame -= 10;

                            p.sprite.setTextureRect(sf::IntRect(34 * int(currentFrame), 84, 34, 52));                      
                        }                      
                    }
           
        }
       
        if (p.isMove) {
            distance = sqrt((tempX - p.x) * (tempX - p.x) + (tempY - p.y) * (tempY - p.y));
            if (distance > 2) {
                p.x += 0.1 * time * (tempX - p.x) / distance;
                p.y += 0.1 * time * (tempY - p.y) / distance;
             
            }
            else
            {
                p.isMove = false;
            }
        }

        sf::Vector2i localPosition = Mouse::getPosition(window);
        if (localPosition.x < 3) { view.move(-1.0 * time, 0); }
        if (localPosition.x > window.getSize().x-3) { view.move(1.0 * time, 0); }
        if (localPosition.y > window.getSize().y - 3) { view.move(0, 1.0 * time); }
        if (localPosition.y < 3) { view.move(0, -1.0 * time); }


        window.setView(view);
        p.update(time);
        window.clear();    
        window.draw(map);    
        changeView();

        text_fps.setString(fps);
        text_fps.setPosition(20, 20);
        window.draw(text_fps);

       
        window.draw(p.sprite);
        window.display();
    }

    return 0;
}

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: Sprite animation with mouse control
« Reply #1 on: March 19, 2021, 09:37:41 am »
You could predefine certain rects for each direction and then when changing direction, set the specific rect
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

KRS

  • Newbie
  • *
  • Posts: 19
  • The burning you feel?
    • View Profile
    • Email
Re: Sprite animation with mouse control
« Reply #2 on: March 19, 2021, 09:36:47 pm »
Hello! First I think you need to find angle of vector that character will be traveling, and depending on angle - set suitable textureRect

Here is little demonstration - for example if vector of traveling lies between -45 and 45 degrees, you pick TextureRect where character looks right, and so on.

To find angle of vector that character will be traveling use this formula:
A•B = |A||B|cosΘ (where Θ is angle between vector A and B)
But dont be scared yet.
From this formula we have that angle will be equal to
Θ = acos([AB] / [|A||B|])
but if with normalized vectors:
Θ = acos(AB)
Where A - x axis vector wich is (1;0)
B - vector of your character travel
And then
if (angle < 45 && angle > -45) characterLookRight;
And  for each direction. Hope I helped you! ;D

frienzy

  • Newbie
  • *
  • Posts: 13
    • View Profile
    • Email
Re: Sprite animation with mouse control
« Reply #3 on: April 03, 2021, 12:02:41 am »
Yes I got it working now, but I had it a bit more complex than that - I had the rotation being performed (the sprite animation cycling not actual sprite rotation).
So I had two variables - float currentAngle and float newAngle - where everytime mouse is being clicked the newAngle is saved to currentAngle. After the click the difference in angle is being calculated and normalized into third variable = angle.
int angle = (int)newAngle - (int)currentAngle;
      angle += (angle > 180) ? -360 : (angle < -180) ? 360 : 0;

this line makes sure that Angle is always 0 in the direction the character is facing.
And then just perform the check if Angle is changing and if changing inside the condition += currentAngle until the currentAngle reaches newAngle. (performing sprite animations along the way while degrees are adding.