Page 1 of 1

Beginning SDL - Part 5.2 - Game Objects

#1 stayscrisp  Icon User is offline

  • フカユ
  • member icon

Reputation: 999
  • View blog
  • Posts: 4,177
  • Joined: 14-February 08

Posted 02 September 2010 - 06:00 PM

*
POPULAR

Beginning SDL - Part 5.2 - Game Objects

So in the last tutorial we created a GameObject class that took advantage of the sprite class from earlier tutorials. It didn't really do too much but it was only the start. In this tutorial we will really harness the power of C++ using inheritance and polymorphism.

So to start things off lets create a class derived from our GameObject class and call it Player, this will be a controllable character.

Create Player.h and Player.cpp
#ifndef PLAYER_H
#define PLAYER_H

#include "GameObject.h"

class Player : public GameObject // this is how we inherit from GameObject
{
	public:
	
		Player() {}
		~Player() {}
		
		void Load(char* filename);
		void Update();
		void Draw();
		void Clean();
		
	private:
		
		SDL_Surface* m_pSprite;
		
};
#endif



#include "Player.h"

void Player::Load(char* filename)
{
	/* For the moment our player does not need to have any specific loading function
	so we can just use the load function from the GameObject parent class, like so. */
	GameObject::Load(filename);
}

void Player::Update()
{
	// nothing for now
}

void Player::Draw()
{
	/* Again we are drawing a sprite so lets reuse the function found in the parent class */
	GameObject::Draw();
}

void Player::Clean()
{
    /* And again we free the surface using the function from game object */
	GameObject::Clean();
}



Before I continue I think that you should know of a few basic rules of thumb for inheritance, a derived class should model the "is a" concept, for example here I have derived player from gameobject, this fits the rule because player "is a" game object. If you find that an object models the "has a" concept then
you should think about creating a member inside the class for that object. For example a player "has a" weapon so it should have a member of type weapon.

Another rule of thumb is to only derive from a class if an object has different behaviour not if the object only changes some data, this can be achieved using a simpler solution of member variables. I got an example from a book of if you had created an enemy class and then you derived a boss class from it, say you wanted to create a boss that was faster or had more health, would you then derive a super boss from boss? you could but you would find that the super boss class is a little empty as you only really had to
change the health and acceleration of the boss (I know at this point my player class seems to be breaking that rule but it will change and I am really just trying to show how this kind of thing works :)).

Hope that is some food for thought, but anyway onwards...

So now in our playstate we can create an object of type player instead of game object in the same way and naruto will pop up again.

#ifndef _PLAY_STATE_H_
#define _PLAY_STATE_H_

#include "SDL.h"
#include "GameState.h"
#include "GameObject.h"
#include "Player.h" // add our new header
#include "Sprite.h"

class PlayState : public GameState
{
public:
	void Init();
	void Clean();

	void Pause();
	void Resume();

	void HandleEvents(Game* game);
	void Update(Game* game);
	void Draw(Game* game);

	// Implement Singleton Pattern
	static PlayState* Instance()
	{
		return &m_PlayState;
	}

protected:

	PlayState() {}

private:
	static PlayState m_PlayState;

	Player * player; // create a player instead of a game object

};



Playstate.cpp
#include <stdio.h>

#include "SDL.h"
#include "PlayState.h"

PlayState PlayState::m_PlayState;

void PlayState::Init()
{       
	player = new Player(); // now we create a player
	player->Load("naruto.bmp"); // load in the same way

	printf("PlayState Init Successful\n");
}

void PlayState::Clean()
{
	player->Clean(); // clean in the same way

	printf("PlayState Clean Successful\n");
}

void PlayState::Pause()
{
	printf("PlayState Paused\n");
}

void PlayState::Resume()
{
	printf("PlayState Resumed\n");
}

void PlayState::HandleEvents(Game* game) 
{
	SDL_Event event;

	if (SDL_PollEvent(&event)) {
		switch (event.type) {
						case SDL_QUIT:
							game->Quit();
							break;

						case SDL_KEYDOWN:
							switch (event.key.keysym.sym) {
						case SDLK_RIGHT: {
							break;
										 }
							}
		}
	}
}

void PlayState::Update(Game* game) 
{
	

}

void PlayState::Draw(Game* game) 
{
	SDL_FillRect(game->GetScreen(),NULL,0x000000);
	player->Draw(); // draw in the same way
	SDL_Flip(game->GetScreen());
}



