Now we have some base code on which we can build our engine. Since I'm sure you're eager to get something drawing on the screen, let's make that the objective for this tutorial.
By the way, if you haven't read the parts of the tutorial preceding this, I suggest you do that here
SFML Graphics
We already saw a couple of features from the SFML api in the last tutorial, namely sf::Renderwindow. In this tutorial, we'll be looking at the basic graphics api available to us.
The first class we'll need is sf::Image, which is used for loading, manipulating and saving images. We can store images, load images from a file, save images to a file, even manipulate individual pixels in the image all with the Image class. All you need to do is create an instance of an Image, like so: sf::Image myImage;. Once you've created an instance, you can fill it with information in a number of ways, but the method we'll be using first is loading an image from a file. Doing this is a simple command: myImage.LoadFromFile(filename)
Once you've loaded an image, time to draw right? Not yet! Since Images are large, slow objects, SFML wraps them with a smaller and faster class called sf::Sprite. The Sprite defines how to draw the image. You can define where to draw it, what portion of the image to draw, you can scale it, rotate it, flip it, and more. The best part is that it's fast, because Sprites don't actually contain any of the data. Instead, it stores a reference to an Image, and uses its data to figure out what to draw.
There's a catch however. Since Sprites don't actually contain a copy of the Image, if the Image is destroyed (by leaving its scope for instance), the Sprite won't have anything to draw. We could store a copy of each image for every sprite, but that would be inefficient. However, we need to make sure that any Images we use for sprites remain intact for as long as we need it.
The solution is to have an Image manager class. This class will hold a vector of Images, and we can use it to add or retrieve Images to use with our Sprites. So, let's create a new header file for our class, and call it "ImageManager.h".
#include <vector> #include <SFML\Graphics.hpp> class ImageManager { private: vector<sf::Image> imageList; public: ImageManager(); ~ImageManager(); void AddImage(sf::Image& image); sf::Image& GetImage(int index); } #endif
Not a lot to this class at the moment, but this will suffice for now. We're using a vector to keep all of our images so we can dynamically allocate them, and the class implements two accessors for adding and retrieving Images. It is important to note that we are manipulating the Images by reference. If we were passing the Images around by value, we would be creating copies of Images, and it would be hard to manage them.
Now let's add in the code for this class in a new code file called "ImageManager.cpp".
#include "ImageManager.h" #include <vector> #include <SFML\Graphics.hpp> ImageManager::ImageManager() { } ImageManager::~ImageManager() { } void ImageManager::AddImage(sf::Image& image) { imageList.push_back(image); } sf::Image& ImageManager::GetImage(int index) { return imageList[index]; }
Nothing too complicated.
Setting up the Tile class
Now that we have our ImageManager, we can move on to how we're going to draw them to the screen. Since this is a Top-down 2D tile engine, our "levels" will consist of a 2D grid of tiles. So let's go ahead and create a Tile class in a new header file called "Tile.h".
#ifndef _TILE_H #define _TILE_H #include <SFML\Graphics.hpp> class Tile { private: sf::Sprite baseSprite; public: Tile(sf::Image& image); ~Tile(); void Draw(int x, int y, sf::RenderWindow* rw); } #endif
Remember that we're just laying out a basic skeleton, so we don't need to add all the functionality we can think of right away. So for now, our Tile only has a Sprite. We have our constructor/destructor, and then a Draw method. The Draw method will, you guessed it, draw the tile to the RenderWindow at the specified x and y coordinates. Let's write out the code for the Tile class now in a new code file called "Tile.cpp"
#include "Tile.h" #include <SFML\Graphics.hpp> Tile::Tile(sf::Image& image) { baseSprite.SetImage(image, true); } Tile::~Tile() { } void Tile::Draw(int x, int y, sf::RenderWindow* rw) { baseSprite.SetPosition(x, y); rw->Draw(baseSprite); }
Simple right? In our draw method we simply set the position of the Sprite, and draw it with the Renderwindow. We'll be adding a lot more to this eventually, but this will do for now.
Test out the code
Alright, we're ready to draw some sprites! Let's modify our Engine class by adding the following to "Engine.h"
#include "ImageManager.h" private: ImageManager imageManager; void LoadImages(); Tile* testTile;
We include our new ImageManager class, and add an instance of it to our Engine. We've also added a temporary LoadImages method and a test tile to draw. Now let's add to our "Engine.cpp" file:
void Engine::LoadImages() { sf::Image sprite; sprite.LoadFromFile("sprite1.png"); imageManager.AddImage(sprite); testTile = new Tile(imageManager.GetImage(0)); } void Engine::RenderFrame() { window->Clear(); testTile->Draw(0,0, window); window->Display(); }
And don't forget to add a call to LoadImages(); in the Engine::Init() method.
Conclusion
Compile and run. Eureka! We've finally drawn something to the screen. This may seem like a lot of work just to draw a single tile, but we have laid the groundwork for the next tutorial which is going to be big, so hurry up and head over to the next tutorial.
I've also attached the source for this part so you can have something to add to in the next tutorial.
Attached File(s)
-
TileEngineTutorialPart2Source.zip (2.37K)
Number of downloads: 5353
This post has been edited by RevTorA: 26 May 2011 - 07:30 PM