A new space invaders tutorial, in 3D.
Fully 3D
Collision detection and reaction in multiple situations
*
Player movement Limitation
*
Alien bomb hit detection
*
Player bullet hit detection
*
Alien movement limitation
*
Boundary detection (including shelters)
Lose game triggers
Movement limitation
Constructive Solid Geometry (CSG)
Media-less graphics
Demonstrates the following common DarkBASIC Pro elements.
*
Functions
*
Subroutines
*
Data Types
*
Arrays
*
Variable types
*
If Statements
*
Mathematics
*
Select and Case statements
*
CSG Commands
*
Object Commands
*
3D Space setup
*
Lights
*
Camera
*
Action * kidding *
*
Collision commands
*
Text commands
*
System commands
Game Source Code (mostly uncommented as I explain it all in the video)
Main.dba
Gosub DataSetup
Gosub EnvironmentSetup
`Settings
plyr.speed=4
bullet.speed = 4
plyr.lives = 3
enemy(0, 0).speed = 1
Display_Boundary = false
Diag = false
`Create Player Object
plyr.id = NextObjID()
MakePlayerObj()
// `Create Enemys
tid = NextObjID()
MakeEnemy(tid)
boundary.left = NextObjID()
boundary.right = boundary.left + 1
boundary.top = boundary.left + 2
boundary.bottom = boundary.left + 3
boundary.lose = boundary.left + 4
boundary.middle_shield = boundary.left + 5
boundary.left_shield = boundary.left + 6
boundary.right_shield = boundary.left + 7
MakeBndry(Display_Boundary)
//
// `Assign ID's to other loop object
bullet.id = NextObjID()
enemy(0, 0).bomb = bullet.id + 1
//
Do
`Player area
PlyrCntl()
BulletCntl()
//
// `Aliens
MoveInvaders()
AlienBombs()
//
// `Condisions check
GameCheck()
//
// `Display Information
GameText()
Diagnostics(Diag)
//
Sync
Loop
Environment.dba
DataSetup:
#Constant true 1
#Constant false 0
#Constant _blue 0, 0, 255
#Constant _red 255, 0, 0
#Constant _green 0, 255, 0
#Constant cyan 0, 255, 255
#Constant purple 223, 0, 255
#Constant gold 193, 160, 4
#Constant yellow 255, 255, 0
#Constant violet 143, 0, 255
Type Obj
id as byte
speed as float
flag as byte
direction as boolean
bomb a integer
score as word
count as byte
lives as byte
drop as boolean
EndType
Type sys
wres as Word
hres as Word
EndType
Type Collision
lose as integer
top as integer
bottom as integer
left as integer
right as integer
middle_shield as integer
left_shield as integer
right_shield as integer
EndType
GLobal plyr as Obj
Global enemy as Obj
Global bullet as Obj
Global temp as Obj
Global boundary as Collision
Global system as sys
Dim enemy(9, 4) as Obj
system.wres = screen width()
system.hres = screen height()
Return
EnvironmentSetup:
Sync On
Sync Rate 60
Backdrop On
Color BackDrop 0
Hide Mouse
Autocam Off
Randomize Timer()
Position Camera 0, 0, 300
Point Camera 0, 0, 0
Make Light 1
Position Light 1, 0, 0, 300
Return
Functions.dba
Function NextObjID()
id = 0
state = false
Repeat
Inc id
If Object Exist(id) = 0 then state = true
UNTIL state = true
ENDFUNCTION id
Function MakePlayerObj()
Make object Box plyr.id, 25, 15, 10
brush = NextObjID()
Make Object Box brush, 5, 5, 10
Position Object brush, 10, 5, 0
Perform CSG Difference plyr.id, brush
Position Object brush, -10, 5, 0
Perform CSG Difference plyr.id, brush
Position Object brush, 0, 10, 0
Perform CSG Union plyr.id, brush
Delete Object brush
Position Object plyr.id, 0, -150, 0
Color Object plyr.id, rgb(_blue)
ENDFUNCTION
Function MakeEnemy(obj)
For x = 0 to 9
For y = 0 to 4
enemy(x, y).flag = true
enemy(x, y).id = obj
Make Object Box obj, 20, 20, 10
Position Object obj, 135-(x*30), 125-(y*30), 0
Inc obj
Select y
Case 0
Color Object enemy(x, y).id, rgb(cyan)
EndCase
Case 1
Color Object enemy(x, y).id, rgb(violet)
EndCase
Case 2
Color Object enemy(x, y).id, rgb(_green)
EndCase
Case 3
Color Object enemy(x, y).id, rgb(yellow)
EndCase
Case 4
Color Object enemy(x, y).id, rgb(gold)
EndCase
Case Default
UnexpectedError("MakeEnemy()", "y")
EndCase
EndSelect
Next y
Next x
ENDFUNCTION
Function UnexpectedError(FunctionName$, VarName$)
Center Text system.wres/2, system.hres/2, "Error in "+FunctionName$+ " "+VarName$+" is out of expect range."
Sync
Wait Key
End
ENDFUNCTION
Function MakeBndry(setboundary)
// Reference Box
// Make Object Box NextObjID(), 470, 350, 2
Make Object Box boundary.left, 10, 350, 10
Position Object boundary.left, 235, 0, 0
Make OBject Box boundary.right, 10, 350, 10
Position Object boundary.right, -235, 0, 0
Make Object Box boundary.top, 470, 10, 10
Position Object boundary.top, 0, 175, 0
Make Object Box boundary.bottom, 470, 10, 10
Position Object boundary.bottom, 0, -175, 0
Make Object Box boundary.middle_shield, 50, 5, 10
Position Object boundary.middle_shield, 0, -125, 0
Color Object boundary.middle_shield, rgb(purple)
Make Object Box boundary.right_shield, 50, 5, 10
Position Object boundary.right_shield, 150, -125, 0
Color Object boundary.right_shield, rgb(purple)
Make Object Box boundary.left_shield, 50, 5, 10
Position Object boundary.left_shield, -150, -125, 0
Color Object boundary.left_shield, rgb(purple)
Make Object Box boundary.lose, 470, 50, 5
Position Object boundary.lose, 0, -150, 0
color object boundary.lose, rgb(_red)
Displayboundary(setboundary)
ENDFUNCTION
Function Displayboundary(decision)
Select decision
Case true
Show Object boundary.top
Show Object boundary.bottom
Show Object boundary.left
Show Object boundary.right
Show Object boundary.lose
EndCase
Case false
Hide Object boundary.top
Hide Object boundary.bottom
Hide Object boundary.left
Hide Object boundary.right
Hide Object boundary.lose
EndCase
Case Default
UnexpectedError("DisplayBundary()", "decision")
EndCase
EndSelect
ENDFUNCTION
Function PlyrCntl()
If LeftKey() = true then Position Object plyr.id, Object position x(plyr.id)+plyr.speed, Object position y(plyr.id), Object Position z(plyr.id)
If RightKey() = true then Position Object plyr.id, Object position x(plyr.id)-plyr.speed, Object position y(plyr.id), Object Position z(plyr.id)
If SpaceKey() = True then MakeBullet()
If Object collision(plyr.id, boundary.right) = true then Position Object plyr.id, Object Position x(plyr.id)+plyr.speed, object position y(plyr.id), object position z(plyr.id)
If Object collision(plyr.id, boundary.left) = true then Position Object plyr.id, Object Position x(plyr.id)-plyr.speed, object position y(plyr.id), object position z(plyr.id)
ENDFUNCTION
Function MakeBullet()
If Object Exist(bullet.id) = false
Make Object Box bullet.id, 4, 12, 4
Position Object Bullet.id, object position x(plyr.id), object position y(plyr.id)+13, object position z(plyr.id)
Color Object bullet.id, rgb(_red)
ENDIF
EndFunction
Function BulletCntl()
If Object Exist(bullet.id) = true then Position Object bullet.id, object position x(bullet.id), Object position y(bullet.id)+bullet.speed, Object position z(bullet.id)
For x = 0 to 9
For y = 0 to 4
If Object Exist(bullet.id) = true
If enemy(x, y).flag = true
If Object Collision(bullet.id, enemy(x, y).id) = true
enemy(x, y).flag = false
Hide Object enemy(x, y).id
Delete Object bullet.id
Inc plyr.count
Inc plyr.score, 10
EndIf
EndIf
EndIf
Next y
Next x
If Object Exist(bullet.id) = true
If Object Collision(bullet.id, boundary.top) = true or Object Collision(bullet.id, boundary.middle_shield) = true or Object Collision(bullet.id, boundary.left_shield) = true or Object Collision(bullet.id, boundary.right_shield) = true then Delete Object bullet.id
ENDIF
If Object exist(enemy(0, 0).bomb) = true
Position Object enemy(0, 0).bomb, Object Position x(enemy(0, 0).bomb), Object Position y(enemy(0, 0).bomb)-bullet.speed, Object Position z(enemy(0, 0).bomb)
If Object Collision(enemy(0, 0).bomb, boundary.bottom) = true or Object Collision(enemy(0, 0).bomb, boundary.middle_shield) = true or Object Collision(enemy(0, 0).bomb, boundary.left_shield) = true or Object Collision(enemy(0, 0).bomb,right_shield) = true then Delete Object enemy(0, 0).bomb
ENDIF
If Object Exist(enemy(0, 0).bomb) = true
If Object Collision(enemy(0, 0).bomb, plyr.id) = true
Dec plyr.lives
Delete Object enemy(0, 0).bomb
EndIf
ENDIF
ENDFUNCTION
Function MoveInvaders()
For x = 0 to 9
For y = 0 to 4
If enemy(x, y).flag = true
If Object Collision(enemy(x, y).id, boundary.left) = true and enemy(0, 0).direction = 0
enemy(0, 0).direction = 1
enemy(0, 0).drop = true
inc enemy(0, 0).speed, 0.01
EndIf
If Object Collision(enemy(x, y).id, boundary.right) = true and enemy(0,0).direction = 1
enemy(0, 0).direction = 0
enemy(0, 0).drop = true
inc enemy(0, 0).speed, 0.01
EndIf
ENDIF
Next y
NEXT x
Select enemy(0, 0).direction
Case 0
If enemy(0, 0).drop = true
for x = 0 to 9
for y = 0 to 4
position object enemy(x, y).id, Object Position x(enemy(x, y).id), Object Position y(enemy(x, y).id)-5, Object Position z(enemy(x, y).id)
Next y
NEXT x
enemy(0, 0).drop = false
Else
for x = 0 to 9
for y = 0 to 4
position object enemy(x, y).id, Object Position x(enemy(x, y).id)+enemy(0, 0).speed, Object Position y(enemy(x, y).id), Object Position z(enemy(x, y).id)
Next y
NEXT x
ENDIF
EndCase
Case 1
If enemy(0, 0).drop = true
for x = 0 to 9
for y = 0 to 4
position object enemy(x, y).id, Object Position x(enemy(x, y).id), Object Position y(enemy(x, y).id)-5, Object Position z(enemy(x, y).id)
Next y
NEXT x
enemy(0, 0).drop = false
Else
for x = 0 to 9
for y = 0 to 4
position object enemy(x, y).id, Object Position x(enemy(x, y).id)-enemy(0, 0).speed, Object Position y(enemy(x, y).id), Object Position z(enemy(x, y).id)
Next y
NEXT x
ENDIF
EndCase
Case Default
UnexpectedError("MoveInvaders()", "enemy(0,0).direction")
EndCase
EndSelect
for x = 0 to 9
for y = 0 to 4
If Object Collision(enemy(x, y).id, boundary.lose) and enemy(x, y).flag = true
ResetGame("full")
ENDIF
NEXT y
NEXT x
ENDFUNCTION
Function ResetGame(choice$)
Select choice$
Case "full"
Center Text system.wres/2, system.hres/2, "You Lose! Press escape to exit or any key to play again."
Center Text system.wres/2, (system.hres/2)-15, "Final Score: " + Str$(plyr.score)
Sync
Wait Key
For x = 0 to 9
For y = 0 to 4
Position Object enemy(x, y).id, 135-(x*30), 125-(y*30), 0
Show Object enemy(x, y).id
enemy(x, y).flag = 1
plyr.count = 0
plyr.score = 0
plyr.lives = 3
Next y
NEXT x
EndCase
Case "set"
For x = 0 to 9
For y = 0 to 4
Position Object enemy(x, y).id, 135-(x*30), 125-(y*30), 0
Show Object enemy(x, y).id
enemy(x, y).flag = 1
plyr.count = 0
Next y
NEXT x
EndCase
Case Default
UnexpectedError("ResetGame()", "choice$")
EndCase
EndSelect
ENDFUNCTION
Function AlienBombs()
If Object Exist(enemy(0 ,0).bomb) = false
Make Object Sphere enemy(0, 0).bomb, 5
Repeat
x = rnd(9)
y = rnd(4)
UNTIL enemy(x, y).flag = true
Position Object enemy(0, 0).bomb, object position x(enemy(x, y).id), object position y(enemy(x, y).id)-10, object position z(enemy(x, y).id)
Color Object enemy(0, 0).bomb, rgb(cyan)
ENDIF
ENDFUNCTION
Function GameCheck()
If plyr.count = 50 then ResetGame("set")
If plyr.lives = 0 then ResetGame("full")
ENDFUNCTION
Function GameText()
Center Text system.wres/2, 10, "Score: " + Str$(plyr.score)
Center Text system.wres/2, system.hres-20, "Lives: " + Str$(plyr.lives)
ENDFUNCTION
Function Diagnostics(choice)
Select choice
case true
Text 10, 10, "FPS: "+Str$(Screen FPS())
for x = 0 to 9
for y = 0 to 4
Text 10+(x*30), 40+(y*30), str$(enemy(x, y).flag) + " " +str$(enemy(x, y).id)
next y
next x
Text 10, 220, "plyr.id = "+str$(plyr.id)+" bullet.id = " + str$(bullet.id) + " enemy(0, 0).bomb = " + str$(enemy(0, 0).bomb)
Text 10, 240, "enemy(0, 0).speed = " + str$(enemy(0, 0).speed)+" enemy(0, 0). direction" + str$(enemy(0, 0).direction)
EndCase
case default
endcase
EndSelect
ENDFUNCTION
DOWNLOAD PROJECT HERE