Ok that should look the same as we had before when you compile it. Now we are going to code something that essentially encapsulates the entire point of OOP, hopefully I can do justice to just how powerful writing games in this way is and how much easier and more readable your code will become. Even though in the player class we added functions different to the ones in the game object class what was really happening is that the functions being called were still those of the base class (GameObject). This is where virtual functions come in.

If we wish to specify that we want to use the functions in a derived class then we need to prefix the base class functions with the virtual keyword

#ifndef GAME_OBJECT_H
#define GAME_OBJECT_H

#include "Game.h" 
#include "Sprite.h"

class GameObject
{
	public:
	
		GameObject() {}
		virtual ~GameObject() {}
		
		virtual void Load(char* filename);
		virtual void Update();
		virtual void Draw();
		virtual void Clean();
		
	private:
		
		SDL_Surface* m_pSprite;
		
};

#endif



Now when you run this code the functions called for player are those we added earlier (essentially the game object functions). This may not seem to helpful on its own but with this we can use polymorphism which means we can refer to a derived object through a pointer to its base class, now I will show you
how great this idea is.

Lets open up PlayState.h and add a container, in this case a vector of pointers to GameObjects.

#ifndef _PLAY_STATE_H_
#define _PLAY_STATE_H_

#include "SDL.h"
#include "GameState.h"
#include "GameObject.h"
#include "Player.h"
#include "Sprite.h"
#include <vector> // add the vector class 

class PlayState : public GameState
{
public:
	std::vector<GameObject*> GameObjects; // create a vector of pointers to game objects

	void Init();
	void Clean();

	void Pause();
	void Resume();

	void HandleEvents(Game* game);
	void Update(Game* game);
	void Draw(Game* game);

	// Implement Singleton Pattern
	static PlayState* Instance()
	{
		return &m_PlayState;
	}

protected:

	PlayState() {}

private:
	static PlayState m_PlayState;

	Player * player;

};
#endif



And now in Playstate.cpp we can push objects into this container and then write some loops to call each of their functions

#include <stdio.h>

#include "SDL.h"
#include "PlayState.h"

PlayState PlayState::m_PlayState;

void PlayState::Init()
{       
	player = new Player();
	player->Load("naruto.bmp");

	GameObjects.push_back(player); // push the newly created player onto the vector
	
	printf("PlayState Init Successful\n");
}

void PlayState::Clean()
{

	for(unsigned int i = 0;i < GameObjects.size();i++) {
		if(!GameObjects[i]) continue;

		GameObjects[i]->Clean();
	}

	printf("PlayState Clean Successful\n");
}

void PlayState::Pause()
{
	printf("PlayState Paused\n");
}

void PlayState::Resume()
{
	printf("PlayState Resumed\n");
}

void PlayState::HandleEvents(Game* game) 
{
	SDL_Event event;

	if (SDL_PollEvent(&event)) {
		switch (event.type) {
						case SDL_QUIT:
							game->Quit();
							break;

						case SDL_KEYDOWN:
							switch (event.key.keysym.sym) {
						case SDLK_RIGHT: {
							break;
										 }
							}
		}
	}
}

void PlayState::Update(Game* game) 
{
	for(unsigned int i = 0;i < GameObjects.size();i++) {
		if(!GameObjects[i]) continue;

		GameObjects[i]->Update();
	}
}

void PlayState::Draw(Game* game) 
{
	SDL_FillRect(game->GetScreen(),NULL,0x000000);
	
	for(unsigned int i = 0; i < GameObjects.size(); i++) {
		if(!GameObjects[i]) continue;

		GameObjects[i]->Draw();
	}

	SDL_Flip(game->GetScreen());
}



Lets look at one of these loops in more detail

for(unsigned int i = 0;i < GameObjects.size();i++) {
		if(!GameObjects[i]) continue;

		GameObjects[i]->Update();
	}



essentially this loop says that while i is less than the amount of objects within the container it should call the Update function, before that it checks that the vector is not empty if it is then it just skips
over. Notice we didnt have to explicitly call each function for the player, we looped through a vector of pointers to its parent class, but because of the virtual keyword the appropriate function for that derived object is called. This would work for any object derived from game object. Pretty damn cool huh?

Just to do another test lets create a game object and then a player object with a slightly larger image so we can make use of our vector and see this in action on multiple objects, of course feel free to derive more classes from game object and try them out too.

#ifndef _PLAY_STATE_H_
#define _PLAY_STATE_H_

#include "SDL.h"
#include "GameState.h"
#include "GameObject.h"
#include "Player.h"
#include "Sprite.h"
#include <vector>

class PlayState : public GameState
{
public:
	std::vector<GameObject*> GameObjects;

