Source: AnimatedSprite
This is a class for SFML 2 that provides an easy interface to animate Sprites. The design closely follows the design of the sf::Sprite
class.
Both classes are written by Foaly. They are under the zlib/libpng license (the same that SFML uses). I'd love to hear about your project on the forum and it would be nice if you name me in the credits :)
The usage is quite simple. First you create an Animation
object. The Animation
class is essentially a std::vector
of texture rectangles (sf::IntRect
) and a sf::Texture
reference. So you have to provide a spritesheet and push back your texture reactangles. Then you create a AnimatedSprite
object and provide it with an Animation
. The AnimatedSprite
class inherits from both sf::Drawable
and sf::Transfomable
, so it behaves much like a regular sprite. The only thing you have to do is to call the update(sf::Time deltaTime)
method on every iteration with the time passed since the last iteration (this also allows you to do effects like slow motion without changing the frame time). Of course the AnimatedSprite
class also provides methods to pause, stop and play the animation.
Note that both classes work with references, so you have to keep the sf::Texture
and the Animation
alive as long as you use AnimatedSprite
.
If you find all of this too confusing, then take a look at the example and play with it.
Here is an example of how to use the classes.
Spritesheet:
#include <SFML/Graphics.hpp>
#include "AnimatedSprite.hpp"
#include <iostream>
int main()
{
// setup window
sf::Vector2i screenDimensions(800,600);
sf::RenderWindow window(sf::VideoMode(screenDimensions.x, screenDimensions.y), "Animations!");
window.setFramerateLimit(60);
// load texture (spritesheet)
sf::Texture texture;
if (!texture.loadFromFile("player.png"))
{
std::cout << "Failed to load player spritesheet!" << std::endl;
return 1;
}
// set up the animations for all four directions (set spritesheet and push frames)
Animation walkingAnimationDown;
walkingAnimationDown.setSpriteSheet(texture);
walkingAnimationDown.addFrame(sf::IntRect(32, 0, 32, 32));
walkingAnimationDown.addFrame(sf::IntRect(64, 0, 32, 32));
walkingAnimationDown.addFrame(sf::IntRect(32, 0, 32, 32));
walkingAnimationDown.addFrame(sf::IntRect( 0, 0, 32, 32));
Animation walkingAnimationLeft;
walkingAnimationLeft.setSpriteSheet(texture);
walkingAnimationLeft.addFrame(sf::IntRect(32, 32, 32, 32));
walkingAnimationLeft.addFrame(sf::IntRect(64, 32, 32, 32));
walkingAnimationLeft.addFrame(sf::IntRect(32, 32, 32, 32));
walkingAnimationLeft.addFrame(sf::IntRect( 0, 32, 32, 32));
Animation walkingAnimationRight;
walkingAnimationRight.setSpriteSheet(texture);
walkingAnimationRight.addFrame(sf::IntRect(32, 64, 32, 32));
walkingAnimationRight.addFrame(sf::IntRect(64, 64, 32, 32));
walkingAnimationRight.addFrame(sf::IntRect(32, 64, 32, 32));
walkingAnimationRight.addFrame(sf::IntRect( 0, 64, 32, 32));
Animation walkingAnimationUp;
walkingAnimationUp.setSpriteSheet(texture);
walkingAnimationUp.addFrame(sf::IntRect(32, 96, 32, 32));
walkingAnimationUp.addFrame(sf::IntRect(64, 96, 32, 32));
walkingAnimationUp.addFrame(sf::IntRect(32, 96, 32, 32));
walkingAnimationUp.addFrame(sf::IntRect( 0, 96, 32, 32));
Animation* currentAnimation = &walkingAnimationDown;
// set up AnimatedSprite
AnimatedSprite animatedSprite(sf::seconds(0.2), true, false);
animatedSprite.setPosition(sf::Vector2f(screenDimensions / 2));
sf::Clock frameClock;
float speed = 80.f;
bool noKeyWasPressed = true;
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Escape)
window.close();
}
sf::Time frameTime = frameClock.restart();
// if a key was pressed set the correct animation and move correctly
sf::Vector2f movement(0.f, 0.f);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
{
currentAnimation = &walkingAnimationUp;
movement.y -= speed;
noKeyWasPressed = false;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
{
currentAnimation = &walkingAnimationDown;
movement.y += speed;
noKeyWasPressed = false;
}
if(sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
{
currentAnimation = &walkingAnimationLeft;
movement.x -= speed;
noKeyWasPressed = false;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right))
{
currentAnimation = &walkingAnimationRight;
movement.x += speed;
noKeyWasPressed = false;
}
animatedSprite.play(*currentAnimation);
animatedSprite.move(movement * frameTime.asSeconds());
// if no key was pressed stop the animation
if (noKeyWasPressed)
{
animatedSprite.stop();
}
noKeyWasPressed = true;
// update AnimatedSprite
animatedSprite.update(frameTime);
// draw
window.clear();
window.draw(animatedSprite);
window.display();
}
return 0;
}
////////////////////////////////////////////////////////////
//
// Copyright (C) 2014 Maximilian Wagenbach (aka. Foaly) (foaly.f@web.de)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#ifndef ANIMATION_INCLUDE
#define ANIMATION_INCLUDE
#include <vector>
#include <SFML/Graphics/Rect.hpp>
#include <SFML/Graphics/Texture.hpp>
class Animation
{
public:
Animation();
void addFrame(sf::IntRect rect);
void setSpriteSheet(const sf::Texture& texture);
const sf::Texture* getSpriteSheet() const;
std::size_t getSize() const;
const sf::IntRect& getFrame(std::size_t n) const;
private:
std::vector<sf::IntRect> m_frames;
const sf::Texture* m_texture;
};
#endif // ANIMATION_INCLUDE
////////////////////////////////////////////////////////////
//
// Copyright (C) 2014 Maximilian Wagenbach (aka. Foaly) (foaly.f@web.de)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#include "Animation.hpp"
Animation::Animation() : m_texture(NULL)
{
}
void Animation::addFrame(sf::IntRect rect)
{
m_frames.push_back(rect);
}
void Animation::setSpriteSheet(const sf::Texture& texture)
{
m_texture = &texture;
}
const sf::Texture* Animation::getSpriteSheet() const
{
return m_texture;
}
std::size_t Animation::getSize() const
{
return m_frames.size();
}
const sf::IntRect& Animation::getFrame(std::size_t n) const
{
return m_frames[n];
}
////////////////////////////////////////////////////////////
//
// Copyright (C) 2014 Maximilian Wagenbach (aka. Foaly) (foaly.f@web.de)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#ifndef ANIMATEDSPRITE_INCLUDE
#define ANIMATEDSPRITE_INCLUDE
#include <SFML/Graphics/RenderTarget.hpp>
#include <SFML/System/Time.hpp>
#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Graphics/Transformable.hpp>
#include <SFML/System/Vector2.hpp>
#include "Animation.hpp"
class AnimatedSprite : public sf::Drawable, public sf::Transformable
{
public:
explicit AnimatedSprite(sf::Time frameTime = sf::seconds(0.2f), bool paused = false, bool looped = true);
void update(sf::Time deltaTime);
void setAnimation(const Animation& animation);
void setFrameTime(sf::Time time);
void play();
void play(const Animation& animation);
void pause();
void stop();
void setLooped(bool looped);
void setColor(const sf::Color& color);
const Animation* getAnimation() const;
sf::FloatRect getLocalBounds() const;
sf::FloatRect getGlobalBounds() const;
bool isLooped() const;
bool isPlaying() const;
sf::Time getFrameTime() const;
void setFrame(std::size_t newFrame, bool resetTime = true);
private:
const Animation* m_animation;
sf::Time m_frameTime;
sf::Time m_currentTime;
std::size_t m_currentFrame;
bool m_isPaused;
bool m_isLooped;
const sf::Texture* m_texture;
sf::Vertex m_vertices[4];
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const;
};
#endif // ANIMATEDSPRITE_INCLUDE
////////////////////////////////////////////////////////////
//
// Copyright (C) 2014 Maximilian Wagenbach (aka. Foaly) (foaly.f@web.de)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
// you must not claim that you wrote the original software.
// If you use this software in a product, an acknowledgment
// in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
// and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
////////////////////////////////////////////////////////////
#include "AnimatedSprite.hpp"
AnimatedSprite::AnimatedSprite(sf::Time frameTime, bool paused, bool looped) :
m_animation(NULL), m_frameTime(frameTime), m_currentFrame(0), m_isPaused(paused), m_isLooped(looped), m_texture(NULL)
{
}
void AnimatedSprite::setAnimation(const Animation& animation)
{
m_animation = &animation;
m_texture = m_animation->getSpriteSheet();
m_currentFrame = 0;
setFrame(m_currentFrame);
}
void AnimatedSprite::setFrameTime(sf::Time time)
{
m_frameTime = time;
}
void AnimatedSprite::play()
{
m_isPaused = false;
}
void AnimatedSprite::play(const Animation& animation)
{
if (getAnimation() != &animation)
setAnimation(animation);
play();
}
void AnimatedSprite::pause()
{
m_isPaused = true;
}
void AnimatedSprite::stop()
{
m_isPaused = true;
m_currentFrame = 0;
setFrame(m_currentFrame);
}
void AnimatedSprite::setLooped(bool looped)
{
m_isLooped = looped;
}
void AnimatedSprite::setColor(const sf::Color& color)
{
// Update the vertices' color
m_vertices[0].color = color;
m_vertices[1].color = color;
m_vertices[2].color = color;
m_vertices[3].color = color;
}
const Animation* AnimatedSprite::getAnimation() const
{
return m_animation;
}
sf::FloatRect AnimatedSprite::getLocalBounds() const
{
sf::IntRect rect = m_animation->getFrame(m_currentFrame);
float width = static_cast<float>(std::abs(rect.width));
float height = static_cast<float>(std::abs(rect.height));
return sf::FloatRect(0.f, 0.f, width, height);
}
sf::FloatRect AnimatedSprite::getGlobalBounds() const
{
return getTransform().transformRect(getLocalBounds());
}
bool AnimatedSprite::isLooped() const
{
return m_isLooped;
}
bool AnimatedSprite::isPlaying() const
{
return !m_isPaused;
}
sf::Time AnimatedSprite::getFrameTime() const
{
return m_frameTime;
}
void AnimatedSprite::setFrame(std::size_t newFrame, bool resetTime)
{
if (m_animation)
{
//calculate new vertex positions and texture coordiantes
sf::IntRect rect = m_animation->getFrame(newFrame);
m_vertices[0].position = sf::Vector2f(0.f, 0.f);
m_vertices[1].position = sf::Vector2f(0.f, static_cast<float>(rect.height));
m_vertices[2].position = sf::Vector2f(static_cast<float>(rect.width), static_cast<float>(rect.height));
m_vertices[3].position = sf::Vector2f(static_cast<float>(rect.width), 0.f);
float left = static_cast<float>(rect.left) + 0.0001f;
float right = left + static_cast<float>(rect.width);
float top = static_cast<float>(rect.top);
float bottom = top + static_cast<float>(rect.height);
m_vertices[0].texCoords = sf::Vector2f(left, top);
m_vertices[1].texCoords = sf::Vector2f(left, bottom);
m_vertices[2].texCoords = sf::Vector2f(right, bottom);
m_vertices[3].texCoords = sf::Vector2f(right, top);
}
if (resetTime)
m_currentTime = sf::Time::Zero;
}
void AnimatedSprite::update(sf::Time deltaTime)
{
// if not paused and we have a valid animation
if (!m_isPaused && m_animation)
{
// add delta time
m_currentTime += deltaTime;
// if current time is bigger then the frame time advance one frame
if (m_currentTime >= m_frameTime)
{
// reset time, but keep the remainder
m_currentTime = sf::microseconds(m_currentTime.asMicroseconds() % m_frameTime.asMicroseconds());
// get next Frame index
if (m_currentFrame + 1 < m_animation->getSize())
m_currentFrame++;
else
{
// animation has ended
if (!m_isLooped)
{
m_isPaused = true;
}
else
{
m_currentFrame = 0; // reset to start
}
}
// set the current frame, not reseting the time
setFrame(m_currentFrame, false);
}
}
}
void AnimatedSprite::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
if (m_animation && m_texture)
{
states.transform *= getTransform();
states.texture = m_texture;
target.draw(m_vertices, 4, sf::Quads, states);
}
}