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