Gdc09 Minigames

803 views

Published on

  • Be the first to comment

  • Be the first to like this

Gdc09 Minigames

  1. 1. Mini Games Lessons From Rebuilding Classic Games in C++ and OpenGL Joe Linhoff Eugene Jarvis Darren Torpey
  2. 2. MiniGames Rebuilding Three Classic Joe Linhoff Eugene Jarvis Darren Torpey
  3. 3. DePaul University <ul><li>BS Game Development since 2004 </li></ul><ul><ul><li>programming </li></ul></ul><ul><ul><li>production and design </li></ul></ul><ul><li>MS Game Development </li></ul><ul><li>Animation, Computer Science, Software Engineering, Digital Cinema </li></ul><ul><li>Outstanding Faculty </li></ul><ul><ul><li>Eugene Jarvis (Game Designer In Residence) </li></ul></ul><ul><ul><li>Alexander Seropian (Halo, future GDIR) </li></ul></ul><ul><ul><li>William Muehl, Ed Keenan, Patrick Curry, Alan Turner </li></ul></ul>
  4. 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. 5. Modern Game Design <ul><li>1) Simulate Everything </li></ul><ul><li>2) The most realistic shit ever </li></ul><ul><li>3) goto 1; </li></ul>
  6. 6. Running A Game Dev Class
  7. 7. Infrastructure <ul><li>Tools </li></ul><ul><ul><li>students and instructors use same tool set </li></ul></ul><ul><li>Directory Structure </li></ul><ul><ul><li>bad paths kill projects </li></ul></ul><ul><li>Server </li></ul><ul><ul><li>what kind of support you need </li></ul></ul><ul><li>Communication Channels </li></ul><ul><ul><li>define them </li></ul></ul>
  8. 8. Infrastructure <ul><li>Tools </li></ul><ul><ul><li>MSVS C++ Express Edition </li></ul></ul><ul><ul><li>TortoiseSVN (Subversion) </li></ul></ul><ul><ul><li>IM (Skype) </li></ul></ul><ul><li>Directory Structure </li></ul><ul><ul><li>explained in other slides </li></ul></ul><ul><li>Server </li></ul><ul><ul><li>Subversion </li></ul></ul><ul><ul><li>Wiki </li></ul></ul><ul><li>Communication Channels (manage or drown) </li></ul><ul><ul><li>avoid email </li></ul></ul><ul><ul><li>to students: SVN wiki </li></ul></ul><ul><ul><li>from students: SVN Skype </li></ul></ul>
  9. 9. Setup <ul><li>Setup is critical </li></ul><ul><li>Too many variables for students to do this </li></ul><ul><li>Setup includes directory structure </li></ul><ul><li>Must give working starter kits </li></ul><ul><li>SSID: &quot;joshua weinberg mac bookpro 17&quot; </li></ul><ul><li>www.joeco.com/qe.htm </li></ul>
  10. 10. Setup <ul><li>DirectX runtime </li></ul><ul><ul><li>Google and install lastest runtime from Microsoft's site -- needed for sound and input </li></ul></ul><ul><li>MSVS C/C++ Express Edition </li></ul>
  11. 11. Version Control (highly recommended) <ul><li>Distribution </li></ul><ul><ul><li>you setup the directory structure </li></ul></ul><ul><ul><li>you populate the files </li></ul></ul><ul><li>Help </li></ul><ul><ul><li>students commit files and can IM you for real-time help </li></ul></ul><ul><li>Collection </li></ul><ul><ul><li>commit their work </li></ul></ul><ul><ul><li>time stamped </li></ul></ul><ul><li>TortoiseSVN </li></ul><ul><ul><li>easy to use </li></ul></ul><ul><ul><li>works well </li></ul></ul><ul><ul><li>SVN command line tool for scripting </li></ul></ul><ul><ul><li>free </li></ul></ul><ul><li>Problems </li></ul><ul><ul><li>too much committed </li></ul></ul><ul><ul><li>too little committed </li></ul></ul>
  12. 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. 13. MSVC Properties: paths <ul><li>set in project properties instead of Project and Solution Options </li></ul><ul><li>inherited properties may also provide good solution </li></ul>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. 14. mini.vcproj.user <ul><li>this is the 'short' version of the user file </li></ul><ul><ul><li>mini.vcproj.D630.Joe.user is long version (includes machine and user name) </li></ul></ul><ul><li>used if long version isn't found </li></ul><ul><li>all modifications are saved to the long version </li></ul><ul><li>confusing and not useful to commit the long version since it only works on one machine </li></ul><ul><li>lesson1: make changes and commit short version </li></ul><ul><li>lesson2: always test on a different system </li></ul>
  15. 15. Hello World
  16. 16. Building Hello World <ul><li>should be able to download, launch the solution file, build, and run </li></ul>
  17. 17. config.h <ul><li>one project </li></ul><ul><li>build one program a time </li></ul>// 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. 18. QE <ul><li>lightweight academic game engine </li></ul><ul><li>written in C, supports C++ </li></ul><ul><li>OpenGL </li></ul><ul><li>see reference </li></ul>
  19. 19. Pong, 1972
  20. 20. Teaching Game Development Starting Student Projects <ul><li>Research and brainstorm </li></ul><ul><li>Create &quot;1000 Features&quot; list </li></ul><ul><li>Choose coordinates </li></ul><ul><li>Draw screenshot and world map </li></ul><ul><li>Start very small iterations </li></ul><ul><ul><li>limit scope of iteration to one sitting </li></ul></ul><ul><ul><li>get something running in first five minutes </li></ul></ul><ul><ul><li>bias work in beginning toward visual changes, then input, core mechanic </li></ul></ul><ul><ul><li>keep it working, always be improving </li></ul></ul><ul><li>Start with programmer art </li></ul><ul><li>Write clean code, bracket resources </li></ul><ul><li>Refrain from refactoring until you can't stand it </li></ul><ul><li>Always plan for the future but code for today </li></ul>
  21. 21. Game Development <ul><li>Process </li></ul><ul><ul><li>workflow </li></ul></ul><ul><li>Design </li></ul><ul><ul><li>what are you trying to do </li></ul></ul><ul><li>Development tools and language </li></ul><ul><ul><li>build an exe </li></ul></ul><ul><li>Game development techniques </li></ul><ul><ul><li>solutions to the problem space </li></ul></ul>
  22. 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. 23. 000ZY Coordinates <ul><li>Right handed coordinate system </li></ul><ul><li>Root for all models is (0,0,0) </li></ul><ul><li>Z is forward </li></ul><ul><li>Y is up </li></ul><ul><li>All translation, rotation, scale match Maya </li></ul><ul><ul><li>i.e. given TRS, build matrix such that object draws like it does in Maya </li></ul></ul><ul><li>Choose units </li></ul><ul><ul><li>one unit is one foot </li></ul></ul>
  24. 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. 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. 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. 27. Velocities <ul><li>variable frame rates </li></ul><ul><li>float qeTimeFrame() </li></ul><ul><ul><li>returns seconds since engine start / restart </li></ul></ul><ul><li>keep track of the time since the last update </li></ul><ul><li>use Euler integration </li></ul>// 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. 28. Collisions <ul><li>simplifications </li></ul><ul><ul><li>move, then collide non-moving objects </li></ul></ul><ul><ul><li>don't worry about resolution order </li></ul></ul><ul><ul><li>run through once </li></ul></ul><ul><li>guarantee </li></ul><ul><ul><li>after detection, move objects out of that collision (may be in another -- too bad) </li></ul></ul><ul><ul><li>end up in valid world position </li></ul></ul><ul><ul><li>no movement after collision resolution </li></ul></ul>
  29. 29. Collisions <ul><li>ball v world </li></ul><ul><ul><li>if over right or left </li></ul></ul><ul><ul><ul><li>score point, re-serve </li></ul></ul></ul><ul><ul><li>if over top or bottom </li></ul></ul><ul><ul><ul><li>set to top or bottom </li></ul></ul></ul><ul><ul><ul><li>reflect (flip z vel) </li></ul></ul></ul><ul><li>paddle v world </li></ul><ul><ul><li>make sure player stays on the court </li></ul></ul><ul><li>ball v paddles </li></ul><ul><ul><li>test against near edge of paddle </li></ul></ul><ul><ul><ul><li>set to near edge </li></ul></ul></ul><ul><ul><ul><li>bounce (flip x vel) </li></ul></ul></ul><ul><ul><ul><li>add English (later) </li></ul></ul></ul><ul><li>improvements </li></ul><ul><ul><li>preserve distance when colliding </li></ul></ul><ul><ul><ul><li>don't just set to collision edge </li></ul></ul></ul><ul><ul><ul><li>reflect at collision point </li></ul></ul></ul><ul><li>does order matter? </li></ul><ul><ul><li>theoretically </li></ul></ul><ul><ul><li>unlikely </li></ul></ul><ul><li>fast balls </li></ul><ul><ul><li>could run through the paddle </li></ul></ul><ul><ul><ul><li>depends on paddle size and ball speed </li></ul></ul></ul><ul><ul><ul><li>really need to handle moving collisions </li></ul></ul></ul>
  30. 30. Game Controller Use Singleton Pattern <ul><li>manage the game loop with one Game instance </li></ul><ul><li>good chance to use the Singleton pattern </li></ul>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. 31. qeUpdateBase base class <ul><li>engine base class </li></ul><ul><li>provides virtual </li></ul><ul><ul><li>update() </li></ul></ul><ul><ul><li>draw() </li></ul></ul><ul><ul><li>final() </li></ul></ul><ul><li>adds to list of engine objects </li></ul><ul><li>all objs derived from qeUpdateBase </li></ul><ul><ul><li>update() functions called before any of the draw() functions </li></ul></ul>
  32. 32. qe base class <ul><li>derive from qe for simple objects </li></ul><ul><li>no overhead </li></ul><ul><li>runs through engine's memory system </li></ul><ul><ul><li>keeps count to keep you honest </li></ul></ul><ul><li>zeros memory on allocation </li></ul>
  33. 33. Game Superstructure visualization
  34. 34. Button Counts uns qeInpButton(uns inpb); // QEINPBUTTON_ <ul><li>by default the keys are mapped as buttons </li></ul><ul><li>every up and down transition, the engine adds a value to that count </li></ul><ul><li>single button count value gives state and history </li></ul><ul><li>if odd ==> down </li></ul><ul><ul><li>if(b&1) /* down */; </li></ul></ul><ul><li>store count, come back later and find how many transitions </li></ul><ul><ul><li>good for coins </li></ul></ul>
  35. 35. Joysticks float qeInpJoyAxisf(uns joy,uns axis); // use QEJOYAXIS_ uns qeInpJoyButton(uns joy,uns button); // use QEJOYBUTTON_ <ul><li>joysticks start at 0 </li></ul><ul><ul><li>OK to test even if stick is not present </li></ul></ul><ul><li>the axis is defined </li></ul><ul><li>qeInpJoyAxisf() returns values -1..1 </li></ul><ul><li>mind the DEADZONE </li></ul>
  36. 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. 37. Loading and Playing Sounds <ul><li>capture sounds </li></ul><ul><li>low-res, mono for effects </li></ul><ul><li>wav files </li></ul><ul><li>register </li></ul><ul><li>play </li></ul>// 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. 38. qePrintf() qeLogf() <ul><li>printf-like function </li></ul><ul><li>calls to qePrintf() get added to log file </li></ul><ul><ul><li>qelog.txt </li></ul></ul><ul><li>add to log file directly with qeLogf() </li></ul>
  39. 39. BRK() <ul><li>normal asserts() kill the game -- this can be bad </li></ul><ul><li>code with BRK() to continue running </li></ul><ul><li>the &quot;break&quot; goes away when outside the debugger </li></ul>
  40. 40. Bracket Resources <ul><li>bullet-proof allocation and freeing of resources </li></ul><ul><ul><li>memory </li></ul></ul><ul><ul><li>file-handles </li></ul></ul><ul><ul><li>etc </li></ul></ul><ul><li>usually two ways to kill </li></ul><ul><ul><li>normal object life </li></ul></ul><ul><ul><li>program abort </li></ul></ul><ul><li>good solution </li></ul><ul><ul><li>initialization </li></ul></ul><ul><ul><ul><li>guaranteed to run </li></ul></ul></ul><ul><ul><ul><li>clear all fields </li></ul></ul></ul><ul><ul><li>body </li></ul></ul><ul><ul><li>finalization </li></ul></ul><ul><ul><ul><li>guaranteed to run </li></ul></ul></ul><ul><ul><ul><li>can be triggered when body finishes normally or w/abort </li></ul></ul></ul>
  41. 41. Missile Command, 1980
  42. 42. Strings <ul><li>'chr' in qebase.h defines an 8 bit character for internal programming use </li></ul><ul><li>guarantees & principles </li></ul><ul><ul><li>sizes are always byte sizes of whole buffers </li></ul></ul><ul><ul><li>zero termination guaranteed if dstsize>0 </li></ul></ul><ul><ul><li>pointer-terminated and zero-terminated strings </li></ul></ul><ul><ul><ul><li>much faster, safer </li></ul></ul></ul><ul><ul><li>pointer-terminator always option, pass 0 </li></ul></ul><ul><ul><ul><li>must be zero-terminated </li></ul></ul></ul><ul><li>sz* functions defined qebase.h </li></ul><ul><li>int szcpy(chr *dst,int dstsize,chr *ss,chr *sx); </li></ul><ul><ul><li>ss is string start, sx is pointer-terminator or 0 </li></ul></ul><ul><li>int szfmt(chr *dst,int dstsize,chr *fmt,...); </li></ul><ul><ul><li>printf-like fmt </li></ul></ul>
  43. 43. LLNode <ul><li>Simple doubly linked list node </li></ul><ul><li>Type field t specifies game-specific type </li></ul><ul><li>Type field is zero for list head </li></ul>// 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()
  44. 44. Robotron, 1982

×