DarkBASIC Development Materials (Assembled from DarkBASIC FPS Tutorial)

 

Source Code in (if you installed it the way that I did, otherwise find \Dark Basic Professional\ and drill down from there.)

 

C:\Program Files\The Game Creators\Dark Basic Professional\Help\Tutorials

 

                                 TUTORIAL ONE

                              Designing Your Game

 

The first step in creating a large program is understanding exactly what you need to do. There are different ways to know what this is, and one of the traditional methods is a pen and a piece of paper. You already have an idea what you want to create, and its amazing how many details surface when you apply those thoughts to paper.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


The above illustration demonstrates the kind of things that surfaced for the programmer who wrote these tutorials when faced with the

challenge of writing an FPS Game.

 

TUTORIAL TWO
Collecting Your Media


The next step is locating and preparing all your media. Media can be any file that adds into your main program, usually in the form of 3D worlds, 3D models, sounds, music, images and pictures. We will require most of these types of media to create our FPS Game. Though you would resource these media files yourself from Model, Audio and Resource collections, we have prepared the media you will require for this tutorial.


The media includes a BSP World file for the game level. A sky sphere model for the sky backdrop. A gun model for the player view. An enemy model for the foe we must vanquish. A selection of sounds to bring the game to life including a large looping wave file for our in-game music. A fire image for our particle bullet and a few miscellaneous items to make the game look nicer.

All these media files were sourced from the Dark Basic Professional Resources collection and are all royalty free. It is important that all the media you use is likewise copyrighted to owners who have given express permission before you use their work. The media files once collected are arranged in suitable folders and renamed for easy reference when we focus on our program and away from the names of all our media files.

TUTORIAL THREE
The Program Skeleton


The skeleton of the program is the first thing you will be thinking about after the design stage. Most game skeletons are very similar and include the following. A setup stage to load and prepare your objects. A main loop to run your game logic and a cleanup stage where all the loaded media is released.

 


In Dark Basic Professional you only have to concern yourself with the first two in most cases. All media loaded is released automatically when you leave a Dark Basic Professional program. The first and second stages are very easy to visualise and can be coded in the following two steps. You must first create your setup stage:

rem TUT3A
rem Initial settings
sync on : sync rate 100
backdrop off : hide mouse
 
rem Load all media for game
gosub _load_game
 
rem Setup all objects for game
gosub _setup_game

 

You then create the main loop and any calls you will make over and over again to control the elements within your game:

rem TUT3B
rem Game loop
do
 
 rem Control game elements
 gosub _control_player
 gosub _control_gunandbullet
 gosub _control_enemies
 
 rem Update screen
 sync
 
rem End loop
loop
 

The last step is to create all the subroutines that the first two stages call. Subroutines are blocks of code you can jump to in order to execute instructions that achieve a subtask. A subtask could be something like loading the player, controlling the player, deleting the player and so on. It is easier to understand what a subroutine does when given a useful name:

rem TUT3C
_control_player:
return
 
_control_gunandbullet:
return
 
_control_enemies:
return
 
_control_stats:
return
 
_setup_game:
return
 
_load_game:
return

 

As you can see, each subroutine has a name that describes what it does. At the moment they are all empty and contain no code. Later on in the tutorial we shall fill these subroutines with commands.

 

TUTORIAL FOUR
Loading The World


The first order of business is to create a world in which our game will be set. Once upon a time this was a very difficult proposal and would often become the first major stumbling block for many would be game writers. With the built in BSP commands however, such a task is mind bogglingly simple.


With a single command we can load an entire BSP world. The BSP file contains information about polygons, collision, textures and even special effects also known as shaders. With a second command we can create a skysphere to provide an enclosed environment for our world:

rem TUT4A
rem Load BSP world and sky model
load bsp "world\ikzdm1.pk3","ikzdm1.bsp"
SkyObj=1 : load object "models\sky\am.x",SkyObj

 

When the BSP world and sky are loaded, we must setup the camera and ensure the autocam does not start moving around without our direct control. We must also scale down the size of the sky to bring it closer so we bring it within the maximum distance of our 3D universe and switch off sky culling so polygons that would normally show from the outside of the sky sphere will now show on the inside as well:

rem TUT4B
rem Setup camera
set camera range 1,10000
autocam off
 
rem Setup sky model
set object SkyObj,1,0,0,0,0,0,0
scale object SkyObj,20,20,20

 

In order that the user stay informed, we will provide a text prompt when the BSP is loading. Sometimes BSP worlds can be very large and can take minutes to fully load, so an onscreen prompt is very important. In order to make our game as presentable as possible, we will choose a nice large font:

