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

Author Topic: SFML and Sprites  (Read 9680 times)

0 Members and 1 Guest are viewing this topic.

synok

  • Newbie
  • *
  • Posts: 18
    • View Profile
SFML and Sprites
« on: December 19, 2012, 05:15:18 pm »
Hello all,

I have been searching my head off for an anwser to handling Sprites / Spritesheet.

I have tried doing this with both seperate sprites and spritesheets and I am using SFML 2.0. I have a spritesheet of a character I made and want to animate him. The problem is that the Time function and / or the Clock function does not work. Time / Clock will NOT start ticking after it is created, thus I cannot animate my character.

After all, it makes no sense to me to have one Time and one Clock function. Why not just have one timer function instead of confusing people with more than one function to handle time.

What I have tried so far is adding a spritesheet, seperating them with .setTextureRect and applying new dimensions to the sprite. That worked, as long as the Clock is equal to 0.0. Otherwise it wont work. I tried cutting out each and every frame from the spritesheet loading them individually, still this wont work since Time ALWAYS is equal to 0. Pointless function. Any help suggested for the simplest task of creating an animated sprite with either Spritesheet or seperate Sprites?

If anyone can write a solution, I take everything back that I said. I dare you.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: SFML and Sprites
« Reply #1 on: December 19, 2012, 05:22:23 pm »
A glimpse at the documentation, would've probably already resolved your confusion. ;)
Quote
Represents a time value.
sf::Time encapsulates a time value in a flexible way.
It allows to define a time value either as a number of seconds, milliseconds or microseconds. It also works the other way round: you can read a time value as either a number of seconds, milliseconds or microseconds.
By using such a flexible interface, the API doesn't impose any fixed type or resolution for time values, and let the user choose its own favorite representation.
Time values support the usual mathematical operations: you can add or subtract two times, multiply or divide a time by a number, compare two times, etc.
Since they represent a time span and not an absolute time value, times can also be negative.
In short: sf::Time only represents a value and doesn't increase or decrease. It also is a class and not a function.

If you want something that increases with time, you'll have to use sf::Clock. It will start counting as soon as it's created or when you call restart(), but keep in mind that you can't pause it.

To use a spritesheet you simple load the image into a sf::Texture and pass it to you instance of sf::Sprite. Then you 'cut-out' a sub-rectangle of the whole spritesheet with setTextureRect(). If the spritesheet holds an animation, you'll have to move the texture rectangle accordingly to the sprite position and time.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

synok

  • Newbie
  • *
  • Posts: 18
    • View Profile
Re: SFML and Sprites
« Reply #2 on: December 19, 2012, 05:52:29 pm »
A glimpse at the documentation, would've probably already resolved your confusion. ;)
Quote
Represents a time value.
sf::Time encapsulates a time value in a flexible way.
It allows to define a time value either as a number of seconds, milliseconds or microseconds. It also works the other way round: you can read a time value as either a number of seconds, milliseconds or microseconds.
By using such a flexible interface, the API doesn't impose any fixed type or resolution for time values, and let the user choose its own favorite representation.
Time values support the usual mathematical operations: you can add or subtract two times, multiply or divide a time by a number, compare two times, etc.
Since they represent a time span and not an absolute time value, times can also be negative.
In short: sf::Time only represents a value and doesn't increase or decrease. It also is a class and not a function.

If you want something that increases with time, you'll have to use sf::Clock. It will start counting as soon as it's created or when you call restart(), but keep in mind that you can't pause it.

To use a spritesheet you simple load the image into a sf::Texture and pass it to you instance of sf::Sprite. Then you 'cut-out' a sub-rectangle of the whole spritesheet with setTextureRect(). If the spritesheet holds an animation, you'll have to move the texture rectangle accordingly to the sprite position and time.

Followed your suggestions, now I see two sprites at the same time. And speaking of time, there is no delay for the animation. Here is a picture when he is moving to the right:


eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: SFML and Sprites
« Reply #3 on: December 19, 2012, 06:03:17 pm »
Followed your suggestions, now I see two sprites at the same time.
You're probably not cutting out double the width of the character instead of just once (e.g. you cut-out a square instead of half a square).

And speaking of time, there is no delay for the animation.
I don't know what you mean with delay or what you're expecting...
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

synok

  • Newbie
  • *
  • Posts: 18
    • View Profile
Re: SFML and Sprites
« Reply #4 on: December 19, 2012, 06:13:42 pm »
Followed your suggestions, now I see two sprites at the same time.
You're probably not cutting out double the width of the character instead of just once (e.g. you cut-out a square instead of half a square).

And speaking of time, there is no delay for the animation.
I don't know what you mean with delay or what you're expecting...

Well now I set the sprite width same as the x, for example:

Code: [Select]
playerSprite.setTextureRect(sf::IntRect(22,2,20,40));
Instead of previous code:

Code: [Select]
playerSprite.setTextureRect(sf::IntRect(20,2,40,40));
The character is correctly drawn now, oddly enough.

With delay I mean I want some kind of short delay between the cycling frames, so they are not cycling all at once. A small delay of around 0.1 seconds would make the character walk smooth.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: SFML and Sprites
« Reply #5 on: December 19, 2012, 06:36:24 pm »
The character is correctly drawn now, oddly enough.
Why should this be odd?
One character is 20px in width and 40px in height. If you cut-out a 40px by 40px sub-image you'll obviously get the character twice. ;D

With delay I mean I want some kind of short delay between the cycling frames, so they are not cycling all at once. A small delay of around 0.1 seconds would make the character walk smooth.
So you mean that the animation runs slower?

Well you can use a sf::Clock and wait till the wanted 'delay time' has passed. For example if you want the animation to run at 5 frames per second, you do:
if(clock.getElapsedTime().asSeconds() >= 1.f/5.f)
     ++animationFrame;
... or similar. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

synok

  • Newbie
  • *
  • Posts: 18
    • View Profile
Re: SFML and Sprites
« Reply #6 on: December 19, 2012, 06:48:47 pm »
Yeah, well, what I want is it to draw the first texture rectangle at key pressed, then wait for a few frames, then move the texture rectangle to draw the second frame in the spritesheet, and repeat.

Does it matter where I define sf::Clock? Inside or outside loops for example.

Edit: Everything seems to work now. Thanks eXplo0it3r for your guidance. There is just one small thing left. In the end of the walk cycle he blinks for a small moment, then continues to loop when you hold down the button to move him. Why does he blink? It is like he goes transparent for a frame or so then continue to animate as normal.
« Last Edit: December 19, 2012, 07:07:10 pm by synok »

masskiller

  • Sr. Member
  • ****
  • Posts: 284
  • Pointers to Functions rock!
    • MSN Messenger - kyogre_jb@hotmail.com
    • View Profile
    • Email
Re: SFML and Sprites
« Reply #7 on: December 20, 2012, 07:29:56 am »
Quote
Everything seems to work now. Thanks eXplo0it3r for your guidance. There is just one small thing left. In the end of the walk cycle he blinks for a small moment, then continues to loop when you hold down the button to move him. Why does he blink? It is like he goes transparent for a frame or so then continue to animate as normal.

This may depend on your SpriteSheet (Having an extra block with alpha pixels) and your algorithm, say that the rect you are setting goes out of texture bounds due to an unhandled index value or that it doesn't go out of texture bounds, but it goes to the alpha filled part for a while.
Programmer, Artist, Composer and Storyline/Script Writer of "Origin of Magic". If all goes well this could turn into a commercial project!

Finally back into the programming world!

synok

  • Newbie
  • *
  • Posts: 18
    • View Profile
Re: SFML and Sprites
« Reply #8 on: December 20, 2012, 11:14:36 am »
Oh okay, so what can I do about it? Also, I have noticed when I draw some text, that it also flickers. I am calling it when a mouse button is pressed in a specific area. I think it has something to do with window.display(), but im not sure.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11030
    • View Profile
    • development blog
    • Email
