First Monkey-X Game: Projectiles and Score

We have all sorts of enemies flying at our hero and if he hits them they disappear. Now we need to incentivize him to NOT run into all the bad guys.

Our next steps are to make enemies damage our hero when he runs into them, give him the ability to shoot the enemies before they reach him, and give the player points whenever they successfully destroy an enemy.

Health

Since all of our enemies and our player are Characters in our game, we can handle keeping track of their health in our Character class.

To do so we will add a health field and give each enemy and the player some starting health and a maximum amount of health.

Import fantomX
Class Character
Field box:ftObject
' Give characters health
Field current_health:Int
Field max_health:Int
Field is_player:Bool
' Modify the New method to include health and set health, we will default this to 1
Method New(box:ftObject, health:Int=1, player:Bool=False)
Self.box = box
Self.current_health = health
Self.max_health = health
Self.is_player = player
End
' ............
End

Now we need to change our game creation code and our enemy generator to pass in health for our player and enemies.

Class Game Extends App
' .............
Method OnCreate()
Self.engine = New CustomEngine
default_scene = engine.GetDefaultScene()
default_layer = engine.GetDefaultLayer()
Local box:ftObject = Self.engine.CreateBox(120, 20, engine.GetCanvasWidth()/2, engine.GetCanvasHeight()/2)
box.SetColor(0, 70, 70)
box.SetMaxSpeed(20.0)
box.SetMinSpeed(-20.0)
box.SetColGroup(PLAYER_GROUP)
box.SetColType(Self.engine.ctBox)
box.SetText("PLAYER")
' Give player 3 health
Self.player = New Character(box, 3, True)
Self.enemies = New List<Character>()
Self.last_enemy_time = Millisecs()
Self.next_enemy_interval = 3000
Seed = Millisecs()
End
' ..................
Method CreateEnemy()
Local rand_width:Float = Rnd(3, 7) * 10
Local rand_height:Float = Rnd(3, 7) * 10
Local rand_y:Float = Rnd(rand_height, Self.engine.GetCanvasHeight())
Local box:ftObject = Self.engine.CreateBox(rand_width, rand_height, Self.engine.GetCanvasWidth(), rand_y)
Local rand_color:Float = Rnd(0, 3)
' randomize enemy health to correspond to color
' red enemies have 1, green enemies have 2
' and blue enemies have 3
Local health = 1
Local colors:Int[] = [0, 0, 0]
If (rand_color <= 1.0)
colors = [255, 0, 0]
Else If (rand_color > 1.0 And rand_color <= 2.0)
colors = [0, 255, 0]
health = 2
Else
colors = [0, 0, 255]
health = 3
End
box.SetColor(colors[0], colors[1], colors[2])
box.SetSpeedX(Rnd(10, 30) * -1)
box.SetColGroup(ENEMY_GROUP)
box.SetColWith(PLAYER_GROUP, True)
box.SetText("ENEMY")
' give the enemy the health value based on their color
enemies.AddLast(New Character(box, health))
Self.next_enemy_interval = Rnd(0, 3) * 1000
End
' .................
End

Damage

Now that characters have health, we should give them the ability to take damage.

Let’s change the OnObjectCollisionMethod to cause the player and enemies to damage each other when they run into each other.

Import fantomX
Class CustomEngine Extends ftEngine
Method OnObjectCollision:Int(obj_one:ftObject, obj_two:ftObject)
If (obj_one.GetText() = "PLAYER" And obj_two.GetText() = "ENEMY")
' Change the text to be damaged and set it for both the player and the enemy
' this way players and enemies will only get damaged once per update
obj_two.SetText("DAMAGED")
obj_one.SetText("DAMAGED")
obj_two.SetColWith(obj_one.GetColGroup(), False)
Else If (obj_two.GetText() = "PLAYER" And obj_one.GetText() = "ENEMY")
obj_one.SetText("DAMAGED")
obj_two.SetText("DAMAGED")
obj_one.SetColWith(obj_two.GetColGroup(), False)
End
Return 0
End
End

In our character class we are going to add a method that handles units taking damage. Then we will call it from the character Update method.

Class Character
' ........................
Method Update
' ......................
' Every update, check to see if the character was damaged
If (Self.box.GetText() = "DAMAGED")
Self.TakeDamage(1)
End
End
' Add a method that handles lowering character health
' and tags characters with 0 health as destroyed
Method TakeDamage(amount:Int=0)
Self.current_health -= amount
If (Self.current_health <= 0)
Self.box.SetText("DESTROYED")
Else If (Self.is_player)
Self.box.SetText("PLAYER")
Else
Self.box.SetText("ENEMY")
End
End
End

And in our OnUpdate method, we will make some changes to handle updating characters to damage them and destroy them.

Method OnUpdate()
Local time_delta:Float = Float(engine.CalcDeltaTime())/60.0
If ((Millisecs() - Self.last_enemy_time) > Self.next_enemy_interval)
CreateEnemy()
Self.last_enemy_time = Millisecs()
End
player.Update(engine.GetCanvasWidth(), engine.GetCanvasHeight())
For Local enemy:Character = Eachin Self.enemies
' Add an update to all of our enemies so we can check for damage
enemy.Update(engine.GetCanvasWidth(), engine.GetCanvasHeight())
If (enemy.box.GetText() = "DESTROYED")
enemy.box.Remove()
Self.enemies.RemoveFirst(enemy)
End
End
If engine.GetPaused() = False
engine.Update(time_delta)
engine.CollisionCheck()
End
End

Now when you run the game, when the player runs into an enemy it causes damage instead of outright destroying them. Enemies with more than 1 health won’t be destroyed. And after the player takes too much damage, they will no longer damage enemies. Eventually we will add a GAME OVER or lost life for this case but that will come later.

Projectiles

Now that we have characters taking damage, lets give our player the ability to shoot at enemies and destroy them. We will start by making a projectile class and a projectile type class. Create a new file called projectile.monkey that we can start making projectiles from. It will have a constructor and an update method.

Import fantomX
Import projectile_type
Class Projectile
Field box:ftObject
Field projectile_type:ProjectileType
Method New(box:ftObject, projectile_type:ProjectileType)
Self.box = box
Self.projectile_type = projectile_type
End
Method Update()
Local modifier:Float = 1
If (Self.box.GetSpeedX() < 0)
modifier = -1
End
If (Abs(Self.box.GetSpeedX()) <= Self.projectile_type.max_speed)
Self.box.SetSpeedX(Self.box.GetSpeedX() + (Self.projectile_type.acceleration * modifier))
Else
Self.box.SetSpeedX(Self.projectile_type.max_speed * modifier)
End
End
End

and a file called projectile_type.monkey

Import fantomX
Class ProjectileType
Field power:Int
Field range:Float
Field acceleration:Float
Field max_speed:Float
Method New(power:Int=1, range:Float=1000, acceleration:Float=0, max_speed:Float=200)
Self.power = power
Self.range = range
Self.acceleration = acceleration
Self.max_speed = max_speed
End
End

Projectiles have a variety of attributes. First of all they have a box that can collide with enemies, and when we make enemies that shoot, with the player. Next they have power. This is done so that powerups can make the player’s projectiles stronger.

We also give the projectiles maximum speed, range, and acceleration. This is also for handling upgrades and how different weapons on different player ships or characters will work. We can have a short range, fast moving projectile. We can have a long range projectile that starts slow but speeds up over time which we handle with the Projectile’s Update method. Whatever we want.

The reason we separate the projectile type attributes out is so that we can store each character’s projectile type in their character class.

Firing

Now we want our player to be able to make some kind of projectile when the space bar is pressed. We are going to give the player character a type of projectile to fire and limit their fire rate. The idea is that whenever the fire key or button is pressed, we will true to the game engine and it will create a new projectile at the player’s location.

First we need to pass in a ProjectileType as part of the Character class constructor.

'.........................
Class Game Extends App
'.........................
Method OnCreate()
Self.engine = New CustomEngine
default_scene = engine.GetDefaultScene()
default_layer = engine.GetDefaultLayer()
Local box:ftObject = Self.engine.CreateBox(120, 20, engine.GetCanvasWidth()/2, engine.GetCanvasHeight()/2)
box.SetColor(0, 70, 70)
box.SetMaxSpeed(20.0)
box.SetMinSpeed(-20.0)
box.SetColGroup(PLAYER_GROUP)
box.SetColType(Self.engine.ctBox)
box.SetText("PLAYER")
' create a projectile type for our player
Local projectile_type:ProjectileType = New ProjectileType()
' and add it to the creation call
Self.player = New Character(box, 3, True, projectile_type)
Self.enemies = New List<Character>()
Self.last_enemy_time = Millisecs()
Self.next_enemy_interval = 3000
Seed = Millisecs()
End
'......................
Method CreateEnemy()
Local rand_width:Float = Rnd(3, 7) * 10
Local rand_height:Float = Rnd(3, 7) * 10
Local rand_y:Float = Rnd(rand_height, Self.engine.GetCanvasHeight())
Local box:ftObject = Self.engine.CreateBox(rand_width, rand_height, Self.engine.GetCanvasWidth(), rand_y)
' create a base projectile type for our enemies
Local projectile_type:ProjectileType = New ProjectileType()
Local rand_color:Float = Rnd(0, 3)
Local health = 1
Local colors:Int[] = [0, 0, 0]
If (rand_color <= 1.0)
colors = [255, 0, 0]
Else If (rand_color > 1.0 And rand_color <= 2.0)
colors = [0, 255, 0]
health = 2
Else
colors = [0, 0, 255]
health = 3
End
box.SetColor(colors[0], colors[1], colors[2])
box.SetSpeedX(Rnd(10, 30) * -1)
box.SetColGroup(ENEMY_GROUP)
box.SetColWith(PLAYER_GROUP, True)
box.SetText("ENEMY")
' give enemies a projectile type
enemies.AddLast(New Character(box, health, False, projectile_type))
Self.next_enemy_interval = Rnd(0, 3) * 1000
End
End

And modify the Character class to have a ProjectileType

Import fantomX
Import projectile_type
Class Character
Field box:ftObject
Field current_health:Int
Field max_health:Int
Field is_player:Bool
' Add projectile type
Field projectile_type:ProjectileType
Method New(box:ftObject, health:Int=1, player:Bool=False, projectile_type:ProjectileType)
Self.box = box
Self.current_health = health
Self.max_health = health
Self.is_player = player
' Set initial projectile type
Self.projectile_type = projectile_type
End
'...................
End

Now we are going to add a projectile creation function for our game, and set up the SPACE bar to be our firing control.

#FantomX_UsePhysics = True
Import fantomX
Import character
' Import projectile
Import projectile
Import custom_engine
Class Game Extends App
Field engine:ftEngine
Field default_layer:ftLayer
Field default_scene:ftScene
Field player:Character
Field enemies:List<Character>
Field last_enemy_time:Float
Field next_enemy_interval:Float
' add collection to hold the games projectiles
Field projectiles:List<Projectile>
Const PLAYER_GROUP:Int = 1
Const ENEMY_GROUP:Int = 2
' Add projectile collision group
Const PROJECTILES:Int = 3
Method OnCreate()
Self.engine = New CustomEngine
default_scene = engine.GetDefaultScene()
default_layer = engine.GetDefaultLayer()
Local box:ftObject = Self.engine.CreateBox(120, 20, engine.GetCanvasWidth()/2, engine.GetCanvasHeight()/2)
box.SetColor(0, 70, 70)
box.SetMaxSpeed(20.0)
box.SetMinSpeed(-20.0)
box.SetColGroup(PLAYER_GROUP)
box.SetColType(Self.engine.ctBox)
box.SetText("PLAYER")
Local projectile_type:ProjectileType = New ProjectileType()
Self.player = New Character(box, 3, True, projectile_type)
Self.enemies = New List<Character>()
Self.last_enemy_time = Millisecs()
Self.next_enemy_interval = 3000
' initialize projectile list
Self.projectiles = New List<Projectile>()
Seed = Millisecs()
End
Method OnUpdate()
Local time_delta:Float = Float(engine.CalcDeltaTime())/60.0
If ((Millisecs() - Self.last_enemy_time) > Self.next_enemy_interval)
CreateEnemy()
Self.last_enemy_time = Millisecs()
End
player.Update(engine.GetCanvasWidth(), engine.GetCanvasHeight())
If (KeyDown(KEY_SPACE))
CreateProjectile(player)
End
For Local enemy:Character = Eachin Self.enemies
enemy.Update(engine.GetCanvasWidth(), engine.GetCanvasHeight())
If (enemy.box.GetText() = "DESTROYED")
enemy.box.Remove()
Self.enemies.RemoveFirst(enemy)
End
End
If engine.GetPaused() = False
engine.Update(time_delta)
engine.CollisionCheck()
End
End
Method CreateEnemy()
Local rand_width:Float = Rnd(3, 7) * 10
Local rand_height:Float = Rnd(3, 7) * 10
Local rand_y:Float = Rnd(rand_height, Self.engine.GetCanvasHeight())
Local box:ftObject = Self.engine.CreateBox(rand_width, rand_height, Self.engine.GetCanvasWidth(), rand_y)
Local projectile_type:ProjectileType = New ProjectileType()
Local rand_color:Float = Rnd(0, 3)
Local health = 1
Local colors:Int[] = [0, 0, 0]
If (rand_color <= 1.0)
colors = [255, 0, 0]
Else If (rand_color > 1.0 And rand_color <= 2.0)
colors = [0, 255, 0]
health = 2
Else
colors = [0, 0, 255]
health = 3
End
box.SetColor(colors[0], colors[1], colors[2])
box.SetSpeedX(Rnd(10, 30) * -1)
box.SetColGroup(ENEMY_GROUP)
box.SetColWith(PLAYER_GROUP, True)
box.SetText("ENEMY")
enemies.AddLast(New Character(box, health, False, projectile_type))
Self.next_enemy_interval = Rnd(0, 3) * 1000
End
' Add a method to create new projectiles
Method CreateProjectile(character:Character)
Local projectile_box:ftObject = Self.engine.CreateBox(10, 20, character.box.GetPosX(), character.box.GetPosY())
projectile_box.SetColGroup(PROJECTILES)
If (character.is_player)
projectile_box.SetSpeedX(10)
projectile_box.SetColor(0, 255, 255)
projectile_box.SetColWith(ENEMY_GROUP, True)
Else
projectile_box.SetSpeedX(-10)
projectile_box.SetColor(255, 255, 0)
projectile_box.SetColWith(PLAYER_GROUP, True)
End
projectile_box.SetText("PROJECTILE")
Self.projectiles.AddLast(New Projectile(projectile_box, character.projectile_type))
End
Method OnRender()
engine.Clear(255, 255, 255)
engine.Render()
engine.SetColor(0, 70, 70)
engine.RenderFlush()
End
End
Function Main()
New Game()
End

You should be able to “fire” bullets now. Currently we are not restricting how fast you can fire, which means you can use bullets to draw a line on the screen. Our next step is to limit the players fire rate. We will do that by giving characters a last fired time as well as giving projectile types a rate of fire.

Start by updating the ProjectileType class.

Import fantomX
Class ProjectileType
Field power:Int
Field range:Float
Field acceleration:Float
Field max_speed:Float
' Add rate of fire
Field rate_of_fire:Float
' and also add to constructor
Method New(power:Int=1, range:Float=1000, acceleration:Float=0, max_speed:Float=200, rate_of_fire:Float=2.0)
Self.power = power
Self.range = range
Self.acceleration = acceleration
Self.max_speed = max_speed
Self.rate_of_fire = rate_of_fire
End
End

Then in the Character class, lets add a method that determines whether or not a character can fire.

Import fantomX
Import projectile_type
Class Character
Field box:ftObject
Field current_health:Int
Field max_health:Int
Field is_player:Bool
Field projectile_type:ProjectileType
' last projectile fired time
Field last_projectile_time:Float
Method New(box:ftObject, projectile_type:ProjectileType, health:Int=1, player:Bool=False)
Self.box = box
Self.current_health = health
Self.max_health = health
Self.is_player = player
Self.projectile_type = projectile_type
' Initialize last projectile time
Self.last_projectile_time = Millisecs()
End
'................................
' Add method to determine whether or not this character can fire a projectile
Method FireProjectile:Bool()
If ((Millisecs() - Self.last_projectile_time) > (1000.0 / Self.projectile_type.rate_of_fire))
Self.last_projectile_time = Millisecs()
Return True
Else
Return False
End
End
End

Finally, add the check for whether the player character can fire or not in our games OnUpdate method. Also we are going to make some small modifications to the CreateProjectile method

' Game Class
Method OnUpdate()
Local time_delta:Float = Float(engine.CalcDeltaTime())/60.0
If ((Millisecs() - Self.last_enemy_time) > Self.next_enemy_interval)
CreateEnemy()
Self.last_enemy_time = Millisecs()
End
player.Update(engine.GetCanvasWidth(), engine.GetCanvasHeight())
' Add our character can fire check here
If (KeyDown(KEY_SPACE) And player.FireProjectile())
CreateProjectile(player)
End
For Local enemy:Character = Eachin Self.enemies
enemy.Update(engine.GetCanvasWidth(), engine.GetCanvasHeight())
If (enemy.box.GetText() = "DESTROYED")
enemy.box.Remove()
Self.enemies.RemoveFirst(enemy)
End
End
If engine.GetPaused() = False
engine.Update(time_delta)
engine.CollisionCheck()
End
End
' ............................
Method CreateProjectile(character:Character)
Local projectile_box:ftObject = Self.engine.CreateBox(20, 10, character.box.GetPosX() + character.box.GetWidth()/2, character.box.GetPosY())
projectile_box.SetColGroup(PROJECTILES)
If (character.is_player)
projectile_box.SetSpeedX(50)
projectile_box.SetColor(0, 255, 255)
projectile_box.SetColWith(ENEMY_GROUP, True)
Else
projectile_box.SetPosX(character.box.GetPosX() - character.box.GetWidth()/2)
projectile_box.SetSpeedX(-50)
projectile_box.SetColor(255, 255, 0)
projectile_box.SetColWith(PLAYER_GROUP, True)
End
projectile_box.SetText("PROJECTILE")
Self.projectiles.AddLast(New Projectile(projectile_box, character.projectile_type))
End

Your player’s character should now be firing little light blue bullets about every half second if they hold down the space bar.

Projectile Collision and Damage

Now that we can fire projectiles, we want them to actually hit the enemies and do damage to them. This means we need to modify our CustomEngine Class OnObjectCollision method to tell enemies to be damaged by projectiles and to set projectiles to destroyed when they hit enemies. While we do that, we will also refactor our other collision code to make it cleaner.

Import fantomX
Class CustomEngine Extends ftEngine
Method OnObjectCollision:Int(obj_one:ftObject, obj_two:ftObject)
' Refactor character collision checks to use method
If (obj_one.GetText() = "PLAYER" And obj_two.GetText() = "ENEMY")
CharacterCollision(obj_one, obj_two)
Else If (obj_two.GetText() = "PLAYER" And obj_one.GetText() = "ENEMY")
CharacterCollision(obj_two, obj_one)
End
' Check for projectile collision with enemies
If (obj_one.GetText() = "PROJECTILE" And obj_two.GetText() = "ENEMY")
ProjectileCollision(obj_one, obj_two)
Else If (obj_two.GetText() = "PROJECTILE" And obj_one.GetText() = "ENEMY")
ProjectileCollision(obj_two, obj_one)
End
Return 0
End
' add projectile collison behavior
Method ProjectileCollision(projectile_box:ftObject, enemy_box:ftObject)
projectile_box.SetText("DESTROYED")
enemy_box.SetText("DAMAGED")
projectile_box.SetColWith(enemy_box.GetColGroup(), False)
End
' refactor character collision behavior to a method
Method CharacterCollision(player_box:ftObject, enemy_box:ftObject)
player_box.SetText("DAMAGED")
enemy_box.SetText("DAMAGED")
enemy_box.SetColWith(player_box.GetColGroup(), False)
End
End

We also need to remove all projectiles that hit enemies from the game, so they don’t hit enemies twice. And since we are not currently calling the projectile’s Update method each loop, acceleration will not work yet. Let’s add that in as well as we loop over the projectiles.

' Game Class
Method OnUpdate()
Local time_delta:Float = Float(engine.CalcDeltaTime())/60.0
If ((Millisecs() - Self.last_enemy_time) > Self.next_enemy_interval)
CreateEnemy()
Self.last_enemy_time = Millisecs()
End
player.Update(engine.GetCanvasWidth(), engine.GetCanvasHeight())
If (KeyDown(KEY_SPACE) And player.FireProjectile())
CreateProjectile(player)
End
' Remove destroyed projectiles from our game
' and call projectiles update method
For Local projectile:Projectile = Eachin Self.projectiles
projectile.Update()
If (projectile.box.GetText() = "DESTROYED")
projectile.box.Remove()
Self.projectiles.RemoveFirst(projectile)
End
End
For Local enemy:Character = Eachin Self.enemies
enemy.Update(engine.GetCanvasWidth(), engine.GetCanvasHeight())
If (enemy.box.GetText() = "DESTROYED")
enemy.box.Remove()
Self.enemies.RemoveFirst(enemy)
End
End
If engine.GetPaused() = False
engine.Update(time_delta)
engine.CollisionCheck()
End
End

When you run the game now, bullets that hit enemies should disappear and enemies that take enough damage should be destroyed (at this point red enemies take 1 damage, green take 2, and blue ones take 3).

Score

For the final part of this section, we are going to give the player points whenever he destroys an enemy. To do this we need to assign each enemy a point value, we don’t want those tougher enemies to be worth the same as the weak ones.

To do this, we are going to store a point value in the Character class. The player character won’t use it and that is just fine. We will also make a method for increasing the points earned. Then whenever an enemy with 0 health is destroyed we will give the player that many points. The reason we are checking the health is that soon we will be removing enemies that fly off the screen to keep game performance up.

Import fantomX
Import projectile_type
Class Character
Field box:ftObject
Field current_health:Int
Field max_health:Int
Field is_player:Bool
Field projectile_type:ProjectileType
Field last_projectile_time:Float
' Give characters a point value and points earned field
Field point_value:Int
Field points_earned:Int
' Add the point value in the constructor
Method New(box:ftObject, projectile_type:ProjectileType, point_value:Int=5, health:Int=1, player:Bool=False)
Self.box = box
Self.current_health = health
Self.max_health = health
Self.is_player = player
Self.projectile_type = projectile_type
Self.last_projectile_time = Millisecs()
' initialize the point value and points earned fields
Self.point_value = point_value
Self.points_earned = 0
End
'....................
' Simple method for increasing the score
Method IncreaseScore(points:Int=0)
Self.points_earned += points
End
End

Now we need to modify the Enemy and player creation code to include point values in the constructors.

' Game Class
Method OnCreate()
Self.engine = New CustomEngine
default_scene = engine.GetDefaultScene()
default_layer = engine.GetDefaultLayer()
Local box:ftObject = Self.engine.CreateBox(120, 20, engine.GetCanvasWidth()/2, engine.GetCanvasHeight()/2)
box.SetColor(0, 70, 70)
box.SetMaxSpeed(20.0)
box.SetMinSpeed(-20.0)
box.SetColGroup(PLAYER_GROUP)
box.SetColType(Self.engine.ctBox)
box.SetText("PLAYER")
Local projectile_type:ProjectileType = New ProjectileType()
' Make the player worth 0 points
Self.player = New Character(box, projectile_type, 0, 3, True)
Self.enemies = New List<Character>()
Self.last_enemy_time = Millisecs()
Self.next_enemy_interval = 3000
Self.projectiles = New List<Projectile>()
Seed = Millisecs()
End
'.......................
Method CreateEnemy()
Local rand_width:Float = Rnd(3, 7) * 10
Local rand_height:Float = Rnd(3, 7) * 10
Local rand_y:Float = Rnd(rand_height, Self.engine.GetCanvasHeight())
Local box:ftObject = Self.engine.CreateBox(rand_width, rand_height, Self.engine.GetCanvasWidth(), rand_y)
Local projectile_type:ProjectileType = New ProjectileType()
Local rand_color:Float = Rnd(0, 3)
Local health = 1
' make tougher enemies worth more points
Local points = 10
Local colors:Int[] = [0, 0, 0]
If (rand_color <= 1.0)
colors = [255, 0, 0]
Else If (rand_color > 1.0 And rand_color <= 2.0)
colors = [0, 255, 0]
health = 2
points = 25
Else
colors = [0, 0, 255]
health = 3
points = 45
End
box.SetColor(colors[0], colors[1], colors[2])
box.SetSpeedX(Rnd(10, 30) * -1)
box.SetColGroup(ENEMY_GROUP)
box.SetColWith(PLAYER_GROUP, True)
box.SetText("ENEMY")
' add the points in the constructor
enemies.AddLast(New Character(box, projectile_type, points, health))
Self.next_enemy_interval = Rnd(0, 3) * 1000
End

Now lets add a check in OnUpdate when we destroy an Enemy to increase the player’s score if the enemy’s health is 0 or less.

' Game Class
Method OnUpdate()
Local time_delta:Float = Float(engine.CalcDeltaTime())/60.0
If ((Millisecs() - Self.last_enemy_time) > Self.next_enemy_interval)
CreateEnemy()
Self.last_enemy_time = Millisecs()
End
player.Update(engine.GetCanvasWidth(), engine.GetCanvasHeight())
If (KeyDown(KEY_SPACE) And player.FireProjectile())
CreateProjectile(player)
End
For Local projectile:Projectile = Eachin Self.projectiles
projectile.Update()
If (projectile.box.GetText() = "DESTROYED")
projectile.box.Remove()
Self.projectiles.RemoveFirst(projectile)
End
End
For Local enemy:Character = Eachin Self.enemies
enemy.Update(engine.GetCanvasWidth(), engine.GetCanvasHeight())
If (enemy.box.GetText() = "DESTROYED")
' If the player destroyed the enemy, increase the score
If (enemy.current_health <= 0)
Self.player.IncreaseScore(enemy.point_value)
End
enemy.box.Remove()
Self.enemies.RemoveFirst(enemy)
End
End
If engine.GetPaused() = False
engine.Update(time_delta)
engine.CollisionCheck()
End
End

Now whenever we destroy and enemy, our players score will get updated. But we can’t see what the score is right now. Our next step will be to draw the player’s score on the screen. We will do that my adding a text draw to our OnRender method to draw the player’s score in the upper left hand corner of the screen.

' Game Class
Method OnRender()
engine.Clear(255, 255, 255)
engine.Render()
' Set the text color to black and draw the score on the screen
engine.SetColor(0, 0, 0)
engine.GetCanvas().DrawText("Player Score: " + Self.player.points_earned, engine.GetLocalX(10), engine.GetLocalY(10))
engine.SetColor(0, 70, 70)
engine.RenderFlush()
End

That wraps up projectiles and scoring for now. Our next step is to be able to actually end the game when the player takes too much damage and to add some other game states.

Tutorial Part 7

I Want to Be a Better Developer