rem TUT4C
rem Select font
set text font "arial" : set text size 16
set text to bold : set text transparent
 
rem Loading prompt
sync : center text screen width()/2,screen height()/2,"LOADING" : sync

 

The reason for the pre and post additions of the SYNC command is that we have initialised our program with the SYNC ON command. This tells the computer that we would like to update the screen ourselves; manually. In order to see the loading prompt, a sync must be made after we have drawn the text.

 

TUTORIAL FIVE
Adding A Player


After the world has been loaded, we must create a player for our game. As a first person shooter, the player is essentially the camera. You move the camera and are treated to a view of the world in the first person.


So that we can re-use our player initialization code, we will place the code somewhere we can trigger it by the simple switching of a variable value. So that the player is initialized the first time we run the game, we will set the trigger variable first:

rem TUT5A
rem Trigger player initialization
restart=1

 

We then provide the code that will setup the player when this variable is set to one. By using BSP collision on the camera, we completely automate the handling of collision within the 3D world. This not only provides sliding collision on any surface, but does so at great speed:

rem TUT5B
rem In case of restart
if restart=1
   restart=0
   set bsp collision off 1
   rotate camera 0,0,0
   position camera 2,2,2
   set bsp camera collision 1,0,0.75,0
endif

 

After the player has been setup, we can start to control it. We will first need to provide control of the players rotation, movement and gravity:

rem TUT5C
rem Control player direction
rotate camera camera angle x(0)+(mousemovey()/2.0),camera angle y(0)+(mousemovex()/2.0),0
 
rem Control player movement
cx#=camera angle x(0) : cy#=camera angle y(0)
if upkey()=1 then xrotate camera 0,0 : move camera 0,0.2 : xrotate camera 0,cx#
if downkey()=1 then xrotate camera 0,0 : move camera 0,-0.2 : xrotate camera 0,cx#
if leftkey()=1 then yrotate camera 0,cy#-90 : move camera 0.2 : yrotate camera 0,cy#
if rightkey()=1 then yrotate camera 0,cy#+90 : move camera 0.2 : yrotate camera 0,cy#
if wrapvalue(camera angle x(0))>40 and wrapvalue(camera angle x(0))<180 then xrotate camera 0,40
if wrapvalue(camera angle x(0))>180 and wrapvalue(camera angle x(0))<280 then xrotate camera 0,280
 
rem Apply simple gravity to player
position camera camera position x(),camera position y()-0.1,camera position z()

 

In addition, we must ensure the player is always at the center of the universe. To that end, when ever the player moves we must adjust the position of the sky and the 3D listener which represents the player in our game:

rem TUT5D
rem Player is always focal point of sky
position object SkyObj,camera position x(),camera position y(),camera position z()
 
rem Position listener at player for 3D sound
position listener camera position x(),camera position y(),camera position z()
rotate listener camera angle x(),camera angle y(),camera angle z()
 

TUTORIAL SIX
Adding A Gun


It would be a very short shooter game if our player did not have a gun, and bullets to fire. In traditional first person style, the gun protrudes from the base of the screen and the bullets fire into a crosshair fixed to the center of the screen.


The first step is to load all the models, sounds and images we will need to create the visual effect. We must load a gun model, some appropriate sounds, some sniper music and a crosshair for the screen:

rem TUT6A
rem Load model for gun
GunObj=2 : load object "models\gun\gun.x",GunObj
 
rem Load all sounds
GunSnd=1 : load sound "sounds\gun.wav",GunSnd
ImpactSnd=2 : load 3dsound "sounds\impact.wav",ImpactSnd
DieSnd=3 : load sound "sounds\die.wav",DieSnd
 
rem Load music (WAV best for looping)
MusicSnd=101 : load sound "sounds\ingame.wav",MusicSnd
loop sound MusicSnd : set sound volume MusicSnd,80
 
rem Load images
FireImg=1 : load image "images\fire.bmp",FireImg
CrossHairImg=2 : load image "images\crosshair.bmp",CrossHairImg

 

Once the media has been loaded, we can setup the objects before the game begins. The gun needs to be locked to the screen as it is part of the player camera now. We also need to create a bullet object and the crosshair for the screen:

rem TUT6B
rem Setup gun for player
lock object on GunObj
scale object GunObj,2,2,4
rotate object GunObj,270,0,0
position object GunObj,0.5,-1,2
disable object zdepth GunObj
 
rem Create object for bullet
BulletObj=3 : make object cube BulletObj,0.1
 
rem Create simple sprite based crosshair
sprite 1,320-16,240-16,CrossHairImg
set sprite 1,0,1

 

