Skip to main content

Godot: Single Level 2D Platformer (Part 4) Obstacles and Enemies

This is part 4 of the Godot 2D Platformer series. This continues on from part 3.

The focus of this part of the tutorial is to create a moving obstacle or enemy that will kill the player if the player touches the object.

An extension to this will be to add a mechanic where if the player touches the top of the enemy / obstacle the player will destroy it, in a way that is similar to Mario Bros. This will be covered in part 5.

For this project you need:

  • a player object that can move

  • a platform

  • other art assets for the enemy / obstacle.

Create a new scene and save it as Obstacle.

Add the following nodes to the scene:

  • RigidBody2D (rename this to Obstacle)
    • CollisionShape2D (set to rectangle)

    • Sprite

    • Area2D (rename KillArea)

      • CollisionShape2D (rename KillPlayer)

In the inspector, change the Gravity value of the RigidBody2D to 0. This will stop the object falling to the ground.

Add a sprite image. This example has used tile_0128.png from the previously used asset packs.

Add a new RectangleShape2D to the CollisionShape2D for the KillPlayer and main Obstacle

Scale each of these to map the collision area that you want.

Make the KillPlayer area slightly larger than the other CollisionShape

Now we need to detect if the obstacle is touching the player.

If it is we will create a signal that will be used to send this information to the player to let the player know that they have been hit.

We will then get the player to restart the game in the same way as used for the death area in a previous tutorial.

Create a new script called Obstacle.gd and attach it to the Obstacle scene.

Go to the Obstacle scene and select the KillArea Obstacle node.

We are going to create a signal (event) that will detect when the KillArea has been entered.

Select Node and then the body_entered signal.

Connect the signal to the Obstacle script.

This will add a function to the script to handle the signal.

Add the code at 1 to the script to print a message to check that the player has been collided with.

extends RigidBody2D

# Declare member variables here. Examples:
# var a = 2
# var b = "text"


# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
#func _process(delta):
# pass

func _on_KillArea_body_entered(body):
# 1 Print hit message
print("Obstacle hit player")

Now add the obstacle to the main level scene.

Test your program. When the player contacts the obstacle Obstacle hit player should be printed.

Now we want to modify the player script so that we have a respawn function that will reset the player when the death has occured.

Open the Player.dg script.

Add points 26 and 27 to add a respawn() function to the script.

# 1 Extend the kinematic body node to allow for 2d physics
extends KinematicBody2D

# 2 Create a gravity variable of type int and set it to 800
# In capitals as it is a constant and won't change
var GRAVITY : int = 80

# 3 Create a variable to store the player velocity
# Set this to the type of a 2D vector e.g. x and y
var velocity: Vector2 = Vector2()
# 7 Create a variable to set the speed of the player
# 20 Update the speed that the player moves
var speed : int = 50
# 14 Jump force
var jump : int = 50

# 4 Create a sprite variable and link it to the sprite node. When the node is
# initialised.
onready var sprite = $Sprite
# 21 Add a variable for the audio stream player
onready var audioPlayer = $AudioStreamPlayer2D

# Called when the node enters the scene tree for the first time.
func _ready():
# 26 Find the obstacle node
var obstacle_node = get_tree().get_root().find_node("Obstacle", true, false)
# 27 Link up the signal and set function to run
obstacle_node.connect("player_hit", self, "handle_player_spotted")
pass # Replace with function body.

# 28 function to run when the signal triggers
func handle_player_spotted():
print("Received hit from obstacle")

# 5 Runs the physics updates to the game
func _physics_process(delta):
# 6 Update the y axis velocity by the time elapsed
velocity.y += delta * GRAVITY

# 8 Check is the left input is pressed
# note player_left is the action in the input map
if Input.is_action_pressed("player_left"):
# 9 Update the velocity by the speed of the player
# 18 set x velocity to -speed to have fixed speed
velocity.x = -speed
# 10 do the same for the moving right
elif Input.is_action_pressed("player_right"):
# 19 set x velocity to speed to have fixed speed
velocity.x = speed
# 11 If left or right is not pressed set the x velocity to 0
else:
velocity.x = 0

# 12 Flip the sprite depending on the direction of movement
if velocity.x < 0:
sprite.flip_h = false
elif velocity.x > 0:
sprite.flip_h = true

# 15 Detect the jump button being pressed
# 17 Add the is_on_floor() check to the jump condition
if Input.is_action_pressed("player_jump") && is_on_floor():
# 16 Multiply by -1 to turn the velocity into a negative value
velocity.y = jump * -1
# 22 Play the audio for the jump
audioPlayer.play()

# 13 Move the object, the second parameter sets the vertical axis
move_and_slide(velocity, Vector2.UP)

func _on_DeathArea_body_entered(body):
# 23 Test object overlap
print("Player death")
# 24 Reload the scene
#get_tree().reload_current_scene()
# 25 Respawn player at location
position = $"../SpawnPoint".position

# 26 Respawn function
func respawn():
# 27 respawns the player
position = $"../SpawnPoint".position

Now we need to link this function to the signal.

Switch back to the obstacle script.

Add the code at 2. This will call the respawn function of the object that collided with the obstacle. Note that if there is now respawn function on the colliding object an error will occur and potentially crash the program.

extends RigidBody2D

# Declare member variables here. Examples:
# var a = 2
# var b = "text"


# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
#func _process(delta):
# pass

func _on_KillArea_body_entered(body):
# 1 Print hit message
print("Obstacle hit player")
# 2 Call the respawn function of the player (body) using dot notation
body.respawn()

Test the program. Now when the player hits the obstacle the game should reset.

Congratulations you have now created an obstacle that will kill the player.