Voilà ma classe d'animation utilisé avec SFML. Elle a pour but premier d'être flexible et facile à adapté à notre code source. Par contre, elle utilise la classe PausableClock et la classe dérivée LayerAnim utilise la classe Layer

Anim

Cette classe est la base de l'animation, elle gère le changement de frame ainsi que la gestion du temps (pause, arrêt et jouer). Son changement de frame est codé de manière que si il y a un temps plus grand que le temps normal entre 2 frames, il passe le nombre de frame. Par exemple, si j'ai 0.5 sec entre chaque frame et que j'attends 2 sec avant de l'afficher, ce sera le 5e frame qui sera affiché et non le 2e. Attention, la méthode update() met le frame selon le temps, ainsi si vous appeler la méthode nextFrame() et que ensuite vous appelez update(), vous n'aurez plus le même frame si ce n'est pas lui qui est supposé être affiché selon le temps. Pour une telle utilisation vous devrez mettre l'animation en pause afin d'afficher les frames que vous voulez. Cette classe est virtuel, vous devrez soit la dériver ou utilisé une des classes qui suit.

Anim.h

#ifndef ANIM_H
#define ANIM_H
 
#include <SFML/System.hpp>
#include <core/PausableClock.h>
 
/*!
*	\class Anim
*	Implémente la gestion des animation selon le temps.
*/
class Anim
{
public:
	Anim(void);
	virtual ~Anim(void);
 
	//!Change au prochain frame d'animation
	void nextFrame();
 
	//!Change au frame d'animation défini par 'count'
	virtual void setFrame(const unsigned int &count);
 
	//!Change au premier frame animation
	void reset();
 
	//!Retourne le frame d'animation courante
	unsigned int currentFrame() const;
 
 
	//!Définis si l'animation est en boucle (choice=true)
	void loop(const bool &choice);
 
	//!Retourne si l'animation est joué en boucle
	bool isLoop() const;
 
 
	//!Joue l'animation
	void play();
 
	//!Arrete l'animation et remet le compteur à zéro
	void stop();
 
	//!Met l'animation en pause et laisse le compteur où il en est.
	void pause();
 
	//!Retourne true si l'animation joue
	bool isPlaying() const;
 
	//!Définis le délais en seconde entre chaque frame.
	void setDelay(const float &delay);
 
	//!Retourne le délai entre chaque frame
	float delay() const;
 
	//!Met à jour l'animation
	virtual void update();
 
	//!Retourne le nombre de frame de l'animation
	virtual unsigned int getSize() const=0;
protected:
	//!Timer de l'animation
	core::PausableClock m_time;
 
private:
	//!Frame courant de l'animation
	unsigned int m_frameCount;
	//!Delai en seconde entre chaque animation
	float m_delay;
	//!Si l'animation est en boucle
	bool m_isLoop;
	//!Si l'animation est en train de jouer
	bool m_play;
};
 
#endif

Anim.cpp

#include "Anim.h"
 
Anim::Anim(void) : m_time()
{
	m_frameCount=0;
	m_delay=0.f;
	m_isLoop=true;
	m_play=true;
}
 
Anim::~Anim(void)
{
}
 
void Anim::nextFrame()
{
	if(currentFrame()==getSize()-1)
	{
			setFrame(0);
			if(!isLoop())
				stop();
	}
	else
		setFrame(currentFrame()+1);
}
 
void Anim::setFrame(const unsigned int &count)
{
	if(count<getSize())
		m_frameCount=count;
	else
		m_frameCount=0;
}
 
void Anim::reset()
{
	stop();
	play();
}
 
void Anim::loop(const bool &choice)
{
	m_isLoop=choice;
}
 
void Anim::play()
{
	m_play = true;
	m_time.Play();
}
 
void Anim::stop()
{
	Anim::m_play = false;
	setFrame(0);
	m_time.Stop();
}
 
void Anim::pause()
{
	Anim::m_play = false;
	m_time.Pause();
}
 
bool Anim::isPlaying() const
{
	return m_play;
}
 
void Anim::setDelay(const float &delay)
{
	m_delay=delay;
}
 
float Anim::delay() const
{
	return m_delay;
}
 
unsigned int Anim::currentFrame() const
{
	return m_frameCount;
}
 
bool Anim::isLoop() const
{
	return m_isLoop;
}
 
