113
GD4N C++ wrapper for SDL

Game Programming I - GD4N

Embed Size (px)

DESCRIPTION

Building a simple C++ Game Engine using SDL1.2 GD4N stands for Game Development for Noobs Used by UP ITTC (2011-2012)

Citation preview

Page 1: Game Programming I - GD4N

GD4N  C++  wrapper  for  SDL  

Page 2: Game Programming I - GD4N

Purpose  

Ê  Object-­‐Oriented  Programming  approach  on  video  game  development  

Ê  Automatic  Resource  Management  

Ê  GD4N  means  Game  Development  for  Noobs  J  

Page 3: Game Programming I - GD4N

Set  Up  

Ê  Create  a  solution  (or  workspace)  called  GD4N_Games.    This  is  where  we’ll  put  our  GD4N  and  the  games  that  depend  on  it.  

Ê  Create  a  project  in  GD4N_Games,  called  GD4N,  that  builds  a  library  (either  static  or  dynamic).  

Ê  Link  the  following  libraries  to  GD4N:  Ê  SDL,  SDL_image,  SDL_mixer,  SDL_ttf,  OpenGL  

Ê  Add  the  corresponding  header  &  library  locations  

Ê  SDL_image  and  SDL_mixer  may  require  additional  libraries,  add  those  accordingly  

Ê  Build  the  library  which  will  be  linked  by  succeeding  projects  

Page 4: Game Programming I - GD4N

Test  

Ê  Create  an  empty  project  in  GD4N_Games  called  GD4N_Test  Ê  Windows:  Win32  Application  

Ê  Mac  OSX:  Cocoa  Application  

Ê  Link  the  following  libraries:  Ê  GD4N,  SDL,  OpenGL  

Ê  Add  the  header  locations:  Ê  GD4N  Project  folder,  all  SDL  headers  

Page 5: Game Programming I - GD4N

Test  

Ê  Copy  the  files  from  src/GD4N_Test  to  your  project  directory  

Ê  Load  the  files  into  your  project  

Ê  Build  and  Run  

Ê  You  should  see  a  black  800x600  screen  that  can  only  be  closed  by  pressing  the  close  button  

Ê  If  successful,  create  a  template  based  on  GD4N_Test  

Page 6: Game Programming I - GD4N

Design  Paradigms  Optional  Topic  

Page 7: Game Programming I - GD4N

Design  Paradigms  

Ê  GD4N  uses  a  list  of  design  paradigms  to  perform  resource  management  and  implement  the  managers  to  be  as  separate  as  possible  

Ê  It  is  not  necessary  to  fully  understand  these  since  the  point  of  a  framework  is  to  remove  the  necessity  to  know  these  things  but  knowing  these  will  help  you  see  the  game  engine  pieces  clearly  

Page 8: Game Programming I - GD4N

Singleton  Design  Pattern  

Ê  Limit  the  instance  of  a  class  to  only  one,  hence  SINGLEton  

Ê  The  Singleton  is  created  upon  first  use  (lazy  initialization)  and  is  destroyed  when  the  program  ends  

Ê  Usually  used  to  represent  Managers  and  Devices  

Page 9: Game Programming I - GD4N

(Simplified)  Object  Pool  

Ê  A  dynamic  container  that  stores  objects  

Ê  Adding  and  Removing  instances  are  facilitated  by  the  pool  

Ê  Note:  Ideally,  an  object  pool  prevents  the  overhead  of  new  and  delete  operators  by  re-­‐initializing  and  shutting  down  instances.    The  client  simply  requests  for  an  instance  and  the  object  pool  gives  a  pre-­‐allocated  resource  (if  available).  

Page 10: Game Programming I - GD4N

Template  Method  Pattern  

Ê  Define  a  set  of  methods  to  be  overridden  by  its  children  

Ê  The  parent  class  is  intended  to  be  abstract  

Ê  Not  all  the  methods  need  to  be  overridden  

Page 11: Game Programming I - GD4N

GD4N  Resources  

Page 12: Game Programming I - GD4N

constants.h  

Ê  Each  project  should  have  a  constants.h  which  will  contain  all  the  unique  IDs  pertaining  to  the  different  resources.    Each  resource  to  be  used  should  have  a  corresponding  ID.  

Page 13: Game Programming I - GD4N

CSurface  

Ê  Wraps  around  SDL_Surface  

Ê  To  load  an  image  Ê  CSurface::Load(imagepath,  id)  where  imagepath  is  the  path  to  

the  image  and  id  is  a  unique  identifier  

Ê  Drawing  the  image  is  in  the  SVideoManager  

Ê  See  the  CSurface.h  for  more  information  

Page 14: Game Programming I - GD4N

CSurfaceSheet  

Ê  A  Surface  Sheet  (or  Sprite  Sheet)  is  an  image  composed  of  equal-­‐sized  frames.  

Ê  In  this  example,  we  have  an  image  composed  of  a  snake  head  looking  at  4  different  directions.  

Ê  The  image  is  40x40  pixels  with  2  rows  and  2  cols,  making  each  frame  20x20  pixels.    The  maximum  number  of  frames  is  rows  x  cols,  which  is  4  in  this  case.  

Ê  See  CSurfaceSheet.h  for  more  information  

Page 15: Game Programming I - GD4N

CSurfaceSheetAnimated  

Ê  This  is  the  animated  sprite  sheet.    Inheriting  from  CSurfaceSheet,  this  class  animates  by  going  to  the  next  frame  automatically.  

Ê  Before  animating,  it  is  important  to  set  the  sprite  dimensions  as  well  as  the  animation  speed  (frames  per  second).  

Ê  See  CSurfaceSheetAnimated.h  for  more  information  

Page 16: Game Programming I - GD4N

CFont  

Ê  Wraps  around  TTF_Font  (SDL_ttf)  

Ê  To  load  a  font  Ê  CFont::Load(fontpath,  id)  where  fontpath  is  the  path  to  the  true  

type  font  and  id  is  a  unique  identifier  

