SFML community forums

General => General discussions => Topic started by: SFMLNewGuy on July 31, 2020, 04:40:18 am

Title: Thor and Animation
Post by: SFMLNewGuy on July 31, 2020, 04:40:18 am
Hello,

So I'm trying to use thor and things aren't going as smoothly as I thought they would. At the moment I have an isometric sprite sheet and I'm just trying to watch it animate the walk directions with W A S D and the diagonals of the keys. It must have something to do with the "queue" because it seems like a "lag" to the next animation and sometimes it never gets called even. I might be doing something wrong, but a lot of the functions in the tutorial aren't there. Such as

animator.playAnimation("walk");
animator.stopAnimation();
animator.isPlayingAnimation()

#include <Thor/Animations.hpp>
#include <Thor/Input.hpp>
#include <SFML/Graphics.hpp>
const int Sprite_Width = 160;
const int Sprite_Height = 160;

// Adds a range of frames, assuming they are aligned as rectangles in the texture.
// animation:      FrameAnimation to modify
// x:              Column index of the texture rectangle
// [yFirst,yLast]: Bounds for row indices (if yLast < yFirst, add frames in reverse order)
// duration:       Relative duration of current frame (1 by default)
void addFrames(thor::FrameAnimation& animation, int x, int yFirst, int yLast, float duration = 1.f) {
        const int step = (yFirst < yLast) ? +1 : -1;
        yLast += step; // so yLast is excluded in the range

        for (int y = yFirst; y != yLast; y += step)
                animation.addFrame(duration, sf::IntRect(Sprite_Width * x, 160 * y, Sprite_Width, 160));
}

// Plays an animation, and updates the text correspondingly
// animator:    Thor animator to play the animation
// animationId: Name of the animation to play
// restart:     true if the queued animations are discarded, false if the new animation is enqueued at the end
// display:     SFML text that displays the current animation
void playAnimation(thor::Animator<sf::Sprite, std::string>& animator, const std::string& animationId, bool restart, sf::Text& display) {
        // Function to call before animation is played
        auto onStart = [&display, animationId]() {
                display.setString(animationId);
        };
        restart = false;
        // Function to call after animation is played
        auto onFinish = [&display]() {
                display.setString("(idle)");
        };

        // Enqueue animation and callbacks -- restart determines whether queue is reset
        (restart ? animator.play() : animator.queue())
                << thor::Playback::notify(onStart)
                << animationId
                << thor::Playback::notify(onFinish);
}

int main() {
        sf::RenderWindow window(sf::VideoMode(1280, 768), "Thor Animation");
        window.setVerticalSyncEnabled(true);
        window.setKeyRepeatEnabled(false);

        sf::Font font;
        if (!font.loadFromFile("Media/sansation.ttf"))
                return 1;


        sf::Text animationText("(idle)", font, 14u);
        animationText.setPosition(100.f, 150.f);
        animationText.setFillColor(sf::Color(250, 215, 11));

        // Create texture based on sf::Image
        sf::Texture texture;
        if (!texture.loadFromFile("Media/Sprite_1.png"))
                return 1;

        // Create sprite which is animated
        sf::Sprite sprite(texture);
        sprite.setPosition(100.f, 100.f);

        // Define walk animation
        thor::FrameAnimation walkDown;
        for(auto i = 0; i < 8; i++)
                addFrames(walkDown, i, 0, 0);          
        thor::FrameAnimation walkLeft;
        for (auto i = 8; i < 16; i++)
                addFrames(walkLeft, i, 0, 0);          
        thor::FrameAnimation walkRight;
        for (auto i = 16; i < 24; i++)
                addFrames(walkRight, i, 0, 0);                 
        thor::FrameAnimation walkUp;
        for (auto i = 0; i < 8; i++)
                addFrames(walkUp, i, 1,1 );                    

        thor::FrameAnimation walkDownLeft;
        for (auto i = 8; i < 16; i++)
                addFrames(walkDownLeft, i, 1, 1);              
        thor::FrameAnimation walkUpLeft;
        for (auto i = 16; i < 24; i++)
                addFrames(walkUpLeft, i, 1, 1);        
         thor::FrameAnimation walkDownRight;
        for (auto i = 0; i < 8; i++)
                addFrames(walkDownRight, i, 2, 2);                     
         thor::FrameAnimation walkUpRight;
        for (auto i = 8; i < 16; i++)
                addFrames(walkUpRight, i, 2, 2);               
       

        // Register animations with their corresponding durations
        thor::AnimationMap<sf::Sprite, std::string> animations;
        animations.addAnimation("walkDown", walkDown, sf::seconds(1.f));
        animations.addAnimation("walkLeft", walkLeft, sf::seconds(1.f));
        animations.addAnimation("walkUp", walkUp, sf::seconds(1.f));
        animations.addAnimation("walkRight", walkRight, sf::seconds(1.f));
        animations.addAnimation("walkDownLeft", walkDownLeft, sf::seconds(1.f));
        animations.addAnimation("walkUpLeft", walkUpLeft, sf::seconds(1.f));
        animations.addAnimation("walkDownRight", walkDownRight, sf::seconds(1.f));
        animations.addAnimation("walkUpRight", walkUpRight, sf::seconds(1.f));
        // Create animator referring to this map, and play 'stand' animation to bring the sprite into an initial state
        thor::Animator<sf::Sprite, std::string> animator(animations);
        animator.play() << "walkDownLeft";
       
        // Create clock to measure frame time
        sf::Clock frameClock;

        thor::ActionMap<std::string> keyMap;

        thor::Action w(sf::Keyboard::W, thor::Action::Hold);
        thor::Action a(sf::Keyboard::A, thor::Action::Hold);
        thor::Action s(sf::Keyboard::S, thor::Action::Hold);
        thor::Action d(sf::Keyboard::D, thor::Action::Hold);
        keyMap["Down"] = s;
        keyMap["Up"] = w;
        keyMap["Left"] = a;
        keyMap["Right"] = d;

        thor::Action sa = s && a;
        thor::Action wa = w && a;
        thor::Action sd = s && d;
        thor::Action wd = w && d;
        keyMap["DownLeft"] = sa;
        keyMap["UpLeft"] = wa;
        keyMap["DownRight"] = sd;
        keyMap["UpRight"] = wd;

        thor::Action esc(sf::Keyboard::Escape, thor::Action::PressOnce);
        thor::Action stop(sf::Keyboard::Space, thor::Action::PressOnce);
        keyMap["quit"] = esc;
        keyMap["Stop"] = stop;

        // Main loop
        while (window.isOpen()) {
                // Handle events
                sf::Event event;
                while (window.pollEvent(event)) {

                        // React to different action types
                        if (keyMap.isActive("quit") || event.type == sf::Event::Closed)
                        {
                                window.close();
                                return 0;
                        }

                }
                        // Poll the window for new events, update actions
                        keyMap.update(window);

                if (keyMap.isActive("DownLeft")) {
                        animationText.setString("(Walk Down-Left)");
                        playAnimation(animator, "walkDownLeft", event.key.code, animationText);
                }
                else if (keyMap.isActive("UpLeft")) {
                        animationText.setString("(Walk Up-Left)");
                        playAnimation(animator, "walkUpLeft", event.key.code, animationText);
                }
                else if (keyMap.isActive("DownRight")) {
                        animationText.setString("(Walk Down-Right)");
                        playAnimation(animator, "walkDownRight", event.key.code, animationText);
                }
                else if (keyMap.isActive("UpRight")) {
                        animationText.setString("(Walk Down-Right)");
                        playAnimation(animator, "walkUpRight", event.key.code, animationText);
                }
                else if (keyMap.isActive("Down")) {
                        animationText.setString("(Walk Down)");
                        playAnimation(animator, "walkDown", event.key.code, animationText);
                }
                else if (keyMap.isActive("Left")) {
                        animationText.setString("Walk Left");
                        playAnimation(animator, "walkLeft", event.key.code, animationText);
                }
                else if (keyMap.isActive("Up")) {
                        animationText.setString("Walk Up");
                        playAnimation(animator, "walkUp", event.key.code, animationText);
                }
                else if (keyMap.isActive("Right")) {
                        animationText.setString("Walk Right");
                        playAnimation(animator, "walkRight", event.key.code, animationText);
                }
                else {
                        animator.stop();
                }
       
       
                // Update animator and apply current animation state to the sprite
                animator.update(frameClock.restart());
                animator.animate(sprite);

                // Draw everything
                window.clear(sf::Color(50, 50, 50));
                window.draw(animationText);
                window.draw(sprite);
                window.display();
        }
}

 

