• No results found

In order to further demonstrate the concepts discussed in this chapter, we should examine the case study provided on the CD in this chapter’s directory. It is a game titled Shoot ’em, where the player simply fires at oncoming enemy spaceships. It is a simple game using simple graphics, but it should serve as an example of implementing several of the topics we just covered. This is not a complete game, and many of the tasks required by a commercial quality game have been left out (such as sound and music output) so as not to complicate the code. The architecture used in this example game will be the basis for examples to come.

Of particular interest to this chapter is the game loop itself. The example implements a simplistic game loop similar to the first loop architecture we’ve discussed. It is state driven, and throughout the code you can see where this state is modified and how it affects the overall game processing. This game loop is embodied in the DrawSurfaces procedure. Take a look at this procedure to get a taste of how to implement an actual game loop.

This case study example is presented in its entirety to get you familiar with the types of examples

demonstrated throughout the book. There are several programming techniques implemented in this example that we have yet to discuss, such as sprite manipulation. This example also contains some DirectX

initialization code that we haven’t yet discussed. Peruse the first part of the example to get familiar with the function calls that we’ll be exploring in subsequent chapters. Beginning with the DrawSurfaces procedure, we see some techniques that we’ve outlined in this chapter. This example also demonstrates how to logically control such entities as sprites and background animation elements with arrays. Later in the book, we’ll extend this to a more general approach for controlling game entities.

Previous Table of Contents Next [an error occurred while processing this directive]

Products | Contact Us | About Us | Privacy | Ad Info | Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-2000 EarthWeb Inc. All rights reserved. Reproduction whole or in part in any form or medium without express written permission of

EarthWeb is prohibited. Read EarthWeb's privacy statement.

Search Tips

Advanced Search

Delphi Graphics and Game Programming Exposed with DirectX 7.0 by John Ayres

Wordware Publishing, Inc.

ISBN: 1556226373 Pub Date: 12/01/99 Search this book:

Previous Table of Contents Next

Listing 2-3: The Shoot ’em example game

unit ShootEmU;

{******************************************************************************

Shoot Example Application Author: John Ayres

Like most examples in the book, this one takes advantage of the baseline DirectX application code. It implements a very simplistic game in order to demonstrate basic game architecture. The architecture of a game is arbitrary and will in part be determined by the type of game being created. However, most games share similar tasks in order to generate a frame of animation, and this example should serve to familiarize the user with some of the more common gaming application tasks.

******************************************************************************

Copyright © 1999 by John Ayres, All Rights Reserved

This example utilizes sprites from the freeware sprite library SpriteLib by Ari Feldman.

Sprite lib sprites Copyright (c) 1998 by Ari Feldman. Check out http://www.chromewave.com

This code is freeware and can be used for any personal or commercial purposes without royalty or license. Use at your own risk.

******************************************************************************}

interface uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, DDraw;

Title

---{these constants are used to modify specific attributes of the DirectX application, such as the color of the main form, the requested resolution, etc.}

COOPERATIVELEVEL = DDSCL_FULLSCREEN or DDSCL_ALLOWREBOOT or DDSCL_ALLOWMODEX or DDSCL_EXCLUSIVE;

SURFACETYPE = DDSCAPS_COMPLEX or DDSCAPS_FLIP or DDSCAPS_PRIMARYSURFACE;

const

{this user-defined message is used to start the flipping loop}

WM_DIRECTXACTIVATE = WM_USER + 200;

{the player’s ship index}

PLAYERSHIPIDX = 0;

{the total number of ships}

NUMSHIPS = 5;

{the total number of bullets}

NUMBULLETS = 19;

{the total number of explosions}

NUMEXPLOSIONS = 5;

{the total number of stars}

NUMSTARS = 100;

type

{tracks the overall game state}

TGameState = (gsDemo, gsPlaying, gsIntermission);

{our base sprite class}

TSprite = class

FrameWidth, FrameHeight,

XFrameStartOffset, YFrameStartOffset: Integer;

BoundBox,

CollisionBox: TRect;

Living: Boolean;

CurAnimTime,

AnimThreshold: LongWord;

procedure Move; virtual;

procedure Draw; virtual;

end;

{our star class, descended from sprites}

TStar = class(TSprite) public

Color: Byte;

procedure Move; override;

procedure Draw; override;

end;

{the explosion class, also descended from sprites}

TExplosion = class(TSprite)

public

procedure Move; override;

end;

{the form class}

TfrmDXAppMain = class(TForm)

