📄 unit1.pas
字号:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
DGCMap, DGCILib, DGC, mmsystem, ExtCtrls, DGCSnd, dgcslib, dsound;
const
MISSILES = 9; // Number of missiles that can be moving at one time
FIRE_RATE = 15; // The rate at which the missiles are fired
EXPLODE_RATE = 3; // Timing for the explosions for the car
type
TMissile = record //This holds info for each missile/explosion
Free : Boolean; //Indicates in use or not
XPos : Integer; //XPos on the screen
YPos : Integer; //YPos on the screen
YSpeed : Integer; //Used to keep moving with the map
Cell : Integer; //current cell for missile animation
Explode : Boolean; //use info for explosion
ExpCell : Integer; //current cell for explosion animation
AnimSpeed: Integer; //Speed of the animation (wait # frames)
AnimInc : Integer; //The number of frames passed since last update
end;
TForm1 = class(TForm)
DGCScreen1: TDGCScreen;
DGCImageLib1: TDGCImageLib;
DGCMapLib1: TDGCMapLib;
DGCImageLib2: TDGCImageLib;
Timer1: TTimer;
DGCSoundLib1: TDGCSoundLib;
DGCAudio1: TDGCAudio;
procedure DGCScreen1Flip(Sender: TObject);
procedure FormKeyPress(Sender: TObject; var Key: Char);
procedure Timer1Timer(Sender: TObject);
procedure DGCAudio1Initialize(Sender: TObject);
procedure DGCScreen1Initialize(Sender: TObject);
private
{ Private declarations }
YPos : Integer; // Y Position of the race map
PX : Integer; // Car's Horizontal position
Speed : Integer; // Car's speed
Accel : Integer; // Crude acceleration counter
CarAnim : Integer; // Car animation frame
AnimDir : Integer; // Which way to move the car anim index
AnimTick : Integer; // number of frames since last update (for car)
Score : array[0..6] of byte; // holds the score
LapTime : array[0..3] of byte; // holds the lap time
MissCnt : array[0..1] of byte; // holds the number of missiles
Shield : Integer; // shield strength
Missile : Array[0..Missiles] of TMissile; // holds missile data
Fired : Integer; //A missile was just fired!
FirePause : Integer; //amount of time since last missile fired
OkToFire : Boolean; //It's now OK to fire a missile (timing)
ExpPause : Integer; //amount of time between expolsions (car)
OkToExp : Boolean; //It's now ok to do the next explosion
Explosions: integer; //Number of explosions that have occured (car)
ExpScreen : TDGCSurface; // a custom surface to grab the screen before car explosions
//These next 2 arrays are used to create a copy of the sound effect so that
//the same sound can be played multiple times at once.
MissileSnd: array[0..MISSILES] of IDirectSoundBuffer;
ExplodeSnd: array[0..MISSILES] of IDirectSoundBuffer;
//This array holds the map details so that we can restore it after a race
MapHold : Array[0..599, 0..14] of byte;
//For these procedures, look at the actual procedure to get a better description
//of what that procedure does.
procedure CheckKeyboard; //Gets keyboard input during race
procedure AnimateCar; //Updates the car sprite
procedure UpdateTrack; //Updates the track position
procedure CheckTile(X,Y : Integer); //Checks for a tile ahead/behind car
procedure DisplayScore; //Draws the score to the screen
procedure AddToScore(a,b,c,d,e,f,g : integer); //Adds points to the score
procedure DisplayShield; //Draws the shied info to the screen
procedure DisplayTime; //Draws the time info to the screen
procedure AddToTime(a,b,c,d : integer); //Adds the time
procedure FireMissile; //Launch a missile
procedure UpdateMissiles; //Update fired missiles
procedure DisplayMissiles; //Draws missile info on the screen
procedure AddToMissiles(a,b : Integer); //Increases number of missiles
procedure SubtractMissile; //Decreases number of missiles
procedure ShowIntro; //Shows the opening screen
procedure ExplodeCar; //Explodes the car!
procedure DrawCarExplosion; //Draws the explosions for the car
public
{ Public declarations }
end;
var
Form1 : TForm1;
//These values are used for the "Frequency" for the car engine --
//The sound frequency is picked by the speed of the car
HumFreq : Array[0..3] of Integer = (11000,19500,26300,33100);
ShowTitle : Boolean = True; // Showing the title stuff ?
ShowHelp : Boolean = False; // Showing Help Screen?
Quit : Boolean = False; // User hit escape during intro
GameOver : Boolean = False; // Car exploded - game over!
implementation
{$R *.DFM}
procedure TForm1.DGCScreen1Flip(Sender: TObject);
begin
If ShowTitle then ShowIntro; //This is here for a clean exit when the user quits
If GameOver then //If shields are gone then destroy car and exit the main game
begin
ExplodeCar; // draw the explosion
Exit; // quit this round
end;
CheckKeyboard; //Checks for "game" keys (CTRL & Cursor)
UpdateTrack; //Updates the track position
AnimateCar; //Animates the car
DGCScreen1.DrawMap(DGCScreen1.Back,0,0,0,40,YPos,false); // Draw map to screen
UpdateMissiles; //Show any fired missiles
DGCScreen1.Back.Draw(PX,160,DGCScreen1.Images[CarAnim],True); // Draw Car
// The next 4 things just display the score, sheild, time, and available missiles
DisplayScore;
DisplayShield;
DisplayTime;
DisplayMissiles;
end;
procedure TForm1.ExplodeCar;
var
pause : Integer;
begin
If Quit then Exit; // If the user has opted to quit the game, exit from here!
Explosions := 0; //reset the number of explosions shown
pause := 0; //reset the pause count
DGCScreen1.CreateSurface(ExpScreen,320,240); // create a surface to hold current screen
DGCScreen1.Back.Erase(0); // erase the background of the game screen
DGCScreen1.DrawMap(DGCScreen1.Back,0,0,0,40,YPos,false); // draw the map in the last position
DisplayScore; // the next couple of things display the regular info
DisplayShield;
DisplayTime;
DisplayMissiles;
ExpScreen.Draw(0,0,DGCScreen1.Back,False); //copy the rebuit screen to the final surface
//We'll draw 50 explosions and wait a few frames to let the explosions finish before exiting
while (Explosions <= 50) and (Pause < 60) do
begin
Application.ProcessMessages; // allow normal system stuff !! A MUST!
DGCScreen1.Back.Erase(0); // clear the background
DGCScreen1.Back.Draw(0,0,ExpScreen,False); // refresh the background
DrawCarExplosion; // Draw the explosions
If Explosions >= 50 then Inc(Pause); //if we have reach the allowable number of
//explosions, pause a few frames to finish anims
DGCScreen1.Flip; //flip the buffers -- this also do the vsync timing (directX thing)
end;
Shield := 100; //reset shields
ShowTitle := True; //game over, now show the into again
ExpScreen.Free; //free the temp surface
end;
procedure TForm1.DrawCarExplosion;
var
loop : integer;
begin
//This loop will reset the OkToExp flag every so often so that the explosions don't all
//go at one time -- better visual!
If not OkToExp then //if waiting
begin
Inc(ExpPause); //increase the explosion pause counter
If ExpPause >= EXPLODE_RATE then //if the pause amount is > than the wait amount
begin //then reset the flag & counter
ExpPause := 0;
OkToExp := True;
end;
end;
for loop := 0 to MISSILES do //go through all the missiles
begin
//If the missile is not used and it is ok to fire a missile and less than 50
//missiles have been fired, then set up a new explostion
//NOTE: The same array (for missiles) is being used for the car explosions
If (Missile[loop].Free) and (OkToExp) and (Explosions < 50) then
begin
Missile[Loop].Free := False; //this entry is now being used!
Inc(Explosions); //Increase the number of explosions fired!
Missile[Loop].Explode := True; //set the animation type
Missile[Loop].ExpCell := 3; //starting image number
Missile[Loop].AnimInc := 0; //init timing counter
Missile[Loop].AnimSpeed := 5; //how fast to do exp anim
Missile[Loop].XPos := (PX - 20) + Random(45); //position the explosion
Missile[Loop].YPos := 140 + Random(45); //position the explosion
ExplodeSnd[Loop].SetCurrentPosition(0); //reset sound fx position
ExplodeSnd[Loop].Play(0,0,0); //play the sound fx
OkToExp := False; //now we have to wait a few!
end;
If not Missile[Loop].Free then // if missile (explosion) is being used
begin
//Draw the current explosion animation frame
DGCScreen1.Back.Draw(Missile[Loop].XPos, Missile[Loop].YPos,
DGCScreen1.Images[Missile[Loop].ExpCell],True);
Inc(Missile[Loop].AnimInc); //increase timing counter
If Missile[Loop].AnimInc > Missile[Loop].AnimSpeed then //time for next frame?
begin
Inc(Missile[Loop].ExpCell); //increase anim cell
Missile[Loop].AnimInc := 0; //reset timing counter
If Missile[Loop].ExpCell > 7 then //end of anim
begin
Missile[Loop].Free := True; //free the array element for use
ExplodeSnd[Loop].Stop; //stop the sound!
end;
end;
end;
end;
end;
procedure TForm1.DisplayMissiles;
begin
//This routine displays the missile graphic at the bottom of the screen along
//with the number of available missiles.
//NOTE: The SCORE, TIME, & MISSILE counters are all handled manually -- this
// is a lot faster than trying to convert the numbers to strings, checking
// for certain values, then drawing the appropriate image.
//Draw the missile image
DGCScreen1.Back.Draw(056,224,DGCScreen1.Images[25],True);
//Draw the ":"
DGCScreen1.Back.Draw(070,227,DGCScreen1.Images[20],True);
//Draw the first digit
DGCSCreen1.Back.Draw(077,227,DGCScreen1.Images[MissCnt[0]+10],True);
//Draw the second digit
DGCScreen1.Back.Draw(084,227,DGCScreen1.Images[MissCnt[1]+10],True);
end;
procedure TForm1.UpdateMissiles;
var
loop : Integer;
Tile : TDGCMapPos;
begin
If Fired < 0 then exit; //If no missiles fired, might as well exit this routine!
//This little block handles some timing for firing missiles. If we didn't time how
//often the missiles could be fired then they would all fire pretty much at the
//same time -- which may be good in another game, but not so in this demo.
If not OkToFire then //If it's not time to fire another missile
begin
Inc(FirePause); //increase the firing pause counter
If FirePause >= FIRE_RATE then //if it passes the firing rate constant
begin //reset the flag so that another missile can
FirePause := 0; //be fired
OkToFire := True;
end;
end;
//process all missiles!
for loop := 0 to MISSILES do
begin
If not Missile[loop].Free then // if missile is in use
begin
//get the current tile under the tip of the missile
Tile := DGCScreen1.GetTileDrawn(Missile[Loop].XPos,Missile[Loop].YPos);
If Tile.Tile = 4 then // Is the tile a 'ROCK' ?
begin
Missile[Loop].Explode := True; // set up the rock explosion stuff
Missile[Loop].ExpCell := 3;
Missile[Loop].AnimInc := 0;
Missile[Loop].AnimSpeed := 5;
end;
//If the current missile is not in the explode state, move it up the screen
If not Missile[Loop].Explode then
begin
Inc(Missile[Loop].YPos,Missile[Loop].YSpeed); // move the missile
//If the car is moving backwards, you need to accomidate the missile's location
If Speed < 0 then Inc(Missile[Loop].YPos, Speed);
//If missile is off the top of the screen then free the missile array element for use
If Missile[loop].YPos < 0 then Missile[Loop].Free := True;
//increase the animation timing counter
Inc(Missile[Loop].AnimInc);
If Missile[Loop].AnimInc > Missile[Loop].AnimSpeed then //can update anim?
begin
//The missile only has 2 frame, image 8 & 9 -- toggle between the two
If Missile[Loop].Cell = 8 Then Missile[Loop].Cell := 9 else
Missile[Loop].Cell := 8;
Missile[Loop].AnimInc := 0; //reset the timing counter
end;
//Draw the current animation cell to the screen
DGCScreen1.Back.Draw(Missile[Loop].XPos, Missile[Loop].YPos,
DGCScreen1.Images[Missile[Loop].Cell],True);
end
else // do the rock explosion!
begin
//Is this the first cell of the animation explostion sequence?
If (Missile[Loop].ExpCell = 3) and (Missile[Loop].AnimInc = 0) then
begin
DGCScreen1.SetMapTile(0,Tile.MapX,Tile.MapY,5); //remove the rock
AddToScore(0,0,0,0,0,1,0); //add points
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -