Godot Lesson 11: Heart Pickups¶
Summary¶
This adds enemy drops and heart pickups.
Prerequisites¶
Lessons 1-10
Video¶
Code¶
Repository: https://github.com/los-alamos-steam-lab/godot-tutorial/tree/11-heart-pickups
Pickup Code¶
Major Changes from the Video:
Created a class name for easier inheritance.
class_name pickup
extends Area2D
export(bool) var disappears = false
# Called when the node enters the scene tree for the first time.
func _ready():
connect("body_entered", self, "body_entered")
connect("area_entered", self, "area_entered")
func area_entered(area):
var area_parent = area.get_parent()
if area_parent.name == "sword":
body_entered(area_parent.get_parent())
func body_entered(body):
pass
Key Code¶
extends pickup
func body_entered(body):
# I replace body.get(keys) with body.keys because I want this
# to fail if the player does not have a keys variable
# I also made MAXKEYS a player constant to make it easier to change
if body.name == "player" && body.keys < body.MAXKEYS:
# Pickup the key and then delete it.
body.keys += 1
queue_free()
Heart Code¶
extends pickup
func body_entered(body):
# I replace body.get(health) with body.health because I want this
# to fail if the player does not have a keys variable
if body.name == "player" && body.health < body.MAXHEALTH:
# Pickup the heart and then delete it.
body.health += 1
queue_free()
Entity Code¶
class_name entity
extends KinematicBody2D
# we put this here instead of autoloading it
# nothing wrong with autoload, but I prefer things in the code
var dir = directions.new()
# "CONSTANTS"
var SPEED = 0
var TYPE = "ENEMY"
var DAMAGE = null
var MAXHEALTH = 1
# MOVEMENT
var movedir = Vector2.ZERO
var knockdir = Vector2.ZERO
var spritedir = "down"
var hitstun = 0
var health = MAXHEALTH
var texture_default = null
var texture_hurt = null
func _ready():
# keep the enemies frozen until they enter the camera scene
if TYPE == "ENEMY":
set_physics_process(false)
# set the collision mask to collide with the
# walls in the camera scene
set_collision_mask_bit(1,1)
texture_default = $Sprite.texture
# make the hurt texture the same name and path as the default texture
# but replace .png with _hurt.png
texture_hurt = load($Sprite.texture.get_path().replace(".png", "_hurt.png"))
func movement_loop():
var motion
# if you aren't in hitstun then move normally
# otherwise get knocked back
if hitstun == 0:
motion = movedir.normalized() * SPEED
else:
motion = knockdir.normalized() * 125
# move_and_slide takes care of collisions and has you slide
# along walls that are blocking your path
move_and_slide(motion, Vector2.ZERO)
func spritedir_loop():
match movedir:
Vector2.LEFT:
spritedir = "left"
Vector2.RIGHT:
spritedir = "right"
Vector2.UP:
spritedir = "up"
Vector2.DOWN:
spritedir = "down"
# This changes our player animation. "animation" is a string
# of the sort "idle", "push", or "walk"
func anim_switch(animation):
var newanim = str(animation, spritedir)
if $anim.current_animation != newanim:
$anim.play(newanim)
func damage_loop():
# If you're in hitstun countdown the timer
health = min(health, MAXHEALTH)
if hitstun > 0:
hitstun -= 1
$Sprite.texture = texture_hurt
else:
$Sprite.texture = texture_default
# if the enemy should be dead
if TYPE == "ENEMY" && health <= 0:
# create the death animation, put it where the enemy was and destroy the enemy
var drop = randi() % 4
if drop == 0:
instance_scene(preload("res://pickups/heart.tscn"))
instance_scene(preload("res://enemies/enemy_death.tscn"))
queue_free()
# for any area that is overlapping the entity's hitbox
for area in $hitbox.get_overlapping_areas():
# Body is the area's parent - a weapon or an entity
var body = area.get_parent()
# if the entity isn't already hit, and the body gives damage,
# and the body is a different type that the entity
if hitstun == 0 and body.get("DAMAGE") != null and body.get("TYPE") != TYPE:
# decrease health by the body's damage
health -= body.get("DAMAGE")
# Set the hitstun timer
hitstun = 10
# set knockdir to the opposite of the entity approached
# the body from
knockdir = global_transform.origin - body.global_transform.origin
# Accepts an actual item scene not the name of the scene
func use_item(item):
# create an instance of the item
var newitem = item.instance()
# add it to the group with item name and the id of its parent
newitem.add_to_group(str(newitem.get_name(), self))
# make it a child of the entity
add_child(newitem)
# if there are already too many items of that type on the screen, delete it
if get_tree().get_nodes_in_group(str(newitem.get_name(), self)).size() > newitem.maxamount:
newitem.queue_free()
func instance_scene(scene):
var new_scene = scene.instance()
new_scene.global_position = global_position
get_parent().add_child(new_scene)