procedure FormCreate(Sender: TObject);

procedure FormDestroy(Sender: TObject);

procedure FormKeyDown(Sender: TObject; var Key: Word;

Shift: TShiftState);

procedure FormActivate(Sender: TObject);

private

{ Private declarations }

{flips back to the GDI surface to display the exception error message}

procedure ExceptionHandler(Sender: TObject; ExceptionObj: Exception);

{the main rendering loop}

procedure AppIdle(Sender: TObject; var Done: Boolean);

{intercepts certain messages to provide appropriate functionality}

procedure AppMessage(var Msg: TMsg; var Handled: Boolean);

{flips the DirectX surfaces}

procedure FlipSurfaces;

{restores any lost surfaces}

procedure RestoreSurfaces;

{draws the contents of surfaces}

procedure DrawSurfaces;

public

{tracks the overall game state}

GameState: TGameState;

{tracks the level and score, number of ships left to destroy before

advancing to the next level, and the number of lives the player has left}

Level, Score,

NumShipsToNextLevel, LivesLeft: Integer;

{timing variable for pausing in the intermission mode}

TimeAccum: Longint;

{indicates if the player can shoot or not}

CanShoot: Boolean;

{tracks all of the living ships}

Ships: array[0..NUMSHIPS] of TSprite;

{tracks all of the living player bullets}

PlayerBullets: array[0..NUMBULLETS] of TSprite;

{tracks all of the living enemy bullets}

EnemyBullets: array[0..NUMBULLETS] of TSprite;

{tracks all of the living explosions}

Explosions: array[0..NUMEXPLOSIONS] of TExplosion;

{tracks all of the living stars}

Stars: array[0..NUMSTARS] of TStar;

{these are various procedures that initialize certain variables and generalize control of certain elements of the game}

procedure StartEnemy;

procedure StartExplosion(XPos, YPos: Integer);

procedure StartBullet(XPos,YPos,XVel,YVel: Integer;PlayerBullet: Boolean);

procedure ResetPlayer;

procedure ResetAll;

procedure PlayerShipKilled;

procedure NewGame;

end;

var

frmDXAppMain: TfrmDXAppMain;

{the main DirectDraw interface}

FDirectDraw: IDirectDraw4;

{the interfaces for the primary and back buffer surfaces and bitmap image}

FPrimarySurface, FBackBuffer,

FGraphicsImage: IDirectDrawSurface4;

{the palette interface}

FPalette: IDirectDrawPalette;

implementation uses

DXTools, DDUtil, MMSystem, gensuppt;

{$R *.DFM}

{ *—->> BASELINE APPLICATION CODE <<—-* }

{ — the callback function used to ensure that the selected graphics mode — — is supported by DirectX — } function EnumModesCallback(const EnumSurfaceDesc: TDDSurfaceDesc2;

Information: Pointer): HResult; stdcall;

begin

{if the height, width, and color depth match those specified in the constants, then indicate that the desired graphics mode is supported}

if (EnumSurfaceDesc.dwHeight = DXHEIGHT) and (EnumSurfaceDesc.dwWidth = DXWIDTH) and

(EnumSurfaceDesc.ddpfPixelFormat.dwRGBBitCount = DXCOLORDEPTH) then Boolean(Information^) := TRUE;

Result := DDENUMRET_OK;

end;

{ —> Events Hooked to the Application <— }

{ — this event is called when an exception occurs, and simply flips back — — to the GDI surface so that the exception dialog box can be read — } procedure TfrmDXAppMain.ExceptionHandler(Sender: TObject;

ExceptionObj: Exception);

begin

{disconnect the OnIdle event to shut off the rendering loop}

Application.OnIdle := nil;

{if the DirectDraw object was successfully created, flip to the GDI surface}

if Assigned(FDirectDraw) then FDirectDraw.FlipToGDISurface;

{display the exception message}

MessageDlg(ExceptionObj.Message, mtError, [mbOK], 0);

{reconnect the OnIdle event to reenter the rendering loop}

Application.OnIdle := AppIdle;

end;

{ — handles certain messages that are required to make DirectX function — — properly within Delphi — } procedure TfrmDXAppMain.AppMessage(var Msg: TMsg; var Handled: Boolean);

begin

case Msg.Message of WM_ACTIVATEAPP:

{unhook the OnIdle event when the application is being deactivated.

this will stop all rendering}

if not Boolean(Msg.wParam) then Application.OnIdle := nil else

{upon activating the application, send ourselves the user-defined message}