When the user presses the mouse button, the gun must be fired. In firing the gun we must first make sure the gun has cooled down enough to allow another shot. This is a storyline to cover for a technical limitation as our tutorial game allows only one bullet to be fired at any one time. When the gun is fired, a sound is played and the bullet is created at the players position and rotated to face the target:

rem TUT6C
rem Control gun firing
if mouseclick()=1 and bullet=-50
 bullet=100
 play sound GunSnd
 position object BulletObj,camera position x(0),camera position y(0),camera position z(0)
 rotate object BulletObj,camera angle x(0),camera angle y(0),0
 set bsp object collision 2,BulletObj,0.1,1
 move object BulletObj,0.2
endif

 

During the life of the bullet, we must move it forward and ensure it is destroyed when it hits a wall. We use the built in BSP collision setup in the previous code segment to make this task simple:

rem TUT6D
rem Control life of bullet
if bullet>0
 
 rem If bullet collides with BSP world
 if bsp collision hit(2)=1 or bulletimpact=1
  rem End bullet on wall
  position sound ImpactSnd,object position x(BulletObj), object     position y(BulletObj), object position z(BulletObj)
  play sound ImpactSnd
  bulletimpact=0
  bullet=0
 else
  rem Move bullet
  dec bullet
  move object BulletObj,0.5
 endif
 
 rem Bullet dies
 if bullet=0
  set bsp collision off 2
 endif
 
else
 rem Gun recharge phase
 if bullet>-50 then dec bullet
endif
 
 

TUTORIAL SEVEN
Adding Enemies


All shooters need something to shoot at. In this case we opt for those poor aliens again. They may have come to visit us with a message of peace, but our player is interested only in target practice.


The first step is to load the models for our enemy objects:

rem TUT7A
rem Load models for enemies
EneObj=11
for ene=EneObj to EneObj+4
 load object "models\enemy\H-Alien Psionic-Idle.x",ene
 append object "models\enemy\H-Alien Psionic-Die.x", ene, total object frames(ene)+1
 position object ene,2,2,4
 loop object ene,0,25
next ene

 

Not forgetting the sounds we will need to give our enemies a realistic presence within the game. With additional use of 3D sounds and a scary alien breathing sound we can create the creepy effect of 'what's just around the corner' for our game:

rem TUT7B
EnemySnd=11 : load 3dsound "sounds\enemy.wav",EnemySnd
EnemygunSnd=12 : load 3dsound "sounds\enemygun.wav",EnemygunSnd
EnemydieSnd=13 : load 3dsound "sounds\enemydie.wav",EnemydieSnd

 

Now we have loaded our enemy media, we must control them within our game. We must control each enemy, ensuring we can move, rotate, handle gravity and play 3D sound for them:

rem TUT7C
rem Variable for finding closest enemy
cdist#=9999.99
 
rem Handle enemies within world
for ene=EneObj to EneObj+4
 
 rem If enemy alive
 if object visible(ene)=1
 
 rem Kill this enemy
 killenemy=0
 
 rem Move enemy on a slow curve for appearance of intelligence
 if object angle z(ene)=0
   yrotate object ene,wrapvalue(object angle y(ene)+2)
 endif
 if object angle z(ene)=1
  yrotate object ene,wrapvalue(object angle y(ene)-2)
 endif
 if object angle z(ene)=2
  move object ene,0.05
 else
  move object ene,0.02
 endif
 
 rem Switch direction of curve based on a random value
 if rnd(200)=1 then zrotate object ene,rnd(1)
 
 rem Handle gravity for enemy
 position object ene,object position x(ene),object position y(ene)-0.01,object position z(ene)
 
 rem Work out angle and distance between enemy and player
 dx#=camera position x(0)-object position x(ene)
 dy#=camera position y(0)-object position y(ene)
 dz#=camera position z(0)-object position z(ene)
 dist#=abs(sqrt(abs(dx#*dx#)+abs(dy#*dy#)+abs(dz#*dz#)))
 viewa#=wrapvalue(atanfull(dx#,dz#))
 obja#=wrapvalue(object angle y(ene))
 if viewa#>180 then viewa#=viewa#-360
 if obja#>180 then obja#=obja#-360
 
 rem Closest enemy emits the enemy sound
 if dist#26+25 and object visible(ene)=1
  killenemy=1
 endif
 
 if killenemy=1
  set bsp collision off 3+(ene-EneObj)
  hide object ene : dec aliensleft
  killenemy=0
 endif
 
 rem If enemy alive ENDIF
 endif
 
next ene

 

