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 the 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:
And the HUD should now look like:
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:
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: