In 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 <vector>
#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:
#include "CEntity.h"
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:
#include "CEntity.h"
//...
private:
CEntity Entity1;
CEntity Entity2;
Now, lets load these two Entities. Open up CApp_OnInit.cpp and add the following:
if(Entity1.OnLoad("./entity1.bmp", 64, 64, 8) == false) {
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:
for(int i = 0;i < CEntity::EntityList.size();i++) {
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:
for(int i = 0;i < CEntity::EntityList.size();i++) {
if(!CEntity::EntityList[i]) continue;
CEntity::EntityList[i]->OnRender(Surf_Display);
}
And the same thing in CApp_OnCleanup.cpp:
for(int i = 0;i < CEntity::EntityList.size();i++) {
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.
Email (required, not published):
Website:
Email (required, not published):
Website:
Also: To make the animations less blurry, I put the following at the end of CApp_OnRender after the SDL_Flip(Surf_Display);:
SDL_FillRect(Surf_Display, NULL, SDL_MapRGB(Surf_Display->format,0,0,0));
P.S: If something inherits from CEntity, does it have its own static list of Entities of that type?
Email (required, not published):
Website:
When you put the SDL_FillRect after SDL_Flip it would go to the next time it flips so it's it's basically the very background.
P.S Question No they would still go in the same list because it's static...
Email (required, not published):
Website:
Email (required, not published):
Website:
if(Surf_Entity == NULL || Surf_Display == NULL)
My code before was:
if(Surf_Entity == NULL || Surf_Display)
Therefore the Surf_Display is true and the OnRender returns before drawing the entity.
Email (required, not published):
Website:
To start off, I am using VS2010. I know that you recommend CodeBlocks, which I have, but I prefer VS2010. I'm sure I will migrate to CB some day, I may even have to after these errors, but if it's at all possible, I would like to fix my errors and continue this tutorial track with Visual Studio.
Anyway, this is my problem:
my Code is completely identical to the code in these tutorials, just to get that out of the way. My errors start with the CEntity.cpp source file. I'm getting a missing semi-colon syntax error right after the #include "CEntity.h" line. After that, I get an error C2936: 'std::vector<_Ty>' : template-class-id redefined as a global data variable. after that i get an avalanche of errors, at least 50 to be a little more precise. I'm assuming that the two files created in this tutorial do not compile correctly and the dependancies are not met in the following files, but I can't figure out where the error originates from. Can anyone help? Is it simply something to do with VS2010? Hope to hear from someone soon.
Email (required, not published):
Website:
Email (required, not published):
Website:
Email (required, not published):
Website:
Email (required, not published):
Website:
Email (required, not published):
Website:
error: 'Surf_Display' was not declared in this scope
specifically at the line:
CEntity::EntityList[i]->OnRender(Surf_Display);
Can someone help?
Thanks in advance! :-)
Email (required, not published):
Website:
Email (required, not published):
Website:
In a game like Lode Runner for example ( http://en.wikipedia.org/wiki/Lode_Runner ) as part of the gameplay the player is able to blast holes in a certain type of tile to trap the guards and/or just dig down through the floor. Would each piece of tile that can be blasted be considered an entity because it can be interacted with? Assuming you were loading in levels from a text file (similar to maps in the next tutorial) would you load certain tiles as an entity and the rest as non-entities?
Email (required, not published):
Website:
I have this code:
...
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;
...
its CEntity.cpp file. Any ideas :( ?
Email (required, not published):
Website:
In your CSurface.h, add this in the public section:
static bool Transparent(SDL_Surface* Surf_Dest, int R, int G, int B);
In you CSurface.cpp:
bool CSurface::Transparent(SDL_Surface* Surf_Dest, int R, int G, int B) {
if(Surf_Dest == NULL) {
return false;
}
SDL_SetColorKey(Surf_Dest, SDL_SRCCOLORKEY | SDL_RLEACCEL, SDL_MapRGB(Surf_Dest->format, R, G, B));
return true;
}
It worked for me and I recommend you to try to understand instead of copying and pasting codes.
Best regards,
Civilian
Email (required, not published):
Website:
Anyone know how to solve this? Since i guess loading a new image for every projectile would be a real sucky solution. Thanks
Email (required, not published):
Website:
Email (required, not published):
Website:
Email (required, not published):
Website:
I solved this by adding a black picture to my graphics folder and loading it to the Surf_Display in OnRender before the actual rendering, so that the space behind the edge would be black as you'd expect it to be.
Email (required, not published):
Website:
Can't seem to figure out how to do it, just end up with black screen, or black screen that crashes after a few seconds.
Email (required, not published):
Website:
if((Surf_Black = CSurface::OnLoad("./gfx/black.png")) == NULL) {
return false;
}
in CApp_OnInit.cpp and put CSurface::OnDraw(Surf_Display, Surf_Black, 0, 0); into CApp_OnRender.
!!Important!! The black Surface must be drawn before everything else, otherwise it will hide other elements.
Email (required, not published):
Website:
// Fill the screen black
SDL_FillRect( Surf_Screen, &Surf_Screen->clip_rect, SDL_MapRGB( Surf_Screen->format, 0x00, 0x00, 0x00 ) );
Email (required, not published):
Website:
Email (required, not published):
Website: