Spacefight – The GameObject – part 4
A lot of objects in the game has a many things in common. They contain an Animationlibrary, a position on the screen, width and height. They also got common functions like:
- Listen
Sends any event from the user’s keyboard/mouse/joystick to the object to handle. - Update
Calculates where the object should be positioned next time, if the object should do any kind of action during this frame. - Draw
It will get a frame from the object’s current animation and draw it on the screen. The screen surface will be passed into this function. - Handle collision
If the object collides with another game object, it will be told what it’s been colliding with through here via a collision detection routine.
I created a base game object like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
#ifndef __GAMEOBJECT_H__ #define __GAMEOBJECT_H__ #define BORDER_WIDTH 1 #include <SDL/SDL.h> #include <string> #include <vector> #include "constants.h" #include "utils.h" #include "errorcodes.h" #include "animationlibrary.h" using namespace std; class GameObject { protected: struct pos { int x, y; } position; string objType; string animName; int id; Uint32 ticksToLive = 0; int damage; bool termination = false; bool colliding = false; int stopForMs = 0; int parentId = -1; int zIndex = 0; SDL_Rect rect; AnimationLibrary* animLib = NULL; public: static int counter; std::string objectType(void){ return objType; }; bool operator< (const GameObject &other) const { return zIndex > other.zIndex; } void setXY(int x, int y) { position.x = x; position.y = y; } GameObject(int startPosX, int startPosY, AnimationLibrary* a); int getZIndex(){ return zIndex; } void setX(int x){position.x = x;} void setY(int y){position.y = y;} void terminate(){ termination = true;} int getWidth(); int getHeight(); void stopFor(int ms){ stopForMs = ms;} int getId() { return id; } bool killMe(){ if(ticksToLive != 0 && ticksToLive < SDL_GetTicks()){ return true; } else { return termination; } } virtual void listen(SDL_Event &event, vector <GameObject*> &refObjects) = 0; virtual void init(void) = 0; virtual void update(vector <GameObject*> &refObjects) = 0; SDL_Rect getRect(); virtual void handleCollision(vector <GameObject*> gameObjectList, vector <GameObject*> &refObjects) = 0; virtual void draw(SDL_Surface* screen) = 0; bool isColliding(){return colliding;} void setCollision(bool val){ colliding = val; } void drawBorder(SDL_Surface* screen); void posUpdate(); int getX(){ return position.x;} int getY(){ return position.y;} int getParentId(){return parentId;} pos getPosition(){return position;} ~GameObject(); }; #endif /* __GAMEOBJECT_H__ */ |
As you can see it the game object has a number of virtual functions for update, listen, draw and handleCollision. This means that the sub class needs to implement all these function. It works as an abstract method if you know Java, PHP or similar. The GameObject class will never be instantiated. It will only be used to inherit from.
I have added a SDL_Rect on the game object as well. This will be used for collision detection later on. I might change this to a list of rectangles instead. Since some objects might be more complex and need more detailed “hit” surfaces.
So what is the point in all these shared methods?
It makes it possible for me to create a vector (or list) that contains GameObjects. The game object can be an Enemy, it can be an explosion, a rocket or the player or any new type. All of them shares the same function name for updating their position, but their implementation might be very different.
In the game-loop the game objects will be used something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// PSEUDO-CODE Player* player = new Player(); Enemy* en1 = new Enemy(); Enemy* en2 = new Enemy(); Enemy* en3 = new Enemy(); std::vector <GameObject* > gameObjectList; gameObjectList.push_back(player); gameObjectList.push_back(en1); gameObjectList.push_back(en2); gameObjectList.push_back(en3); while(gameIsRunning){ // Go through all the game objects. for(std::vector <GameObject* >::iterator it = gameObjectList.begin(); it != gameObjectList.end(); it++ ){ (*it)->listen(event); (*it)->update(); (*it)->draw(screen); } SDL_Flip(screen); SDL_Delay(1000/FRAME_RATE); } |
The example above is very simplified. It doesn’t mention the screen or the event in the game loop. Before iterating through the game objects a background image should probably be drawn on the screen surface. This way each loop will put a background image on the screen, and start drawing the enemies, bullets and player from the game object list, until it composed a complete game screen.
Layers doesn’t exists in SDL. What has been drawn first will end up in the back. And what has been drawn last will end up in the front.
If you look in the GameObject header code. You will see there is a variable called zIndex. All the game objects in the vector will be sorted on this variable. The lower number the higher up in the list the object will be placed. If the specific gameobject has the highest zIndex value, it will be in the end of the list and therefore be drawn on top of all the other game objects.
I will probably come back to the game object code at some point. To add references to audio or other properties that needs to implemented by all objects. But it will work for now.
Leave a Reply