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

Author Topic: [SOLVED] setTextureRect() not behaving as intended  (Read 115 times)

0 Members and 1 Guest are viewing this topic.

a_wee_lad

  • Newbie
  • *
  • Posts: 2
    • View Profile
[SOLVED] setTextureRect() not behaving as intended
« on: January 29, 2025, 08:17:52 pm »
Hello! 

I am trying to create an animation by creating a texture with a sprite sheet, then moving the rectangle that gets rendered with setTextureRect().  I modeled this approach after the tutorial here: https://www.sfml-dev.org/tutorials/3.0/graphics/sprite/#loading-a-texture.

The sprite sheet is 128 x 32 with 4 total animation frames.

The first frame of the animation is displayed properly, then the rightmost column of pixels is stretched across the sprite making a horizontal line after the first call to setTextureRect().

Short Version:
First frame draws properly then subsequent frames (after calling setTextureRect()) look like the rightmost column of the texture gets smeared across the screen.

See attachments for screenshots of the first frame and subsequent smeared frames.

Thank you in advance!

main.cpp
#include <SFML/Graphics.hpp>
#include "Scene.h"

int main()
{
    auto window = sf::RenderWindow(sf::VideoMode({1000u, 600u}), "window");
    window.setFramerateLimit(60);

    Scene sc;

    while (window.isOpen())
    {
        while (const std::optional event = window.pollEvent()) //loops over all events to see WHAT happened
        {
            //handle window closing event
            if (event->is<sf::Event::Closed>())
            {
                window.close();
            }

            //process user input events
            //if key is pressed or released, check all possible controls and set scene movement flags
            if(event->is<sf::Event::KeyPressed>() || event->is<sf::Event::KeyReleased>())
            {
                //W = up
                if(sf::Keyboard::isKeyPressed(sf::Keyboard::Scan::W))
                {
                    sc.setUp(true);
                } else
                {
                    sc.setUp(false);
                }

                //A = left
                if(sf::Keyboard::isKeyPressed(sf::Keyboard::Scan::A))
                {
                    sc.setLeft(true);
                }else
                {
                    sc.setLeft(false);
                }

                //S = down
                if(sf::Keyboard::isKeyPressed(sf::Keyboard::Scan::S))
                {
                    sc.setDown(true);
                }else
                {
                    sc.setDown(false);
                }

                //D = right
                if(sf::Keyboard::isKeyPressed(sf::Keyboard::Scan::D))
                {
                    sc.setRight(true);
                }else
                {
                    sc.setRight(false);
                }
            }


        }

        window.clear();

        //window.draw(*sc.getChk());
        sc.moveChk();
        sc.draw(window);

        window.display();
    }
}


 

Scene.h
#include <SFML/Graphics.hpp>

class Scene
{
    //movement flags
    bool up;
    bool down;
    bool left;
    bool right;

    sf::View view;

    //0 == idle
    //1 == walking
    int state;

    //multiplier on move vector
    float speed;

    int faceDirection;

    //this object will help render the png file of a chicken
    sf::Texture chkIdleText;
    sf::Texture chkWalkText;
    sf::Texture mapText;

    sf::Sprite chkIdle;
    sf::Sprite chkWalk;
    sf::Sprite map;

    sf::Clock clock;
    sf::Time elapsed;

    float ANIMATION_FRAME_TIME;

    int TILE_SIZE;
    int IDLE_NUM_FRAMES;
    int current_idle_frame;

    public:

    Scene();

    void setUp(bool input) { up = input; };
    void setDown(bool input) {down = input; };
    void setLeft(bool input) { left = input; };
    void setRight(bool input) {right = input; };

    void moveChk();

    sf::Sprite getChk();

    void draw(sf::RenderWindow &window);
};

Scene.cpp
//a scene will have characters
#include "Scene.h"
#include <iostream>


//Default Constructor
Scene::Scene() : //have to declare texture and chk here because they do not have default parameterless constructors
chkIdleText("/Users/me/Documents/Projects/chk/build/bin/chicken_idle.png", false, sf::IntRect({0, 0}, {32, 32})),
chkWalkText("/Users/me/Documents/Projects/chk/build/bin/chicken_walk.png", false, sf::IntRect({0, 0}, {32, 32})),
mapText("/Users/me/Documents/Projects/chk/build/bin/wholeMap01.png", false, sf::IntRect({0, 0}, {1152, 1152})),
chkIdle(chkIdleText),
chkWalk(chkWalkText),
map(mapText)
{
    up = false;
    left = false;
    down = false;
    right = false;

    speed = 3.f;

    chkIdle.setPosition({400.f, 300.f});

    view.setCenter(chkIdle.getPosition());
    view.setSize({400.f, 240.f});

    state = 0;

    faceDirection = 1; //1 or -1

    ANIMATION_FRAME_TIME = 0.2f;

    int TILE_SIZE = 32;
    int IDLE_NUM_FRAMES = 4;
    int current_idle_frame = 0;
   

}