PostMessage(Application.Handle, WM_DIRECTXACTIVATE, 0, 0);

WM_DIRECTXACTIVATE:

begin

{upon activating, restore all surfaces (reloading their memory as necessary), hook up the OnIdle event, and redraw the contents of

{ — initialize essential form properties — }

procedure TfrmDXAppMain.FormCreate(Sender: TObject);

var

iCount: Integer;

begin

Randomize;

{set up the application exception handler}

Application.OnException := ExceptionHandler;

{initialize form properties; note that the FormStyle property must be set to fsStayOnTop}

{initialize the game state to demo}

GameState := gsDemo;

{create all of the star objects}

for iCount := 0 to NUMSTARS do begin

{create the star}

Stars[iCount] := TStar.Create;

{set its position}

Stars[iCount].XPos := Random(DXWIDTH);

Stars[iCount].YPos := Random(DXHEIGHT);

{set its velocity. stars are only going to move from the top of the screen to the bottom, so we’re only concerned with the horizontal velocity}

Stars[iCount].XVel := 0;

Stars[iCount].YVel := Random(5)+1;

{set the color of the star based on its velocity}

case Stars[iCount].YVel of

1..2 : Stars[iCount].Color := 252;

{ — provides essential cleanup functionality — } procedure TfrmDXAppMain.FormDestroy(Sender: TObject);

var

iCount: Integer;

begin

{disengage our custom exception handler}

Application.OnException := nil;

{remember, we do not have to explicitly free the DirectDraw objects, as they will free themselves when they go out of context (such as when the

application is closed)}

for iCount := 0 to NUMSTARS do Stars[iCount].Free;

end;

{ — this method initializes DirectX and creates all necessary objects — } procedure TfrmDXAppMain.FormActivate(Sender: TObject);

var

{we can only get a DirectDraw4 interface from the DirectDraw interface, so we need a temporary interface}

TempDirectDraw: IDirectDraw;

{structures required for various methods}

DDSurface: TDDSurfaceDesc2;

DDSCaps: TDDSCaps2;

{flag used to determine if the desired graphics mode is supported}

SupportedMode: Boolean;

begin

{if DirectDraw has already been initialized, exit}

if Assigned(FDirectDraw) then exit;

{create a temporary DirectDraw object. this is used to create the desired DirectDraw4 object}

DXCheck( DirectDrawCreate(nil, TempDirectDraw, nil) );

try

{we can only get a DirectDraw4 interface through the QueryInterface method of the DirectDraw object}

DXCheck( TempDirectDraw.QueryInterface(IID_IDirectDraw4, FDirectDraw) );

finally

{now that we have the DirectDraw4 object, the temporary DirectDraw object is no longer needed}

TempDirectDraw := nil;

end;

{set the cooperative level to that defined in the constants}

DXCheck( FDirectDraw.SetCooperativeLevel(Handle, COOPERATIVELEVEL) );

{hook up the application message handler}

Application.OnMessage := AppMessage;

{call EnumDisplayModes and verify that the desired graphics mode is indeed supported}

FillChar(DDSurface, SizeOf(TDDSurfaceDesc2), 0);

DDSurface.dwSize := SizeOf(TDDSurfaceDesc2);

DDSurface.dwFlags := DDSD_HEIGHT or DDSD_WIDTH or DDSD_PIXELFORMAT;

DDSurface.dwHeight := DXHEIGHT;

DDSurface.dwWidth := DXWIDTH;

DDSurface.ddpfPixelFormat.dwSize := SizeOf(TDDPixelFormat_DX6);

DDSurface.ddpfPixelFormat.dwRGBBitCount := DXCOLORDEPTH;

SupportedMode := FALSE;

DXCheck( FDirectDraw.EnumDisplayModes(0, @DDSurface, @SupportedMode, EnumModesCallback) );

{if the desired graphics mode is not supported by the DirectX drivers, display an error message and shut down the application}

if not SupportedMode then begin

MessageBox(Handle, PChar(The installed DirectX drivers do not support a + display mode of: +IntToStr(DXWIDTH)+’ X +

{set the display resolution and color depth to that defined in the constants}

DXCheck( FDirectDraw.SetDisplayMode(DXWIDTH, DXHEIGHT, DXCOLORDEPTH, 0, 0) );

{initialize the DDSurface structure to indicate that we will be creating a complex flipping surface with one backbuffer}

FillChar(DDSurface, SizeOf(TDDSurfaceDesc2), 0);

DDSurface.dwSize := SizeOf(TDDSurfaceDesc2);

DDSurface.dwFlags := DDSD_CAPS or DDSD_BACKBUFFERCOUNT;

DDSurface.ddsCaps.dwCaps := SURFACETYPE;

DDSurface.dwBackBufferCount := BUFFERCOUNT;

{create the primary surface object}

DXCheck( FDirectDraw.CreateSurface(DDSurface, FPrimarySurface, nil) );

{indicate that we want to retrieve a pointer to the backbuffer (the surface immediately behind the primary surface in the flipping chain) }

FillChar(DDSCaps, SizeOf(TDDSCaps2), 0);

DDSCaps.dwCaps := DDSCAPS_BACKBUFFER;

{retrieve the surface}

DXCheck( FPrimarySurface.GetAttachedSurface(DDSCaps, FBackBuffer) );

{load the palette from the bitmap to be used and attach it to the primary surface}

FPalette := DDLoadPalette(FDirectDraw, ExtractFilePath(ParamStr(0))+

SpriteImgs.bmp’);

DXCheck( FPrimarySurface.SetPalette(FPalette) );

{load the bitmap image containing the background and animation frames}

FGraphicsImage := DDLoadBitmap(FDirectDraw, ExtractFilePath(ParamStr(0))+

SpriteImgs.bmp’);

{indicate which color in the bitmap is transparent}

DXCheck( DDSetColorKey(FGraphicsImage, $00000000) );

{initialize the surfaces with the images}

DrawSurfaces;

{post a message that will hook up the OnIdle event and start the main rendering loop}

PostMessage(Handle, WM_ACTIVATEAPP, 1, 0);

end;

{ —> Form Methods <— }

{ — this method is called in order to flip the surfaces — } procedure TfrmDXAppMain.FlipSurfaces;

var

DXResult: HResult;

begin

{perform the page flip. note that the DDFLIP_WAIT flag has been used, indicating that the function will not return until the page flip has been performed. this could be removed, allowing the application to perform other processing until the page flip occurs. however, the application will need to continuously call the Flip method to ensure that the page flip takes place}

elseDXResult := FPrimarySurface.Flip(nil, DDFLIP_WAIT);

{if the surfaces were lost, restore them. on any other error, raise an exception}

if DXResult = DDERR_SURFACELOST then RestoreSurfaces

else if DXResult <> DD_OK then DXCheck(DXResult);

end;

{ — this method is called when the surface memory is lost — — and must be restored. surfaces in video memory that — — contain bitmaps must be reinitialized in this function — } procedure TfrmDXAppMain.RestoreSurfaces;

begin

{restore the primary surface, which in turn restores any implicit surfaces}

FPrimarySurface._Restore;

{restore the bitmap image’s surface}

FGraphicsImage._Restore;

{reload the bitmap into the surface}

DXCheck( DDReLoadBitmap(FGraphicsImage, ExtractFilePath(ParamStr(0))+

SpriteImgs.bmp’) );

end;

{ — this method is continuously called by the application, and provides — — the main rendering loop. this could be replaced by a custom — — while..do loop — } procedure TfrmDXAppMain.AppIdle(Sender: TObject; var Done: Boolean);

begin

{indicates that the application should continuously call this method}

Done := FALSE;

{if DirectDraw has not been initialized, exit}

if not Assigned(FDirectDraw) then Exit;

{draw surface content and flip the surfaces}

DrawSurfaces;

FlipSurfaces;

end;

{ — this method is called when the contents of the surfaces need to be — — drawn. it will be continuously called by the AppIdle method, so any — — rendering or animation could be done within this method — }

{erase the last frame of animation}

ColorFill(FBackBuffer, $00000000, nil);

{draw all of the background stars. the stars are a background animation that are unaffected by any game state, so we always draw them}

for iCount := 0 to NUMSTARS do begin

Stars[iCount].Move;

Stars[iCount].Draw;

end;

{perform various actions based on the current game state}

case GameState of

{in the intermission state, we want to draw the current

(CanShoot) then

{update the bullet based on velocity}

StartExplosion(Ships[PLAYERSHIPIDX].XPos,

Ships[PLAYERSHIPIDX].YPos);

destroy it}

{update the ship’s position based on velocity}

{ —> Deletable Events <— } ///

{ — as a matter of convenience this framework will terminate when the — — Escape key is pressed, but this should probably be deleted and — — replaced with your own terminate methods — } procedure TfrmDXAppMain.FormKeyDown(Sender: TObject; var Key: Word;

Shift: TShiftState);

{ *—->> END BASELINE APPLICATION CODE <<—-* }

{ TSprite }

{if it is time to increment the current frame...}

if timeGetTime - CurAnimTime > AnimThreshold then begin

{increment the frame of animation}

Inc(CurFrame);

{roll it over if necessary}

if CurFrame>=NumFrames then CurFrame := 0;

{record the time}

CurAnimTime := timeGetTime;

end;

{draw the next animation frame into the backbuffer}

SourceRect := Rect(XFrameStartOffset+(FrameWidth*CurFrame), YFrameStartOffset, XFrameStartOffset+

(FrameWidth*CurFrame)+FrameWidth, YFrameStartOffset+FrameHeight);

{if the sprite is fully on screen...}

if (YPos >= 0) and (YPos <= DXHEIGHT-FrameHeight) then {draw it to the screen}

FBackBuffer.BltFast(XPos, YPos, FGraphicsImage, SourceRect, DDBLTFAST_SRCCOLORKEY OR DDBLTFAST_WAIT) else

begin

{...otherwise, it is partially hidden at the top or bottom, so determine the rectangular portion of the sprite that is visible}

if YPos < 0 then begin

DestY := 0;

SourceRect := Rect(SourceRect.Left, SourceRect.Top-YPos, SourceRect.Right, SourceRect.Bottom);

end else begin

DestY := YPos;

SourceRect := Rect(SourceRect.Left, SourceRect.Top, SourceRect.Right, SourceRect.Top+(DXHEIGHT-YPos));

end;

{copy the image portion to the screen}

FBackBuffer.BltFast(XPos, DestY, FGraphicsImage, SourceRect, DDBLTFAST_SRCCOLORKEY OR DDBLTFAST_WAIT);

end;

end;

procedure TSprite.Move;

begin

{move the sprite according to its horizontal velocity}

XPos := XPos + XVel;

YPos := YPos + YVel;

{clip the sprite to the boundaries of the screen}

if XPos > DXWIDTH-FrameWidth then begin

XPos := DXWIDTH-FrameWidth;

XVel := 0-XVel;

{update its bounding box}

CollisionBox := Rect(XPos+BoundBox.Left, YPos+BoundBox.Top, XPos+BoundBox.Right, YPos+BoundBox.Bottom);

{if the sprite has gone off the top or bottom of the screen, it is dead}

if (YPos < 0-FrameHeight) or (YPos > DXHEIGHT) then Living := FALSE;

end;

{ TExplosion }

procedure TExplosion.Move;

begin

{explosions don’t actually move, but an explosion’s lifespan is over when it has played all of its frames}

if CurFrame+1 >= NumFrames then Living := FALSE;

end;

{ TStar }

procedure TStar.Move;

begin

{stars only move vertically, so update their position}

YPos := YPos + YVel;

{clip them to the boundaries of the screen, wrapping around if necessary}

if YPos < 0 then

FBackBuffer.Lock(nil, SurfaceDesc, DDLOCK_SURFACEMEMORYPTR or DDLOCK_WAIT,0);

{color the appropriate pixel in the backbuffer using the star’s color. this is a very fast way to plot individual pixels}

try

Byte(Pointer(Longword(SurfaceDesc.lpSurface)+

(YPos*SurfaceDesc.lPitch+XPos))^) := Color;

finally

{the buffer must be unlocked before it can be flipped}

procedure TfrmDXAppMain.StartBullet(XPos, YPos, XVel, YVel: Integer;

PlayerBullet: Boolean);

var

iCount: Integer;

begin

{if we’re starting a player bullet}

if PlayerBullet then

PlayerBullets[iCount].XFrameStartOffset := 372;

PlayerBullets[iCount].YFrameStartOffset := 48;

PlayerBullets[iCount].BoundBox := Rect(0, 0, 10, 17);

PlayerBullets[iCount].CurAnimTime := timeGetTime;

PlayerBullets[iCount].AnimThreshold := 10000;

{this bullet has been created, we can exit}

{otherwise, search for an open slot in the enemy bullets array}

for iCount := 0 to NUMBULLETS do

EnemyBullets[iCount].CurFrame := 0;

EnemyBullets[iCount].FrameWidth := 10;

EnemyBullets[iCount].FrameHeight := 10;

EnemyBullets[iCount].XFrameStartOffset := 382;

EnemyBullets[iCount].YFrameStartOffset := 51;

EnemyBullets[iCount].BoundBox := Rect(0, 0, 10, 10);

EnemyBullets[iCount].CurAnimTime := timeGetTime;

EnemyBullets[iCount].AnimThreshold := 10000;

{this bullet has been created, we can exit}

{search for an open slot in the ships array. notice that the player’s ship is always located at the first index, so we skip it}

for iCount := PLAYERSHIPIDX+1 to NUMSHIPS do if Ships[iCount] = nil then

Ships[iCount].XFrameStartOffset := 186;

Ships[iCount].YFrameStartOffset := 0;

Ships[iCount].BoundBox := Rect(3, 3, 59, 33);

Ships[iCount].CurAnimTime := timeGetTime;

Ships[iCount].AnimThreshold := 100;

{the sprite was created, we can exit}

{search for an open slot in the explosions array}

for iCount := 0 to NUMEXPLOSIONS do if Explosions[iCount] = nil then begin

{create the explosion sprite}

Explosions[iCount] := TExplosion.Create;

{initialize the sprite properties}

Explosions[iCount].NumFrames := 6;

Explosions[iCount].CurFrame := 0;

Explosions[iCount].FrameWidth := 62;

Explosions[iCount].FrameHeight := 47;

Explosions[iCount].XFrameStartOffset := 0;

Explosions[iCount].YFrameStartOffset := 48;

Explosions[iCount].Living := TRUE;

Explosions[iCount].XPos := XPos;

Explosions[iCount].YPos := YPos;

Explosions[iCount].CurAnimTime := timeGetTime;

Explosions[iCount].AnimThreshold := 100;

{the sprite was created, we can exit}

Break;

PlayerBullets[iCount] := nil;

EnemyBullets[iCount].Free;

EnemyBullets[iCount] := nil;

end;

{free all of the explosions}

for iCount := 0 to NUMEXPLOSIONS do begin

Explosions[iCount].Free;

Explosions[iCount] := nil;

end;

{reset the pause timer}

TimeAccum := -1;

end;

procedure TfrmDXAppMain.ResetPlayer;

begin

{make sure the player ship is created}

if not Assigned(Ships[PLAYERSHIPIDX]) then Ships[PLAYERSHIPIDX] := TSprite.Create;

{initialize the player ship sprite properties}

Ships[PLAYERSHIPIDX].NumFrames := 3;

Ships[PLAYERSHIPIDX].BoundBox := Rect(3, 3, 59, 45);

Ships[PLAYERSHIPIDX].Living := TRUE;

Ships[PLAYERSHIPIDX].XPos := DXWIDTH div 2;

Ships[PLAYERSHIPIDX].YPos := DXHEIGHT-Ships[PLAYERSHIPIDX].FrameHeight-20;

Ships[PLAYERSHIPIDX].XVel := 0;

Ships[PLAYERSHIPIDX].YVel := 0;

Ships[PLAYERSHIPIDX].CurAnimTime := timeGetTime;

Ships[PLAYERSHIPIDX].AnimThreshold := 100;

{indicate that the player can now shoot}

CanShoot := TRUE;

end;

procedure TfrmDXAppMain.PlayerShipKilled;

begin

{decrement the number of lives}

Dec(LivesLeft);

{reset all of the sprites}

ResetAll;

{if the player has no more lives left, go to demo; otherwise go to intermission}

if LivesLeft < 0 then GameState := gsDemo else

GameState := gsIntermission;

end;

end.

Summary

In this chapter, we discussed the most important and most basic issues involved in designing game application software.

When designing the architecture of game application software, it is important to keep these points in mind:

• Every game, no matter how complex, can be broken down into three constituent parts: graphics, sound, and user input.

• The specific architecture of a game will vary widely depending on the type of game and its inherent functionality.

Even so, most games usually perform similar tasks in a similar order. From a high-level standpoint, game software can be thought of in terms of six elements that perform necessary tasks: Initialization, Game Introduction, Game Startup, the Game Loop, Game Ending, and Shutdown and Exit.

• The game loop is usually the focal point of a game application, and is where all of the action takes place. It processes a series of instructions over and over as quickly as possible to produce the on-screen images that reflect the current state of the game. This processing continues with or without user interaction. It can be implemented in a variety of ways, and must perform several different tasks as rapidly as possible. Technically speaking, a game loop is responsible for three things: retrieving input, updating game objects, and rendering a new frame of animation.

Additionally, it must perform collision detection, enemy AI, and other tasks commensurate with the specific type of

Additionally, it must perform collision detection, enemy AI, and other tasks commensurate with the specific type of