SDL Entities
SDL Tutorials February 22nd, 2008In this new tutorial, as I had promised before, we are going to take our hand at creating entities. Entities, for all gaming purposes, are anything that can be interacted with in any way, shape, or form. Some examples might be a monster or a treasure chest that you can open. In this sense, practically everything within the game that moves is an Entity. A rock that is part of a map, which never moves, is not an entity. But if you wanted that rock to move for whatever reason, then we’d make it an Entity. This tutorial will be split into 3 different tutorials. The first, this one you are reading, will deal with a basic Entity class structure. The next tutorial will veer off slightly to build a Map class via a tileset. Then, the last tutorial, which is what a lot of people have trouble with, will deal with Entity to Map collision, and Entity to Entity Collision.
Update:
- Fixed class below to have a virtual destructor. (Thanks Andras!)
Lets get started by creating two new files called CEntity.cpp and CEntity.h. Open up the header file and add the following:
#include "CAnimation.h"
#include "CSurface.h"
class CEntity {
public:
static std::vector<CEntity*> EntityList;
protected:
CAnimation Anim_Control;
SDL_Surface* Surf_Entity;
public:
float X;
float Y;
int Width;
int Height;
int AnimState;
public:
CEntity();
virtual ~CEntity();
public:
virtual bool OnLoad(char* File, int Width, int Height, int MaxFrames);
virtual void OnLoop();
virtual void OnRender(SDL_Surface* Surf_Display);
virtual void OnCleanup();
};
Now, open up the cpp file and add the following:
std::vector<CEntity*> CEntity::EntityList;
CEntity::CEntity() {
Surf_Entity = NULL;
X = Y = 0.0f;
Width = Height = 0;
AnimState = 0;
}
CEntity::~CEntity() {
}
bool CEntity::OnLoad(char* File, int Width, int Height, int MaxFrames) {
if((Surf_Entity = CSurface::OnLoad(File)) == NULL) {
return false;
}
CSurface::Transparent(Surf_Entity, 255, 0, 255);
this->Width = Width;
this->Height = Height;
Anim_Control.MaxFrames = MaxFrames;
return true;
}
void CEntity::OnLoop() {
Anim_Control.OnAnimate();
}
void CEntity::OnRender(SDL_Surface* Surf_Display) {
if(Surf_Entity == NULL || Surf_Display == NULL) return;
CSurface::OnDraw(Surf_Display, Surf_Entity, X, Y, AnimState * Width, Anim_Control.GetCurrentFrame() * Height, Width, Height);
}
void CEntity::OnCleanup() {
if(Surf_Entity) {
SDL_FreeSurface(Surf_Entity);
}
Surf_Entity = NULL;
}
Okay, now for some basic explanation. What we are doing here is encapsulating the basic 5 components I mentioned within the first lesson (excluding Events, which will be handled in a later lesson). This allows us to handle Entities within the game much more easily, rather than clumping them together with everything else in the game within the main CApp class. This will also be the way we handle other things as well. The first thing you may notice is a static vector called EntityList. This vector will hold all of our entities, easily accessible through CEntity::EntityList, because it’s declared as a static. I should make a special note here: we declare this EntityList within CEntity because it prevents from circular dependencies later on. An example of this is trying to get a Map to communicate with Entities, and Entities to get to communicate with the Map. Such as CMap declaring a CEntity member, and CEntity declaring a CMap member. It would cause problems on the compile level.
So this vector contains all of our Entities within the game. Notice that each member of the vector is a pointer. This is because later on we are going to inherit this CEntity class for Entity specific classes. So, for example, if we were going to make a Megaman game, we would have a CMegaMan class inheriting the CEntity class. And, via polymorphism, we can store that CMegaMan class within the EntityList. This is the very reason why we declared the functions above as virtuals, and certain members as protected.
Next, we have basic information about the Entity, common to all Entities, coordinates, dimensions, and a surface for its image. Next, we have a loading function that basically takes a filename, and loads the image. By default, we have it setting a transparent color. I’d like to step aside here for a moment to let you all know that certain things I do aren’t set in stone. You can, and are encourage, to take this code and modify to your liking. You may want more parameters on your OnLoad function, or you may want less. You may not want a default transparent color, who knows. I encourage you to test different things. Don’t worry, my code will still be here if you mess things up.
Next, we have a basic OnLoop function that handles basic calculations. Right now we are only calculating Animation. Also please note that we have only set the MaxFrames for the Animation, and left the defaults in place. Next, we have the OnRender function. Instead of making it render to the display only, I’ve allowed a parameter to specify where to render this entity. This could be any surface you want. So you could, if you wanted, render one entity onto another.
Lastly, we have an OnCleanup function that restores memory and all that stuff.
Like I mentioned in the beginning, this is a basic Entity class structure, it basically doesn’t do much yet, but don’t fret, it soon will in coming lessons. So lets get it working. Open up CApp.h and add the header file to the top, and declare two Entities:
//…
private:
CEntity Entity1;
CEntity Entity2;
Now, lets load these two Entities. Open up CApp_OnInit.cpp and add the following:
return false;
}
if(Entity2.OnLoad("./entity2.bmp", 64, 64, 8) == false) {
return false;
}
Entity2.X = 100;
CEntity::EntityList.push_back(&Entity1);
CEntity::EntityList.push_back(&Entity2);
Now, depending on the images you use, you need to set the values appropriately on the OnLoad function. I’ve reused the yoshi image from the previous lesson, and if you still need it:

