MiniGamesRebuilding Three Classic
Joe LinhoffEugene JarvisDarren Torpey
Missile Command, 1980Atari
Learning Objectives
dynamic object management with lists
C-style polymorphism screen to world, world
to screen coordinate
Dynamic Objects
Don't know how many enemy missiles there will be at any time
Missiles can split (MIRVs)
Don't know how many player missiles there will be at any time
The static ways will not work
Lists are the backbone of game development
Doubly linked lists make it easy and quick to unlink nodes
Dynamic Infrastructureinit body final
Good code construction requires guaranteed init and final
Init sets up all list heads
Final runs through & frees all lists
The lists 'own' the object write code such that only way to object is through
list; do not keep any other copies always test pointers before use generally: one owner for every resource
Game Controller Class Owns Game Objects
Simplifies collisions and other multi-type operations
init is in constructor initializes all lists by
making them list heads final is in 'final'
zaps all remaining objects in lists
not destructor b/c we want more control
idea is to clean up after yourself
// JFL 13 Aug 08Game::Game(chr *name) : qeUpdateBase(name,0,GAMEID_GAME){ this->name = qeObjName(this->_oShared); LLMakeHead(&this->listOfGameThings); LLMakeHead(&this->listOfWorlds); LLMakeHead(&this->listOfBuildings); LLMakeHead(&this->listOfPlayers); LLMakeHead(&this->listOfBadGuys); LLMakeHead(&this->listOfPlayerProjs); LLMakeHead(&this->listOfBadGuyProjs);} // Game::Game()
// JFL 18 Aug 08void Game::final(){ // don't call directly, use objRemove() Game *game; if((game=Game::instance)) { gameZapList(&game->listOfBadGuyProjs); gameZapList(&game->listOfPlayerProjs); gameZapList(&game->listOfBadGuys); gameZapList(&game->listOfPlayers); gameZapList(&game->listOfBuildings); gameZapList(&game->listOfWorlds); gameZapList(&game->listOfGameThings); delete game; // free the C++ object Game::instance=0; // reset pointer }} // Game::final()
Game Objects
Constructors must setup their own node LLMakeNode() here
part of init Destructor not
explicitly needed
// JFL 14 Aug 07World::World(chr *name){ szcpy(this->name,sizeof(this->name),name,0); LLMakeNode(this,GAMEID_WORLD); SET3(this->xyzMin,-110,0,0); SET3(this->xyzMax,110,200,0);} // World::World()
// JFL 14 Aug 07int World::draw(void){ qefnDrawGrid(50,10); qefnDrawAxes(1); return 0;} // World::draw()
Owners Must Always Guarantee Cleanup
Game::final() runs through all the lists of objects it owns and zaps them after all the objects it owns are zapped, it deletes itself
and zeros its Singleton instance pointer not called directy, Game derives from qeUpdateBase
and call to objRemove() causes engine to call final() when it's safe
// JFL 18 Aug 08void Game::final(){ // don't call directly, use objRemove() Game *game; if((game=Game::instance)) { gameZapList(&game->listOfBadGuyProjs); gameZapList(&game->listOfPlayerProjs); gameZapList(&game->listOfBadGuys); gameZapList(&game->listOfPlayers); gameZapList(&game->listOfBuildings); gameZapList(&game->listOfWorlds); gameZapList(&game->listOfGameThings); delete game; // free the C++ object Game::instance=0; // reset pointer }} // Game::final()
Zap & Zap List
Use while when unlinking
Zap unlink calls destructor frees memory 'zap' b/c it's
descriptive, short, not generally used, and cool
// JFL 25 Jan 09int gameZapList(LLNode *head){ LLNode *n; while((n=head->next)&&n->t) gameZap(n); return 1; // nonzero return assumed} // gameZapList()
// JFL 25 Jan 09int gameZap(LLNode *n){ // unlink the game object LLUnlink(n);
// call destructor for game objects switch(n->t) { case GAMEID_INPUT:((Input*)n)->~Input();break; case GAMEID_CAMERA:((Camera*)n)->~Camera();break; case GAMEID_HUD:((HUD*)n)->~HUD();break; case GAMEID_WORLD:((World*)n)->~World();break; case GAMEID_BUILDING:((Building*)n)->~Building();break; case GAMEID_PLAYER:((Player*)n)->~Player();break; case GAMEID_PROJ:((Proj*)n)->~Proj();break; case GAMEID_BADGUY:((BadGuy*)n)->~BadGuy();break; default: BRK(); // add handler for type } // switch
// free the memory delete (qe*)n;
return 1; // nonzero return assumed} // gameZap()
Simple Doubly Linked C-Style Polymorphic Lists
Build class with list node first Unique type identifiers signal type Use static_cast<TYPE>(node) Scanning is easy
LLNode *n;BadGuy *bad;
for(n=this->listOfBadGuys.next;n->t;n=n->next){ if(n->t!=GAMEID_BADGUY) continue; bad=(BadGuy*)n; // handle bad guy } // for
Scan from any node forward or backward, skip list head
Use of continue to minimize nesting "Should" use static_cast<>()
Top Related