Bonjour !
Alors voilà, je viens de faire ma propre classe Anim, pour gérer les animations à l'écran. Il n'y a pour l'instant aucune fonction de pause ou même de déplacement. Considérez donc que ce code marche pour quelque chose comme une fontaine. Dans les grandes lignes, on va demander à la classe de faire un vector de textures ( chaque texture contiendra un fichier png, soit une étape du mouvement ). Ensuite, selon le temps écoulé, on va charger un élément x du vector dans un sprite, sprite qu'on va ensuite dessiner à l'écran. Ma classe est une dérivée de la classe sf::Drawable, du coup je sais pas trop si c'est bien d'utiliser des sprite à l'intérieur de ma classe, sachant que les sprites sont eux mêmes dérivés de la classe sf::Drawable.
Enfi bref, voici la bête :
Anim.cpp :
#include "Anim.h"
#include <SFML/Graphics.hpp>
/* À chaque appel de cette fonction, on charge un nouveau fichier dans m_texture, puis on place une copie de m_texture dans un nouvel
élément de Tableau. i compte le nombre de fois que l'on répète cette action, soit le nombre d'éléments de Tableau */
void Anim::addFrame(std::string chemin)
{
sf::Texture m_texture;
m_texture.loadFromFile(chemin);
Tableau.push_back(m_texture);
i++;
}
/* Simple réutilisation de la fonction setPosition de la classe sf::Sprite */
void Anim::setPosition(int x, int y)
{
m_sprite.setPosition(x, y);
}
/* À chaque passage de la boucle while, on regarde le rapport suivant : temps écoulé/temps d'affichage de chaque image. Ce rapport,
tronqué à la première décimale grace à une pseudo conversion en entier, nous permet de nous déplacer dans Tableau. Chaque texture,
chaque élément du tableau est successivement chargé dans m_sprite, m_sprite étant ensuite affiché à l'écran par la fonction
virtuelle draw, présente dans le header de cette classe.
On remarque un décalage de 1 : juste après le lancement de clock, le rapport, encore inférieur à 1, est tronqué à sa première décimale
et vaut donc 0. Or, la première case de Tableau n'est pas la case [0] mais la case [1] puisque la première fois qu'on a modifié le
Tableau c'était après un push_back. L'erreur est ensuite corrigée dans la fonction setTexture, par a-1. Notons enfin le clock.restart() :
la fonction marche en boucle, il est donc normal de réinitialiser l'horloge une fois que l'on a dépassé le temps total d'affichage d'une
boucle */
void Anim::update()
{
int a=((clock.getElapsedTime().asSeconds())/framerate)+1;
if (a>i)
{
clock.restart();
}
else if (a<=i)
{
m_sprite.setTexture(Tableau[a-1]);
}
}
/* x est le temps d'affichage de chaque frame, framerate représente donc le nombre de frame par seconde */
void Anim::setFrameRate(float x)
{
x=1/x;
framerate=x;
}
Anim.h :
#ifndef Anim_h
#define Anim_h
#include <SFML/Graphics.hpp>
class Anim : public sf::Drawable
{
public:
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const
{
target.draw(m_sprite, states);
}
void addFrame(std::string chemin);
void setPosition(int x, int y);
void update();
void setFrameRate(float x);
private:
std::vector<sf::Texture>Tableau;
sf::Sprite m_sprite;
sf::Clock clock;
unsigned int i;
float framerate;
};
#endif
main.cpp :
#include <SFML/Graphics.hpp>
#include "Anim.h"
int main()
Anim fontaine;
fontaine.addFrame("/Users/blabla/0.png");
fontaine.addFrame("/Users/blabla/1.png");
fontaine.addFrame("/Users/blabla/2.png");
fontaine.addFrame("/Users/blabla/3.png");
fontaine.addFrame("/Users/blabla/4.png");
fontaine.setFrameRate(20);
fontaine.setPosition(300, 200);
sf::RenderWindow window(sf::VideoMode(800, 600), "Fenetre");
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
{
window.close();
}
}
window.clear();
fontaine.update();
window.draw(fontaine);
window.display();
}
return 0;
}
Voilà voilà… Et donc je me demandais si vous aviez des suggestions d'améliorations à faire, enfin ma classe marche plutôt bien, mais je suis ouvert aux critiques donc je viens vous demander votre avis !
Mr Pchoun.
Ben ça pour le coup je vois pas trop ce que tu me reproches, toutes mes variables sont en minuscules, et le nom des fonctions commence par une minuscule puis les autres mots par des majuscules.
Rien de te choque dans le nommage de ces trois membres ?
Tableau
m_sprite
clock
Je suis pas trop pour : imaginons que j'ai besoin d'afficher une grosse animation à l'écran, une animation qui prenne quelque chose du genre 900x900, et que j'ai besoin de 50 étapes de mouvement par exemple. Tu me conseilles de faire une feuille de sprite si j'ai bien compris, mais j'avais vu sur un autre sujet que le chargement d'image est limité par un poids limite, fixé par la carte graphique. Si ma feuille de sprite a des dimensions trop grandes, j'ai peur qu'elle soit trop lourde que la carte graphique ( pas forcément la mienne, mais celle des éventuels autres utilisateurs ) n'accepte pas. Tu peux me dire ce que tu en penses ?
C'est un bon argument. Ceci-dit généralement on anime des "petites" choses, pas des entités de 900x900. La plupart des classes d'animation fonctionnent comme ça, et je n'ai encore jamais vu personne s'en plaindre ;)
C'est à dire ? Il faudrait que je déclare un sf::Clock dans mon main, et que je l'envoie en argument de ma classe, lorsque j'initialise ma fontaine ? Ça me parait pas très propre.. À moins que ça m'évite, ensuite, d'avoir à écrire un fontaine.update() à la fin ? Parce que justement, ce update me parait pas très propre non plus
Dans une application complète, le code de plus haut niveau va forcément gérer le temps, il n'y a pas que ton animation qui a besoin du temps. En fait toute la logique de l'application dépend en théorie du temps, et si chaque classe s'amuse à gérer sa propre horloge, ça ce n'est pas très propre. Imagine que l'utilisateur implémente un algorithme de gestion du timestep, ou bien veuille ralentir/accélérer le temps, ou bien encore mettre en pause le jeu. Dans tous ces cas ta classe ne marche pas.
Quant à l'update, où voudrais-tu le mettre ? Typiquement, les classes vont avoir une fonction d'update (pour faire tous les calculs) et de dessin (qui elle est const !). Encore une fois, il faut voir plus loin que simplement ta classe, il faut la concevoir de manière à ce qu'elle puisse s'intégrer sans mal dans un squelette typique d'application.
Faut croire que si.. Je l'ai pas bien expliqué dans le Anim.cpp mais en tous cas, si on enlève ces +1 et -1, un des frame est vide, du coup une de mes images est blanches. C'est pas très joli ! Je vais essayer de retravailler ça.
Bien sûr, on est dans une loop, ce clock.restart() n'est là que pour marquer la fin du séquençage d'un certain nombre d'images, et pas la fin du séquençage de toutes les images chargées .
Que dis-tu de ça (pas testé) :
void Anim::update()
{
int frame = int(clock.getElapsedTime().asSeconds() / framerate) % Tableau.size();
m_sprite.setTexture(Tableau[frame]);
}
Ca corrige et améliore au passage d'autres trucs que je n'avais pas noté la première fois.
Ca va commencer à merder au bout de quelques heures car on ne redémarre jamais l'horloge, mais c'était plus joli comme ça :D