SlideShare a Scribd company logo
Mini Games Lessons From Rebuilding Classic Games in C++ and OpenGL Joe Linhoff Eugene Jarvis Darren Torpey
MiniGames Rebuilding Three Classic Joe Linhoff Eugene Jarvis Darren Torpey
DePaul University BS Game Development since 2004 programming production and design MS Game Development Animation, Computer Science, Software Engineering, Digital Cinema Outstanding Faculty Eugene Jarvis (Game Designer In Residence) Alexander Seropian (Halo, future GDIR) William Muehl, Ed Keenan, Patrick Curry, Alan Turner
Workshop Target This is a discussion on the teaching of game development and will focus on the use of mini-games to teach game programming and game design. There will be no art.
Modern Game Design 1) Simulate Everything 2) The most realistic shit ever 3) goto 1;
Running A Game Dev Class
Infrastructure Tools students and instructors use same tool set Directory Structure bad paths kill projects Server what kind of support you need Communication Channels define them
Infrastructure Tools MSVS C++ Express Edition TortoiseSVN (Subversion) IM (Skype) Directory Structure explained in other slides Server Subversion Wiki Communication Channels (manage or drown) avoid email to students: SVN wiki from students: SVN Skype
Setup Setup is critical Too many variables for students to do this Setup includes directory structure Must give working starter kits SSID: "joshua weinberg mac bookpro 17" www.joeco.com/qe.htm
Setup DirectX runtime Google and install lastest runtime from Microsoft's site -- needed for sound and input MSVS C/C++ Express Edition
Version Control (highly recommended) Distribution you setup the directory structure you populate the files Help students commit files and can IM you for real-time help Collection commit their work time stamped TortoiseSVN easy to use works well SVN command line tool for scripting free Problems too much committed too little committed
Directory Structure dev -- development root can exist anywhere class1 student1 student2 class2 student1 student2 projects gdc09 <-- $(SolutionDir) art mini -- code files for mini-games qeStartup.c m_minipong.cc mini.vcproj mini.vcproj.user <-- user properties default game.sln bin -- shared bin files qeblue.dll freeglut.dll inc -- shared headers for qe and freeglut qe.h qec.h qefn.h GL/glut.h GL/freeglut.h ...  lib -- shared lib qeblue.lib freeglut.lib
MSVC Properties: paths set in project properties instead of Project and Solution Options inherited properties may also provide good solution Set in all Configurations Debugging Working Directory: $(SolutionDir) Environment: path=$(SolutionDir)../../bin;%path% C/C++ General Additional Include Directories: $(SolutionDir)../../inc Linker General Additional Library Directories: $(SolutionDir)../../lib Input Additional Dependencies: qeblue.dll
mini.vcproj.user this is the 'short' version of the user file mini.vcproj.D630.Joe.user is long version (includes machine and user name) used if long version isn't found all modifications are saved to the long version confusing and not useful to commit the long version since it only works on one machine lesson1: make changes and commit short version lesson2: always test on a different system
Hello World
Building Hello World should be able to download, launch the solution file, build, and run
config.h one project build one program a time // config.h #ifndef CONFIG_H #define CONFIG_H // build one at a time #define BUILD_HELLO  1 // hello world #define BUILD_MINIPONG  0 // pong #define BUILD_MINIMISSILE 0 // missile command #define BUILD_MINIROBO  0 // robotron #endif // ndef CONFIG_H // Copyright (C) 2007-2009 Joe Linhoff, All Rights Reserved // m_hello.c #include &quot;config.h&quot; // include the config file first #if BUILD_HELLO // compile this app #include &quot;qe.h&quot; // engine include file // qeMain() int qeMain(int argc,chr *argv[]) { qePrintf(&quot;%s / %s / %s&quot;,__FILE__,glGetString(GL_VERSION),qeVersion()); qePrintf(&quot;Hello World&quot;); // turn control over to the engine until the user closes the program qeForever(); return 0; } // qeMain() #endif // compile this app // EOF
QE lightweight academic game engine written in C, supports C++ OpenGL see reference
Pong, 1972
Teaching Game Development  Starting Student Projects Research and brainstorm Create &quot;1000 Features&quot; list Choose coordinates Draw screenshot and world map Start very small iterations limit scope of iteration to one sitting get something running in first five minutes bias work in beginning toward visual changes, then input, core mechanic  keep it working, always be improving Start with programmer art Write clean code, bracket resources Refrain from refactoring until you can't stand it Always plan for the future but code for today
Game Development Process workflow Design what are you trying to do Development tools and language build an exe Game development techniques solutions to the problem space
1000 Features (handout) unique value 0..1000 possible feature for your game -- focus on what you see, hear, and how to get it on the screen
000ZY Coordinates Right handed coordinate system Root for all models is (0,0,0) Z is forward Y is up All translation, rotation, scale match Maya i.e. given TRS, build matrix such that object draws like it does in Maya Choose units one unit is one foot
Cameras software metaphor // JFL 03 Oct 08 class Camera : public qe { public: chr name[16]; // name float fovyHalfRad; // in radians float nearClip; // near clipping plane float farClip; // far clipping plane float winWidth; // in pixels float winHeight; // in pixels float winWDivH; // window aspect ratio float nearHeight; // height at near plane float mat12[12]; // camera matrix int draw(); // draw-step function Camera(chr *name); // constructor }; // class Camera // setup -- happens once in mini-pong this->nearClip = 1; this->farClip = 500; this->fovyHalfRad = 0.5*((63*PI)/180.0); this->nearHeight =  this->nearClip * MathTanf(this->fovyHalfRad); // camera matrix -- from world space into camera space SET3(pos,0,CAMERA_Y,0); // position of camera SET3(at,0,0,0); // where camera is looking at SET3(up,0,0,-1); // the camera's up direction qeCamLookAtM12f(this->mat12,pos,at,up); // camera mat // draw -- set every frame before you draw if(qeGetWindowSize(&this->winWidth,&this->winHeight)<0) bret(-2); // jump to function exit this->winWDivH=this->winWidth/this->winHeight; // set the PROJECTION matrix (the camera lens) glMatrixMode(GL_PROJECTION); glLoadIdentity(); float yy = this->nearHeight; float xx=this->nearHeight*this->winWDivH; glFrustum(-xx,xx,-yy,yy,this->nearClip,this->farClip); // MODELVIEW (position and orientation of the camera) glMatrixMode(GL_MODELVIEW); glLoadIdentity(); qeGLM12f(this->mat12); // set matrix
OpenGL 4x4 Matrices (M16) #define VecTransformM16(_d_,_v_,_m_) // d=dstvec v=srcvec m=mat16 (_d_)[0]=(_v_)[0]*(_m_)[M16_11]+(_v_)[1]*(_m_)[M16_21] +(_v_)[2]*(_m_)[M16_31]+(_m_)[M16_X], (_d_)[1]=(_v_)[0]*(_m_)[M16_12]+(_v_)[1]*(_m_)[M16_22] +(_v_)[2]*(_m_)[M16_32]+(_m_)[M16_Y], (_d_)[2]=(_v_)[0]*(_m_)[M16_13]+(_v_)[1]*(_m_)[M16_23] +(_v_)[2]*(_m_)[M16_33]+(_m_)[M16_Z] #define VecRotM16(_d_,_v_,_m_) (_d_)[0]=(_v_)[0]*(_m_)[M16_11]+(_v_)[1]*(_m_)[M16_21] +(_v_)[2]*(_m_)[M16_31], (_d_)[1]=(_v_)[0]*(_m_)[M16_12]+(_v_)[1]*(_m_)[M16_22] +(_v_)[2]*(_m_)[M16_32], (_d_)[2]=(_v_)[0]*(_m_)[M16_13]+(_v_)[1]*(_m_)[M16_23] +(_v_)[2]*(_m_)[M16_33] float mat[16]; glGetFloatv(GL_MODELVIEW_MATRIX,mat);
QE 3x4 matrices (M12) non-standard: XYZ and 3x3 rotation matrix #define VecTransformM12(_d_,_v_,_m_) (_d_)[0]=(_v_)[0]*(_m_)[M12_11]+(_v_)[1]*(_m_)[M12_21] +(_v_)[2]*(_m_)[M12_31]+(_m_)[M12_X], (_d_)[1]=(_v_)[0]*(_m_)[M12_12]+(_v_)[1]*(_m_)[M12_22] +(_v_)[2]*(_m_)[M12_32]+(_m_)[M12_Y], (_d_)[2]=(_v_)[0]*(_m_)[M12_13]+(_v_)[1]*(_m_)[M12_23] +(_v_)[2]*(_m_)[M12_33]+(_m_)[M12_Z] #define VecRotM12(_d_,_v_,_m_) (_d_)[0]=(_v_)[0]*(_m_)[M12_11]+(_v_)[1]*(_m_)[M12_21] +(_v_)[2]*(_m_)[M12_31], (_d_)[1]=(_v_)[0]*(_m_)[M12_12]+(_v_)[1]*(_m_)[M12_22] +(_v_)[2]*(_m_)[M12_32], (_d_)[2]=(_v_)[0]*(_m_)[M12_13]+(_v_)[1]*(_m_)[M12_23] +(_v_)[2]*(_m_)[M12_33]
Velocities variable frame rates float qeTimeFrame() returns seconds since engine start / restart keep track of the time since the last update use Euler integration // JFL 25 Jan 09 class Ball : public qe { public: chr name[16]; // name float timeOfLastUpdate; // in seconds float xyz[3]; // current float vel[3]; // velocity Ball(chr *name); // constructor int update(); // update function int draw(); // draw function }; // class Ball // update, move the ball float t; // find time since last update t=this->timeOfLastUpdate; this->timeOfLastUpdate=qeTimeFrame(); t=this->timeOfLastUpdate-t; // delta // xyz += vel*t this->xyz[0]+=this->vel[0]*t; this->xyz[1]+=this->vel[1]*t; this->xyz[2]+=this->vel[2]*t;
Collisions simplifications move, then collide non-moving objects don't worry about resolution order run through once guarantee after detection, move objects out of that collision (may be in another -- too bad) end up in valid world position no movement after collision resolution
Collisions ball v world if over right or left score point, re-serve if over top or bottom set to top or bottom reflect (flip z vel) paddle v world make sure player stays on the court ball v paddles test against near edge of paddle set to near edge bounce (flip x vel) add English (later) improvements preserve distance when colliding don't just set to collision edge  reflect at collision point does order matter? theoretically unlikely fast balls could run through the paddle depends on paddle size and ball speed really need to handle moving collisions
Game Controller Use Singleton Pattern manage the game loop with one Game instance good chance to use the Singleton pattern Game *Game::instance=0; // initialize Singleton // JFL 13 Aug 08 Game::Game(chr *name) : qeUpdateBase(name,0,GAMEID_GAME) { // constructor this->name = qeObjName(this->_oShared); // get name } // Game::Game() // JFL 16 Aug 08 void Game::InstanceDelete() { if(Game::instance) Game::instance->objRemove(); // request obj removal  } // GameInstanceDelete() // JFL 16 Aug 08 Game* Game::InstanceNew() { if(!Game::instance) Game::instance = new Game(&quot;game1&quot;); return Game::instance; } // Game::InstanceNew() // JFL 16 Aug 08 Game* Game::InstanceGet() { return Game::instance; } // Game::InstanceGet() // JFL 03 Oct 08 class Game : public qeUpdateBase { // game controller record chr *name; // points to system name  static Game *instance; // singleton Game(chr *name); // constructor public: static Game* InstanceNew(); static Game* InstanceGet(); static void InstanceDelete(); }; // class Game
qeUpdateBase base class engine base class provides virtual update() draw() final() adds to list of engine objects all objs derived from qeUpdateBase update() functions called before any of the draw() functions
qe base class derive from qe for simple objects no overhead runs through engine's memory system keeps count to keep you honest zeros memory on allocation
Game Superstructure visualization
Button Counts uns qeInpButton(uns inpb); // QEINPBUTTON_ by default the keys are mapped as buttons every up and down transition, the engine adds a value to that count single button count value gives state and history if odd ==> down if(b&1) /* down */; store count, come back later and find how many transitions good for coins
Joysticks float qeInpJoyAxisf(uns joy,uns axis); // use QEJOYAXIS_ uns qeInpJoyButton(uns joy,uns button); // use QEJOYBUTTON_ joysticks start at 0 OK to test even if stick is not present the axis is defined qeInpJoyAxisf() returns values -1..1 mind the DEADZONE
Draw simple filled rectangle glColor3f(1,1,1); glPolygonMode(GL_FRONT,GL_FILL); // draw filled polygons glBegin(GL_QUADS); // draw quads counter-clockwise from camera's view glVertex3f(-1,0,-3); glVertex3f(-1,0,3); glVertex3f(2,0,3); glVertex3f(2,0,-3); glEnd();
Loading and Playing Sounds capture sounds low-res, mono for effects wav files register play // setup sound &quot;bump&quot; on channel 1 if((r=qeSndNew(&quot;bump&quot;,M_SNDNEW_CH_1,0,&quot;art/sounds/pongbump.wav&quot;))<0) BRK(); // trigger the sound qeSndPlay(&quot;bump&quot;);
qePrintf() qeLogf() printf-like function calls to qePrintf() get added to log file qelog.txt add to log file directly with qeLogf()
BRK() normal asserts() kill the game -- this can be bad code with BRK() to continue running the &quot;break&quot; goes away when outside the debugger
Bracket Resources bullet-proof allocation and freeing of resources memory file-handles etc usually two ways to kill normal object life program abort good solution initialization guaranteed to run clear all fields body finalization guaranteed to run can be triggered when body finishes normally or w/abort
Missile Command, 1980
Strings 'chr' in qebase.h defines an 8 bit character for internal programming use guarantees & principles sizes are always byte sizes of whole buffers zero termination guaranteed if dstsize>0 pointer-terminated and zero-terminated strings much faster, safer pointer-terminator always option, pass 0 must be zero-terminated sz* functions defined qebase.h int szcpy(chr *dst,int dstsize,chr *ss,chr *sx); ss is string start, sx is pointer-terminator or 0 int szfmt(chr *dst,int dstsize,chr *fmt,...); printf-like fmt
LLNode Simple doubly linked list node Type field t specifies game-specific type Type field is zero for list head // linked list typedef struct _llnode { struct _llnode *next; struct _llnode *prev; int t; // type: listhead=0, others=non-zero } LLNode; // JFL 23 Aug 06 // JFL 20 Mar 08; re-worked from DL void LLMakeHead(LLNode *h) { h->next=h->prev=h; h->t=0; } // LLMakeHead() // JFL 20 Mar 08; re-worked from DL // JFL 18 May 08; link to self void LLMakeNode(LLNode *n,int t) { n->next=n->prev=n; n->t=t; } // LLMakeNode() // JFL 23 Aug 06 // JFL 20 Mar 08; re-worked from DL void LLLinkAfter(LLNode *h,LLNode *n) { n->next=h->next; n->next->prev=n; n->prev=h; h->next=n; } // LLLinkAfter() // JFL 05 May 06 // JFL 20 Mar 08; re-worked from DL void LLLinkBefore(LLNode *h,LLNode *n) { n->prev=h->prev; n->prev->next=n; n->next=h; h->prev=n; } // LLLinkBefore() // JFL 05 May 06 // JFL 20 Mar 08; re-worked from DL void LLUnlink(LLNode *n) { n->prev->next=n->next; n->next->prev=n->prev; n->next=n->prev=n; // multiple unlinks OK } // LLUnlink()
Robotron, 1982
 