//function to handle movement based on movement flags
void Scene::moveChk()
{
   
    elapsed = clock.getElapsedTime();
    if(elapsed.asSeconds() > ANIMATION_FRAME_TIME)
    {
        clock.restart();
        current_idle_frame++;
    }
   
    if(current_idle_frame > 2)
    {
        current_idle_frame = 0;
    }

    chkIdle.setTextureRect(sf::IntRect({current_idle_frame*32, 0}, {32, 32}));

    //std::cout << "Frame " + std::to_string(current_idle_frame) << std::endl;
    std::cout << "Time: " + std::to_string(elapsed.asSeconds()) << std::endl;
    std::cout << "position: " << std::to_string(chkIdle.getTextureRect().position.x) << std::endl;
   

    sf::Vector2f moveVector = {0.f, 0.f};
    if(up)
    {
        moveVector.y -= 1.f;
    }
    if(left)
    {
        moveVector.x -= 1.f;
    }
    if(down)
    {
        moveVector.y += 1.f;
    }
    if(right)
    {
        moveVector.x += 1.f;
    }

    if(moveVector.x > 0)
    {
        faceDirection = 1;
    }
    if(moveVector.x < 0)
    {
        faceDirection = -1;
    }
   

    chkIdle.move(moveVector*speed);
    sf::Vector2f viewCenter = {16.f + chkIdle.getPosition().x, 16.f + chkIdle.getPosition().y};
    view.setCenter(viewCenter);
}


sf::Sprite Scene::getChk()
{
    return chkIdle;
}

void Scene::draw(sf::RenderWindow &window)
{
    window.draw(map);
    window.draw(chkIdle);
    window.setView(view);
}



In case it is helpful output from those couts is this (proof the starting position of the IntRect I am passing is changing how I intended):
position: 0
Time: 0.017715
position: 0
Time: 0.034941
position: 0
Time: 0.052652
position: 0
Time: 0.070406
position: 0
Time: 0.087994
position: 0
Time: 0.105468
position: 0
Time: 0.122767
position: 0
Time: 0.139580
position: 0
Time: 0.156016
position: 0
Time: 0.173749
position: 0
Time: 0.190835
position: 0
Time: 0.207555
position: 32
Time: 0.017718
position: 32
Time: 0.035526
position: 32
Time: 0.053212
position: 32
Time: 0.069989
position: 32
Time: 0.087652
position: 32
Time: 0.105509
position: 32
Time: 0.123270
position: 32
Time: 0.140073
position: 32
Time: 0.157795
position: 32
Time: 0.175556
position: 32
Time: 0.192875
position: 32
Time: 0.210999
position: 64
Time: 0.017329
position: 64
Time: 0.034536
position: 64
Time: 0.051827
position: 64
Time: 0.068951
position: 64
Time: 0.086540
position: 64
Time: 0.103306
position: 64
Time: 0.120053
position: 64
Time: 0.136952
position: 64
Time: 0.154061
position: 64
Time: 0.170862
position: 64
Time: 0.187679
position: 64
Time: 0.205449
position: 0
 
« Last Edit: Today at 04:31:20 am by a_wee_lad »

kojack

  • Sr. Member
  • ****
  • Posts: 357
  • C++/C# game dev teacher.
    • View Profile
Re: setTextureRect() not behaving as intended
« Reply #1 on: Today at 02:50:38 am »
The problem is the last parameter to the texture constructor:
chkIdleText("/Users/me/Documents/Projects/chk/build/bin/chicken_idle.png", false, sf::IntRect({0, 0}, {32, 32}))
The last parameter is which part of the image to load. When you say sf::IntRect({0, 0}, {32, 32}), that means don't load the entire image, just load the first frame. If you make it:
chkIdleText("/Users/me/Documents/Projects/chk/build/bin/chicken_idle.png", false)
then it should work.

a_wee_lad

  • Newbie
  • *
  • Posts: 2
    • View Profile
Re: setTextureRect() not behaving as intended
« Reply #2 on: Today at 04:31:02 am »
Compiler was complaining when I didn't give a 3rd argument to the constructor, but specifying the full size of the image in the constructor fixed the problem!

Thank you!!

kojack

  • Sr. Member
  • ****
  • Posts: 357
  • C++/C# game dev teacher.
    • View Profile
Re: [SOLVED] setTextureRect() not behaving as intended
« Reply #3 on: Today at 04:57:23 am »
Strange. Without the size it should be picking this constructor:
explicit Texture(const std::filesystem::path& filename, bool sRgb = false);
But as long as you have it working. :)