Godot Lesson 4: Knockback¶
Summary¶
This lesson creates knockback forcing the entity away from the attacker when hit. Entities also do not get damage while being knocked back.
Prerequisites¶
An entity class with one enemy (Lessons 1, 2, 3)
Video¶
Code¶
Repository: https://github.com/los-alamos-steam-lab/godot-tutorial/tree/4-knockback
Entity Code¶
Major Changes from the Video:
Declared DAMAGE because otherwise the references in entity fail.
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"
# have to declare damage here so we can set it in the child scripts
var DAMAGE = null
# MOVEMENT
var movedir = Vector2.ZERO
var knockdir = Vector2.ZERO
var spritedir = "down"
var hitstun = 0
var health = 1
# Putting this here so that we can setup future calls from the
# child scripts and not have them fail
func _ready():
return
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() * SPEED * 1.5
# 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
if hitstun > 0:
hitstun -= 1
# for any body that is overlapping the entity's hitbox
for body in $hitbox.get_overlapping_bodies():
# 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 = transform.origin - body.transform.origin
Player Code¶
Major Changes from the Video:
The _ready func is used to set TYPE and other “constants”
extends entity
# ready function lets us set "constants" when the file loads
func _ready():
SPEED = 70
TYPE = "PLAYER"
# _physics_process is called by the game engine
func _physics_process(delta):
controls_loop()
movement_loop()
spritedir_loop()
damage_loop()
# We're setting our animation here. I've replaced Vector2(0,-1)
# with Vector2.UP for readability, and so forth. These are new to godot 3.1
# I've also changed the order of the if statement to prioritize being
# idle if movedir is zero and created a single (very long) if statement
# for testing the push animation.
if movedir == Vector2.ZERO:
anim_switch("idle")
elif is_on_wall():
if (spritedir == "left" and test_move(transform, Vector2.LEFT))\
or (spritedir == "right" and test_move(transform, Vector2.RIGHT))\
or (spritedir == "up" and test_move(transform, Vector2.UP))\
or (spritedir == "down" and test_move(transform, Vector2.DOWN)):
anim_switch("push")
else:
anim_switch("walk")
# controls_loop looks for player input
func controls_loop():
var LEFT = Input.is_action_pressed("ui_left")
var RIGHT = Input.is_action_pressed("ui_right")
var UP = Input.is_action_pressed("ui_up")
var DOWN = Input.is_action_pressed("ui_down")
# By adding our values together, we make it so that one key
# stroke does not take precidence over another, i.e. pushing
# left and right keys at the same time
movedir.x = -int(LEFT) + int(RIGHT)
movedir.y = -int(UP) + int(DOWN)
Stalfos Code¶
Major Changes from the Video:
The _ready func is used to set DAMAGE and other “constants”
extends entity
var movetimer_length = 15
var movetimer = 0
# ready function lets us set "constants" and perform
# other actions when the file loads
func _ready():
SPEED = 40
DAMAGE = 1
$anim.play("default")
movedir = dir.rand()
func _physics_process(delta):
movement_loop()
damage_loop()
# count down the movetimer every tick
if movetimer > 0:
movetimer -= 1
# if the movetime reaches zero or the stalfos is on a wall
# change direction and reset the timer
if movetimer == 0 || is_on_wall():
movedir = dir.rand()
movetimer = movetimer_length