	void Init();
	void Clean();

	void Pause();
	void Resume();

	void HandleEvents(Game* game);
	void Update(Game* game);
	void Draw(Game* game);

	// Implement Singleton Pattern
	static PlayState* Instance()
	{
		return &m_PlayState;
	}

protected:

	PlayState() {}

private:
	static PlayState m_PlayState;

	Player * player;
	GameObject * gameobject; // create another object

};

#endif



PlayState.cpp
#include <stdio.h>

#include "SDL.h"
#include "PlayState.h"

PlayState PlayState::m_PlayState;

void PlayState::Init()
{       
	player = new Player();
	player->Load("naruto.bmp");

	gameobject = new GameObject(); // create our object
	gameobject->Load("ryuk.bmp"); // load a different file

	/* The order you push objects onto the vector determines the order the are drawn in
	so i have placed my game object on first as it is a larger image that would cover my player object */
	GameObjects.push_back(gameobject); // push onto the vector
	GameObjects.push_back(player);
	printf("PlayState Init Successful\n");
}

void PlayState::Clean()
{

	for(unsigned int i = 0;i < GameObjects.size();i++) {
		if(!GameObjects[i]) continue;

		GameObjects[i]->Clean();
	}

	printf("PlayState Clean Successful\n");
}

void PlayState::Pause()
{
	printf("PlayState Paused\n");
}

void PlayState::Resume()
{
	printf("PlayState Resumed\n");
}

void PlayState::HandleEvents(Game* game) 
{
	SDL_Event event;

	if (SDL_PollEvent(&event)) {
		switch (event.type) {
						case SDL_QUIT:
							game->Quit();
							break;

						case SDL_KEYDOWN:
							switch (event.key.keysym.sym) {
						case SDLK_RIGHT: {
							break;
										 }
							}
		}
	}
}

void PlayState::Update(Game* game) 
{
	for(unsigned int i = 0;i < GameObjects.size();i++) {
		if(!GameObjects[i]) continue;

		GameObjects[i]->Update();
	}


}

void PlayState::Draw(Game* game) 
{
	SDL_FillRect(game->GetScreen(),NULL,0x000000);
	for(unsigned int i = 0; i < GameObjects.size(); i++) {
		if(!GameObjects[i]) continue;

		GameObjects[i]->Draw();
	}

	SDL_Flip(game->GetScreen());
}



Notice that we simply create the game object and then pushed it onto the vector, the loops took care of the rest. You should see your two images on screen in the correct order. I advise you to look at this code a few times and really get to grips with it and then try to derive your own classes from game object and push them onto the vector.

As you can see this is extremely powerful and allows for some really nice readable and reusable code, next time I will improve it even further using an abstract base class and loading objects from a file, bet you can't wait :P

Heres the image I used for the game object
Attached File  ryuk.bmp (428.96K)
Number of downloads: 385

Happy Coding!

This post has been edited by stayscrisp: 25 April 2011 - 07:28 AM


Is This A Good Question/Topic? 5
  • +

Replies To: Beginning SDL - Part 5.2 - Game Objects

#2 Regashi  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 17
  • Joined: 15-July 10

Posted 20 September 2010 - 07:01 PM

Can you provide a fully working solution where the source code actually works? I managed to get everything to work, but as soon as i got onto Game Objects.. all i got was error after error.

This post has been edited by Regashi: 20 September 2010 - 07:01 PM

Was This Post Helpful? 0
  • +
  • -

#3 stayscrisp  Icon User is offline

  • フカユ
  • member icon

Reputation: 999
  • View blog
  • Posts: 4,177
  • Joined: 14-February 08

Posted 21 September 2010 - 05:59 AM

Can you explain your errors? I assure you this code works as I build it as I write the tutorials.
Was This Post Helpful? 0
  • +
  • -

#4 Mohanddo  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 50
  • Joined: 07-July 09

Posted 21 September 2010 - 08:55 AM

Thanks alot for these tutorials. I have been following them over the past few days and i finished them today. Really looking forward to the next one.

Just one question: I tried to get the game states working with this new code and i had some problems. Menu -> Game works but after Game->Pause, it crashes. Is this code supposed to be working correctly with the gamestates?

thanks
Was This Post Helpful? 0
  • +
  • -

#5 stayscrisp  Icon User is offline

  • フカユ
  • member icon

Reputation: 999
  • View blog
  • Posts: 4,177
  • Joined: 14-February 08

Posted 21 September 2010 - 09:32 AM