void Anim::update()
{
	if(isPlaying())
	{
		if(delay())
		{
			unsigned int frameCount = (unsigned int)(m_time.GetElapsedTime()/delay());
			if(!isLoop() && frameCount>getSize())
				stop();
			else
			{
				frameCount = frameCount % getSize();
				setFrame(frameCount);
			}
		}
		else nextFrame();
	}
}

LayerAnim

Voilà un exemple d'utilisation de Anim en utilisant les fonctionnalité de Layer où chaque couche est un frame (anim[0] est le premier frame, etc.).

LayerAnim.h

#ifndef LAYERANIM_H
#define LAYERANIM_H
 
#include "Anim.h"
#include "Layer.h"
 
/*
*	Animation où chaque frame est repésenter par une couche de Layer.
*/
class LayerAnim :
	public Anim, public Layer
{
public:
	LayerAnim(void);
 
	//!Retourne le nombre de frame de l'animation
	virtual unsigned int getSize() const;
 
protected:
	virtual void Render(sf::RenderTarget& Target) const;
};
 
#endif

LayerAnim.cpp

#include "LayerAnim.h"
 
LayerAnim::LayerAnim(void)
{
}
 
 
unsigned int LayerAnim::getSize() const
{
	return size();
}
 
void LayerAnim::Render(sf::RenderTarget& Target) const
{
	Anim* th = const_cast<LayerAnim*>(this);
	th->update();
	Target.Draw(*at(currentFrame()));
}

Voilà un court exemple d'utilisation:

int main()
{
	LayerAnim anim;
	anim.setDelay(1.0f);
 
	sf::Sprite frame1;
	sf::Sprite frame2;
	sf::Sprite frame3;
 
	anim.push_back(frame1);
	anim.push_back(frame2);
	anim.push_back(frame3);
 
	while(1)
	{
		...
		app.Draw(anim);
	}
}

ImgAnim

Cette classe d'animation utilise un image avec tout les frames et la découpe automatiquement.

ImgAnim.h

#ifndef IMGANIM_H
#define IMGANIM_H
 
#include <SFML/Graphics.hpp>
#include "Anim.h"
 
/*!
*	\class ImgAnim
*	Comme Anim à la diférence près que la classe utilise une image avec tout les sprites
*	au lieu des couches d'une layer.
*/
class ImgAnim :
	public Anim, public sf::Sprite
{
public:
	/*
	*	\param img Image avec les sprites
	*	\param rows Nombre de frame par animation
	*	\param line Nombre de ligne d'animation
	*	\param leReste Voir la documentation de sf::Sprite
	*/
	ImgAnim(const sf::Image &Img, const unsigned int &nbFrame, const unsigned int &line=1,
			const sf::Vector2f &Position=sf::Vector2f(0, 0),
			const sf::Vector2f &Scale=sf::Vector2f(1, 1), float Rotation=0.f,
			const sf::Color &Col=sf::Color(255, 255, 255, 255));
 
 
	//!Définis les dimension d'un frame
	void setFrameDim(const unsigned int &w, const unsigned int &h);
 
	//!Retourne la dimension d'un frame
	sf::IntRect frameDim() const;
 
	//!Définis le décalage de l'animation sur l'image (permet d'avoir plusieurs anim sur une seule image)
	void setOffset(const unsigned int &x, const unsigned int &y);
 
	//!Retourne un rectangle avec l'offset
	sf::IntRect offset() const;
 
	//!Définis la ligne animé courante
	void setAnimRow(const unsigned int &row);
 
	//!Retourne la ligne animé courante
	int animRow() const;
 
	//!Définis le frame courant
	virtual void setFrame(const unsigned int &count);
 
	//!Met à jour le rectangle d'animation
	void refreshSubRect();
 
	//!Définis le nombre de frame dans l'animation
	void setSize(const unsigned int &size);
 
	//!Retourne le nombre de frame
	virtual unsigned int getSize() const;
protected:
	virtual void Render(sf::RenderTarget& Target) const;
 
private:
	//!Représente le nombre de frame dans l'animation
	unsigned int m_size;
	//!Ligne d'animation courante
	unsigned int m_animRow;
	//!Décalage à gauche du frameset
	unsigned int m_xOffset;
	//!Décalage en haut du frameset
	unsigned int m_yOffset;
};
 
#endif

ImgAnim.cpp

#include "ImgAnim.h"
 
ImgAnim::ImgAnim(const sf::Image &Img, const unsigned int &nbFrame, const unsigned int &line,
			const sf::Vector2f &Position, const sf::Vector2f &Scale,float Rotation, const sf::Color &Col)
