Une classe TileSet vous permettant de rapidement créer votre map à partir d'images ou de cases de couleur.
Nous réutiliserons la classe 'Frame' : Frame
Le code:
#ifndef ZIGO_TILESET #define ZIGO_TILESET #include <map> #include "Frame.hpp" class TileSet : public sf::Drawable { public: // Par defaut TileSet(float NewTileWidth = 0, float NewTileHeight = 0); // Par copie TileSet(const TileSet& Cpy); // destructeur virtual ~TileSet(); // Largeur des Tiles void SetTileWidth(float NewTileWidth); // Hauteur des Tiles void SetTileHeight(float NewTileHeight); // Largeur des Tiles float GetTileWidth() const; // Hauteur des Tiles float GetTileHeight() const; // Taille des Tiles void SetTileSize(float NewTileWidth, float NewTileHeight); // Taille des Tiles void SetTileSize(const sf::Vector2f& TileSize); // Taille des Tiles sf::Vector2f GetTileSize() const; // Affichage virtual void Render(const sf::RenderWindow& Window) const; // retoune l'abcisse tableau int ScreenXToTileX(float ScreenX); // retourne l'ordonnée tableau int ScreenYToTileY(float ScreenY); // Accès au Type de la case X, Y int& operator () (int X, int Y); // Accès au Type de la case X, Y : constant int operator () (int X, int Y) const; // retourne la Frame de type 'Key' const Frame& GetType(int Key); // enregistre une nouvelle Frame pour le type 'Key' void SetType(int Key, const Frame& NewType); // Nombre de Tiles dans la map size_t Size() const; // Nombre de types différents size_t TypeNb() const; // Plus tard, nous pourrons ajouter différentes fonctions liées au fait qu'une 'Anim' est une ressource // Par exemple : LoadFromFile, SaveToFile etc... private: float TileWidth; float TileHeight; std::map<int, std::map<int, int> > myTile; std::map<int, Frame> myType; }; #endif
#include "TileSet.hpp" // Par defaut TileSet::TileSet(float NewTileWidth, float NewTileHeight) { TileWidth = NewTileWidth; TileHeight = NewTileHeight; } // Par copie TileSet::TileSet(const TileSet& Cpy): sf::Drawable(Cpy) { TileWidth = Cpy.TileWidth; TileHeight = Cpy.TileHeight; myType = Cpy.myType; myTile = Cpy.myTile; } // destructeur TileSet::~TileSet() { } // Largeur des Tiles void TileSet::SetTileWidth(float NewTileWidth) { TileWidth = NewTileWidth; } // Hauteur des Tiles void TileSet::SetTileHeight(float NewTileHeight) { TileHeight = NewTileHeight; } // Largeur des Tiles float TileSet::GetTileWidth() const { return TileWidth; } // Hauteur des Tiles float TileSet::GetTileHeight() const { return TileHeight; } // Taille des Tiles void TileSet::SetTileSize(float NewTileWidth, float NewTileHeight) { TileWidth = NewTileWidth; TileHeight = NewTileHeight; } // Taille des Tiles void TileSet::SetTileSize(const sf::Vector2f& TileSize) { TileWidth = TileSize.x; TileHeight = TileSize.y; } // Taille des Tiles sf::Vector2f TileSet::GetTileSize() const { return sf::Vector2f(TileWidth, TileHeight); } // Affichage void TileSet::Render(const sf::RenderWindow& Window) const { // On parcoure le tableau for (std::map<int, std::map<int, int> >::const_iterator it = myTile.begin(); it != myTile.end(); ++it) { for (std::map<int, int>::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) { std::map<int, Frame>::const_iterator TypeIt = myType.find( it2->second ); // Si le type de tile éxiste if (TypeIt != myType.end()) { // On dessine une tile glMatrixMode(GL_MODELVIEW); glPushMatrix(); glTranslatef(it->first * TileWidth, it2->first * TileHeight, 0); glColor4ub(TypeIt->second.Color.r, TypeIt->second.Color.g, TypeIt->second.Color.b, TypeIt->second.Color.a); // Si on doit y afficher une image if (TypeIt->second.Image != NULL) { sf::Rect<float> Rect = TypeIt->second.Image->GetTexCoords(TypeIt->second.Rect); TypeIt->second.Image->Bind(); glBegin(GL_QUADS); glTexCoord2f(Rect.Left, Rect.Top); glVertex2f(0, 0); glTexCoord2f(Rect.Left, Rect.Bottom); glVertex2f(0, TileHeight); glTexCoord2f(Rect.Right, Rect.Bottom); glVertex2f(TileWidth, TileHeight); glTexCoord2f(Rect.Right, Rect.Top); glVertex2f(TileWidth, 0); glEnd(); } // Sinon else { // Disable texturing glDisable(GL_TEXTURE_2D); // Draw the colored rectangle glBegin(GL_QUADS); glVertex2f(0, 0); glVertex2f(0, TileHeight); glVertex2f(TileWidth, TileHeight); glVertex2f(TileWidth, 0); glEnd(); } glMatrixMode(GL_MODELVIEW); glPopMatrix(); } } } } // retoune l'abscisse tableau int TileSet::ScreenXToTileX(float ScreenX) { return static_cast<int>(ScreenX/TileWidth); } // retourne l'ordonnée tableau int TileSet::ScreenYToTileY(float ScreenY) { return static_cast<int>(ScreenY/TileHeight); } // Accès au Type de la case X, Y int& TileSet::operator () (int X, int Y) { return myTile[X][Y]; } // Accès au Type de la case X, Y : constant int TileSet::operator () (int X, int Y) const { std::map<int, std::map<int, int> >::const_iterator It = myTile.find( X ); if (It != myTile.end()) { std::map<int, int>::const_iterator It2 = It->second.find( Y ); if (It2 != It->second.end()) return It2->second; else return 0; } else return 0; } // retourne la Frame de type 'Key' const Frame& TileSet::GetType(int Key) { return myType[Key]; } // Enregistre une nouvelle Frame pour le type 'Key' void TileSet::SetType(int Key, const Frame& NewType) { myType[Key] = NewType; } // Nombre de Tiles dans la map size_t TileSet::Size() const { return myTile.size(); } // Nombre de types différents size_t TileSet::TypeNb() const { return myType.size(); } // Plus tard, nous pourrons ajouter différentes fonctions liées au fait qu'une 'Anim' est une ressource // Par exemple : LoadFromFile, SaveToFile etc...
Un TileSet est un tableau à 2 dimensions, stockant le type de tile pour une coordonnée X,Y. Avant de créer votre map, enregistrez d'abord les différents types de tiles qui devront être affichés. Pour cela rien de plus simple : myTileSet.SetType( TYPE , Frame(&myImage, mySubRect, myColor) ); Après cela, définissez vos cases une a une tout aussi simplement : myTileSet( X , Y ) = TYPE;
On utilise cette image (désolé de la mocheté de la chose, c'est juste pour l'exemple!):
Le code:
#include "TileSet.hpp" int main() { sf::RenderWindow Window(sf::VideoMode(800, 600, 32), "Test"); // On charge les texture de la map sf::Image MapImage; MapImage.LoadFromFile("map.png"); // On créé un TileSet TileSet myTileSet; // On définit les type de Tile myTileSet.SetType(0, Frame(&MapImage, sf::Rect<int>(0, 0, 64, 64))); // Plaine myTileSet.SetType(1, Frame(&MapImage, sf::Rect<int>(0, 64,64, 128))); // Roche myTileSet.SetType(2, Frame(&MapImage, sf::Rect<int>(64, 64, 128, 128))); // Neige-glace myTileSet.SetType(3, Frame(&MapImage, sf::Rect<int>(64, 0,128, 64))); // Eau Frame TestColor; TestColor.Color = sf::Color::Red; // Une frame sans image mais de couleur rouge myTileSet.SetType(4, TestColor); // On définit la taille des Tiles myTileSet.SetTileSize(64, 64); // On remplit le tableau de 'plaine' for (int y = 0; y < 5; y++) for (int x = 0; x < 5; x++) myTileSet(x, y) = 0; // On décor la map... // Un peit lac myTileSet(0, 0) = 3; myTileSet(0, 1) = 3; myTileSet(1, 1) = 3; myTileSet(1, 0) = 3; // Un rocher myTileSet(1, 2) = 1; // de la neige myTileSet(2, 2) = 2; myTileSet(2, 3) = 2; myTileSet(3, 3) = 2; myTileSet(3, 2) = 2; // Une case rouge! myTileSet(4, 4) = 4; // La boucle habituelle sf::Event Event; bool isRunning = true; while (isRunning) { while (Window.GetEvent(Event)) { switch(Event.Type) { case sf::Event::Closed: isRunning = false; break; case sf::Event::KeyReleased: switch(Event.Key.Code) { case sf::Key::Escape: isRunning = false; break; } break; } } Window.Draw(myTileSet); Window.Display(); } return EXIT_SUCCESS; }
A l'avenir, on pourra intégrer des fonctions de lecture/écriture sur le disque pour sauvegarder la map.
Je tiens simplement à mettre en garde que ce système n'est pas optimisé. Voir mon commentaire ici.