Ê  To  use  the  font  (create  a  surface  with  text)  Ê  CSurface::CreateText(fontId,  text,  textcolor,  id)  where  fontId  is  

the  id  of  the  font  to  be  used  and  id  is  the  unique  identifier  to  the  generated  surface.  

Ê  See  CFont.h  for  more  options  

Page 17: Game Programming I - GD4N

CMusic  (background  music)  

Ê  Wraps  around  Mix_Music  (SDL_mixer)  

Ê  To  load  music  Ê  CMusic::Load(musicpath,  id)  where  musicpath  is  the  path  to  the  

audio  file  and  id  is  a  unique  identifier  

Ê  Playing  music  is  handled  by  the  SAudioManager  

Ê  See  CMusic.h  for  more  information  

Page 18: Game Programming I - GD4N

CChunk  (sound  effects)  

Ê  Wraps  around  Mix_Chunk  (SDL_mixer)  

Ê  To  load  a  sound  effect  Ê  CChunk::Load(sfxpath,  id)  where  sfxpath  is  the  path  to  the  

sound  effect  and  id  is  the  unique  identifier  

Ê  Player  sound  effects  is  handled  by  the  SAudioManager  

Ê  See  CChunk.h  for  more  information  

Page 19: Game Programming I - GD4N

GD4N  Managers  

Page 20: Game Programming I - GD4N

SInputManager  

Ê  All  the  input  states  and  events  are  stored  and  updated  through  this  manager  

Ê  This  manager  handles  multiple  devices  such  as  mouse  and  keyboard  

Ê  To  access,  use  sInput

Page 21: Game Programming I - GD4N

SVideoManager  

Ê  Everything  seen  on  the  screen  is  handle  by  this  manager  

Ê  This  manager  represents  the  video  device  

Ê  To  access,  use  sVideo

Page 22: Game Programming I - GD4N

SAudioManager  

Ê  Playing,  pausing,  and  stopping  sound  effects  and  music  tracks  are  handled  by  this  manager  

Ê  Multiple  sound  effects  can  be  played  simultaneously  

Ê  Only  1  music  track  can  be  played  at  a  time  

Ê  To  access,  use  sAudio

Page 23: Game Programming I - GD4N

STimeManager  

Ê  This  manager  keeps  track  of  the  time  since  the  game  has  started  and  the  number  of  seconds  between  frames  

Ê  To  access,  use  sTime

Page 24: Game Programming I - GD4N

Game  Manager  

Page 25: Game Programming I - GD4N

CGameManager  

Ê  The  umbrella  class  that  represents  the  game  itself  

Ê  Special  type  of  Manager  because  it  needs  to  be  inherited  

Ê  Initializes  and  Shuts  down  all  other  modules  (managers)  

Ê  Handles  the  changing  of  scenes,  loading  of  resources,  management  of  game  objects  

Ê  Each  game  should  have  a  class  that  inherits  from  CGameManager  and  overrides  Init()  

Page 26: Game Programming I - GD4N

CGameManager  Inherited  Properties  

Ê  char  *windowTitle;  Ê  Defines  the  title  of  the  window  

Ê  Must  be  changed  before  calling  Init();  

Ê  char  *icon;  Ê  Defines  the  path  to  the  icon  of  the  window  

Ê  Must  be  changed  before  calling  Init();  

Page 27: Game Programming I - GD4N

CGameManager  Overrideable  Methods  

Ê  bool  Init();  Ê  By  default,  this  initializes  SDL  and  GD4N  (must  be  called  in  derived  

class)  Ê  Register  scenes  Ê  Set  SDL  and  GD4N  settings  Ê  Determine  initial  scene  

Ê  void  CleanUp();  Ê  By  default,  this  releases  and  shuts  down  SDL  Ê  Not  normally  overridden  

Ê  void  LoadResources();  Ê  Loading  of  assets  should  occur  here  

Page 28: Game Programming I - GD4N

CGameManager  Utility  Methods  

Ê  void  StopPlaying();  Ê  Public  method  to  shut  down  the  game  

Ê  void  ChangeScene(int  scene);  Ê  Change  scenes  

Ê  void  AddScene(SceneInit  newScene);  Ê  Register  a  scene  to  be  used  by  ChangeScene()  

Page 29: Game Programming I - GD4N

CGameManager  Header  

Ê  There  are  more  methods  at  your  disposal  

Ê  See  CGameManager.h  for  more  information  

Page 30: Game Programming I - GD4N

Game  Object  

Page 31: Game Programming I - GD4N

Game  Object  

Ê  Everything  that  either  has  logic  or  is  visible  is  a  game  object.  

Ê  CGameObject  is  the  template  class  for  all  game  objects.  

Ê  Each  game  object  should  inherit  from  the  CGameObject  class.    The  game  objects  should  override  the  methods  which  are  called  by  the  game  loop.  

Page 32: Game Programming I - GD4N

CGameObject  Inherited  Properties  

Ê  int  id;  Ê  A  unique  identifier  generated  when  added  to  the  pool  (handled  by  the  

constructor)  Ê  Has  a  getter  

Ê  int  type;  Ê  A  type  that  is  game-­‐specific  used  for  collisions  (assigned  during  

constructor).    The  different  types  are  located  in  constants.h  Ê  Has  a  getter  

Ê  bool  isVisible;  Ê  A  flag  to  determine  if  the  object  should  be  draw  or  not  Ê  Has  a  getter  and  setter  

Ê  bool  isActive;  Ê  A  flag  to  determine  if  the  object  should  perform  logic  or  not  Ê  Has  a  getter  and  setter  

Page 33: Game Programming I - GD4N

CGameObject  Overrideable  Methods  

Ê  void  Draw();  Ê  Contains  the  draw  calls  

Ê  void  DrawGUI();  Ê  Contains  the  drawing  of  GUI  calls  

Ê  void  Update();  Ê  Performs  logic  

Ê  void  CollidesWith(CGameObject  *other);  Ê  Performs  logic  upon  collision  

Ê  bool  IsCollidingWith(CGameObject  *other);  Ê  Checks  for  collision  against  another  game  object  