More Related Content

Gdc09 Minigames

  • 1. Mini Games Lessons From Rebuilding Classic Games in C++ and OpenGL Joe Linhoff Eugene Jarvis Darren Torpey
  • 2. MiniGames Rebuilding Three Classic Joe Linhoff Eugene Jarvis Darren Torpey
  • 3. DePaul University BS Game Development since 2004 programming production and design MS Game Development Animation, Computer Science, Software Engineering, Digital Cinema Outstanding Faculty Eugene Jarvis (Game Designer In Residence) Alexander Seropian (Halo, future GDIR) William Muehl, Ed Keenan, Patrick Curry, Alan Turner
  • 4. Workshop Target This is a discussion on the teaching of game development and will focus on the use of mini-games to teach game programming and game design. There will be no art.
  • 5. Modern Game Design 1) Simulate Everything 2) The most realistic shit ever 3) goto 1;
  • 6. Running A Game Dev Class
  • 7. Infrastructure Tools students and instructors use same tool set Directory Structure bad paths kill projects Server what kind of support you need Communication Channels define them
  • 8. Infrastructure Tools MSVS C++ Express Edition TortoiseSVN (Subversion) IM (Skype) Directory Structure explained in other slides Server Subversion Wiki Communication Channels (manage or drown) avoid email to students: SVN wiki from students: SVN Skype
  • 9. Setup Setup is critical Too many variables for students to do this Setup includes directory structure Must give working starter kits SSID: &quot;joshua weinberg mac bookpro 17&quot; www.joeco.com/qe.htm
  • 10. Setup DirectX runtime Google and install lastest runtime from Microsoft's site -- needed for sound and input MSVS C/C++ Express Edition
  • 11. Version Control (highly recommended) Distribution you setup the directory structure you populate the files Help students commit files and can IM you for real-time help Collection commit their work time stamped TortoiseSVN easy to use works well SVN command line tool for scripting free Problems too much committed too little committed
  • 12. Directory Structure dev -- development root can exist anywhere class1 student1 student2 class2 student1 student2 projects gdc09 <-- $(SolutionDir) art mini -- code files for mini-games qeStartup.c m_minipong.cc mini.vcproj mini.vcproj.user <-- user properties default game.sln bin -- shared bin files qeblue.dll freeglut.dll inc -- shared headers for qe and freeglut qe.h qec.h qefn.h GL/glut.h GL/freeglut.h ... lib -- shared lib qeblue.lib freeglut.lib
  • 13. MSVC Properties: paths set in project properties instead of Project and Solution Options inherited properties may also provide good solution Set in all Configurations Debugging Working Directory: $(SolutionDir) Environment: path=$(SolutionDir)../../bin;%path% C/C++ General Additional Include Directories: $(SolutionDir)../../inc Linker General Additional Library Directories: $(SolutionDir)../../lib Input Additional Dependencies: qeblue.dll
  • 14. mini.vcproj.user this is the 'short' version of the user file mini.vcproj.D630.Joe.user is long version (includes machine and user name) used if long version isn't found all modifications are saved to the long version confusing and not useful to commit the long version since it only works on one machine lesson1: make changes and commit short version lesson2: always test on a different system
  • 16. Building Hello World should be able to download, launch the solution file, build, and run
  • 17. config.h one project build one program a time // config.h #ifndef CONFIG_H #define CONFIG_H // build one at a time #define BUILD_HELLO 1 // hello world #define BUILD_MINIPONG 0 // pong #define BUILD_MINIMISSILE 0 // missile command #define BUILD_MINIROBO 0 // robotron #endif // ndef CONFIG_H // Copyright (C) 2007-2009 Joe Linhoff, All Rights Reserved // m_hello.c #include &quot;config.h&quot; // include the config file first #if BUILD_HELLO // compile this app #include &quot;qe.h&quot; // engine include file // qeMain() int qeMain(int argc,chr *argv[]) { qePrintf(&quot;%s / %s / %s&quot;,__FILE__,glGetString(GL_VERSION),qeVersion()); qePrintf(&quot;Hello World&quot;); // turn control over to the engine until the user closes the program qeForever(); return 0; } // qeMain() #endif // compile this app // EOF
  • 18. QE lightweight academic game engine written in C, supports C++ OpenGL see reference
  • 20. Teaching Game Development Starting Student Projects Research and brainstorm Create &quot;1000 Features&quot; list Choose coordinates Draw screenshot and world map Start very small iterations limit scope of iteration to one sitting get something running in first five minutes bias work in beginning toward visual changes, then input, core mechanic keep it working, always be improving Start with programmer art Write clean code, bracket resources Refrain from refactoring until you can't stand it Always plan for the future but code for today
  • 21. Game Development Process workflow Design what are you trying to do Development tools and language build an exe Game development techniques solutions to the problem space
  • 22. 1000 Features (handout) unique value 0..1000 possible feature for your game -- focus on what you see, hear, and how to get it on the screen
  • 23. 000ZY Coordinates Right handed coordinate system Root for all models is (0,0,0) Z is forward Y is up All translation, rotation, scale match Maya i.e. given TRS, build matrix such that object draws like it does in Maya Choose units one unit is one foot
  • 24. Cameras software metaphor // JFL 03 Oct 08 class Camera : public qe { public: chr name[16]; // name float fovyHalfRad; // in radians float nearClip; // near clipping plane float farClip; // far clipping plane float winWidth; // in pixels float winHeight; // in pixels float winWDivH; // window aspect ratio float nearHeight; // height at near plane float mat12[12]; // camera matrix int draw(); // draw-step function Camera(chr *name); // constructor }; // class Camera // setup -- happens once in mini-pong this->nearClip = 1; this->farClip = 500; this->fovyHalfRad = 0.5*((63*PI)/180.0); this->nearHeight = this->nearClip * MathTanf(this->fovyHalfRad); // camera matrix -- from world space into camera space SET3(pos,0,CAMERA_Y,0); // position of camera SET3(at,0,0,0); // where camera is looking at SET3(up,0,0,-1); // the camera's up direction qeCamLookAtM12f(this->mat12,pos,at,up); // camera mat // draw -- set every frame before you draw if(qeGetWindowSize(&this->winWidth,&this->winHeight)<0) bret(-2); // jump to function exit this->winWDivH=this->winWidth/this->winHeight; // set the PROJECTION matrix (the camera lens) glMatrixMode(GL_PROJECTION); glLoadIdentity(); float yy = this->nearHeight; float xx=this->nearHeight*this->winWDivH; glFrustum(-xx,xx,-yy,yy,this->nearClip,this->farClip); // MODELVIEW (position and orientation of the camera) glMatrixMode(GL_MODELVIEW); glLoadIdentity(); qeGLM12f(this->mat12); // set matrix
  • 25. OpenGL 4x4 Matrices (M16) #define VecTransformM16(_d_,_v_,_m_) // d=dstvec v=srcvec m=mat16 (_d_)[0]=(_v_)[0]*(_m_)[M16_11]+(_v_)[1]*(_m_)[M16_21] +(_v_)[2]*(_m_)[M16_31]+(_m_)[M16_X], (_d_)[1]=(_v_)[0]*(_m_)[M16_12]+(_v_)[1]*(_m_)[M16_22] +(_v_)[2]*(_m_)[M16_32]+(_m_)[M16_Y], (_d_)[2]=(_v_)[0]*(_m_)[M16_13]+(_v_)[1]*(_m_)[M16_23] +(_v_)[2]*(_m_)[M16_33]+(_m_)[M16_Z] #define VecRotM16(_d_,_v_,_m_) (_d_)[0]=(_v_)[0]*(_m_)[M16_11]+(_v_)[1]*(_m_)[M16_21] +(_v_)[2]*(_m_)[M16_31], (_d_)[1]=(_v_)[0]*(_m_)[M16_12]+(_v_)[1]*(_m_)[M16_22] +(_v_)[2]*(_m_)[M16_32], (_d_)[2]=(_v_)[0]*(_m_)[M16_13]+(_v_)[1]*(_m_)[M16_23] +(_v_)[2]*(_m_)[M16_33] float mat[16]; glGetFloatv(GL_MODELVIEW_MATRIX,mat);
  • 26. QE 3x4 matrices (M12) non-standard: XYZ and 3x3 rotation matrix #define VecTransformM12(_d_,_v_,_m_) (_d_)[0]=(_v_)[0]*(_m_)[M12_11]+(_v_)[1]*(_m_)[M12_21] +(_v_)[2]*(_m_)[M12_31]+(_m_)[M12_X], (_d_)[1]=(_v_)[0]*(_m_)[M12_12]+(_v_)[1]*(_m_)[M12_22] +(_v_)[2]*(_m_)[M12_32]+(_m_)[M12_Y], (_d_)[2]=(_v_)[0]*(_m_)[M12_13]+(_v_)[1]*(_m_)[M12_23] +(_v_)[2]*(_m_)[M12_33]+(_m_)[M12_Z] #define VecRotM12(_d_,_v_,_m_) (_d_)[0]=(_v_)[0]*(_m_)[M12_11]+(_v_)[1]*(_m_)[M12_21] +(_v_)[2]*(_m_)[M12_31], (_d_)[1]=(_v_)[0]*(_m_)[M12_12]+(_v_)[1]*(_m_)[M12_22] +(_v_)[2]*(_m_)[M12_32], (_d_)[2]=(_v_)[0]*(_m_)[M12_13]+(_v_)[1]*(_m_)[M12_23] +(_v_)[2]*(_m_)[M12_33]
  • 27. Velocities variable frame rates float qeTimeFrame() returns seconds since engine start / restart keep track of the time since the last update use Euler integration // JFL 25 Jan 09 class Ball : public qe { public: chr name[16]; // name float timeOfLastUpdate; // in seconds float xyz[3]; // current float vel[3]; // velocity Ball(chr *name); // constructor int update(); // update function int draw(); // draw function }; // class Ball // update, move the ball float t; // find time since last update t=this->timeOfLastUpdate; this->timeOfLastUpdate=qeTimeFrame(); t=this->timeOfLastUpdate-t; // delta // xyz += vel*t this->xyz[0]+=this->vel[0]*t; this->xyz[1]+=this->vel[1]*t; this->xyz[2]+=this->vel[2]*t;
  • 28. Collisions simplifications move, then collide non-moving objects don't worry about resolution order run through once guarantee after detection, move objects out of that collision (may be in another -- too bad) end up in valid world position no movement after collision resolution
  • 29. Collisions ball v world if over right or left score point, re-serve if over top or bottom set to top or bottom reflect (flip z vel) paddle v world make sure player stays on the court ball v paddles test against near edge of paddle set to near edge bounce (flip x vel) add English (later) improvements preserve distance when colliding don't just set to collision edge reflect at collision point does order matter? theoretically unlikely fast balls could run through the paddle depends on paddle size and ball speed really need to handle moving collisions
  • 30. Game Controller Use Singleton Pattern manage the game loop with one Game instance good chance to use the Singleton pattern Game *Game::instance=0; // initialize Singleton // JFL 13 Aug 08 Game::Game(chr *name) : qeUpdateBase(name,0,GAMEID_GAME) { // constructor this->name = qeObjName(this->_oShared); // get name } // Game::Game() // JFL 16 Aug 08 void Game::InstanceDelete() { if(Game::instance) Game::instance->objRemove(); // request obj removal } // GameInstanceDelete() // JFL 16 Aug 08 Game* Game::InstanceNew() { if(!Game::instance) Game::instance = new Game(&quot;game1&quot;); return Game::instance; } // Game::InstanceNew() // JFL 16 Aug 08 Game* Game::InstanceGet() { return Game::instance; } // Game::InstanceGet() // JFL 03 Oct 08 class Game : public qeUpdateBase { // game controller record chr *name; // points to system name static Game *instance; // singleton Game(chr *name); // constructor public: static Game* InstanceNew(); static Game* InstanceGet(); static void InstanceDelete(); }; // class Game
  • 31. qeUpdateBase base class engine base class provides virtual update() draw() final() adds to list of engine objects all objs derived from qeUpdateBase update() functions called before any of the draw() functions
  • 32. qe base class derive from qe for simple objects no overhead runs through engine's memory system keeps count to keep you honest zeros memory on allocation
  • 34. Button Counts uns qeInpButton(uns inpb); // QEINPBUTTON_ by default the keys are mapped as buttons every up and down transition, the engine adds a value to that count single button count value gives state and history if odd ==> down if(b&1) /* down */; store count, come back later and find how many transitions good for coins
  • 35. Joysticks float qeInpJoyAxisf(uns joy,uns axis); // use QEJOYAXIS_ uns qeInpJoyButton(uns joy,uns button); // use QEJOYBUTTON_ joysticks start at 0 OK to test even if stick is not present the axis is defined qeInpJoyAxisf() returns values -1..1 mind the DEADZONE
  • 36. Draw simple filled rectangle glColor3f(1,1,1); glPolygonMode(GL_FRONT,GL_FILL); // draw filled polygons glBegin(GL_QUADS); // draw quads counter-clockwise from camera's view glVertex3f(-1,0,-3); glVertex3f(-1,0,3); glVertex3f(2,0,3); glVertex3f(2,0,-3); glEnd();
  • 37. Loading and Playing Sounds capture sounds low-res, mono for effects wav files register play // setup sound &quot;bump&quot; on channel 1 if((r=qeSndNew(&quot;bump&quot;,M_SNDNEW_CH_1,0,&quot;art/sounds/pongbump.wav&quot;))<0) BRK(); // trigger the sound qeSndPlay(&quot;bump&quot;);
  • 38. qePrintf() qeLogf() printf-like function calls to qePrintf() get added to log file qelog.txt add to log file directly with qeLogf()
  • 39. BRK() normal asserts() kill the game -- this can be bad code with BRK() to continue running the &quot;break&quot; goes away when outside the debugger
  • 40. Bracket Resources bullet-proof allocation and freeing of resources memory file-handles etc usually two ways to kill normal object life program abort good solution initialization guaranteed to run clear all fields body finalization guaranteed to run can be triggered when body finishes normally or w/abort
  • 42. Strings 'chr' in qebase.h defines an 8 bit character for internal programming use guarantees & principles sizes are always byte sizes of whole buffers zero termination guaranteed if dstsize>0 pointer-terminated and zero-terminated strings much faster, safer pointer-terminator always option, pass 0 must be zero-terminated sz* functions defined qebase.h int szcpy(chr *dst,int dstsize,chr *ss,chr *sx); ss is string start, sx is pointer-terminator or 0 int szfmt(chr *dst,int dstsize,chr *fmt,...); printf-like fmt
  • 43. LLNode Simple doubly linked list node Type field t specifies game-specific type Type field is zero for list head // linked list typedef struct _llnode { struct _llnode *next; struct _llnode *prev; int t; // type: listhead=0, others=non-zero } LLNode; // JFL 23 Aug 06 // JFL 20 Mar 08; re-worked from DL void LLMakeHead(LLNode *h) { h->next=h->prev=h; h->t=0; } // LLMakeHead() // JFL 20 Mar 08; re-worked from DL // JFL 18 May 08; link to self void LLMakeNode(LLNode *n,int t) { n->next=n->prev=n; n->t=t; } // LLMakeNode() // JFL 23 Aug 06 // JFL 20 Mar 08; re-worked from DL void LLLinkAfter(LLNode *h,LLNode *n) { n->next=h->next; n->next->prev=n; n->prev=h; h->next=n; } // LLLinkAfter() // JFL 05 May 06 // JFL 20 Mar 08; re-worked from DL void LLLinkBefore(LLNode *h,LLNode *n) { n->prev=h->prev; n->prev->next=n; n->next=h; h->prev=n; } // LLLinkBefore() // JFL 05 May 06 // JFL 20 Mar 08; re-worked from DL void LLUnlink(LLNode *n) { n->prev->next=n->next; n->next->prev=n->prev; n->next=n->prev=n; // multiple unlinks OK } // LLUnlink()
  • 45.