Adding Enemies to Our Game

This is Part 5 of the Godot series. Read Previous Parts here.

All the code is here, under Part 5

In this post, we will add an enemy to our game. The enemy will walk left and right, and we will be able to kill it by jumping on it.

Create a new scene called enemy. We will copy player.tscn, so it will be a KinematicBody2D, with children CollisionShape2d and AnimatedSprite.

Add the enemy walking animations :

And add a CollisionShape2d as a rectangle:

It doesn't have to be perfect, as we won't really be using it much.

Instead, we'll have two new areas/zones– one at the top of the enemy where jumping on will kill the enemy; and another area in the body of the enemy, so the player can die if the enemy touches it.

To enemy, add 2 new Aread2d:

And to each of the above, add CollisionShape2Ds:

Why are we doing it this way rather than using the default CollisionShape2d? Because only the Area2d has a body_entered() function, which we will need later.

Add a top_hit_area near the head:

And same for the body:

On the level1 file, create a node2d called enemies, and link our enemy.tscn file to it. Check that the size of the enemy is similar to the player, and resize if needed.

Create a script for enemy, and then click on the body_entered() function for the AREA_TOP region:

and connect to the script in enemy.gd you created. Do the same for the AREA_BODY below. You will have 2 new functions in enemy.gd

func _on_AREA_TOP_body_entered(body):
	pass # Replace with function body.


func _on_AREA_BODY_body_entered(body):
	pass # Replace with function body.

Let's update the code for top:

func _on_AREA_TOP_body_entered(body):
	if body.get_name() == "player":
		print("enemy dead!")
		queue_free()

The code checks if a body enters the top area(which remember, is on top of the enemy), and if the body is the player, it prints "enemy dead" and then the enemy is deleted using the queue_free() function.

Test the code:

Jumping on the enemy kills it.

Now let's add code so the player dies when it hits the enemy.

For this case, we want the player to handle the death--not the enemy. So we will send a signal to the player object when the player is hit by the enemy.

Signals in Godot

Signals allow us to keep different parts of the code independent. Rather than sharing variables and having messy code, scenes can send each other signals when something changes. Like, the player being hit.

Another advantage of this is: Now the enemy code doesn't have to stop to process the hit. It sends the signal and moves on, it's the player scene's job to handle the signal. Both scenes can work independently.

Add this to the top of the code:

signal player_hit

Update the code:

func _on_AREA_BODY_body_entered(body):
	if body.get_name() == "player":
		emit_signal("player_hit")

So when the body area of the enemy is entered, the player_hit signal is emitted.

Now we need to connect this signal to the player script.

Go to the level1 screen, select the enemy object, and you should see the new signal:

Double click on it and select the player script, and add this code:

func _on_enemy_player_hit():
	print("You were hit by the enemy!")
	get_tree().change_scene("res://level1.tscn")

As before, we reload the screen when the player dies. Let's try it out.

So the player dies when it touches the enemy, and you can still kill it by jumping on it.

We need one more thing: The enemy just stands there. It would be better if it moved a little.

Enemy Movement

We will keep the movement simple– the enemy will simply move left and right.

Add this to the enemy.gd script:

var velocity = Vector2(0,0)
const GRAVITY = 20
var SPEED = 30
var counter = 0

and update the physics process function:

func _physics_process(_delta):
	counter += 1
	velocity.x = SPEED
	velocity.y += GRAVITY
	velocity = move_and_slide(velocity, Vector2.UP)
	$AnimatedSprite.play("walk")
	if counter >= 100:
		$AnimatedSprite.flip_h = !$AnimatedSprite.flip_h
		SPEED *= -1
		counter = 0

The code should be simple– as its roughly the same as the player code. Every 100 frames, we change the direction of our enemy (by multiplying SPEED by -1).

One last thing to do is to set the masks:

The enemy is on layer 4 (enemy), and the masks are set to 1(player) and 3 (environment). We need Mask 3, otherwise the enemy falls thru the floor.

Give it a try

I will now redesign the level a little and add a few more enemies.

And there we go. Our simple game is coming around.

Last thing to do is add a UI, for when the player wins / dies. We'll do that next.