The entire code (Unit1.pas) is small, so I will post it now
(assuming it is okay with posting 500 lines code in the list)
{Code}
//--------------------------------------------------
// 3D-MAZE
// A 3D Puzzle Game By Erfan Arabfakhri [ Delphi 6 ]
//--------------------------------------------------
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, OpenGL,
ExtCtrls, StdCtrls, MPlayer, glaux, Grids;
type
TMainForm = class(TForm)
GameMatrix: TStringGrid;
MediaPlayer1: TMediaPlayer;
XYLCD: TEdit;
AboutBox: TMemo;
FinishMsgBox: TMemo;
MusicTimer: TTimer;
LoopTimer: TTimer;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormResize(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure MusicTimerTimer(Sender: TObject);
procedure LoopTimerTimer(Sender: TObject);
private
{ Private declarations }
rc:HGLRC; // Rendering Context
dc:HDC; // Device Context
procedure DrawGameMatrix;
procedure Idle(Sender: TObject; var Done: Boolean);
public
IsFinished:boolean;
Keys: array [0..255] of boolean;
cnt:integer;
MyPos:string[1];
MusicID:Integer;
{ Public declarations }
end;
type MyMap = record
Items:string[1];
end;
var
MainForm: TMainForm;
texture: array [0..5] of GLuint; // Array for storing textures *
X, Z : glFloat;
HeadMovement, HeadMovAngle : glFloat;
Heading : glFloat;
Speed : Double;
ID:integer;
implementation
{$R *.DFM}
// loading external functions from opengl dll *
procedure glGenTextures(n: GLsizei; var textures: GLuint); stdcall; external opengl32;
procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external opengl32;
// procedure to determine camera position in game array *
procedure CheckMyPosition();
var
Xpos,Zpos:Double;
XCell,YCell:Integer;
i,j:integer;
lngh:Double;
begin
Xpos:=Int(X);
Zpos:=Int(Z);
lngh:=-6; // block length *
// a loop to read all blocks using block length *
for j:=0 to 19 do
for i:=0 to 19 do
begin
if (Xpos >= -3 + (lngh*i)) and (Xpos <= 3 + (lngh*i)) and
(Zpos >= 6 - (lngh*j)) and (Zpos <= 12 - (lngh*j)) then
begin
XCell:=i+1;
YCell:=j+1;
end
end;
try
// reading value of currect block *
MainForm.MyPos:=MainForm.GameMatrix.Cells[XCell-1,YCell-1];
// determine if we reached the last block ( goal ) *
if (XCell-1 = 19) and (YCell-1= 19) then
begin
MainForm.IsFinished:=True;
with MainForm do
begin
try
MediaPlayer1.Stop;
LoopTimer.Enabled:=False;
AboutBox.Visible:=False;
XYLCD.Visible:=False;
FinishMsgBox.Visible:=True;
//Finish.Play;
except
end;
end;
end;
// display the current position in a editline *
MainForm.XYLCD.Text:= ' X [ ' + IntToStr(XCell) + ' ] | Y [ ' + IntToStr(YCell) + ' ]';
except
end;
end;
// procedute to load all textures *
procedure LoadGLTextures;
var
texture1: PTAUX_RGBImageRec;
texture2: PTAUX_RGBImageRec;
texture3: PTAUX_RGBImageRec;
texture4: PTAUX_RGBImageRec;
texture5: PTAUX_RGBImageRec;
begin
// check if textures are exist ? *
if (FileExists('img01.bmp') = True) and
(FileExists('img02.bmp') = True) and
(FileExists('img03.bmp') = True) and
(FileExists('img04.bmp') = True) and
(FileExists('img05.bmp') = True) then
begin
// reading the textures *
texture1 := auxDIBImageLoadA('img01.bmp');
if (not Assigned(texture1)) then Halt(1);
texture2 := auxDIBImageLoadA('img02.bmp');
if (not Assigned(texture2)) then Halt(1);
texture3 := auxDIBImageLoadA('img03.bmp');
if (not Assigned(texture2)) then Halt(1);
texture4 := auxDIBImageLoadA('img04.bmp');
if (not Assigned(texture2)) then Halt(1);
texture5 := auxDIBImageLoadA('img05.bmp');
if (not Assigned(texture2)) then Halt(1);
// storing textures in variables *
glGenTextures(1, texture[1]);
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, texture1^.sizeX, texture1^.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture1^.data);
glGenTextures(1, texture[2]);
glBindTexture(GL_TEXTURE_2D, texture[1]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, texture2^.sizeX, texture2^.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture2^.data);
glGenTextures(1, texture[3]);
glBindTexture(GL_TEXTURE_2D, texture[2]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, texture3^.sizeX, texture3^.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture3^.data);
glGenTextures(1, texture[4]);
glBindTexture(GL_TEXTURE_2D, texture[3]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, texture4^.sizeX, texture4^.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture4^.data);
glGenTextures(1, texture[5]);
glBindTexture(GL_TEXTURE_2D, texture[4]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, texture5^.sizeX, texture5^.sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, texture5^.data);
end;
end;
// procedure to draw game matrix ( 20x20 ) in 3D format *
procedure TMainForm.DrawGameMatrix();
var
i,j:integer;
begin
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
// using selected texture *
glBindTexture(GL_TEXTURE_2D, texture[ID]);
glLoadIdentity(); // Reset The View
glTranslatef(0,0,-1.1); // depth *
glRotate(Heading, 0, 1, 0); // look angle *
glTranslatef(X, -0.5 + HeadMovement, Z); // moving camera *
// loop for drawing 3D scene based on value of each block *
for i:=1 to 20 do
begin
for j:=1 to 20 do
begin
glTranslatef(0.0,0.0,-6.0);// moving the center of scale *
// drawing path using squares *
if (GameMatrix.Cells[i-1,j-1]='X') or (GameMatrix.Cells[i-1,j-1]='S') or (GameMatrix.Cells[i-1,j-1]='E') then
begin
glBegin(GL_QUADS);
// floor squares *
glTexCoord2f(0.0,0.0); glVertex3f(-3.0,-1.0,-6.0);
glTexCoord2f(1.0,0.0); glVertex3f( 3.0,-1.0,-6.0);
glTexCoord2f(1.0,1.0); glVertex3f( 3.0,-1.0, 0.0);
glTexCoord2f(0.0,1.0); glVertex3f(-3.0,-1.0, 0.0);
// ceiling squares *
glTexCoord2f(0.0,0.0); glVertex3f(-3.0, 4.0,-6.0);
glTexCoord2f(1.0,0.0); glVertex3f( 3.0, 4.0,-6.0);
glTexCoord2f(1.0,1.0); glVertex3f( 3.0, 4.0, 0.0);
glTexCoord2f(0.0,1.0); glVertex3f(-3.0, 4.0, 0.0);
glEnd;
end;
// drawing ceilings ( hole in floor )
if GameMatrix.Cells[i-1,j-1]='C' then
begin
glBegin(GL_QUADS);
// ceiling squares *
glTexCoord2f(0.0,0.0); glVertex3f(-3.0, 4.0,-6.0);
glTexCoord2f(1.0,0.0); glVertex3f( 3.0, 4.0,-6.0);
glTexCoord2f(1.0,1.0); glVertex3f( 3.0, 4.0, 0.0);
glTexCoord2f(0.0,1.0); glVertex3f(-3.0, 4.0, 0.0);
glEnd();
end;
// drawing floors ( hole in ceilings )
if GameMatrix.Cells[i-1,j-1]='F' then
begin
glBegin(GL_QUADS);
// floor squares *
glTexCoord2f(0.0,0.0); glVertex3f(-3.0,-1.0,-6.0);
glTexCoord2f(1.0,0.0); glVertex3f( 3.0,-1.0,-6.0);
glTexCoord2f(1.0,1.0); glVertex3f( 3.0,-1.0, 0.0);
glTexCoord2f(0.0,1.0); glVertex3f(-3.0,-1.0, 0.0);
glEnd();
end;
// drawing walls using 4 vertical squares *
if GameMatrix.Cells[i-1,j-1]='W' then
begin
glBegin(GL_QUADS);
// drawing squares *
glTexCoord2f(1.0,0.0); glVertex3f( 3.0,-1.0,-6.0);
glTexCoord2f(1.0,1.0); glVertex3f( 3.0, 4.0,-6.0);
glTexCoord2f(0.0,1.0); glVertex3f( 3.0, 4.0, 0.0);
glTexCoord2f(0.0,0.0); glVertex3f( 3.0,-1.0, 0.0);
// drawing squares *
glTexCoord2f(1.0,0.0); glVertex3f(-3.0,-1.0,-6.0);
glTexCoord2f(1.0,1.0); glVertex3f(-3.0, 4.0,-6.0);
glTexCoord2f(0.0,1.0); glVertex3f(-3.0, 4.0, 0.0);
glTexCoord2f(0.0,0.0); glVertex3f(-3.0,-1.0, 0.0);
// drawing squares *
glTexCoord2f(1.0,0.0); glVertex3f(-3.0,-1.0, 0.0);
glTexCoord2f(1.0,1.0); glVertex3f(-3.0, 4.0, 0.0);
glTexCoord2f(0.0,1.0); glVertex3f( 3.0, 4.0, 0.0);
glTexCoord2f(0.0,0.0); glVertex3f( 3.0,-1.0, 0.0);
// drawing squares *
glTexCoord2f(1.0,0.0); glVertex3f(-3.0,-1.0,-6.0);
glTexCoord2f(1.0,1.0); glVertex3f(-3.0, 4.0,-6.0);
glTexCoord2f(0.0,1.0); glVertex3f( 3.0, 4.0,-6.0);
glTexCoord2f(0.0,0.0); glVertex3f( 3.0,-1.0,-6.0);
glEnd;
end;
end;
glTranslatef(6.0,0.0,6.0*20); // moving the center of scale *
end;
end;
// procedure to initialize opengl scene *
procedure glInit();
begin
LoadGLTextures(); // loading textures *
glEnable(GL_TEXTURE_2D);// enabling textures *
glClearColor(0.0, 0.0, 0.0, 1.0);// set background color ( black ) *
glShadeModel(GL_SMOOTH); // using smooth coloring method *
glClearDepth(1.0);// depth *
glEnable(GL_DEPTH_TEST);// enabling depth *
glDepthFunc(GL_LESS);// enabling depth *
// building a black colored fog *
glEnable(GL_FOG);
glFogi(GL_FOG_MODE, GL_LINEAR);
glFogf(GL_FOG_START, 0.0); // fog strat *
glFogf(GL_FOG_END, 35.0); // fog end *
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //Realy Nice perspective calculations
end;
// main form oncreate procedure *
procedure TMainForm.FormCreate(Sender: TObject);
var pfd : TPIXELFORMATDESCRIPTOR;
pf : Integer;
DeviceMode : TDevMode;
begin
// changing display resolution *
with DeviceMode do
begin
dmSize:=SizeOf(DeviceMode);
dmBitsPerPel:=32;
dmPelsWidth:=640;
dmPelsHeight:=480;
dmFields:=DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
end;
ChangeDisplaySettings(DeviceMode,CDS_FULLSCREEN);
// positioning main form for fullscreen view *
MainForm.Top:=0;
MainForm.Left:=0;
MainForm.Height:=Screen.Height;
MainForm.Width:=Screen.Width;
IsFinished:=False;
// opengl decelerations *
dc:=GetDC(MainForm.Handle);
ID:=0;
MusicID:=0;
Heading :=45; // look angle *
X :=1.0; // start posotion *
Z :=8; // start depth *
Speed:=0.25;
// PixelFormat
pfd.nSize:=sizeof(pfd);
pfd.nVersion:=1;
pfd.dwFlags:=PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL or PFD_DOUBLEBUFFER or 0;
pfd.iPixelType:=PFD_TYPE_RGBA; // PFD_TYPE_RGBA or PFD_TYPEINDEX
pfd.cColorBits:=32;
pf :=ChoosePixelFormat(dc, @pfd); // Returns format that most closely matches above pixel format
SetPixelFormat(dc, pf, @pfd);
rc :=wglCreateContext(dc); // Rendering Context = window-glCreateContext
wglMakeCurrent(dc,rc); // Make the DC (MainForm) the rendering Context
// Initialist GL environment variables
glInit;
MainForm.Resize;
// when the app has spare time, render the GL scene
Application.OnIdle := Idle;
end;
procedure TMainForm.FormDestroy(Sender: TObject);
begin
wglMakeCurrent(0,0);
wglDeleteContext(rc);
end;
// procedure for controlling the game using application idle time *
procedure TMainForm.Idle(Sender: TObject; var Done: Boolean);
begin
Done:=False;
// if game is not finished ( not in goal position ( 20x20 ) )
if IsFinished = False then
begin
// move forward *
if (Keys[Ord(VK_UP)] = True) then
begin
// moving in x direction *
X := X - sin(Heading*pi/180)* Speed;
// moving in z direction *
Z := Z + cos(Heading*pi/180)* Speed;
HeadMovAngle :=HeadMovAngle + 5;
// simulating walking sensation using a sinus wave *
HeadMovement :=0.25*sin(HeadMovAngle*pi/90);
if IsFinished = False then CheckMyPosition;
// simulating clipping by checking current block value *
while (MyPos = 'W') or (MyPos = '') do
begin
X := X + sin(Heading*pi/180)* Speed*1;
Z := Z - cos(Heading*pi/180)* Speed*1;
HeadMovAngle :=HeadMovAngle - 5;
HeadMovement :=0.25*sin(HeadMovAngle*pi/90);
if IsFinished =False then CheckMyPosition;
end;
end;
// move backward *
if (Keys[Ord(VK_DOWN)] = True) then
begin
X := X + sin(Heading*pi/180)* Speed;
Z := Z - cos(Heading*pi/180)* Speed;
HeadMovAngle :=HeadMovAngle - 5;
HeadMovement :=0.25*sin(HeadMovAngle*pi/90);
if IsFinished =False then CheckMyPosition;
while (MyPos = 'W') or (MyPos = '') do
begin
X := X - sin(Heading*pi/180)* Speed*1;
Z := Z + cos(Heading*pi/180)* Speed*1;
HeadMovAngle :=HeadMovAngle + 5;
HeadMovement :=0.25*sin(HeadMovAngle*pi/90);
if IsFinished =False then CheckMyPosition;
end;
end;
// look angle control *
if Keys[Ord(VK_LEFT)] = True then Heading:=Heading - Speed*8;
if Keys[Ord(VK_RIGHT)] = True then Heading:=Heading + Speed*8;
end;
// if step on a hole you will fall *
if (MyPos='C') then
begin
HeadMovement:=HeadMovement+0.1;
// sound of falling *
end;
DrawGameMatrix(); // drawing thew scene *
SwapBuffers(DC); // swap opengl buffers *
end;
// keyboard monitoring procedure;
procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
DeviceMode : TDevMode;
begin
// terminate application when escape is hit *
if Ord(Key) = VK_ESCAPE then
begin
with DeviceMode do
begin
dmSize:=SizeOf(DeviceMode);
dmBitsPerPel:=32;
dmPelsWidth:=800;
dmPelsHeight:=600;
dmFields:=DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
end;
ChangeDisplaySettings(DeviceMode,CDS_FULLSCREEN);
LoopTimer.Enabled:=False;
MediaPlayer1.Close;
Application.Terminate;
end;
if IsFinished = False then
begin
// show current position on game screen *
if Ord(Key) = VK_F1 then
begin
if XYLCD.Visible = False then XYLCD.Visible:=True else XYLCD.Visible:=False;
end;
// show about window on game screen *
if Ord(Key) = VK_SPACE then
begin
if AboutBox.Visible = False then AboutBox.Visible:=True else AboutBox.Visible:=False;
end;
// change texture on F2 press *
if Ord(Key) = VK_F2 then
begin
ID:=ID+1;
if ID = 6 then ID:=0;
end;
// change music on F3 press *
if Ord(Key) = VK_F3 then
begin
MusicID:=MusicID+1;
if MusicID = 10 then MusicID:=0;
LoopTimer.Enabled:=False;
MediaPlayer1.Stop;
MusicTimer.Enabled:=True;
end;
// restart level on space hit *
end else
begin
if Ord(Key) = VK_SPACE then
begin
FinishMsgBox.Visible:=False;
MusicTimer.Enabled:=True;
IsFinished:=False;
Heading :=45;
X :=1.0;
Z :=8;
end;
end;
Keys[Key] := True;
end;
procedure TMainForm.FormResize(Sender: TObject);
begin
glViewport(0, 0, MainForm.Width, MainForm.Height); // Set the viewport for the OpenGL window
glMatrixMode(GL_PROJECTION); // Change Matrix Mode to Projection
glLoadIdentity(); // Reset View
if MainForm.Height= 0 then MainForm.Height:=1; // prevent divistion by zero
gluPerspective(45.0, MainForm.Width/MainForm.Height, 1.0, 500.0); // Do the perspective calculations. Last value = max clipping depth
glMatrixMode(GL_MODELVIEW); // Return to the modelview matrix
end;
// main form onshow procedure *
procedure TMainForm.FormShow(Sender: TObject);
var
fp:file of MyMap;
temp:MyMap;
i,j:integer;
begin
MainForm.Cursor:=crNone;
AboutBox.Cursor:=crNone;
FinishMsgBox.Cursor:=crNone;
// reading the .mp file and store it in game matrix *
if FileExists(ParamStr(1)) = True then
begin
AssignFile(fp,ParamStr(1));
Reset(fp);
for i:=0 to 19 do
for j:=0 to 19 do
begin
Read(fp,temp);
GameMatrix.Cells[i,j]:=temp.Items;
end;
CloseFile(fp);
end;
if FileExists(ParamStr(1)) = False then Application.Terminate;
// MusicTimer.Enabled:=True;
end;
// onkeyup monitoring *
procedure TMainForm.FormKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
Keys[Key] := False;
end;
procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
var
DeviceMode : TDevMode;
begin
with DeviceMode do
begin
dmSize:=SizeOf(DeviceMode);
dmBitsPerPel:=32;
dmPelsWidth:=800;
dmPelsHeight:=600;
dmFields:=DM_BITSPERPEL or DM_PELSWIDTH or DM_PELSHEIGHT;
end;
ChangeDisplaySettings(DeviceMode,CDS_FULLSCREEN);
end;
end;
end.