8. Projectiles

Whether you want to launch plasma, magic or fruit, a projectile algorithm is what you are looking for. It might sound simple enough but when you start to break it down and think it through it is quite an involved process. Not necessarily complicated, just a lot of little things to keep track of.

  1. Setup collisions between projectiles and the targets
  2. Create a new projectile every time the "fire key" is pressed
  3. Update the location of each new projectile
  4. Check for collisions
  5. Switch to an explosion animation if collided
  6. Remove the object the projectile collided with and the projectile

Phew! This might seem like the toughest of all 12 lessons but most of it involves code you've already used. You don't have to understand it all at once and the important parts you will understand.


If you haven't already downloaded the tutorial files do so now, unzip to your desktop and OPEN up lesson8.bb, functions8.bb, and loadworld8.bb (below) and RUN / LANUCH the lesson8.bb program by pressing the ROCKET ICON

and you should see this...

NEW CODE is BOLD.

Here's the CODE!
lesson8.bb

Include "functions8.bb"

Global escape_key = 1
Global screen_width = 640, screen_height = 480

Graphics3D screen_width, screen_height, 16, 2
SetBuffer BackBuffer()


; Add projectile type
Global coll_player = 1, coll_characters = 2, coll_objects = 3, coll_projectile = 4

; Add projectiles to the collision list
Collisions coll_projectile, coll_characters, 1,1
Collisions coll_characters, coll_player, 1, 2
Collisions coll_player, coll_characters, 1, 2
Collisions coll_player, coll_objects, 1, 2
Collisions coll_characters, coll_characters, 1, 2
Collisions coll_characters, coll_objects, 1, 2

AmbientLight 200, 200, 200

light1 = CreateLight(2)

cam1 = CreateCamera()
CameraViewport cam1, 0, 0, screen_width, screen_height

Include "loadworld8.bb"

LoadMusic()
LoadSFX()

MoveMouse screen_width/2, screen_height/2

While Not KeyHit( escape_key )

   ; Keep any projectiles and explosions updated
    UpdateProjectiles()

    MoveCharacters( player )
    If run_vol# > 0 Then AnimSprite( player, anim_tex, 200, 4 )
    object_key_control( player )
    ChannelVolume runchannel, run_vol#
    PositionEntity sky, EntityX( player ), EntityY( player ), EntityZ( player )

    UpdateWorld
    RenderWorld

    ; 2D here


Flip
Wend

End


loadworld8.bb

; Add projectile Type
Type Projectile
   Field sprite, time_out
End Type


; Add Explosion Type
Type Explosion
   Field sprite, scale#
End Type
. . .
; Load Projectile Sprites
explosion_sprite = LoadSprite( "explosion.bmp" )
HideEntity explosion_sprite

projectile_sprite = LoadSprite( "heart.bmp" )
EntityRadius projectile_sprite, 2
EntityType projectile_sprite, coll_projectile
HideEntity projectile_sprite
. . .


functions8.bb

. . .
; Add projectile variables
Global player, projectile_sprite, explosion_sprite, hit, shoot, boom

Function LoadSFX()
   ; Load projectile sounds
   shoot = LoadSound( "shoot.wav" )
   SoundVolume shoot, .9
   boom = LoadSound( "boom.wav" )
   SoundVolume boom, .5

   . . .
End Function

Function object_key_control( obj )
   ; Add spacebar = fire
   If KeyHit(57) Then CreateProjectile( player )
   . . .
End Function

Function UpdateProjectiles()
   For P.Projectile = Each Projectile
      UpdateProjectile( P )
   Next
   For E.Explosion = Each Explosion
      UpdateExplosion( E )
   Next
End Function

Function CreateProjectile.Projectile( source )
   P.Projectile = New Projectile
   P\time_out = 150
   P\sprite = CopyEntity( projectile_sprite, source )

   ; Don't want projectiles coming out of player's feet :)
   MoveEntity P\sprite, 0, 2, 0
   EntityParent P\sprite, 0
   shootChannel = PlaySound ( shoot )
   Return P
End Function

Function UpdateProjectile( P.Projectile )
   If CountCollisions( P\sprite )
      If EntityCollided( P\sprite, coll_characters )
         For k = 1 To CountCollisions( P\sprite )
            hit = CollisionEntity( P\sprite, k )
            If GetEntityType( hit ) = coll_characters
               Exit
            EndIf
         Next
         boomChannel = PlaySound (boom)
         CreateExplosion( P )
         FreeEntity P\sprite
         Delete P
         Return
      EndIf
   EndIf
   P\time_out = P\time_out - 1
   If P\time_out = 0
      FreeEntity P\sprite
      Delete P
      Return
   EndIf
   MoveEntity P\sprite, 0, 0, 1
End Function

Function CreateExplosion.Explosion( P.Projectile )
   E.Explosion = New Explosion
   E\scale = 1
   E\sprite = CopyEntity( explosion_sprite, P\sprite )
   EntityParent E\sprite, 0
   Return E
End Function