Now, remember how I stated we are basically encapsulating the basic functions of a game within the Entity class? We have to call those functions now in the respective CApp functions. So, open up CApp_OnLoop.cpp and add the following:
if(!CEntity::EntityList[i]) continue;
CEntity::EntityList[i]->OnLoop();
}
We are basically running through each Entity in our vector, and calling the OnLoop function. Simple enough! (And we’re doing an error checking so we don’t call any NULL pointers). Now, lets do the same things in CApp_OnRender.cpp:
if(!CEntity::EntityList[i]) continue;
CEntity::EntityList[i]->OnRender(Surf_Display);
}
And the same thing in CApp_OnCleanup.cpp:
if(!CEntity::EntityList[i]) continue;
CEntity::EntityList[i]->OnCleanup();
}
CEntity::EntityList.clear();
Note that I added the clear function call, which clears out the vector to nothing. Basically a reset.
Great, now try getting this thing to compile. You should see two yoshis running together on the screen. In the next lesson we’re going to looking at making Maps, and creating a basic file format for our Maps.
SDL Entities – Tutorial Files:
Win32: Zip, Rar
Linux: Tar (Thanks Gaten)












February 26th, 2008 at 5:20 am
Great tutorials man, really good stuff you’ve got here. One small complaint though: No allowances for us Linux users
And in my ongoing effort to fix what I don’t like, I made a Makefile for this tutorial and placed it in an archive for ya! Once it’s untarred, a simple:
make
make clean
Should leave you (us) with an executable named ’sdl_entities’. It should work on any Linux system with SDL installed correctly. Just my way of saying thanks for these tutorials.
http://gaten.homelinux.net/sdl_entities.tar.gz
February 26th, 2008 at 7:49 am
Thanks gaten! I always appreciate extra help. I don’t know if you are up for this, but it would be great as well to have Linux versions for the other tutorials as well. Perhaps this will turn into something like NeHe, with various versions. (I’ve posted the link above as well).
February 26th, 2008 at 8:06 pm
I’d love to help, although time’s going to be tight for a while. I’ll start by making linux archives of all your tuts, and then go from there. The code should never have to change (the whole cross platform thing
), but I’ll be sure to test them all out when I have time. Email me so we can work on this in a cooperative manner.
March 3rd, 2008 at 10:29 am
I can see this site has some programming experience in general. I appreciate the site, really good tutorials and you are one of a few that takes time to explain deeply how to work with classes + header files. I will definitly use this site as reference. Good job mates!
March 3rd, 2008 at 11:06 am
Thanks Blommis, glad that I can be of help!
March 18th, 2008 at 5:18 pm
These are great tutorials man they’re really helping me i really appreciate the time you’ve put into the site its really good
i’m having a small problem with this tutorial
when i tried to use
CEntity::EntityList.push_back(&Entity1);
i got some errors this is the jist of it
App.obj : error LNK2001: unresolved external symbol “public: static class std::vector<class CEntity *,class std::allocator > CEntity::EntityList”
any help you could give would be greatly appriciated
March 18th, 2008 at 5:52 pm
It seems that the static member for EntityList has not been declared. Make sure you have std::vector CEntity::EntityList; at the top of the CEntity.cpp file after the header file.
March 18th, 2008 at 7:01 pm
Thanks i didn’t see that at the top and i’ve never used vectors before so i wasn’t sure what to do.
Thanks for the help.
March 31st, 2008 at 7:11 am
No problem.
April 5th, 2008 at 3:52 pm
Link to the “Linux” version is broken. Since I’m using Mac OSX, I can’t use the .rar either, so I really appreciate having the alternative link. You might consider using .zip even for the Win32 version, then anyone could use it. So far I am really liking the tutorials, they are giving me a lot of good ideas for my own code.
April 5th, 2008 at 4:33 pm
Instead of using a integer index to go through the vector, you might consider using an iterator. Using an index is an extra unnecessary step, and it’s not the “C++ way”.
It probably doesn’t make any difference in practice though.
April 7th, 2008 at 7:00 am
It all depends I suppose on the programmer. Some people are strict C++ (which the question begs, why are you using SDL then?). I’m trying to stay as beginner friendly as possible, but again, feel free to take this code and make it your own.
April 17th, 2008 at 12:58 pm
Great tutorials! Now I can practice on both SDL-programming and OOP because of these tutorials.
May 16th, 2008 at 10:58 am
OOP is your friend.
May 27th, 2008 at 12:03 pm
Absolutly amazing tutorials! Gaten, your link doesn’t work.
June 4th, 2008 at 6:50 am
Thanks, I’ll ask Gaten about it (will probably hold them here from now on).
June 13th, 2008 at 6:08 am
Thanks for the good tutorial! I am using some of your code in a little project, but I’ve come across the following problem:
In the loop over the vector,
if(!CEntity::EntityList[i]) continue;
does not seem to work for me. I get the error “no match for ‘operator!’”
Comparing the item to NULL also doesn’t work. Any idea what could be causing this?
Thanks in advance
June 13th, 2008 at 6:41 am
CPCT,
That’s really weird. Do you have any code that I can look at? It’ll be easier that way to determine what the problem is.
June 13th, 2008 at 11:33 am
So I have an object WandContainer which contains a vector of Wand objects.
In WandContainer.h: std::vector WandList;
Then, in WandContainer.cpp, I have
void WandContainer::OnRender(SDL_Surface* Surf_Display, int MapX, int MapY){
//Draw all the wands
for(int i = 0;i WandContainer::WandList.std::vector::operator[] [with _Tp = Wand, _Alloc = std::allocator](((unsigned int)i))’
Also, I am compiling this with gcc/g++ version 4.2.3 under Linux
June 13th, 2008 at 11:36 am
Some of my text went missing after the submit
I have put a full version of my post here:
http://users.skynet.be/klaasdc/question.txt
June 13th, 2008 at 11:40 am
CPCT,
The reason we use if(!CEntity::EntityList[i]) continue; in the tutorial is because the vector we have is made up of pointers. That if statement makes sure we have a valid pointer. Now, for your Wands, you do not have a vector of pointers, but of objects. Meaning, you don’t have to have that if statement in there anymore.
//Draw all the wands
for(int i = 0;i < WandList.size();i++) {
WandList[i].OnRender(Surf_Display, MapX, MapY);
}
Hope that helps.
June 13th, 2008 at 11:48 am
Wow, thanks for the blazing fast reply!
June 13th, 2008 at 11:48 am
No prob Bob.
June 20th, 2008 at 5:03 am
Hi Tim,
Just wanted to say that these tutorials have been an excellent resource for me. I was interested in starting to write a game and wasnt really sure how I should go about it but this has been a great starting point.
Looking forward to many more excellent tutorials.
On a side note I had a slight issue with the Entities tutorial on Linux. when the 2 Yoshis were animated there appears to be a white after image left like the outline of each frame remained on screen after the next frame was displayed. The image displayed correctly during the Animation tutorial from what I remember so Im not sure if I left out a setting somewhere.
I didnt want to simply copy and paste without finding out what was wrong with my code.
June 20th, 2008 at 6:54 am
Gearoid Donnellan,
Thanks! Are you typing in the code as you go along, or are you basing your code off of the Linux sources? I would check your code against the code found within the zip files above (even though it says Win32 the code is good for Linux). I’d mainly look at CAnimation.cpp. The line that probably concerns you is:
if(CurrentFrame >= MaxFrames – 1) {
If your line looks different, like:
if(CurrentFrame >= MaxFrames) {
Then that could be the problem. Let me know if that helps.
June 20th, 2008 at 8:06 pm
Yep that was the problem cheers. Usually type as I go musta missed that one
June 23rd, 2008 at 8:43 pm
Using Visual C++ Express 2008, after adding in the code and compile with /MD runtime library option, receive the following error:
… error LNK2019: unresolved external symbol __imp___CrtDbgReportW …
The error was solved by changing the option to /MDd. Read more about it:
http://gps678.com/26/896f16580b963551.html
July 8th, 2008 at 3:32 pm
What compiler are you using? I’m using CodeBlocks and it doesn’t work at all with the stl library. The vector is giving the MINGW compiler fits, and after installing the stl headers, I still get the same errors. I made no modifications to your code. It just doesn’t like the template set up. Please help. Thank you!
July 9th, 2008 at 2:08 pm
cormega215,
Not sure why you are having problems with STL. I assume you downloaded CodeBlocks with Mingw. If not, you’re going to have problems. A clean install of CodeBlocks and SDL should be all you need.
July 9th, 2008 at 2:20 pm
This is the error that I’m getting when I compile. Again the error I believe is coming from the vector template that is used for the vector entity structure.
Error:
error: an explicit specialization must be preceded by ‘template ‘
this is from type_traits.h that’s about the only problem that I can find. Thanks for your previous reply by the way.
July 10th, 2008 at 6:50 am
Take a look at this link and let me know if it helps:
http://forums.devshed.com/software-design-43/template-problem-242619.html
July 10th, 2008 at 11:23 am
Thanks for the link. It gave me some insight on how to go about writing the statement. However, I’m still having some trouble with how to go about the order of declarations. Here’s what I have so far.
from CEntity.h
template vector
{
std::vector EntityList;
}
from CEntity.cpp at the beginning of the CEntity default constructor:
template class vector CEntity::EntityList;
It’s still giving me the same error though.I’m probably doing this wrong. Any suggestions?
July 11th, 2008 at 4:01 pm
cormega215,
I think you are a little confused about the whole vector template. In our code we should not have to declare the vector template. All we ever should have to do is:
#include <vector>
std::vector<int> MyVector;
If that doesn’t work, then don’t mess around with your code; it means that the STL library is not properly installed for whatever reason. Which I would uninstall CodeBlocks, go and redownload CodeBlocks (the one with Mingw – important), and reinstall. Then try it again.
Also, a note here. In your class you would do:
class MyClass {
public:
static std::vector<int> MyVector;
};
In the header:
std::vector<int> MyClass::MyVector;
Make sure you also have the right form.
July 15th, 2008 at 12:47 pm
Okay, Something just isn’t making any sense. I re-downloaded codeblocks and I coded a simple program that would print out a message if I could successfully declare a integer vector from the STL library. And I still get errors from the type_traits.h stating that a specialization is needed for template. There must be something that your doing different to make it work. Here is my code:
#include
#include
int main()
{
std::vector MyVector;
cout << “This works!!”;
}
I can’t make it simpler than this! And it still is giving me trouble. I checked the compiler settings and everything is referenced in it’s right directory. The include files and the library files are linked correctly and I still am having trouble. Please send a reply ASAP! I really appreciate it. Thank you.
July 15th, 2008 at 12:48 pm
Edit:
By the way, the include files are there. It must not have taken when I posted the code. They are:
#include
and #include
July 15th, 2008 at 1:00 pm
Alright, firstly, I fixed stupid WordPress to allow <> characters. Secondly, I assume your original post is:
#include <iostream.h>
#include <vector>
int main()
{
std::vector<int> MyVector;
cout << "This works!!";
}
If so, the above code works perfectly fine for me. I suppose I have some further questions then, what OS are you on? Do you have any prior installations of IDEs, Compilers, and/or Libraries?
Also, a quick note. iostream.h is deprecated (use <iostream> and std::cout) and you forgot a return on the function.
December 9th, 2008 at 6:30 pm
Hey Tim, I have problem to compile this code.
1>—— Build started: Project: Game, Configuration: Debug Win32 ——
1>Linking…
1>CApp_OnCleanup.obj : error LNK2019: unresolved external symbol __imp___CrtDbgReportW referenced in function "public: class CEntity * & __thiscall std::vector<class CEntity *,class std::allocator<class CEntity *> >::operator[](unsigned int)" (??A?$vector@PAVCEntity@@V?$allocator@PAVCEntity@@@std@@@std@@QAEAAPAVCEntity@@I@Z)
1>CApp_OnInit.obj : error LNK2001: unresolved external symbol __imp___CrtDbgReportW
1>CApp_OnLoop.obj : error LNK2001: unresolved external symbol __imp___CrtDbgReportW
1>CApp_OnRender.obj : error LNK2001: unresolved external symbol __imp___CrtDbgReportW
1>C:\Users\DV6646US\Desktop\SDL – Engine Game\Source Code\Project Files\Game\Debug\Game.exe : fatal error LNK1120: 1 unresolved externals
1>Build log was saved at "file://c:\Users\DV6646US\Desktop\SDL – Engine Game\Source Code\Project Files\Game\Debug\BuildLog.htm"
1>Game – 5 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
December 15th, 2008 at 11:00 am
Emilio,
It seems that you are using Visual Studio? If you’re willing to give it up, I recommend you use CodeBlocks (and I have several reasons as to why). If you are not willing, take a look at this (http://msdn.microsoft.com/en-us/abx4dbyh.aspx). You need to link a library for debugging information (this is Visual Studio specific), mainly the /MTd flag in your compiler options.
February 19th, 2009 at 6:39 pm
Hello there!
I had a problem with your project in VS 2005 Express, so I switched to codeblocks where a new error occured:
Linking executable: bin\SDL Tutorial 6.exe
CApp.h.gch\Release_CApp_h_gch: file not recognized: File format not recognized
collect2: ld returned 1 exit status
Process terminated with status 1 (0 minutes, 0 seconds)
0 errors, 0 warnings
I found the solution in the codeblocks-forums(http://forums.codeblocks.org/index.php?topic=8513.0), where somebody else had the same problem.
rightclick on CApp.h -> Properties -> Build -> uncheck “Link file”
Another Problem was the #include
Changed it to #include and now everything works fine.
Thank you for your great tutorials. They are nice to read and perfect for beginners, who want to do something else then console-crap.
Greetings,
a german boy
February 19th, 2009 at 6:42 pm
changed #include SDL.h to #include SDL/SDL.h
April 2nd, 2009 at 1:19 pm
I’ve got a problem with the code that loops through the entities to run OnLoop, OnRender(…), etc etc.
When I loop through, the program runs for a bit and crashes never getting past the point of running the functions on entities for the first index.
I’ve tried eliminating the loop, pushing one entity into the vector, and accessing it this way and it still fails. What’s strange is that entitylist.size() returns the correct value, however I can’t run any functions without it crashing. Also my instance variables are non-existent.
This is very weird that it gets to this point because I have the NULL check in my loops… strange.
April 22nd, 2009 at 7:24 am
Can I see your vector declaration, your entity declarations, and how you are pushing them into the vector?
May 19th, 2009 at 9:39 am
Hi There,
I’m trying to dynamically generate some entities e.g. ‘make more yoshi’s when I left-click’
I’m trying things such as:
CEntity * NewYoshi = new CEntity;
CEntity::EntityList.push_back(NewYoshi);
with absolutely no luck
I’m pretty new to C++ (if you can’t guess!) but am experienced with PHP (if that helps)
Can you please elaborate on how to dynamically generate entities as-needed and put them into the entity list for processing?
Kind regards,
David
May 25th, 2009 at 5:38 pm
Found the solution to (37) Emilio’s post. Had the problem myself and figured if someone else did they could use this.
Go to your project properties:
Configuration Properties > C/C++ > Preprocessor
Remove _DEBUG from your Preprocessor Definitions. Visual Studio seems to have some problems with storing SDL information in vectors with that in there.
Hope this helps someone.
May 26th, 2009 at 3:09 am
Hey David,
I was intrigued by your question and so tried it out myself and got the following to work (sorry, my variable names/coding style is different from Tim’s so you’ll have to decipher it)
—
CEntity* NewYoshi;
NewYoshi = new CEntity();
if((NewYoshi->OnLoad(”yoshi.bmp”, 64, 64, 8)))
{
if(CEntity::entityList.back())
{
NewYoshi->fltX = (CEntity::entityList.back()->fltX) + 100;
CEntity::entityList.push_back(NewYoshi);
}
}
it’s possible that you were achieving your goal but just placing it over the original yoshi? if you weren’t updating the X value i believe that would be the outcome. just a thought.
p.s. this only alters the X var, i’ll leave you to tackle changing the Y var when appropriate
May 26th, 2009 at 3:11 am
oops, just realised that this won’t work if there isn’t already something in the entityList – move the push_back command outside of the if(CEntity:entityList.back()) condition if you’re starting out with an empty list
June 2nd, 2009 at 8:20 am
Sorry for my spelling – am not native english.
Thank you for really good tutorial.
I have question related to entitie class. Let say we want to create 100 entities that will be using the same sprite sheet (SDL_Surface) – how to load this sprite sheet only once and use it for all the entities insted of loading the same file 100 times and waste memory?
June 2nd, 2009 at 8:25 pm
Hey marcin,
You’ll notice that CEntity is using an SDL_Surface* for it’s surface. Pointers are great for allowing you to access onE copy of something instead of making lots of copies of the same item.
What you will need to do is create a function to set the new entities surface based on an SDL_Surface*.
An easy way to do this would be to overload the OnLoad function so that there are two versions, one which accepts a file name, and another which accepts an SDL_Surface*.
i.e.
void OnLoad(char* file, int x, int y, int maxframes);
void OnLoad(SDL_Surface* surface, int x, int y, int maxframes);
Remember to keep all the other arguments as you still need to set the height etc.
You’ll need access to a copy of the loaded surface, you could do this easily by making a ‘master’ entity that sits at entityList[0] but isn’t displayed on screen for ease of access (there would be a more robust way to do it, but this is the quick for testing).
Also, if you don’t want your program to crash on exit, you’ll need to change the CleanUp function in CEntity so that it’s not trying to perform SDL_FreeSurface on an invalid pointer.
(Not trying to step on your toes Tim, just trying to save you time so you can provide us with more great SDL tutorials
)
June 6th, 2009 at 11:08 am
Thank you J your answear helps a lot
June 6th, 2009 at 11:40 am
J,
No problem at all. I wish more people would help each other out.