To make sure that our aliens appear creepy, we will constantly play their presence sound effect, and scale the 3D sound listener so you only hear them when they are very close to the player:

rem TUT7D
rem Start the enemy presence sound
loop sound EnemySnd
scale listener 0.1
 

TUTORIAL EIGHT
Adding Logic


Logic is the cornerstone of every game, no matter what kind of game it is. Logic is responsible for everything that happens in the game. In this sense, logic is the code to control when you kill an alien, when the alien kills you, when you win the game and when you lose the game. As this tutorial does not attempt to write a complete game, we will add the logic to restart when the player dies and the logic to move the aliens around the world and allow the player to shoot them.


We can give our aliens the appearance of intelligence by letting them move towards the player when they are looking in the right direction, and the player is close enough:

rem TUT8A
rem If enemy 'facing player' and 'on similar height' and 'close', zoom in
if abs(viewa#-obja#)<10.0 and abs(dy#)<5.0 and dist#<30.0
 if object angle z(ene)<>2 then play sound EnemygunSnd
 rotate object ene,0,viewa#,2
 set object speed ene,2
else
 set object speed ene,1
endif

 

When the player gets too close to the enemy, the player must die. As we wish the game to continue, we shall reset the players position and in order to avoid the enemy constantly attacking the player, we remove the alien from the game:

rem TUT8B
rem If enemy gets too close to player, player dies
if dist#<2.0
 play sound DieSnd
 for x=0 to 100
  point camera object position x(ene), object position y(ene)+(x/20.0), object position z(ene)
  sync
 next x
 restart=1
 killenemy=1
endif

 

When the bullet gets too close to an enemy, both the enemy and the bullet die. The bullet will of course live again when the player fires the gun, but the enemy alas will not be so fortunate this game:

rem TUT8C
rem If enemy and bullet in same space, enemy dies
if bullet>0
 if object collision(BulletObj,ene)>0
  play sound EnemydieSnd
  play object ene,26,26+50
  set object speed ene,1
  bulletimpact=1
 endif
endif

 

Having all the enemies in front of you does not make for a very good game, so adding logic to space them out and give them collision so they don't move through walls provides good enemy logic for the game:

rem TUT8D
rem Place enemies throughout world and set BSP collision for them
aliensleft=0
restore EnemyPosData
for ene=EneObj to EneObj+4
 read ex#,ey#,ez#
 position object ene,ex#,ey#,ez#
 set bsp object collision 3+(ene-EneObj),ene,0.75,0
 yrotate object ene,180 : fix object pivot ene
 inc aliensleft
next ene

 

As the above code uses the RESTORE and READ commands, we must provide some DATA statements containing coordinates to place the enemies throughout the world. These coordinates were calculated by walking around with the player and writing down the coordinate of the player:

rem TUT8E
rem Enemy position data within level
EnemyPosData:
data -9.27,9.98,-2.78
data -16.54,-0.22,19.18
data 2.0,9.0,25.0
data -2.0,-9.0,25.0
data 2,4.0,10.0
 

TUTORIAL NINE
Adding Shine


Shine is the fine layer of detail that turns a good game into a great game. Shine is why you like the latest model of car, stereo and washing machine. It’s the six coats of polish you pay for through the nose.


We could add shine forever. For this tutorial we shall add just two coats of shine. Some statistical shine and particle bullet shine. For our stats, we shall inform the user how many aliens remain in the level, when the level is complete and how far away the player is to the nearest alien. The function below does all this:

rem TUT9A
rem Distance to next alien
if aliensleft>0 then text 20,screen height()-40,"DISTANCE READING:"+str$(abs(cdist#*100))
 
rem Aliens Left Stat
s$=str$(aliensleft)+" ALIENS LEFT"
if aliensleft=0 then s$="LEVEL COMPLETE!" : stop sound EnemySnd
text 640-20-text width(s$),screen height()-40,s$

 

To add our particle bullet shine, we simply have to create and control a particles object and use the position of the bullet object as our guide. There are three areas of code we must modify. The creation, control and destruction of the bullet.

Bullet creation:

 
rem TUT9B
if particles exist(1)=1 then delete particles 1
make particles 1, FireImg, 50, 0.5
set particle emissions 1,10
set particle speed 1,0.01

 

Bullet control:

 
rem TUT9C
rem Update particle using bullet object position
set particle emissions 1,1+(bullet/10)
rotate particles 1,90-object angle x(BulletObj),object angle y(BulletObj)+180,0
position particles 1,object position x(BulletObj),object position y(BulletObj),object position z(BulletObj)

 

 

 

Bullet destruction:

 
rem TUT9D
set particle emissions 1,0