Page 34: Game Programming I - GD4N

CGameObject  Utility  Methods  

Ê  bool  IsVisible();  

Ê  bool  IsActive();  

Ê  int  GetType();  

Ê  int  GetID();  

Ê  void  SetVisible();  

Ê  void  SetActive();  

Page 35: Game Programming I - GD4N

SnakeGD4N  So  it  has  begun  

Page 36: Game Programming I - GD4N

Game  Specifications  

Ê  The  game  can  be  played  by  1-­‐2  players  

Ê  Controls  Ê  First  player:  arrow  keys  

Ê  Second  player:  WASD  

Ê  Eating  the  apple  would  increase  the  consuming  snake’s  length  by  1  

Ê  Hitting  the  grass  (boundary),  another  snake  or  yourself  would  be  your  doom  

Page 37: Game Programming I - GD4N

Getting  Started  

Ê  Create  a  project,  SnakeGD4N,  based  on  GD4N_Test  Ê  Double  check  the  framework,  libraries  and  header  paths!  

Ê  Rename  CTestGameManager  to  CSnakeGameManager  Ê  In  the  h,  cpp  and  main.cpp  

Ê  Don’t  forget  the  macro  sGameManager!  

Ê  Copy  the  assets  in  SnakeGD4N-­‐00  to  your  working  directory  Ê  bkgd  Ê  images  

Ê  sfx  

Page 38: Game Programming I - GD4N

Resources  

Ê  Create  constants.h  defining  the  following  enumerations  

Ê  Include  constants.h  in  CSnakeGameManager.cpp  

enum SFX_IDS { SFXID_EAT,

}; enum MUSIC_IDS {

MUSICID_01, }; enum SURFACE_IDS {

SURFID_SNAKEHEAD1 = 0, SURFID_SNAKEHEAD2, SURFID_SNAKEBODY1, SURFID_SNAKEBODY2, SURFID_FOOD, SURFID_GROUND, SURFID_TITLE, SURFID_TITLEOPTION1, SURFID_TITLEOPTION2, SURFID_TITLEOPTION3, SURFID_SELOPTION, SURFID_INGAMEMENU,

};

Page 39: Game Programming I - GD4N

Resources  

Ê  Include  the  following  header  files  into  CSnakeGameManager.cpp  Ê  CChunk.h  Ê  CMusic.h  Ê  CSurface.h  

Ê  Use  the  namespace  GD4N  globally  

Ê  In  CSnakeGameManager,  override  LoadResources()  with  the  following   CChunk::Load((char *)"sfx/eat.wav", SFXID_EAT); sChunkPool->CleanUp();

CMusic::Load((char *)"01-Molly.mp3", MUSICID_01); sMusicPool->CleanUp();

Page 40: Game Programming I - GD4N

Resources  

Ê  LoadResources()  continued  

CSurface::Load((char *)"images/head1.png", SURFID_SNAKEHEAD1); CSurface::Load((char *)"images/head2.png", SURFID_SNAKEHEAD2); CSurface::Load((char *)"images/body1.png", SURFID_SNAKEBODY1); CSurface::Load((char *)"images/bkgd.jpg", SURFID_GROUND); CSurface::Load((char *)"images/food.png", SURFID_FOOD); CSurface::Load((char *)"images/titlescreen.png", SURFID_TITLE); CSurface::Load((char *)"images/option01.png", SURFID_TITLEOPTION1); CSurface::Load((char *)"images/option02.png", SURFID_TITLEOPTION2); CSurface::Load((char *)"images/option03.png", SURFID_TITLEOPTION3); CSurface::Load((char *)"images/option.png", SURFID_SELOPTION); CSurface::Load((char *)"images/ingamemenu.png", SURFID_INGAMEMENU); sSurfacePool->CleanUp();

Page 41: Game Programming I - GD4N

Initialization  

Ê  Include  the  following  header  files  Ê  SVideoManager.h  Ê  SAudioManager.h  

Ê  In  the  constructor,  we  set  the  window  title.    This  can  actually  be  placed  inside  the  Init().  windowTitle = (char *)"Snake!";

Ê  Override  the  Init()  and  set  the  video  mode  to  440x400x32  with  flags  SDL_HWSURFACE  |  SDL_DOUBLEBUF.    This  should  be  called  before  the  parent’s  Init()  otherwise,  this  will  have  no  effect.  

sVideo->SetVideoMode(440, 440, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);

Ê  Call  the  parent’s  Init()  to  initialize  SDL  and  GD4N  if (!CGameManager::Init()) return false;

Ê  When  Init()  goes  well,  return  true return true;

Page 42: Game Programming I - GD4N

Test!  

Ê  Build  and  run  the  program  

Ê  You  should  see  a  black  440x440  window  that  does  nothing  

Ê  It  is  a  good  practice  to  test  after  every  few  lines  of  code.  Believe  me,  the  benefits  will  go  a  long  way.  

Ê  See  src/SnakeGD4N-­‐01  for  any  clarifications  

Page 43: Game Programming I - GD4N

The  (Back)Ground  The  first  game  object  and  the  first  scene  

Page 44: Game Programming I - GD4N

Creating  the  Ground  

Ê  Let’s  create  our  first  Game  Object!  Let’s  call  it  CGround.  

Ê  First,  we  create  the  header  file  called  CGround.h   #include "CGameObject.h" class CGround : public GD4N::CGameObject { protected: void Draw(); public: CGround(); ~CGround(); };

Page 45: Game Programming I - GD4N

Creating  the  Ground  

Ê  Then  the  source  code,  CGround.cpp   #include "CGround.h" #include "SVideoManager.h" #include "constants.h" CGround::CGround() : CGameObject() { } CGround::~CGround() { } void CGround::Draw() { sVideo->Draw(SURFID_GROUND); }  Ê  Question:  What  does  the  ground  do?  

Page 46: Game Programming I - GD4N

Creating  Scenes  

Ê  Now  we  have  created  a  game  object,  we  need  to  put  it  in  a  scene  in  the  CSnakeGameManager.  

