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. ;)
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.
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.
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.
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:
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:
if(clock.getElapsedTime().asSeconds() >= 0.05f)
case since I had the following error:
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.
That's why. Now, he animates very fast, is there a solution to solve that?
If your animation is too fast just increase the delay time. Delay time depends on what kind of effect you want to give, how many sprites are you animating and so on, it's perfectly tweekable as you may wish it to be.
I am guessing that T is causing the delay I am looking for in this place.
It is indeed what causes it, notice that the getElapsedTime() function returns an sf::Time value that can be compared to other sf::Time values, take a look at this code, which is the SFML source of Time.cpp
bool operator <=(Time left, Time right)
{
return left.asMicroseconds() <= right.asMicroseconds();
}
bool operator >=(Time left, Time right)
{
return left.asMicroseconds() >= right.asMicroseconds();
}
Instead of manually converting times and ridding of the T parameter you can just compare times as needed, it has even more precision than the seconds comparison.
I just noticed that in my code I left a mistaken "asSeconds()" call. That was a mistake I overlooked since you should be comparing sf::time with another sf::time and not with a float. asSeconds() returns a float and it doesn't know how to compare itself with time, which is the reason your compiler whines.
It should look something like this:
if(clock.getElapsedTime() >= T)
My bad for overlooking this when I was refactoring your code. With that change and leaving the parameter T as I set it should work well and with no compiling errors.
Edit: As a side-note, you can perfectly use variables instead of the plain numbers, it makes your code more controllable over-time, mainly when you forget what that number meant. Not to mention that it can also may make your code work in more than one situation.
All you need to do is set the width and height variable before your loop using the texture's getSize function:
unsigned width = (texture name).getSize().x;
unsigned heigth = (texture name).getSize().y;
///further settings of the particular sprite sheet.
///Game loop and fancy stuff.
And the code can work for any sprite sheet that be organized in a horizontal way. You should always aim for making your solution broader unless you require something extremely specific you'll never use for anything else. It's always better to write one semi-universal animator than an animator for each sprite sheet you use.
While were at it, I do not understand why the text kind of "blinks". I added
Code: [Select]
window.draw(text);
in the event of where the mouse button is pressed. This should render the text only, I guess.
If I understand well, what you want to do is to render only the text when you click and nothing else. In that case you need to make the draw calls as a response of when the mouse isn't being clicked, in other words, the false part of the mouse event polling.
///The event loop.
/**The conditional of your mouse and all it does. Draws only the text*/
else
{ /*draws everything.*/ }
So in this particular case all your draw calls should be based on events (or lack of them) and no draw call should be outside of your event polling I guess.