Title: Re: Thor and Animation
Post by: SFMLNewGuy on July 31, 2020, 11:08:59 am
[SOLVED] - But still would like to be recommended "your favorite" SFML input wrapper. Thanks

So I solved the problem, but I really could use some help or referenced a simple but complete input library. One thing I haven't been a fan of is SFML keyboard (compared to SDL). Does anyone know of a nice little setup that has a (KeyDown, KeyUp, etc.) wrapper? In one of my SFML books, they go over something but I can't recall which one.

My problem now is if I hold Up(W)+Left(A) and it goes to the up left sprite, if I let go of 'Left' (A) it doesn't change to 'Up' forcing me to lift up all the keys and then just preess 'Up' (w) for the correct sprite.
bool down = false, left = false, up = false, right = false, downLeft = false, upLeft = false, downRight = false, upRight = false;

void setFalse() {
        down = false;
        left = false;
        up = false,
        right = false;
        downLeft = false;
        upLeft = false;
        downRight = false;
        upRight = false;
}
 

                if (keyMap.isActive("DownLeft")) {
                        if (!downLeft) {
                                setFalse();                     // Solved key transition problem.
                                animationText.setString("(Walk_Down-Left)");
                                playAnimation(animator, "walkDownLeft", true, animationText);
                                downLeft = true;
                        }
                }
                else if (keyMap.isActive("UpLeft")) {
                        if (!upLeft) {
                                setFalse();                     // Solved key transition problem.
                                animationText.setString("(Walk_Up-Left)");
                                playAnimation(animator, "walkUpLeft", true, animationText);
                                upLeft = true;
                        }
                }
                else if (keyMap.isActive("DownRight")) {
                        if (!downRight) {
                                setFalse();             // Solved key transition problem.
                                animationText.setString("(Walk_Down-Right)");
                                playAnimation(animator, "walkDownRight", true, animationText);
                                downRight = true;
                        }
                }
                else if (keyMap.isActive("UpRight")) {
                        if (!upRight) {
                                setFalse();                     // Solved key transition problem.
                                animationText.setString("(Walk_Down-Right)");
                                playAnimation(animator, "walkUpRight", true, animationText);
                                upRight = true;
                        }
                }

                else if (keyMap.isActive("Down")) {
                        if (!down) {
                                setFalse();                             // Solved key transition problem.
                                animationText.setString("(Walk_Down)");
                                playAnimation(animator, "walkDown", true, animationText);
                                down = true;
                        }
                }
                else if (keyMap.isActive("Left")) {

                        if (!left) {
                                setFalse();                     // Solved key transition problem.
                                animationText.setString("Walk_Left");
                                playAnimation(animator, "walkLeft", true, animationText);
                                left = true;
                        }
                }
                else if (keyMap.isActive("Up")) {
                        if (!up) {
                                setFalse();                     // Solved key transition problem.
                                animationText.setString("Walk_Up");
                                playAnimation(animator, "walkUp", true, animationText);
                                up = true;
                        }
                }
                else if (keyMap.isActive("Right"))
                {
                        if(!right)
                        {
                                setFalse();                                             // Solved key transition problem.
                                animationText.setString("Walk_Right");
                                playAnimation(animator, "walkRight", true, animationText);
                                right = true;
                        }
                }
                 else {
               
                         animator.stop();
                 }
 

Clear the bools before each keypress to have it transition smoothly.
Title: Re: Thor and Animation
Post by: Nexus on August 03, 2020, 11:06:50 pm
Regarding first post: the API changed a bit between Thor versions, in case of doubt generate the docs directly from source (Doxygen option in CMake).

Regarding second post: you're doing it extremely complicated, and with a large amount of boilerplate.
The normal algorithm is this:
sf::Vector2 vel;
if (up)
  vel.y -= 1;
if (down)
  vel.y += 1;
if (left)
  vel.x -= 1;
if (right)
  vel.x += 1;

// account for diagonal movement