Kamis, 12 Februari 2009

A GameProgrammer’s Journey

A GameProgrammer’s Journey

Table of contents
A GameProgrammer’s Journey 1
Table of contents 2
Tutorial 1 – The basic framework for DelphiX 3
Introduction 3
Before we start 3
Start easy 3
Images & Sound 6
Initialize 6
Displaying the menu 7
After the menu 8
Ending the game 9
Congratulations 10


Tutorial 1 – The basic framework for DelphiX
Introduction
Welcome to my first DGP tutorial. In this tutorial you will learn how to create a basic framework for a game.

Before you can go any further, make sure you have the following:

• Delphi version 3, 4 or 5. Version 6 is possible but you'll need the conversion from Turbo;
• DelphiX, get it here;
• Photoshop or a similar program, (but only if you want to use you own graphics!);
• ome basic knowledge of Delphi(pascal) and programming.

Apart from the tutorial itself, you may use everything else for your own purposes (ie. Graphics & code). An e-mail saying that you've used something for you own game would be appreciated though.

Enjoy.
Before we start
Before we start with this tutorial I will first explain what we are going to do. In this tutorial, which is actually going to be a the first of two, I will tell you how to make a basis for your own games.

I'll show you how to make the menu, which you can use to navigate to the various parts in the game. Next, I will show you how to build the story page for the game we will make in tutorial 2.
Start easy
If you haven't already done so, start Delphi.

Place the following DelphiX components on the Form:

• DXDraw: This component is used for drawing.
• DXInput; This component handles the user input.
• DXTimer; A better timer than the standard Delphi Timer.
• DXSpriteEngine; This component handles spritemovements, -collisions, and more
• DXSound; This component is used for sound.
• DXWaveList; it contains all your .wav files used in the game.
• DXImageList; it contains all your images used in the game.

Some of these components have properties that need to have a value before we can use them properly:

• DXDraw: set the Align property to alClient.
• DXSpriteEngine: set the DXDraw property to DXDraw1.
• DXTimer: Set the interval to 16 (gives us a framerate of 60) and enabled to false;
• DXWaveList: set the DXSound property at DXSound1. (Get the sounds at the bottom of the page).
• DXImageList: (Get the sounds at the bottom of the page). Right click on the component and load the dxg-file. All images that you need are now loaded. Also, set the DXDraw property to DXDraw1.

When the game starts, we do not want to see those ugly windowborders. In order to make this happen you have to change the borderStyle property of the Form in bsNone. The height and width of the form will be set in the code. This way you don't have to worry about your formsize all the time.

When the game starts we want to show the player a menu where he or she can start a new game, read the instructions or quit the game. Of course, you may add extra items like options or perhaps an intro, but I'll just stick to these three menuitems.

type TGameState = (gsStart, gsMenu, gsReset, gsGame, gsEndGame, gsGameOver, gsInstructions, gsEnd);

Declare the following variables:

var Form1 : TForm1;
Points : word;
GameState : TGameState;
GameCounter : Integer;

And create the following procedures:

implementation

$R *.DFM}

procedure doStart;
begin
// do the init stuff here, like loading images
end;

procedure doMenu;
begin
// do menu stuff here
end;

procedure doInstructions;
begin
// do Instructions stuff here
end;

procedure doReset;
begin
// put the things before the game starts
end;

procedure doGame;
begin
// put the game stuff in here
end;

procedure doEndGame;
begin
// do stuff here like freeing sprites
end;

procedure doGameOver;
begin
// do gameover stuff here like showing gameover message
end;

procedure doEnd;
begin
// do end stuff here like freeing images and all
end;

procedure ProcessGamestate;
begin
case GameState of
gsStart : doStart;
gsMenu : doMenu;
gsInstructions :doInstructions;
gsReset : doReset;
gsGame : doGame;
gsEndGame : doEndGame;
gsGameOver: doGameOver;
gsEnd : doEnd;
end;
end;

Let's see what all this does. When the game starts the GameState variable will be set to gsStart. The onTimer event (below) will call the ProcessGamestate procedure. The ProcessGamestate procedure looks at the GameState variable and starts the correct procedure.

procedure TForm1.DXTimer1Timer(Sender: TObject; LagCount: Integer);
begin
inc(GameCounter);
try
ProcessGamestate;
except
dxtimer1.Enabled:=false;
messagedlg('An error has occurred',mterror,[mbok],0);
end;
dxdraw1.Flip;
end;
Images & Sound
(Doubleclick on the icon to get the images for this tutorial)
Initialize
Every game has somesort of initialization. Variables that need to be set, images that have to load, that sort of things. All that are we going to do in the doStart procedure. DelphiX does have its own Initialize procedure, but we will use that for setting the timer and setting the gamestate to gsStart.

Doubleclick on the DXDraw1Initialize event of DXDraw and place the following code:

procedureTForm1.DXDraw1Initialize(Sender: TObject);
begin
Form1.DXTimer1.Enabled:=true;
GameState:=gsStart;
end;

Setting the Height, Width and Position of the form will be done in the onCreate event of the Form:

procedure TForm1.FormCreate(Sender: TObject);
begin
Form1.Width:=350;
Form1.Height:=350;
form1.Position:=poScreenCenter;
randomize;
end;

For the menu we are going to need a picture showing all of the menuitems. First we'll declare an extra set of variables. A thing about variable names. Make sure you use names that mean something. x2 or y3 may work fine, but they won't make your code look nice. You'll probably have a hard time reading it later too.

...
MenuChoice : byte;
MenuImage : TDirectDrawSurface;
SelectorImage : TDirectDrawSurface;

Then in the doStart procedure add these lines:

