Basic Tower Defense done.

Published April 12, 2010
Advertisement
So, I finished up the very basics of a tower defense game today. I added in win/lose conditions today. Now, the player still spawns the mobs manually, so I guess it's not completely a basic tower defense game... but I wanted to move on to the screen handling before I went there, there's certainly enough code to move into screens as it is.

After implementing the Win/Lose conditions today, I started immediately on the ScreenManager functionality. This will handle the different screens in the game, like Main Menu, Loading screen, Game Won screen, Game Over screen, Level screens, etc.

Up until now I've stuffed all scene loading into the GameSystem class, and I'm now moving that into my LevelOne screen class, which is quite a bit of code-moving, which I'm not the most fond of doing... but I guess it was the right order to do things anyway, as I got the basic game set up really fast, for then to improve by adding support for multiple levels/screens.

I should have the code ported to the new screen system tomorrow, which then will give the Win/Lose conditions some more meat, as those would actually load a new screen and not just print out some text :P

The way I've planned my screen system is quite simple. Each screen derives from interface IScreen, which holds pure virtual functions for load(), exit(), enter() and leave().

	virtual void load() = 0; //Load all content contained by this screen	virtual void exit() = 0; //Unload all content contained by this screen	virtual void enter() = 0; //Enter this screen, as in, it should start rendering and receiving updates	virtual void leave() = 0; //Leave this screen, as in, it should stop rendering and receiving updates (paused)


IScreen inherits from my SceneGraph::Node class, this way the enter() function would only set the IScreen as child node of the GameSystem or ScreenManager to start receiving display and update calls, and the leave() function would remove itself from it's parent to stop receiving these update and display calls. Load() would instantiate and load all data and logic for the specific screen, while exit would destruct it. This way I can enter and leave screens without being forced to destroy them, which really isn't necessary for the more simple screens like MainMenu and Load screen, which I also want to enter fast without any load time.

I've then implemented a push/pop system in the ScreenManager. The manager holds a map over all screens available, mapped by the id of each screen. It also holds a screen_history stack, so that when I push a screen, it will be put on top of the stack, and then it's handled in a FILO order. This way I can quite easily push on a menu from a level, which puts the level to a pause and pushes the menu on the stack, through the stack the menu knows that if ESC is pressed, it shouldn't exit but rather return to the previous screen on the stack.

The same method makes it fairly easy to work with the Load screen when changing levels, as you push the Load screen on the stack, then exit the current screen, load the next screen and then pop the top of the stack, which then leaves the load screen and enters the new screen.

I haven't tested my code yet, so there's bound to be some stupid mistakes :P But here's the way I handle pushing and popping:
void ScreenManager::pushScreen(IScreen *screen, unsigned int changeMode){	switch(changeMode)	{	case STATE_CHANGE_OVERWRITE:		{			//Clear history and enter the load screen			screen_history.clear();			pushScreen(NULL, STATE_CHANGE_LOAD);			//While the load screen is displaying, exit the current screen and load the new one			if(current_screen)				current_screen->exit();			screen->load();			screen_history.push_back(screen);			//We're done loading the new screen, so pop the load screen, so that the new screen can display			popScreen();		} break;	case STATE_CHANGE_OVERLAY:		{			//insert at the front of the queue			screen_history.insert(screen_history.begin(), screen);			//enter the new frontear of the history queue			screen_history[0]->enter();			//leave the previous screen paused			current_screen->leave();			//And there's a new current screen in town!			current_screen = screen_history[0];		} break;	case STATE_CHANGE_LOAD:		{			//Just push on the load screen and display it as current screen			screen_history.push_back(load_screen);			load_screen->enter();			current_screen = load_screen;		} break;	};}void ScreenManager::popScreen(){	if(screen_history.empty())		return;	//pop the front of the history queue	screen_history.erase(screen_history.begin());	//enter the new frontear of the history queue	screen_history[0]->enter();	//leave the old frontear	current_screen->leave();	//And there's a new current screen in town!	current_screen = screen_history[0];}


Sorry, no screens today :) Once I have something working on the screen handling side, I should have more to show. Approximately 2 weeks until deadline!
0 likes 2 comments

Comments

Staffan E
Sounds pretty much alike to the system I use for Citizen, apart from semantics. I call mine Scenes though [smile]. And each scene also implements an input handler to filter input through the stack. So far I've had no issues with this setup.
April 12, 2010 12:54 PM
Trefall
Ah, cool. That's probably a good way to do it. Since a Main Menu won't need the same handling of input as, say, an ingame HUD or actual ingame input would.

Right now I connect input keys to commands that is pushed into the system, which is the same set of commands that my AI can use to control game objects. It would probably be powerful to extract this to some kind of file I can load, and then having different files for, say, a menu screen and a level screen.

Cool :)
April 12, 2010 01:21 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement