• Andrew Jonhardt

A simple health pickup

I'm afraid this post will be a little basic to most Godot users. My game's design shifted abruptly this past week to something smaller, leaner, and far more uncertain. I've found myself questioning everything up to the choice for the game's name, which I may soon change. So, I'll just be focusing on a few small and unchanging parts of my game, and I'm going to return to calling it Project Splatter until I can re-decide on the name.

Before I can get into the health pickup, let's cover a solution I learned from HeartBeast on YouTube I use as part of the pickup's foundation: the "DetectionZone".

The DetectionZone in my project is an Area2D node with an empty CollisionShape2D as a child. Essentially, the DetectionZone is a re-usable means for any object of your choice to see and react to the player. Any time you import the DetectionZone into a scene, you can right-click on it, enable Editable Children, and scale the DetectionZone's CollisionShape2D to be however large or small you need it to be just in that one scene.

As the DetectionZone only exists to see the player, I've removed it from all physics Layers and set it up on a Mask layer to detect whenever the player collides with it.

The script attached to the DetectionZone is brief:

extends Area2D

var player = null

func can_see_player():
	return player != null

func _on_DetectionZone_body_entered(body):
	player = body

func _on_DetectionZone_body_exited(_body):
	player = null

Because the DetectionZone exists on a Mask layer that can only see the player, it cannot interact with any other physics body. So, it's safe to connect the Area2D that serves as the root of DetectionZone to itself using the body_entered and body_exited signals. Assuming you size the CollisionShape2D correctly, the body signals should set the "player" variable to be equal to the actual player in-game.

The function can_see_player returns a true or false depending on whether the player is within range of the CollisionShape2D you've setup. If player != null (if the variable "player" is equal to something), then can_see_player = true.

So, DetectionZone is immediately useful as a foundation for all pickups in my game because it's already setup to only interact with the player. The rest of the pickup is just a Sprite, AnimationPlayer, and Node2D, and the whole thing could probably be refined further.

The reason I have a Node2D (labeled "HealthRoot") as the root of the scene, instead of the Sprite, is because of the simple animation I made from within Godot.

As you may (or not) be able to tell, the animation for the heart pickup was made from physically manipulating the image of the heart from within the editor. To me, it now looks like the heart is being squished instead of spinning. I'm hoping new players will assume the heart is spinning. Setting the DetectionZone (represented by the blue collision square) to be a child outside of the warping Sprite avoids the problem of the DetectionZone also being squished.

The script used by the health pickup plays off the DetectionZone and could just modify the "health" value for the player. Unfortunately, my player health system has become a mess due to the need to update player health bar and move the current health value between scenes (I didn't plan it out very well).

extends Node2D

onready var detect = $DetectionZone

var heal = 1

func _process(delta):
	if detect.can_see_player():
		if Global.player_health == 3:
			detect.player.health += heal
			detect.player.emit_signal("health_change", detect.player.health)
			Global.player_health += heal

The above script runs every frame, takes an amount to heal the player by (the variable "heal"), and a reference to the DetectionZone in the same scene.

If the can_see_player function from within DetectionZone returns true, the script checks a variable in a Global script (a script all other scripts can see) called "player_health". If player_health = 3, or maximum health, the pickup just destroys itself. I'm hoping to re-enforce the need for accuracy in movement choices, so a health pickup that destroys itself if the player accidentally touches it when they don't need it fits my current design.

However, if the player is low on health, the pickup increases the health value of the player (detect.player.health += heal), forces the player object to emit a signal that is tied to the health UI (detect.player.emit_signal("health_change", detect.player.health)), and updates the Global player health variable again, before deleting itself.

My health pickup is a bit of a mess. However, it's my hope that the concepts behind the pickup are basic enough to be useful for most projects.

My next post will be on the 19th. Until then!

© 2023 by Andrew Jonhardt. Proudly created with Wix.com