Ê  Create  a  method  in  CSnakeGameManager   void CSnakeGameManager::Scene00() { new CGround(); }

Ê  Then  we  register  that  scene  and  load  it  in  Init()   AddScene(CSnakeGameManager::Scene00); ChangeScene(0);

Page 47: Game Programming I - GD4N

Test!  

Ê  Build  and  run  the  program.  

Ê  You  should  see  the  ground  displayed  on  the  screen.  

Ê  The  Game  Loop  inside  CGameManager  automatically  calls  the  Draw()  method  of  all  the  visible  game  objects  in  the  scene.  

Ê  See  src/SnakeGD4N-­‐02  for  any  clarifications  

Page 48: Game Programming I - GD4N

The  Snake  Head  Reacting  to  Input,  Using  the  Time,  Sprite  Sheets  

Page 49: Game Programming I - GD4N

Creating  the  Snake  Head  

Ê  Our  CSnake  game  object  will  react  to  the  arrow  keys,  moving  the  snake  head  accordingly  

Page 50: Game Programming I - GD4N

CSnake.h  

#include "CGameObject.h" #include "TVector2.h" #include "constants.h" class CSnake : public GD4N::CGameObject { protected: void Update(); void Draw(); GD4N::TVector2<int> position; public: CSnake(); ~CSnake(); };

Page 51: Game Programming I - GD4N

CSnake  Constructor  and  Deconstructor  

#include "SInputManager.h" #include "SVideoManager.h" #include "STimeManager.h" #include "CSnake.h" CSnake::CSnake() : CGameObject() { position.x = 1; position.y = 1; } CSnake::~CSnake() { }

Page 52: Game Programming I - GD4N

CSnake  Update  and  Draw  

void CSnake::Update() { if (sInput->GetKeyDown(SDLK_RIGHT)) { position.x++; } else if (sInput->GetKeyDown(SDLK_LEFT)) { position.x--; } else if (sInput->GetKeyDown(SDLK_DOWN)) { position.y++; } else if (sInput->GetKeyDown(SDLK_UP)) { position.y--; } } void CSnake::Draw() { sVideo->Draw(SURFID_SNAKEHEAD1, position * 20); }

Page 53: Game Programming I - GD4N

Instantiate  CSnake  

Ê  Create  an  instance  of  CSnake  in  Scene00  after  CGround  

Ê  Build  and  run  the  program  and  you  will  see  the  head1.png  displayed  on  the  screen  

Ê  Moving  the  arrow  keys  would  move  the  snake  head  accordingly  

Page 54: Game Programming I - GD4N

Explaining  the  Position  

Ê  The  position  is  represented  using  2  integers,  x  and  y.    Note  that  our  snake  head  is  actually  an  image  with  width  and  height  equal  to  20  

Ê  When  the  snake  moves  to  the  right,  it’s  actually  moving  20  pixels  to  the  right  though  it  is  only  incremented  by  1  in  the  Update()  

Ê  The  factor  of  20  is  applied  in  the  Draw()  method.  Meaning,  our  play  area  (or  screen  dimensions)  is  440x440  but  the  position  values  will  only  range  from  0  to  22.  

Page 55: Game Programming I - GD4N

Problems  

Ê  Things  you  may  have  noticed  1.  The  snake  should  be  moving  continuously,  frame-­‐independently  2.  head1.png  contains  four  snake  heads!  3.  No  boundary  conditions!  

Ê  We  fix  the  first  problem  by  adding  2  variables  for  the  movement  relative  to  time  and  2  variables  for  the  direction  (current  and  next)  

Ê  For  the  second  problem,  we  use  a  Surface  Sheet  to  represent  the  snake  head  image  in  order  to  draw  the  corresponding  head  depending  on  the  direction  

Ê  We  will  consider  boundary  conditions  later  on  when  we  go  to  collisions  

Page 56: Game Programming I - GD4N

Direction  Type  

Ê  In  constants.h,  add  the  following  enum   enum direction_t {

DIR_UP = 0, DIR_DOWN, DIR_LEFT, DIR_RIGHT

};

 

Page 57: Game Programming I - GD4N

More  Snake  Properties  and  Methods  

Ê  In  CSnake.h,  add  the  following  include  

#include "CSurfaceSheet.h"

Ê  And  add  the  following  protected  properties  and  methods  void ReactToInput(); void Move();

direction_t dir; direction_t newDir; float timeLast; // time since last movement float timeBetween; // time between movements GD4N::CSurfaceSheet *headTexture;

Page 58: Game Programming I - GD4N

CSnake()  and  ~CSnake()  

Ê  If  you  looked  at  head1.png,  you  will  notice  that  it’s  a  2x2  sprite  sheet  where  the  first  frame  (upper  left)  is  facing  left  and  the  second  (upper  right)  is  facing  right.    The  third  frame  (lower  left)  is  for  up  and  the  last  frame  is  for  down.  

CSnake::CSnake() : CGameObject() { position.x = 1; position.y = 1; newDir = dir = DIR_RIGHT; timeBetween = 0.1f; timeLast = -timeBetween; headTexture = new GD4N::CSurfaceSheet(SURFID_SNAKEHEAD1); headTexture->SetSpriteDimensions(2, 2); headTexture->SetCurrentFrame(1); } CSnake::~CSnake() { delete headTexture; headTexture= 0; }

Page 59: Game Programming I - GD4N

CSnake::Update()  

Ê  Update  is  growing  very  big  and  we  need  to  modularize  it  into  2  smaller  functions,  ReactToInput()  and  Move();  

Ê  The  snake  will  only  move  when  timeBetween  seconds  have  lapsed  void CSnake::Update() { ReactToInput(); if (timeLast + timeBetween < sTime->GetTime()) { timeLast = sTime->GetTime(); dir = newDir; Move(); } }

 

Page 60: Game Programming I - GD4N

CSnake::ReactToInput()  and  Draw()  

Ê  As  a  rule  in  Snake,  if  you’re  moving  to  the  right,  you  can’t  make  a  180  degree  turn  and  face  left.    You  can  only  change  your  direction  to  up  or  down.  