procedure doStart;
begin
// do the init stuff here, like loading images
menuImage:=TDirectDrawSurface.Create(Form1.DXDraw1.DDraw);
menuImage.LoadFromGraphic(Form1.DXImageList1.Items.Items[16].picture.Graphic);
SelectorImage :=TDirectDrawSurface.Create(Form1.DXDraw1.DDraw);
SelectorImage.LoadFromGraphic(Form1.DXImageList1.Items.Items[17].picture.Graphic);
SelectorImage.TransparentColor:=clblack;
menuChoice := 1;

GameState := gsMenu;
end;

After creating and loading the two images, (one for the menu and one for selecting the menuitems) the GameState will be set to gsMenu.
Displaying the menu
Displaying the menu seems fairly easy now. The menuImage and selectorImage, we created in the previous chapter, must both be drawn in the correct procedure. Furthermore, the selectorImage has to change location when the user is browsing through the menuitems.

Let's start with the location thing. Doubleclick at the onKeyUp event of Form1 and past the following code:

procedure TForm1.FormKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if key = vk_escape then
begin
case GameState of
gsMenu : GameState := gsEnd;
gsInstructions : GameState := gsMenu;
gsGame : GameState := gsEndGame;
gsGameOver : GameState := gsEndGame;
end;
end;

if (key = vk_return) and (GameState = gsMenu) then
begin
case menuChoice of
1 : GameState := gsReset;
2 : GameState := gsInstructions;
3 : GameState := gsEnd;
end;
end;

if (key = vk_up) and (GameState = gsMenu) then
begin
if menuChoice = 1 then menuChoice := 3 else dec(menuChoice);
end;
if (key = vk_down) and (GameState = gsMenu) then
begin
if menuChoice = 3 then menuChoice := 1 else inc(menuChoice);
end;
end;

The first few lines of code handles the escape key. When the player is in the game or the Instructionsscreen he can return to the menu by pressing escape. As you can see, this is simply done by changing the GameState variable.

When the returnkey is being pressed (and gamestate = gsMenu!) the gameState variable changes using the menuChoice variable. If the value of menuChoice is 1, gsReset will be started, if the value is 2 the gsInstructions will be started. etc.

When, in the menu, the up- or down arrows are pressed, the variable menuChoice will be changed accordingly. If menuChoice is for example 3 and the up-arrow is pressed, it will change to 2. However, if it's 1 and the up arrow is pressed. It will be set to 3!

To display the selectorImage we simply need to check menuChoice, and multiply it with the height of the picture + the space between the menuitems(25+11). The menuImage needs little explanation, it's drawn at coordinates (0,0).

procedure doMenu;
begin
// do menu stuff here
Form1.DXDraw1.surface.Draw(0,0,menuImage.clientrect,menuImage,false);
Form1.DXDraw1.surface.Draw(56,147+(menuChoice-1)*36,
selectorImage.clientrect,selectorImage,true);
end;
After the menu
The game isn't going to be discussed here, but I don't want this menuitem not to work so I'm going to add a small text saying that it is in progress or something.

Changing the font color, brushStyle and gameState in the doReset procedure will be enough for the moment.

procedure doReset;
begin
// put the things before the game starts here

Form1.DXDraw1.surface.Canvas.Font.Color:=clred;
Form1.DXDraw1.surface.Canvas.Brush.Style:=bsclear;
GameState := gsGame;
end;

procedure doGame;
begin
// put the game stuff in here

Form1.DXDraw1.surface.Fill(0);
Form1.DXDraw1.surface.Canvas.TextOut(115,160,'This game is in progress.');
Form1.DXDraw1.surface.Canvas.TextOut(90,180,'Press escape to return to the menu.');
Form1.DXDraw1.surface.Canvas.release;
end;

Remember, always call 'release'. Otherwise your game will crash and (usually if the game is in fullscreen mode) Windows too.

The instructionsscreen is nearly the same. Only this time we'll display an image instead of text.

First, we declare a new image:

...
MenuImage : TDirectDrawSurface;
SelectorImage : TDirectDrawSurface;
InstructionImage : TDirectDrawSurface;

Then add the following lines in the doStart procedure:

...
SelectorImage.LoadFromGraphic(Form1.DXImageList1.Items.Items[17].picture.Graphic);
InstructionImage :=TDirectDrawSurface.Create(Form1.DXDraw1.DDraw);
InstructionImage.LoadFromGraphic(Form1.DXImageList1.Items.Items[18].picture.Graphic);
menuChoice := 1;
GameState := gsMenu;

end;

Then in the doInstructions procedure place this single line of code:

procedure doInstructions;
begin
// do story stuff here
Form1.DXDraw1.surface.Draw(0,0,InstructionImage.clientrect,InstructionImage,false);
end;

That's all, quite simple huh :)
Ending the game
Before the game ends, thus returning the player to the OS, all of the images used in the game have to be released.

To 'free' the images used in the menu and the instructionsscreen. Put the following lines in the doEnd procedure:

procedure doEnd;
begin
// do end stuff here like freeing images and all

Form1.DXTimer1.Enabled:=false;
menuImage.Free;
SelectorImage.Free;
instructionImage.Free;

// and finally close the game
Application.Terminate;
end;

Remember, this procedure is called in the menu.
Congratulations
The menu and all of it's items are finished. Everything within the menu should work properly now. Well done, you have successfully finished this tutorial.

I trust all of this has not been too difficult? I actually don't hope so, because easier than this just isn't possible. (If it is, please me mail, I'd really like to know how).

In the next tutorial I'm going to show you how to use all this to make a really cool space shooter... Until then, happy coding.

Tidak ada komentar:

Posting Komentar