: sf::Sprite(Img,Position,Scale,Rotation,Col)
{
	m_animRow=0;
	//Le constructeur par défaut prend en compte qu'il n'y a aucun offset
	SetSubRect(sf::IntRect(0,0,Img.GetWidth()/nbFrame,Img.GetHeight()/line));
	m_xOffset=0;
	m_yOffset=0;
	m_size=nbFrame;
}
 
 
void ImgAnim::setAnimRow(const unsigned int &row)
{
	m_animRow=row;
	refreshSubRect();
}
 
int ImgAnim::animRow() const
{
	return m_animRow;
}
 
void ImgAnim::setFrameDim(const unsigned int &w, const unsigned int &h)
{
	sf::IntRect tRect = GetSubRect();
	SetSubRect(sf::IntRect(tRect.Left,tRect.Top,tRect.Left+w,tRect.Top+h));
}
 
sf::IntRect ImgAnim::frameDim() const
{
	sf::IntRect tRect = GetSubRect();
	return sf::IntRect(0,0,tRect.GetWidth(),tRect.GetHeight());
}
 
void ImgAnim::setOffset(const unsigned int &x, const unsigned int &y)
{
	m_xOffset=x;
	m_yOffset=y;
 
	refreshSubRect();
}
 
sf::IntRect ImgAnim::offset() const
{
	return sf::IntRect(0,0,m_xOffset,m_yOffset);
}
 
int unsigned ImgAnim::getSize() const
{
	return m_size;
}
 
void ImgAnim::setSize(const unsigned int &size)
{
	m_size=size;
}
 
 
void ImgAnim::setFrame(const unsigned int &count)
{
	Anim::setFrame(count);
	refreshSubRect();
}
 
void ImgAnim::refreshSubRect()
{
	sf::IntRect tRect = GetSubRect();
	SetSubRect(sf::IntRect(tRect.GetWidth()*currentFrame()+m_xOffset,
							tRect.GetHeight()*m_animRow+m_yOffset,
							tRect.GetWidth()*(currentFrame()+1)+m_xOffset,
							tRect.GetHeight()*m_animRow+tRect.GetHeight()+m_yOffset));
}
 
 
void ImgAnim::Render(sf::RenderTarget& Target) const
{
	ImgAnim* th = const_cast<ImgAnim*>(this);
	th->update();
	sf::Sprite::Render(Target);
}

Voilà un exemple d'utilisation avec un spriteset comme celui-ci:

        //Charge l'image de zelda
	sf::Image imgZelda;
	imgZelda.LoadFromFile("zelda_sprite.png");
	imgZelda.SetSmooth(false);
	imgZelda.CreateMaskFromColor(sf::Color(255,0,255));
 
	//Créer 4 animation de 8 frame chaque à partir de l'image imgZelda
	ImgAnim zelda(imgZelda,8,4);
	while(1)
	{
		const sf::Input& Input = App.GetInput();
		if(Input.IsKeyDown(sf::Key::Left) )
		{
			zelda.Move(-SPEED,0.f);
			zelda.setAnimRow(3);
			zelda.play();
		}
 
		if(Input.IsKeyDown(sf::Key::Right) )
		{			
			zelda.Move(SPEED,0.f);
			zelda.setAnimRow(1);
			zelda.play();
		}
		if(Input.IsKeyDown(sf::Key::Up))
		{
			zelda.Move(0.f,-SPEED);
			zelda.setAnimRow(2);
			zelda.play();
		}
		if(Input.IsKeyDown(sf::Key::Down))
		{
			zelda.Move(0.f,SPEED);
			zelda.setAnimRow(0);
			zelda.play();
		}
		if(!Input.IsKeyDown(sf::Key::Down) &&
			!Input.IsKeyDown(sf::Key::Up) &&
			!Input.IsKeyDown(sf::Key::Right) &&
			!Input.IsKeyDown(sf::Key::Left))
		{
			zelda.stop();
		}
		App.Draw(zelda);
	}

Si votre image contient plusieurs frameset, le 2e et 3e arguments du constructeur sont inutile puisque qu'ils ne tiennent pas compte d'un quelconque offset, laissez leur valeur à 1. Pour bien procédé, commencer par donner la position de votre frameset sur l'image avec setOffset(). Ensuite, donnez la dimension de vos frames avec setFrameDim(). Finalement, donnez le nombre de frame par animation avec setSize().

 
fr/sources/animation.txt · Last modified: 2009/01/21 21:28 by isra17
 
Recent changes RSS feed Creative Commons License Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki