We’d love to hear from you. Whether you have a question that you’d like us to answer as part of our FAQ,  you’ve identified something that just doesn’t quite look right in one of our tutorials, you have a business opportunity or for any other reason, please use this simple form to contact us.

We’ll do our best to provide a prompt response.

Like what you see?...pass it on!

Populating the Game World

Now we have all the pieces we need to create our game, let’s update the GameScene to populate it with both the Player class as well as enemies. While we are at it, we are going to add a camera so we can scroll off screen!

But first, we are going to add a simple HUD, which is a 2D interface that is drawn over top of our game display. We will simply display the total kill count in the top right corner.

To create the HUD, create a new CanvasLayer node, parented to the GameSceneRoot node and rename it HUD. Add a label to the newly created HUD, rename it Kills and set the text to Kills:0. We also configure the Font as we did earlier when creating the Stage label.

Your Node hierarchy should now look like:

Scene-with-HUD-added

And the HUD should now look like:

Game-Level-with-Kills

Now we add the following code:

extends Node2D

enum GameState { Loading, Running, GameOver }
onready var State = GameState.Loading
onready var enemyObj = preload("res://Enemy/Enemy.tscn")

var Player = null
var cam = null

func _ready():
  var labelText = "Stage " + str(globals.currentStage)
  $Label.set_text(labelText)
  $AnimationPlayer.play("Stage Display")

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
  $HUD/Kills.set_text("Kills:" + str(globals.kills))
  pass

func _input(event):
  if(State == GameState.GameOver):
    if(event.is_action("PLAYER_SHOOT")):
      get_tree().change_scene("TitleScene/TitleScene.tscn")   

func startAnimationDone():
  $Label.visible = false
  Player = load("res://Player/Player.tscn").instance()
  Player.position = Vector2(300,720/2)
  cam = Camera2D.new()
  cam.position.x = 360
  cam.make_current()
  Player.add_child(cam)
  add_child(Player)
  SpawnEnemies()
  State = GameState.Running

func _on_Area2D_area_entered(area):
  #Did we hit the trigger to queue boss fight?
  if(area.get_collision_layer_bit(4)):
    if(State == GameState.Running):
      Player.speed = 0
      globals.currentStage = globals.currentStage + 1
      get_tree().reload_current_scene()
     
func PlayerDied():
  for i in Player.get_children():
    i.queue_free()
  remove_child(Player)
  State = GameState.GameOver

  $Label.set_text("Game Over")
  $Label.visible = true
  $Label.set_position(Vector2(1280/2 - 200, 720/2))

func SpawnEnemy(x,y):
  var enemy = enemyObj.instance()
  enemy.set_position(Vector2(x,y))
  add_child(enemy)

func SpawnEnemies():
  randomize()
  # One extra plane per stage
  for i in range(0,10+globals.currentStage):
    SpawnEnemy(700 + randi()%5000,randi()%int(get_viewport_rect().size.y))

func SpawnBossWave():
  for i in range(0,10):
    SpawnEnemy(3800 + randi()%220, randi()%720)

Now we’ve got a game! One thing you will notice is the game continues to run past the end of the level. To get around this, we are going to create an Area2D that tells the game we are at the end of the level. We already implemented the code, _on_Area2D_area_entered()… now we just need to implement an area on our map.

Create a new node parented to the root node called triggers, then inside this node create an Area2D. Finally, create a CollisionShape of type Rectangle 30 wide and 360 high, and draw this half a screen (1280/2) from the end of the level, like so:

End-of-level-trigger-in-scene

Then wire up the area_entered() signal to call _on_Area2D_area_entered() when we hit this code. Finally, let’s set up the Area2D collisions like so:

End-of-level-area2d-trigger

Configuring Collisions<<PreviousTable of ContentsNext>>Adding Shooting to the Game

Like what you see?...pass it on!

script ends --->