Ê  dir  is  the  current  direction  while  newDir  is  the  next  direction  

void CSnake::ReactToInput() { if (sInput->GetKeyDown(SDLK_RIGHT) && dir != DIR_LEFT) { newDir = DIR_RIGHT; } else if (sInput->GetKeyDown(SDLK_LEFT) && dir != DIR_RIGHT) { newDir = DIR_LEFT; } else if (sInput->GetKeyDown(SDLK_DOWN) && dir != DIR_UP) { newDir = DIR_DOWN; } else if (sInput->GetKeyDown(SDLK_UP) && dir != DIR_DOWN) { newDir = DIR_UP; } } void CSnake::Draw() { sVideo->Draw(headTexture, position * 20); }

Page 61: Game Programming I - GD4N

CSnake::Move()  

Ê  When  the  snake  moves,  it  changes  its  position  and  the  frame  to  display  void CSnake::Move() { switch (dir) { case DIR_UP: position.y--; surface->SetCurrentFrame(2); break; case DIR_DOWN: position.y++; surface->SetCurrentFrame(3); break; case DIR_LEFT: position.x--; surface->SetCurrentFrame(0); break; case DIR_RIGHT: position.x++; surface->SetCurrentFrame(1); break; } }

Page 62: Game Programming I - GD4N

Test!  

Ê  Build  and  run  the  program!  

Ê  You  will  see  the  snake  head  moving  in  its  current  direction  unless  it’s  changed  by  pressing  the  arrow  keys  

Ê  See  src/SnakeGD4N-­‐03  for  any  clarifications  

Page 63: Game Programming I - GD4N

Snake  Body  Game  Objects  controlling  other  Game  Objects  

Page 64: Game Programming I - GD4N

Body  Movement  

Ê  The  body  of  the  snake  is  composed  of  segments  

Ê  Each  segment  follows  the  one  before  it  

Ê  The  “neck”  is  the  segment  right  before  the  head  

Ê  The  “tail”  is  the  last  segment  of  the  body  

Page 65: Game Programming I - GD4N

CSnakeSegment.h  

#include "CGameObject.h" #include "CSurfaceSheet.h" #include "TVector2.h" #include "constants.h" class CSnakeSegment : public GD4N::CGameObject { protected: void Draw(); GD4N::CSurfaceSheet *bodyTexture; public: CSnakeSegment(); ~CSnakeSegment(); GD4N::TVector2<int> position; };

Page 66: Game Programming I - GD4N

CSnakeSegment.cpp  

CSnakeSegment::CSnakeSegment() : CGameObject() { bodyTexture = new GD4N::CSurfaceSheet(SURFID_SNAKEBODY1); bodyTexture->SetSpriteDimensions(2, 5); bodyTexture->SetCurrentFrame(0); } CSnakeSegment::~CSnakeSegment() { delete bodyTexture; bodyTexture = 0; } void CSnakeSegment::Draw() { sVideo->Draw(bodyTexture, position * 20); } Ê  The  CSnakeSegment  does  not  have  logic.    Notice  that  it’s  only  concerned  with  drawing  

itself.    Its  position  property  is  public,  however.    Which  means,  another  game  object  could  simply  change  the  segment’s  position  and  it  will  be  drawn  unto  to  that  new  position.  

Page 67: Game Programming I - GD4N

CSnake.h  

Ê  CSnake  will  handle  the  segments.    Add  the  following  includes  and  protected  properties  

#include "CSnakeSegment.h" #include <vector> int length; int desiredLength; std::vector<CSnakeSegment*> body;

Page 68: Game Programming I - GD4N

CSnake.cpp  

Ê  Set  the  initial  value  of  length  and  desiredLength  to  0  in  the  constructor.  

length = desiredLength = 0;

Ê  For  testing  purposes,  we  increase  the  length  of  the  snake  by  pressing  the  space  bar.    Add  the  following  lines  in  ReactToInput()  

if (sInput->GetKeyDown(SDLK_SPACE))

desiredLength++;

Page 69: Game Programming I - GD4N

CSnake.cpp  

