HW2 Solutions Page
Return to
homework page.
//Only modified files from demo03 from book displayed.
//defines.h: essential defines for Invader game
//Copyright Ian Parberry, 1999
//Last updated April 3, 2000
//modified for invader porblem by Chris Pollett 2001
#ifndef __DEFINES_H__
#define __DEFINES_H__
#define SCREEN_WIDTH 640 //pixels wide
#define SCREEN_HEIGHT 480 //pixels high
#define COLORS 256 //number of colors
#define COLOR_DEPTH 8 //number of bits to store colors
#define TRANSPARENT_COLOR 255 //transparent palette position
//
//mod's for invaders
//
#define BUNKER_NUM 3 // number of bunkers
#define BUNKER_OFFSET SCREEN_WIDTH/(BUNKER_NUM+1) // space b/w bunkers
#define MIN_LEFTPOS SCREEN_WIDTH/(2*BUNKER_NUM) // farthest invaders/player can
// move left
#define MAX_RIGHTPOS SCREEN_WIDTH-MIN_LEFTPOS // farthest invaders/player can
// move right
#endif
// File Name: InvaderGroup.h
//
// Class Name: CInvaderGroup
//
// Purpose: acts as a container for a space invader CObjects
// Has methods to push and remove invaders. Also
// has methods to draw the whole invader group and
// update whole groups position.
//
#ifndef __CIGRP__
#define __CIGRP__
#include <vector>
using namespace std;
#include "objects.h"
#include "defines.h"
#define MAX_INVGRPROWS 3
#define MAX_INVGRPCOLS 4
class CInvaderGroup
{
private:
vector<CObject> v; // holds invaders stored in this group
// CObject are relatively small
// if were larger should use pointer to a CObject
int xVel; //left right speed of invaders
int yOffset; // how far invaders move down when reach a side
public:
CInvaderGroup() {yOffset=0;
xVel=-1;} //constructor
void draw(LPDIRECTDRAWSURFACE surface); //draw invaders to surface
void create(int x,int y,CBaseSprite* inv[MAX_INVGRPROWS]); //create
//instance
//note: probably would be better to have inv
//be a vector.
void move(); //make a move depending on time and speed
void remove(int i){ v.erase(&(v[i]));} //remove object from invader
// group
void push(CObject o) {v.push_back(o);} //add invader ot group
void clear(){ v.clear();} //delete all invaders from group
int getSize() { return v.size();} //return the number of invaders in
//group
int getTop(); // y-position of top most invaders
int getBottom(); // y-position of bottom most invaders
int getRight(); // x-position of rightmost invader
int getLeft(); // x-position of leftmost invader
};
#endif
//
// Filename: InvaderGroup.cpp
//
// Purpose: contains member function defintions for CInvaderGroup
//
#include "invadergroup.h"
//
// Member Function: draw
//
// Purpose: draws each invader in the group to the designated surface
//
void CInvaderGroup::draw(LPDIRECTDRAWSURFACE surface)
{
vector<CObject>::iterator i;
for(i=v.begin(); i !=v.end(); i++)
i->draw(surface);
}
//
// Member Function: create
//
// Purpose: creates a ``default'' space invader group. (As opposed
// to doing push external to the class to make a designer
// one.) The default group is horizontally centered at x and its last
// row is at height y. inv is the array of sprite images for
// the different rows of the group
void CInvaderGroup::create(int x,int y, CBaseSprite* inv[MAX_INVGRPROWS])
{
CObject obj;
int xOld;
int xOffset = SCREEN_WIDTH/(1.5*MAX_INVGRPCOLS); //initial x spacing
//of invaders
xVel = -1; //initial invader velocity
yOffset = y/(MAX_INVGRPROWS); // vertical spacing b/w invaders
x -= (xOffset * MAX_INVGRPCOLS/2); //passed x in assumed to be
//center of invader group
//so now get left side
xOld=x;
for(int i=0; i<MAX_INVGRPROWS; i++)
{
for(int j=0; j<MAX_INVGRPCOLS; j++)
{
obj.create(x, y, xVel,0,inv[i]);
push(obj);
x+=xOffset;
}
x = xOld;
y-= yOffset;
}
yOffset /=4; // yOffset now used to control how far invaders
// move down when reach a side of screen.
}
//
// Member Function: move
//
// Purpose: updates the position of the invader group. Causes group to
// switch direction and move down if reach side of screen.
//
void CInvaderGroup::move()
{
vector<CObject>::iterator i;
for(i=v.begin(); i != v.end(); i++)
{
i->move();
}
if(getLeft() <MIN_LEFTPOS || getRight() > MAX_RIGHTPOS)
{
xVel=-xVel;
for(i=v.begin(); i != v.end(); i++)
{
i->setXVel(xVel);
i->setY(i->getY()+yOffset);
}
}
}
//
// Coordinate member functions:
//
// The next four member functions are used to get the coordinates of
// bounding rectangle of the invader group. Could here could easily
// be improved.
//
int CInvaderGroup::getTop()
{
vector<CObject>::iterator i;
int top = SCREEN_HEIGHT;
int tmp;
for(i=v.begin(); i != v.end(); i++)
{
tmp = i->getY();
if(top > tmp) top=tmp;
}
return top;
}
int CInvaderGroup::getBottom()
{
vector<CObject>::iterator i;
int bot = 0;
int tmp;
for(i=v.begin(); i != v.end(); i++)
{
tmp = i->getY();
if(bot < tmp) bot=tmp;
}
return bot;
}
int CInvaderGroup::getLeft()
{
vector<CObject>::iterator i;
int left = SCREEN_WIDTH;
int tmp;
for(i=v.begin(); i != v.end(); i++)
{
tmp = i->getX();
if(left > tmp) left=tmp;
}
return left;
}
int CInvaderGroup::getRight()
{
vector<CObject>::iterator i;
int right = 0;
int tmp;
for(i=v.begin(); i != v.end(); i++)
{
tmp = i->getX();
if(right < tmp) right=tmp;
}
return right;
}
//objects.h: header file for CObject class
//
// modified with set and get methods for speed and velocity --cpollett 2001
// also added stuff to cycle through sprite frames (similar to later demos).
//
//Copyright Ian Parberry, 1999
//Last updated September 28, 1999
#ifndef __OBJECTS__
#define __OBJECTS__
#include "bsprite.h"
class CObject{ //class for a moving object
private:
int m_nX,m_nY; //current location
int m_nXspeed,m_nYspeed; //current speed
int m_nLastXMoveTime; //last time the object moved
int m_nCurrentFrame; //-CP what frame we're on
int m_nLastFrameTime; //-CP added a variable holding last time
// animation changed
CBaseSprite *m_pSprite; //pointer to sprite
public:
CObject(); //constructor
void draw(LPDIRECTDRAWSURFACE surface); //draw
void create(int x,int y,int xspeed,int yspeed,
CBaseSprite *sprite); //create instance
void accelerate(int xdelta,int ydelta=0); //change speed
void move(); //make a move depending on time and speed
//modified code -CP
//various set and get methods
int getX() {return m_nX;}
int getY() {return m_nY;}
void setX(int x) {m_nX = x;}
void setY(int y) {m_nY = y;}
int getXVel() {return m_nXspeed;}
int getYVel() {return m_nYspeed;}
void setXVel(int x) {m_nXspeed = x;}
void setYVel(int y) {m_nYspeed = y;}
};
#endif
//objects.cpp
//Copyright Ian Parberry, 1999
//Last updated September 28, 1999
#include "objects.h"
#include "timer.h" //game timer
extern CTimer Timer;
CObject::CObject(){ //constructor
m_nX=m_nY=m_nXspeed=m_nYspeed=0;
m_pSprite=NULL; m_nLastXMoveTime=0;
m_nCurrentFrame=0;
m_nLastFrameTime=0;
}
void CObject::draw(LPDIRECTDRAWSURFACE surface){ //draw
m_pSprite->draw(m_nCurrentFrame,m_nX,m_nY,surface);
}
void CObject::create(int x,int y,int xspeed,int yspeed,
CBaseSprite *sprite){
m_nLastXMoveTime=0; //time
m_nLastFrameTime=0;
m_nCurrentFrame=0;
m_nX=x; m_nY=y; //location
m_nXspeed=xspeed; m_nYspeed=yspeed; //speed
m_pSprite=sprite; //image
}
void CObject::accelerate(int xdelta,int ydelta){
//change speed
m_nXspeed+=xdelta; m_nYspeed+=ydelta;
}
void CObject::move(){ //move object
const int XSCALE=8; //to scale back horizontal motion
const int MARGIN=100; //margin on outside of page
const int FRAME_RATE=500;
int xdelta; //change in position
int time=Timer.time(); //current time
if(time-m_nLastFrameTime > FRAME_RATE) //-CP advance a frame of animation
{ // if enough time has passed
m_nCurrentFrame++;
if(m_nCurrentFrame>= m_pSprite->frame_count())
m_nCurrentFrame = 0;
m_nLastFrameTime = time;
}
//horizontal motion
int tfactor=time-m_nLastXMoveTime; //time since last move
xdelta=(m_nXspeed*tfactor)/XSCALE; //x distance moved
m_nX+=xdelta; //x motion
if(m_nX<-MARGIN)m_nX=SCREEN_WIDTH+MARGIN; //wrap left
if(xdelta||m_nXspeed==0) //record time of move
m_nLastXMoveTime=time;
if(m_nX>SCREEN_WIDTH+MARGIN)m_nX=-MARGIN; //wrap right
}
//
//Filename: main.cpp
//
//Purpose: Main window loop. Loads in Graphics controls player and
// inavder group
//
//system includes
#include <windows.h>
#include <windowsx.h>
#include <ddraw.h>
#include <stdio.h>
#include <stdlib.h>
//system defines
#define WIN32_LEAN_AND_MEAN
//custom includes
#include "defines.h" //global definitions
#include "bmp.h" //bmp file reader
#include "timer.h" //game timer
#include "bsprite.h" //for base sprite class
#include "objects.h" //for object class
#include "invadergroup.h" //for space invader group class
//globals
BOOL ActiveApp; //is this application active?
LPDIRECTDRAW lpDirectDrawObject=NULL; //direct draw object
LPDIRECTDRAWSURFACE lpPrimary=NULL; //primary surface
LPDIRECTDRAWPALETTE lpPrimaryPalette; //its palette
LPDIRECTDRAWSURFACE lpSecondary=NULL; //back buffer
LPDIRECTDRAWPALETTE lpSecondaryPalette; //its palette
LPDIRECTDRAWSURFACE lpBackground=NULL; //background image
CTimer Timer; //game timer
CBmpFileReader background; //background image
CBmpSpriteFileReader g_cSpriteImages; //sprite images
CObject spaceship; //spaceship object
CBaseSprite *spaceshipsprite=NULL; //spaceship sprite
CObject bunkers[BUNKER_NUM];
CBaseSprite *bunkersprite=NULL; //bunker sprite
CInvaderGroup invaders;
CBaseSprite *invgrpsprite[MAX_INVGRPROWS]; // invader sprites
int minInvHeight; // as low as invaders allowed to go
//helper functions
LPDIRECTDRAWPALETTE CreatePalette(LPDIRECTDRAWSURFACE surface);
BOOL InitDirectDraw(HWND hwnd);
HWND CreateDefaultWindow(char* name,HINSTANCE hInstance);
BOOL LoadSpaceShipSprite(){ //load player's spaceship image
return spaceshipsprite->load(&g_cSpriteImages,0,1,1);
} //LoadSpaceShipSprite
BOOL LoadBunkerSprite(){ //load bunker image
return bunkersprite->load(&g_cSpriteImages,0,121,1);
} //LoadBunkerSprite
BOOL LoadInvGrpSprite(){ //load invader group images
int i;
for(i=0; i < MAX_INVGRPROWS; i++)
{
switch( i%3)
{
case 0:
if(!invgrpsprite[i]->load(&g_cSpriteImages,0,1,73))
return FALSE;
if(!invgrpsprite[i]->load(&g_cSpriteImages,1,121,73))
return FALSE;
break;
case 1:
if(!invgrpsprite[i]->load(&g_cSpriteImages,0,244,73))
return FALSE;
if(!invgrpsprite[i]->load(&g_cSpriteImages,1,365,73))
return FALSE;
break;
case 2:
if(!invgrpsprite[i]->load(&g_cSpriteImages,0,244,1))
return FALSE;
if(!invgrpsprite[i]->load(&g_cSpriteImages,1,365,1))
return FALSE;
break;
}
}
return TRUE;
} //LoadInvGrpSprite
BOOL LoadImages(){ //load graphics from files to surfaces
int i;
//get the background image
if(!background.load("bckgnd.bmp"))return FALSE; //read from file
background.draw(lpBackground); //draw to background surface
//set palettes in all surfaces
if(!background.setpalette(lpPrimaryPalette))return FALSE;
if(!background.setpalette(lpSecondaryPalette))return FALSE;
//load the plane sprite
if(!g_cSpriteImages.load("sprites.bmp"))return FALSE;
spaceshipsprite=new CBaseSprite(1,121,72);
if(!LoadSpaceShipSprite())return FALSE; //load spaceship images
bunkersprite=new CBaseSprite(1,121,72);
if(!LoadBunkerSprite())return FALSE; //load bunker images
for(i=0; i<MAX_INVGRPROWS; i++)
invgrpsprite[i] = new CBaseSprite(2,115,72);
if(!LoadInvGrpSprite())return FALSE; //load invader group images
return TRUE;
} //LoadImages
void CreateObjects(){
int i; //don't mess with MST vs. rest of the world counter vars
int sHeight= spaceshipsprite->height();
spaceship.create(SCREEN_WIDTH/2,SCREEN_HEIGHT,
0,0,spaceshipsprite); //create plane
for(i=0; i < BUNKER_NUM; i++)
bunkers[i].create((i+1)*BUNKER_OFFSET,SCREEN_HEIGHT-sHeight,
0,0,bunkersprite);
minInvHeight = SCREEN_HEIGHT - sHeight - bunkersprite->height();
invaders.create(SCREEN_WIDTH*2/3,SCREEN_HEIGHT*1/2, invgrpsprite);
} //CreateObjects
BOOL RestoreSurfaces(){ //restore all surfaces
BOOL result=TRUE, flag=FALSE;
int i;
if(SUCCEEDED(lpPrimary->Restore())) //if primary restored
result=result&&background.draw(lpPrimary)&& //redraw image
background.setpalette(lpPrimaryPalette); //set palette
else return FALSE;
if(SUCCEEDED(lpSecondary->Restore())) //if secondary restored
result=result&&background.draw(lpSecondary)&& //redraw image
background.setpalette(lpSecondaryPalette); //set palette
else return FALSE;
if(SUCCEEDED(lpBackground->Restore())) //if background restored
result=result&&background.draw(lpBackground); //redraw image
else return FALSE;
if(spaceshipsprite->Restore()) //if spaceship sprite restored
result=result&&LoadSpaceShipSprite(); //redraw image
else return FALSE;
if(bunkersprite->Restore()) //if bunker sprite restored
result=result&&LoadBunkerSprite(); //redraw image
for(i=0; i < MAX_INVGRPROWS; i++)
{
if(invgrpsprite[i]->Restore()) //if invader sprites restored
flag = TRUE;
else return FALSE;
}
if(flag) result=result&&LoadBunkerSprite(); //redraw image
else return FALSE;
return result;
} //RestoreSurfaces
BOOL PageFlip(){ //return TRUE if page flip succeeds
if(lpPrimary->Flip(NULL,DDFLIP_WAIT)==DDERR_SURFACELOST)
return RestoreSurfaces();
return TRUE;
} //PageFlip
BOOL ComposeFrame(){ //compose a frame of animation
int i;
//draw background
lpSecondary->BltFast(0,0,lpBackground,NULL,
DDBLTFAST_NOCOLORKEY|DDBLTFAST_WAIT);
//move spaceship
spaceship.move();
//
// make sure space ship stays in bounds
//
if(spaceship.getX() < MIN_LEFTPOS)
{
spaceship.setX(MIN_LEFTPOS);
spaceship.setXVel(0);
}
if(spaceship.getX() > MAX_RIGHTPOS)
{
spaceship.setX(MAX_RIGHTPOS);
spaceship.setXVel(0);
}
// move invaders
invaders.move();
//
// make sure invaders stays in bounds
//
if(invaders.getBottom() > minInvHeight)
{
invaders.clear();
invaders.create(SCREEN_WIDTH*2/3,SCREEN_HEIGHT*1/2, invgrpsprite);
Timer.start();
}
//draw objects
spaceship.draw(lpSecondary);
for(i=0; i < BUNKER_NUM; i++)
bunkers[i].draw(lpSecondary);
invaders.draw(lpSecondary);
return TRUE;
} //ComposeFrame
BOOL ProcessFrame(){ //process a frame of animation
ComposeFrame(); //compose a frame in secondary surface
return PageFlip(); //flip video memory surfaces
} //ProcessFrame
BOOL keyboard_handler(WPARAM keystroke){ //keyboard handler
BOOL result=FALSE; //return TRUE if game is to end
switch(keystroke){
case VK_ESCAPE: result=TRUE; break; //exit game
case VK_SPACE: //delete a random invader
invaders.remove(rand()%invaders.getSize());
break;
//
//space ship controls
//
case VK_LEFT:
spaceship.accelerate(-1);
break;
case VK_RIGHT:
spaceship.accelerate(1);
break;
case VK_DOWN:
spaceship.setXVel(0);
break;
default: break;
}
return result;
} //keyboard_handler
//message handler (window procedure)
long CALLBACK WindowProc(HWND hwnd,UINT message,
WPARAM wParam,LPARAM lParam){
switch(message){
case WM_ACTIVATEAPP: ActiveApp=wParam; break;
case WM_CREATE: break;
case WM_KEYDOWN: //keyboard hit
if(keyboard_handler(wParam))DestroyWindow(hwnd);
break;
case WM_DESTROY: //end of game
if(lpDirectDrawObject!=NULL){ //if DD object exists
if(lpSecondary!=NULL) //if secondary surface exists
lpSecondary->Release(); //release secondary surface
if(lpPrimary!=NULL) //if primary surface exists
lpPrimary->Release(); //release primary surface
if(lpBackground!=NULL) //if background exists
lpBackground->Release(); //release background
if(spaceshipsprite!=NULL) //if spaceship sprite exists
spaceshipsprite->Release(); //release spaceship sprite
delete spaceshipsprite; //delete spaceship sprite
if(bunkersprite!=NULL) //if bunker sprite exists
bunkersprite->Release(); //release bunker sprite
delete bunkersprite; //delete bunker sprite
int i;
for(i=0; i < MAX_INVGRPROWS; i++)
{
if(invgrpsprite[i]!=NULL) //if invgrpsprite exists
invgrpsprite[i]->Release(); //release invgrpsprite
delete invgrpsprite[i];
}
lpDirectDrawObject->Release(); //release DD object
}
ShowCursor(TRUE); //show the mouse cursor
PostQuitMessage(0); //and exit
break;
default: //default window procedure
return DefWindowProc(hwnd,message,wParam,lParam);
} //switch(message)
return 0L;
} //WindowProc
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow){
MSG msg; //current message
HWND hwnd; //handle to fullscreen window
hwnd=CreateDefaultWindow("directX demo 3",hInstance);
if(!hwnd)return FALSE;
//set up window
ShowWindow(hwnd,nCmdShow); UpdateWindow(hwnd);
SetFocus(hwnd); //allow input from keyboard
ShowCursor(FALSE); //hide the cursor
//init graphics
BOOL OK=InitDirectDraw(hwnd);//initialize DirectDraw
if(OK)OK=LoadImages(); //load images from disk
if(!OK){ //bail out if initialization failed
DestroyWindow(hwnd); return FALSE;
}
//start game timer
Timer.start();
//create objects
CreateObjects();
//message loop
while(TRUE)
if(PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)){
if(!GetMessage(&msg,NULL,0,0))return msg.wParam;
TranslateMessage(&msg); DispatchMessage(&msg);
}
else if(ActiveApp)ProcessFrame(); else WaitMessage();
} //WinMain
|