Function UpdateExplosion( E.Explosion )
   If E\scale < 5
      E\scale = E\scale + .2
      ScaleSprite E\sprite, E\scale, E\scale
   Else
      FreeEntity E\sprite
      Delete E
      For Character.Sprite = Each Sprite
         If Character\ID = hit Then Delete Character : FreeEntity hit
      Next
   EndIf
End Function
. . .


NEW COMMANDS
lesson8.bb

. . .
; Add projectile type
Global coll_player = 1, coll_characters = 2, coll_objects = 3, coll_projectile = 4

; Add projectiles to the collision list
Collisions coll_projectile, coll_characters, 1,1
. . .
While Not KeyHit( escape_key )

   ; Keep any projectiles and explosions going
    UpdateProjectiles()
. . .

After adding the 4th type of object (projectile) to the global variables
and collision lists we need to create an updater function in the main
game loop that will check for a firing key being pressed, launching
of a projectile and keeping it moving, detecting if it collides with a
worthy target, playing an explosion sprite and destroying both
target and projectile.

loadworld8.bb

; Add projectile Type
Type Projectile
   Field sprite, time_out
End Type


; Add Explosion Type
Type Explosion
   Field sprite, scale#
End Type
. . .
; Load Projectile Sprites
explosion_sprite = LoadSprite( "explosion.bmp" )
HideEntity explosion_sprite

projectile_sprite = LoadSprite( "heart.bmp" )
EntityRadius projectile_sprite, 2
EntityType projectile_sprite, coll_projectile
HideEntity projectile_sprite
. . .

In our loadworld.bb include file we need to add TYPES for the projectile
and explosion animation and the accompanying sprite loading code.

functions8.bb

. . .
; Add projectile variables
Global player, projectile_sprite, explosion_sprite, hit, shoot, boom

Function LoadSFX()
   ; Load projectile sounds
   shoot = LoadSound( "shoot.wav" )
   SoundVolume shoot, .9
   boom = LoadSound( "boom.wav" )
   SoundVolume boom, .5

   . . .
End Function

Function object_key_control( obj )
   ; Add spacebar = fire
   If KeyHit(57) Then CreateProjectile( player )
   . . .
End Function

We start by adding additional global variables at the head of the functions
include file, then add shooting and explosion sounds under the LoadSFX()
function section. In the object_key_control( obj ) function we add the fire
key (57=spacebar).
Function UpdateProjectiles()
   For P.Projectile = Each Projectile
      UpdateProjectile( P )
   Next
   For E.Explosion = Each Explosion
      UpdateExplosion( E )
   Next
End Function
The UpdateProjectiles() function is a function that calls 2 other functions
handling both the projectiles and explosions updating (movement and
animation)
Function CreateProjectile.Projectile( source )
   P.Projectile = New Projectile
   P\time_out = 150
   P\sprite = CopyEntity( projectile_sprite, source )

   ; Don't want projectiles coming out of player's feet :)
   MoveEntity P\sprite, 0, 2, 0
   EntityParent P\sprite, 0
   shootChannel = PlaySound ( shoot )
   Return P
End Function
CreateProjectile.Projectile( source ) is called when the spacbar is hit.
P\time_out = 150 sets the range of the projectile by how long it will fly
before it is removed from the game (too many bullets in play can
eventually slow down the game. The rest is fairly straight forward,
positioning the bullet sprites launch position and playing a "shooting"
sound.
Function UpdateProjectile( P.Projectile )
   If CountCollisions( P\sprite )
      If EntityCollided( P\sprite, coll_characters )
         For k = 1 To CountCollisions( P\sprite )
            hit = CollisionEntity( P\sprite, k )
            If GetEntityType( hit ) = coll_characters
               Exit
            EndIf
         Next
         boomChannel = PlaySound (boom)
         CreateExplosion( P )
         FreeEntity P\sprite
         Delete P
         Return
      EndIf
   EndIf
   P\time_out = P\time_out - 1
   If P\time_out = 0
      FreeEntity P\sprite
      Delete P
      Return
   EndIf
   MoveEntity P\sprite, 0, 0, 1
End Function
Check for bullet-enemy collisions (play "explosion" sound, delete bullet
sprite) , check bullet if it has "timed out" (delete), otherwise move bullet.
Function CreateExplosion.Explosion( P.Projectile )
   E.Explosion = New Explosion
   E\scale = 1
   E\sprite = CopyEntity( explosion_sprite, P\sprite )
   EntityParent E\sprite, 0
   Return E
End Function
Create an explosion sprite in the position of the bullet sprite
Function UpdateExplosion( E.Explosion )
   If E\scale < 5
      ScaleSprite E\sprite, E\scale, E\scale
      E\scale = E\scale + .2
   Else
      FreeEntity E\sprite
      Delete E
      For Character.Sprite = Each Sprite
         If Character\ID = hit Then Delete Character : FreeEntity hit
      Next
   EndIf
End Function
Animate the explosion sprite by simple scaling increase up to 5 times.
Delete the sprite AND enemy after that.


THE CHALLENGE!!!
Try changing the projectile and explosion sprites, sounds, scaling, speed, etc.

NEXT : Lesson 9 - HUD / MousePick