Ê  In  the  Move(),  add  the  following  before  the  switch  statement   if (length < desiredLength) { length++; body.push_back(new CSnakeSegment()); } if (length > 0) { for (int i = length-1; i > 0; i--) { body[i]->position = body[i-1]->position; // all segments will copy the one // before it except for the neck } body[0]->position = position; // neck will copy its position from the head }

Page 70: Game Programming I - GD4N

Test!  

Ê  Build  and  run  the  program!  

Ê  Press  the  space  bar  to  increase  the  length  of  the  snake  

Ê  See  src/SnakeGD4N-­‐04  for  any  clarifications  

Page 71: Game Programming I - GD4N

Body  as  Sprite  Sheet  

Ê  Notice  that  body1.png  is  a  sprite  sheet  with  5  cols  and  2  rows  

Ê  Currently,  we’re  only  displaying  the  first  frame  (frame  0)  

Ê  To  display  the  proper  frame  from  our  sprite  sheet,  we  need  to  determine  if  the  segment  is  the  tail  or  not.  

Ê  If  it’s  the  tail,  we  only  care  what  is  the  direction  of  the  previous  segment  

Ê  If  it’s  not  the  tail,  determine  if  the  direction  of  the  previous  segment  is  the  same  as  the  current  

Page 72: Game Programming I - GD4N

Tail  and  Same  Directions  

Tail  

Next  Direction   Frame  

DIR_UP   2  

DIR_DOWN   7  

DIR_LEFT   6  

DIR_RIGHT   5  

Same  Direction  

Direction   Frame  

DIR_UP  or  DIR_DOWN   1  

DIR_LEFT  or  DIR_RIGHT   0  

Note:  Frame  indices  start  at  0  

Page 73: Game Programming I - GD4N

Different  Directions  (turning)  

Ê  If  the  direction  of  the  previous  segment  is  different  from  the  direction  of  the  current  segment,  it  must  be  turning  

Current  Direction   Next  Direction   Frame  

DIR_UP   DIR_RIGHT  3  

DIR_LEFT   DIR_DOWN  

DIR_RIGHT   DIR_DOWN  4  

DIR_UP   DIR_LEFT  

DIR_DOWN   DIR_RIGHT  8  

DIR_LEFT   DIR_UP  

DIR_RIGHT   DIR_UP  9  

DIR_DOWN   DIR_LEFT  

Note:  Frame  indices  start  at  0  

Page 74: Game Programming I - GD4N

CSnakeSegment  

Ê  Add  the  following  protected  property  and  public  methods  in  the  header  

direction_t dir; direction_t GetDirection() { return dir; } void SetDirection(direction_t newDir, bool isTail = false);

Ê  Implement  SetDirection()  given  the  tables  in  the  previous  slide.    Remember  to  assign  direction  on  the  given  newDir.  

Page 75: Game Programming I - GD4N

CSnake.cpp  

Ê  In  Move();  a  slight  modification  is  applied  in  order  to  set  the  direction  of  the  segments  (aside  from  the  position)  

if (length > 0) { for (int i = length-1; i > 0; i--) { // all segments will copy from the one before it except for the neck body[i]->SetDirection(body[i-1]->GetDirection(), (i == length-1)); body[i]->position = body[i-1]->position; } // neck will copy its direction and position from the head body[0]->SetDirection(dir, (length == 1)); body[0]->position = position; }

Page 76: Game Programming I - GD4N

Test!  

Ê  Once  you’re  done  implementing  SetDirection(),  build  and  run  your  program  

Ê  Ideally,  the  snake’s  body  would  be  more  realistic  J  

Ê  See  src/SnakeGD4N-­‐05  for  the  solution  

Page 77: Game Programming I - GD4N

Food  Point  Collision  

Page 78: Game Programming I - GD4N

Food  

Ê  The  food  is  a  simple  game  object  in  the  sense  that  it  doesn’t  perform  any  logic  until  something  collides  with  it.    Upon  collision,  the  food  simply  randomizes  its  position.  

Ê  Our  food  will  use  an  animated  sprite  sheet  that  has  an  oscillating  animation.    Meaning,  it  animates  from  frame  0  to  3  then  back  to  0  and  repeats,  given  that  the  animation  only  has  4  frames.  

Page 79: Game Programming I - GD4N

Collisions  

Ê  To  work  with  collisions,  we  first  need  to  set  the  different  game  object  types  

Ê  Add  the  following  enum  in  constants.h   enum GAMEOBJECT_TYPES { TYPE_FOOD = 1, TYPE_SNAKE, TYPE_SNAKESEGMENT, TYPE_GROUND, };

Page 80: Game Programming I - GD4N

Type  

Ê  Set  the  type  of  each  game  object  in  their  constructor  

Ê  This  will  affect  CSnake.cpp,  CSnakeSegment.cpp  and  CGround.cpp  

Ê  If  this  step  is  skipped,  collisions  will  not  be  detected  

Page 81: Game Programming I - GD4N

CFood.h  

Ê  Create  CFood.h   #include "CGameObject.h" #include "CSurfaceSheetAnimated.h" #include "TVector2.h" class CFood : public GD4N::CGameObject { protected: void Update(); void Draw(); bool IsCollidingWith(GD4N::CGameObject* other); void CollidesWith(GD4N::CGameObject* other); GD4N::CSurfaceSheetAnimated* foodTexture; GD4N::TVector2<int> position; void RandomizePosition(); public: CFood(); ~CFood(); const GD4N::TVector2<int> & GetPosition() { return position; } };

Page 82: Game Programming I - GD4N

CFood.cpp  

Ê  The  food  will  use  an  animated  sprite  sheet.   #include "CFood.h" #include "SVideoManager.h" #include "SRandom.h" #include "constants.h" #include "CSnake.h" #include "CSnakeSegment.h" #include "CGround.h” CFood::CFood() : CGameObject() { foodTexture = new GD4N::CSurfaceSheetAnimated(SURFID_FOOD); foodTexture->SetSpriteDimensions(2, 2); foodTexture->SetAnimationSpeed(10); foodTexture->SetOscillating(true); RandomizePosition(); type = TYPE_FOOD; }

Page 83: Game Programming I - GD4N

CFood.cpp  

CFood::~CFood() { delete foodTexture; foodTexture = 0; } void CFood::Update() { foodTexture->Update(); } void CFood::Draw() { sVideo->Draw(foodTexture, position * 20); } void CFood::RandomizePosition() { position.x = sRand->Generate(1, 21); position.y = sRand->Generate(1, 21); }

Page 84: Game Programming I - GD4N

CFood  Collision  Testing  

bool CFood::IsCollidingWith(GD4N::CGameObject* other) { switch (other->GetType()) { case TYPE_SNAKE: { CSnake* snake = dynamic_cast<CSnake*>(other); return (position == snake->GetPosition()); } case TYPE_FOOD: { CFood* food = dynamic_cast<CFood*>(other); return (position == food->GetPosition()); } case TYPE_SNAKESEGMENT: { CSnakeSegment* segment = dynamic_cast<CSnakeSegment*>(other); return (position == segment->position); } case TYPE_GROUND: { CGround* ground = dynamic_cast<CGround*>(other); return (position.x < 1 || position.x >= ground->GetWidth() - 1 || position.y < 1 || position.y >= ground->GetHeight() - 1); } } return CGameObject::IsCollidingWith(other); }

Page 85: Game Programming I - GD4N

CFood  Collision  Reaction  

void CFood::CollidesWith(GD4N::CGameObject* other) { switch (other->GetType()) { case TYPE_SNAKESEGMENT: case TYPE_FOOD: case TYPE_GROUND: // Respawn! do { RandomizePosition(); } while (IsCollidingWith(other)); break; case TYPE_SNAKE: // Snake ate this food! RandomizePosition(); break; } }

Page 86: Game Programming I - GD4N

CGround  GetWidth()  and  GetHeight()  

Ê  Notice  that  CFood::IsCollidingWith()  requires  GetWidth()  and  GetHeight()  from  CGround.    Simply  add  these  2  methods  in  CGround  that  returns  the  width  and  height  respectively.  

Page 87: Game Programming I - GD4N

Test!  

Ê  Build  and  run  the  program.  

Ê  The  food  should  be  randomly  generated  in  the  area.  

Ê  When  the  snake  head  eats  (collides  with)  the  food,  the  food  will  be  placed  in  a  different  random  position.  

Ê  See  src/SnakeGD4N-­‐06  for  any  clarifications  

Page 88: Game Programming I - GD4N

Snake  Collisions  

Page 89: Game Programming I - GD4N

Snake  Collisions  

Ê  The  snake  only  has  2  collision  reactions;  death  and  grow  

Ê  When  a  snake  consumes  (collides)  with  the  food,  the  desired  length  of  the  snake  increments  by  1.    Thus,  we  no  longer  need  the  quick  fix  of  pressing  the  space  bar  to  increase  the  length.  

Ê  When  a  snake  collides  with  anything  else  (another  snake  head,  a  snake  body  regardless  of  who  it  belongs  to,  the  ground),  the  snake  will  die.  

Ê  We  simply  need  to  override  the  IsCollidingWith()  and  CollidesWith()  methods  from  CGameObject.  

Page 90: Game Programming I - GD4N

CSnake  Collision  Testing  

bool CSnake::IsCollidingWith(GD4N::CGameObject* other) { switch (other->GetType()) { case TYPE_SNAKE: { CSnake* snake = dynamic_cast<CSnake*>(other); return (position == snake->GetPosition()); } case TYPE_FOOD: { CFood* food = dynamic_cast<CFood*>(other); return (position == food->GetPosition()); } case TYPE_SNAKESEGMENT: { CSnakeSegment* segment = dynamic_cast<CSnakeSegment*>(other); return (position == segment->position); } case TYPE_GROUND: { CGround* ground = dynamic_cast<CGround*>(other); return (position.x < 1 || position.x >= ground->GetWidth() - 1 || position.y < 1 || position.y >= ground->GetHeight() - 1); } } return CGameObject::IsCollidingWith(other); }

Page 91: Game Programming I - GD4N

CSnake  Collision  Reaction  

Ê  Note:  SAudioManager.h  should  be  included   void CSnake::CollidesWith(GD4N::CGameObject* other) { switch (other->GetType()) { case TYPE_FOOD: desiredLength++; sAudio->PlaySound(SFXID_EAT); break; default: // die isActive = false; break; } }

Page 92: Game Programming I - GD4N

Test!  

Ê  Build  and  run  your  code.  

Ê  Test  if  all  the  collision  reactions  are  correct.  

Ê  Note  that  the  quick  fix  of  pressing  space  to  increase  the  snake’s  length  should  be  removed.  

Ê  See  src/SnakeGD4N-­‐07  for  any  clarifications  

Page 93: Game Programming I - GD4N

Title  Screen  Changing  Scenes  

Page 94: Game Programming I - GD4N

Snake  

Ê  Now  that  we  have  the  core  gameplay  working,  we  will  polish  the  game.  

Ê  We  first  add  a  Title  Screen  where  the  player  could  select  between  single  player  and  2  players.    Also,  the  Title  Screen  will  give  the  player  an  option  to  exit  the  game.  

Page 95: Game Programming I - GD4N

CTitle  

Ê  The  Title  Screen  is  actually  another  Game  Object.    This  is  true  for  most  games  –  the  GUI  is  a  Game  Object.  

Ê  The  main  difference  between  this  Game  Object  will  be  the  overriding  of  DrawGUI().    This  method  is  where  we  perform  all  GUI-­‐related  functionality.  

Page 96: Game Programming I - GD4N

CTitle.h  

#include "CGameObject.h" class CTitle : public GD4N::CGameObject { protected:

int selected;

public: CTitle(); void Update(); void DrawGUI();

};

Page 97: Game Programming I - GD4N

CTitle  Constructor  and  Includes  

#include "CTitle.h” #include "CSnakeGameManager.h” #include "SVideoManager.h” #include "SInputManager.h” #include "constants.h” #include "TVector2.h” CTitle::CTitle() : CGameObject() { selected = 0; }

Page 98: Game Programming I - GD4N

CTitle  Update  

void CTitle::Update() { if (sInput->GetKey(SDLK_ESCAPE)) { sGameManager->StopPlaying(); // quit game } if (sInput->GetKeyDown(SDLK_DOWN)) { selected++; } else if (sInput->GetKeyDown(SDLK_UP)) { selected--; } selected = (selected + 3) % 3; if (sInput->GetKeyDown(SDLK_RETURN)) { switch (selected) { case 0: sGameManager->ChangeScene(1); break; case 1: sGameManager->ChangeScene(2); break; case 2: sGameManager->StopPlaying(); break; } } }

Page 99: Game Programming I - GD4N

CTitle  DrawGUI  

Ê  The  DrawGUI()  is  a  bit  more  complicated  than  the  Draw()  methods  because  we’re  handling  different  images  at  once.  

void CTitle::DrawGUI() { using GD4N::TVector2; sVideo->Draw(SURFID_TITLE, TVector2<int>(73, 50)); sVideo->Draw(SURFID_TITLEOPTION1, TVector2<int>(170, 200)); sVideo->Draw(SURFID_TITLEOPTION2, TVector2<int>(170, 250)); sVideo->Draw(SURFID_TITLEOPTION3, TVector2<int>(170, 300)); switch (selected) { case 0: sVideo->Draw(SURFID_SELOPTION, TVector2<int>(140, 200)); break; case 1: sVideo->Draw(SURFID_SELOPTION, TVector2<int>(140, 250)); break; case 2: sVideo->Draw(SURFID_SELOPTION, TVector2<int>(140, 300)); break; } }

Page 100: Game Programming I - GD4N

Adding  the  Scenes  

Ê  Now  that  we  have  the  Title  screen,  we  need  to  add  scenes  which  will  show  the  Title  screen  first  before  going  to  the  different  scenes.  

Ê  In  CSnakeGameManager.h,  add  Scene01()  and  Scene02()  similar  to  Scene00().  

Ê  Register  these  scenes  in  CSnakeGameManager.cpp  similar  to  Scene00()  in  the  Init().  

Ê  Add/modify  the  scenes  to  be  similar  to  the  next  slide.  Note  that  the  appropriate  headers  have  been  included.  

Page 101: Game Programming I - GD4N

Scene  Definitions  

void CSnakeGameManager::Scene00() { new CGround(); new CTitle(); } void CSnakeGameManager::Scene01() { new CGround(); new CSnake(); new CFood(); } void CSnakeGameManager::Scene02() { new CGround(); }

Page 102: Game Programming I - GD4N

Test!  

Ê  Build  and  run  your  program!  

Ê  The  game  should  begin  with  the  Title  Screen  where  you  can  select  the  number  of  players  by  pressing  the  up  or  down  arrow  keys  then  pressing  enter  to  confirm  the  selection.  

Ê  Selecting  QUIT  exits  the  game.  

Ê  Select  1  Player  goes  to  basic  gameplay  with  1  player.  

Ê  Select  2  Player  and  it  enters  an  empty  scene  (except  for  the  ground).  

Ê  See  src/SnakeGD4N-­‐08  for  any  clarifications  

Page 103: Game Programming I - GD4N

In-­‐Game  Menu  Pause  and  Resume  

Page 104: Game Programming I - GD4N

In-­‐Game  Menu  

Ê  Purpose:  Ê  Pause  the  Game  

Ê  Return  to  Title  Screen  

Ê  Similar  to  CTitle,  CInGameMenu  is  a  game  object  that  overrides  DrawGUI()  instead  of  Draw()  

Ê  What’s  special  with  CInGameMenu  is  that  it  should  be  able  to  freeze  all  the  snakes  in  the  game  when  the  game  is  paused.    Therefore,  CInGameMenu  should  have  a  container  of  game  objects  to  pause/resume.  

Page 105: Game Programming I - GD4N

CInGameMenu.h  

#include "CGameObject.h" #include "CSnake.h" #include <list> class CInGameMenu : public GD4N::CGameObject { protected: int selected; std::list<GD4N::CGameObject*> objs; void Update(); void DrawGUI(); void SetObjectsActive(bool isActive); public: CInGameMenu(); ~CInGameMenu(); void AddGameObject(GD4N::CGameObject* obj); };

Page 106: Game Programming I - GD4N

CInGameMenu.cpp  

Ê  Includes,  Constructor  and  Deconstructor   #include "CInGameMenu.h" #include "SVideoManager.h" #include "SInputManager.h" #include "CSnakeGameManager.h" CInGameMenu::CInGameMenu() : GD4N::CGameObject() { // isVisible is used for pause state as well isVisible = false; selected = 0; } CInGameMenu::~CInGameMenu() { objs.erase(objs.begin(), objs.end()); }

Page 107: Game Programming I - GD4N

CInGameMenu.cpp  

Ê  DrawGUI()  and  necessary  methods   void CInGameMenu::DrawGUI() { sVideo->Draw(SURFID_INGAMEMENU, GD4N::TVector2<int>(113, 153)); sVideo->Draw(SURFID_SELOPTION, GD4N::TVector2<int>(126, (selected == 0) ? 205 : 242)); } void CInGameMenu::SetObjectsActive(bool isActive) { for (std::list<GD4N::CGameObject*>::iterator it = objs.begin(); it != objs.end(); it++) { (*it)->SetActive(isActive); } } void CInGameMenu::AddGameObject(GD4N::CGameObject *obj) { objs.push_front(obj); }

Page 108: Game Programming I - GD4N

CInGameMenu.cpp  

void CInGameMenu::Update() { if (sInput->GetKeyDown(SDLK_ESCAPE)) { SetObjectsActive(isVisible); isVisible = !isVisible; selected = 0; } if (isVisible) { if (sInput->GetKeyDown(SDLK_DOWN)) { selected++; } else if (sInput->GetKeyDown(SDLK_UP)) { selected--; } selected &= 1; // limit to 0 or 1 only if (sInput->GetKeyDown(SDLK_RETURN)) { switch (selected) { case 0: SetObjectsActive(isVisible); isVisible = false; break; case 1: // return to title screen sGameManager->ChangeScene(0); break; } } } }

Page 109: Game Programming I - GD4N

Modify  Scene01  

Ê  Now  that  we  have  the  CInGameMenu,  we  add  it  in  Scene01()  and  register  the  snakes  to  it.  

void CSnakeGameManager::Scene01() { new CGround(); new CFood(); CSnake *snake = new CSnake(); CInGameMenu *igm = new CInGameMenu(); igm->AddGameObject(snake); }

 

Page 110: Game Programming I - GD4N

Test!  

Ê  Build  and  run  your  program!  

Ê  From  the  Title  Screen,  select  1  Player  

Ê  Press  ESC  to  open  the  In-­‐Game  Menu.  Notice  how  the  snakes  stop  moving.  Press  ESC  again  or  select  resume  and  press  enter  to  resume  the  game  

Ê  See  src/SnakeGD4N-­‐09  for  any  clarifications  

Ê  The  In-­‐Game  Menu  also  allows  the  player  to  leave  the  game  when  the  snake  dies  

Page 111: Game Programming I - GD4N

Challenge  Complete  2  Player  

Page 112: Game Programming I - GD4N

2  Player  

Ê  Since  we’ve  completed  the  single  player  of  snake,  it’s  time  to  modify  it  to  become  2-­‐player.    Most  of  the  modifications  are  in  CSnake  

Ê  Things  to  do:  Ê  The  snake  should  have  a  way  to  know  if  it’s  first  or  second  player  

Ê  Depending  on  the  player  number;  their  initial  position,  initial  direction  and  controls  should  differ  

Ê  There  should  be  a  way  to  differentiate  one  player  from  the  other  

Ê  Both  snakes  need  to  be  paused  by  the  in-­‐game  menu  

Page 113: Game Programming I - GD4N

Tetris  GD4N