Hi, yes this code should not change any of your state code, do you have a pause state that you push onto the stack? What error do you get when it crashes?
Was This Post Helpful? 1
  • +
  • -

#6 Mohanddo  Icon User is offline

  • D.I.C Head

Reputation: 3
  • View blog
  • Posts: 50
  • Joined: 07-July 09

Posted 21 September 2010 - 10:44 AM

I had a look at my code and i realised that i was supposed to pushstate the puase state in my play state and instead i was change state'ing!

So the difference is that the push and pop state wont clean the last state off the vector so that by deleting the last element on the vector you can go back to it?

Thanks for your help. Looking forward to the next tutorial.

This post has been edited by Mohanddo: 21 September 2010 - 10:47 AM

Was This Post Helpful? 0
  • +
  • -

#7 stayscrisp  Icon User is offline

  • フカユ
  • member icon

Reputation: 999
  • View blog
  • Posts: 4,177
  • Joined: 14-February 08

Posted 21 September 2010 - 11:02 AM

Maybe I should mention this more explicitly in my tutorial as I have had people have this problem before. Glad you like the tutorials, the next is on its way :)
Was This Post Helpful? 0
  • +
  • -

#8 Regashi  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 17
  • Joined: 15-July 10

Posted 21 September 2010 - 07:22 PM

Sorry about that, i wasn't claiming the code to not work, i was just asking for a simple solution to be already made for the people who try and do it by them selfs but keep running into errors because of our own faults.


Well either way, i spent about 2 hours running through my code and reading through these over and over again. I've managed to get it all running fine now.

Love what you've done so far, and this is really helping me understand the basics of game design.

---

Okay just a small question. I knew how to move sprites before using the whole gameobject approach.

So how can i move the player object around via the .x or .y ?

This post has been edited by Regashi: 21 September 2010 - 08:17 PM

Was This Post Helpful? 0
  • +
  • -

#9 jaimcm  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 8
  • Joined: 17-October 10

Posted 17 October 2010 - 02:22 PM

I wasn't too keen on these tutorials to start with but they got really good really fast and I'm addicted haha. When can we expect the next one roughly?

P.S. One thing I can't even begin to get my head around is Quest creation/management. Is this something you will ever go into? I can't seem to find any articles on it at all.

Cheers.
Jai.
Was This Post Helpful? 0
  • +
  • -

#10 Choripan  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 9
  • Joined: 27-December 10

Posted 23 June 2011 - 11:24 AM

Hi! I'm programming a game inspired in your tutorials. It's very easy the way you teach us :D
But I have a problem: when the vector has much objects the game starts to run very slowly. How can I fix it?
Sorry for my bad English...
Thanks for the tutorials
Was This Post Helpful? 0
  • +
  • -

#11 stayscrisp  Icon User is offline

  • フカユ
  • member icon

Reputation: 999
  • View blog
  • Posts: 4,177
  • Joined: 14-February 08

Posted 24 June 2011 - 03:17 AM

How many objects?
Was This Post Helpful? 0
  • +
  • -

#12 gamer27lv  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 15
  • Joined: 11-May 12

Posted 13 May 2012 - 11:18 PM

Hello Staycrisp thank you for these tutorials.

P.S: this is the nerdiest sentence I have ever read but it is awesome:

staycrisp - "In this tutorial we will really harness the power of C++ using inheritance and polymorphism."
Was This Post Helpful? 0
  • +
  • -

#13 gamer27lv  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 15
  • Joined: 11-May 12

Posted 14 May 2012 - 12:11 AM

Ok after going through this I have a question...

is this another basic example that is going to be built on?:
player = new Player();
player->Load("char.jpg");

the reason I am asking is because when I thought about having multiple objects getting created with the same image should I have already loaded that image in some where before hand and be passing in a pointer to it instead?
Was This Post Helpful? 0
  • +
  • -

#14 gamer27lv  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 15
  • Joined: 11-May 12

Posted 17 May 2012 - 01:19 AM

Hey Guys if anyone else is having a problem with only being able to change game states once or twice while having game objects on the screen. I was messing with this for the last two days straight!

add this line to the Clean() function after the loop that deletes the object and it is fixed:
 GameObjects.clear();


18	void PlayState::Clean()
19	{
20	 
21	    for(unsigned int i = 0;i < GameObjects.size();i++) {
22	        if(!GameObjects[i]) continue;
23	 
24	        GameObjects[i]->Clean();
25	    }
26	    GameObjects.clear();
27	    printf("PlayState Clean Successful\n");
28	}


Was This Post Helpful? 0
  • +
  • -

Page 1 of 1