Re: SFML and Sprites
« Reply #9 on: December 20, 2012, 12:12:18 pm »
Oh okay, so what can I do about it? Also, I have noticed when I draw some text, that it also flickers. I am calling it when a mouse button is pressed in a specific area. I think it has something to do with window.display(), but im not sure.
You need to provide code, so we can figure out what's going on. As for now we can only guess around the thousands of different possible mistakes or errors and won't really get to an result. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

synok

  • Newbie
  • *
  • Posts: 18
    • View Profile
Re: SFML and Sprites
« Reply #10 on: December 22, 2012, 12:41:17 pm »
Oh okay, so what can I do about it? Also, I have noticed when I draw some text, that it also flickers. I am calling it when a mouse button is pressed in a specific area. I think it has something to do with window.display(), but im not sure.
You need to provide code, so we can figure out what's going on. As for now we can only guess around the thousands of different possible mistakes or errors and won't really get to an result. ;)

Sorry if I am a bit late, but here is a snippet of code of the animation on keypress:

Code: [Select]

if (event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::D)
playerSprite.setTextureRect(sf::IntRect(2,2,18,40));

if(sf::Keyboard::isKeyPressed(sf::Keyboard::D)){
// Animate Player to walk.
if(clock.getElapsedTime().asSeconds() >= 0.05f)
playerSprite.setTextureRect(sf::IntRect(22,1,18,40));
if(clock.getElapsedTime().asSeconds() >= 0.1f)
playerSprite.setTextureRect(sf::IntRect(42,1,18,40));
if(clock.getElapsedTime().asSeconds() >= 0.15f)
playerSprite.setTextureRect(sf::IntRect(62,2,18,40));
if(clock.getElapsedTime().asSeconds() >= 0.2f)
playerSprite.setTextureRect(sf::IntRect(82,2,18,40));
if(clock.getElapsedTime().asSeconds() >= 0.25f)
playerSprite.setTextureRect(sf::IntRect(102,1,18,40));
if(clock.getElapsedTime().asSeconds() >= 0.3f)
playerSprite.setTextureRect(sf::IntRect(122,1,18,40));
if(clock.getElapsedTime().asSeconds() >= 0.35f)
playerSprite.setTextureRect(sf::IntRect(142,2,18,40));
if(clock.getElapsedTime().asSeconds() >= 0.4f){
playerSprite.setTextureRect(sf::IntRect(160,2,18,40));
clock.restart();
}
charPosX+=.45f;

And the spritesheet I am using is looking like this:



With dimensions of 200x100. Looking to extend the spritesheet later. The "clock" is as you guessed it, a sf::Clock, and the charPosX variable is a float to define the current position of the character.

So the result is a walk animation that blinks for a frame at the end. Same goes with the Text which has alot of unoptimized code, but here it is:

Code: [Select]
if(mouse.getPosition().x<=460&&mouse.getPosition().x>=400){

if(mouse.getPosition().y<=520&&mouse.getPosition().y>=420){
checkFridge=true;
if(checkFridge == true){
useLabelSprite.setTexture(useLabel);
useLabelSprite.setPosition(mouse.getPosition().x-10,mouse.getPosition().y-50);
useLabelSprite.setScale(2.0f,2.0f);

if(event.type==sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left){
text.setPosition(charPosX,charPosY-20);
window.draw(text);
window.display();

}else if(event.type==sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Left){
text.setPosition(charPosX,charPosY-20);
window.draw(text);
window.display();
}
}
}else{
// Unload UseLabel.
checkFridge=false;
useLabelSprite.setScale(0.0f,0.0f);
}
}else{
// Unload UseLabel.
checkFridge=false;
useLabelSprite.setScale(0.0f,0.0f);
}

Thanks for any help.

cire

  • Full Member
  • ***
  • Posts: 138
    • View Profile
Re: SFML and Sprites
« Reply #11 on: December 22, 2012, 05:15:12 pm »
Why are you calling window.display() in event processing code?

synok

  • Newbie
  • *
  • Posts: 18
    • View Profile
Re: SFML and Sprites
« Reply #12 on: December 22, 2012, 11:19:42 pm »
Because the text should be displayed if the mouse is pressed, at the moment.

masskiller

  • Sr. Member
  • ****
  • Posts: 284
  • Pointers to Functions rock!
    • MSN Messenger - kyogre_jb@hotmail.com
    • View Profile
    • Email
Re: SFML and Sprites
« Reply #13 on: December 23, 2012, 12:51:14 am »
As I suspected, your sprite sheet has an extra of transparent pixels, and you start your rect with 22 as a left parameter, when it should be 0 so that it starts with the left-most sprite. In your eight setTextureRect call it falls apart, causing your blinking.

I recommend you use an index for it. You don't need to manually set the rect each time, you can use an index to multiply and you can reduce 8 if's to 1. The index would start in zero and would increase according to time.

This:


if(sf::Keyboard::isKeyPressed(sf::Keyboard::D)){
                        // Animate Player to walk.
                        if(clock.getElapsedTime().asSeconds() >= 0.05f)
                                playerSprite.setTextureRect(sf::IntRect(22,1,18,40));
                        if(clock.getElapsedTime().asSeconds() >= 0.1f)
                                playerSprite.setTextureRect(sf::IntRect(42,1,18,40));
                        if(clock.getElapsedTime().asSeconds() >= 0.15f)
                                playerSprite.setTextureRect(sf::IntRect(62,2,18,40));
                        if(clock.getElapsedTime().asSeconds() >= 0.2f)
                                playerSprite.setTextureRect(sf::IntRect(82,2,18,40));
                        if(clock.getElapsedTime().asSeconds() >= 0.25f)
                                playerSprite.setTextureRect(sf::IntRect(102,1,18,40));
                        if(clock.getElapsedTime().asSeconds() >= 0.3f)
                                playerSprite.setTextureRect(sf::IntRect(122,1,18,40));
                        if(clock.getElapsedTime().asSeconds() >= 0.35f)
                                playerSprite.setTextureRect(sf::IntRect(142,2,18,40));
                        if(clock.getElapsedTime().asSeconds() >= 0.4f){
                                playerSprite.setTextureRect(sf::IntRect(160,2,18,40));
                                clock.restart();
                        }
                        charPosX+=.45f;
 

Can turn into:

const float timeInterval = 0.05f;
sf::Time T = sf::seconds(timeInterval);
unsigned i = 0;

if(sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
    // Animate Player to walk.
        if ( i > maxRows )
    {
                i = 0;
                T = sf::seconds(timeInterval);  
        }  
       
    if(clock.getElapsedTime().asSeconds() >= T)
    {
        playerSprite.setTextureRect(sf::IntRect(i * width, currentColumn * height, width, height));
        ///currentColumn is an index that starts in 0 and increases however you may want it to.
        ++i;
        T = sf::seconds(i * timeInterval);
    }
}
 

As a small sample. That way it becomes more manageable and not dependent on magical numbers. You can use the index as a static variable in a function if you use that algorithm only for said sprite, as it exists only once per program run and it won't be called for any other animation of any other object, else you have the animation index as a class member once you take it all out of main.

Quote
Because the text should be displayed if the mouse is pressed, at the moment.

The display call should always be outside of the event loop. The reason you don't call
display(object);
is because the display function exists for everything that is getting drawn, not just that one object.
« Last Edit: December 23, 2012, 01:30:12 am by masskiller »
Programmer, Artist, Composer and Storyline/Script Writer of "Origin of Magic". If all goes well this could turn into a commercial project!

Finally back into the programming world!

synok

  • Newbie
  • *
  • Posts: 18
    • View Profile
Re: SFML and Sprites
« Reply #14 on: December 26, 2012, 12:43:13 am »
As I suspected, your sprite sheet has an extra of transparent pixels, and you start your rect with 22 as a left parameter, when it should be 0 so that it starts with the left-most sprite. In your eight setTextureRect call it falls apart, causing your blinking.

I recommend you use an index for it. You don't need to manually set the rect each time, you can use an index to multiply and you can reduce 8 if's to 1. The index would start in zero and would increase according to time.

This:


if(sf::Keyboard::isKeyPressed(sf::Keyboard::D)){
                        // Animate Player to walk.
                        if(clock.getElapsedTime().asSeconds() >= 0.05f)
                                playerSprite.setTextureRect(sf::IntRect(22,1,18,40));
                        if(clock.getElapsedTime().asSeconds() >= 0.1f)
                                playerSprite.setTextureRect(sf::IntRect(42,1,18,40));
                        if(clock.getElapsedTime().asSeconds() >= 0.15f)
                                playerSprite.setTextureRect(sf::IntRect(62,2,18,40));
                        if(clock.getElapsedTime().asSeconds() >= 0.2f)
                                playerSprite.setTextureRect(sf::IntRect(82,2,18,40));
                        if(clock.getElapsedTime().asSeconds() >= 0.25f)
                                playerSprite.setTextureRect(sf::IntRect(102,1,18,40));
                        if(clock.getElapsedTime().asSeconds() >= 0.3f)
                                playerSprite.setTextureRect(sf::IntRect(122,1,18,40));
                        if(clock.getElapsedTime().asSeconds() >= 0.35f)
                                playerSprite.setTextureRect(sf::IntRect(142,2,18,40));
                        if(clock.getElapsedTime().asSeconds() >= 0.4f){
                                playerSprite.setTextureRect(sf::IntRect(160,2,18,40));
                                clock.restart();
                        }
                        charPosX+=.45f;
 

Can turn into:

const float timeInterval = 0.05f;
sf::Time T = sf::seconds(timeInterval);
unsigned i = 0;

if(sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
    // Animate Player to walk.
        if ( i > maxRows )
    {
                i = 0;
                T = sf::seconds(timeInterval);  
        }  
       
    if(clock.getElapsedTime().asSeconds() >= T)
    {
        playerSprite.setTextureRect(sf::IntRect(i * width, currentColumn * height, width, height));
        ///currentColumn is an index that starts in 0 and increases however you may want it to.
        ++i;
        T = sf::seconds(i * timeInterval);
    }
}
 

As a small sample. That way it becomes more manageable and not dependent on magical numbers. You can use the index as a static variable in a function if you use that algorithm only for said sprite, as it exists only once per program run and it won't be called for any other animation of any other object, else you have the animation index as a class member once you take it all out of main.

Quote
Because the text should be displayed if the mouse is pressed, at the moment.

The display call should always be outside of the event loop. The reason you don't call
display(object);
is because the display function exists for everything that is getting drawn, not just that one object.

I tweaked your code, and got it to:

Code: [Select]


const float timeInterval = 0.05f;
sf::Time T = sf::seconds(timeInterval);
unsigned i = 0;

// Animate Player to walk.
if ( i > 7 )
{
i = 0;
T = sf::seconds(timeInterval); 

   

{
playerSprite.setTextureRect(sf::IntRect(i * 21, 0 * 42, 21, 42));
///currentColumn is an index that starts in 0 and increases however you may want it to.
i++;
T = sf::seconds(i * timeInterval);
        }


Edit: Noticed I have put the definition of the variables in the loop. That's why. Now, he animates very fast, is there a solution to solve that? Might add that I had to set your variable "T" in the:

Code: [Select]
if(clock.getElapsedTime().asSeconds() >= 0.05f) case since I had the following error:
Quote
error C2678: binary '>=' : no operator found which takes a left-hand operand of type 'float' (or there is no acceptable conversion)
in Visual Studio. I am guessing that T is causing the delay I am looking for in this place.
« Last Edit: December 26, 2012, 01:56:00 am by synok »