Enemy ai knockback problem

Godot Version

4.6.3

Question

How would I apply knockback opposite to the enemy when they’re hit

enemy controller (where the signal is connected to) :

func _on_hurtbox_take_knockback(_knockback_value: float) -> void:
	# code here

hitbox:

#monitorable

@icon("uid://d3u8s752js56h")
class_name Hitbox extends Area2D

@export var damage : int = 1
@export var knockback_force : float = 100.0

func _ready() -> void:
	pass

and hurtbox:

#monitoring

@icon("uid://6dseo6gly164")
class_name Hurtbox extends Area2D

signal take_knockback(knockback_value : float)

@export var health: HealthComponent
var hitbox: Hitbox

func _ready() -> void:
	area_entered.connect(_on_area_entered)
	area_exited.connect(_on_area_exited)
	set_physics_process(false)

func _physics_process(_delta: float) -> void:
	if health.can_be_damaged:
		health.damage(hitbox.damage)
	
	if health.has_been_damaged:
		take_knockback.emit(hitbox.knockback_force)

func _on_area_entered(area: Area2D) -> void:
	hitbox = area
	set_physics_process(true)

func _on_area_exited(_area: Area2D) -> void:
	set_physics_process(false)
	hitbox = null

Solution is partially in the question.

You want the knockback to be opposite to the enemy, but the hurt box checks only on health change.

You need a direction (Vector3) or, as alternative, the entity that caused the knockback.

Without changing a lot, you can pass a Vector3 in health.damage, this will represent force with direction. Or, in alternative, two parameters: one for the strength (as value) and one for an eventually optional direction. I personally don’t like this choice.

If you’re considering in adding complexity, you can also create a RefCounted HitContext, where you can put anything you want, including utility functions. This is the solution I’d personally choose, easily scalable.

About knocking direction, that’s:

(target.global_position - enemy.global_position).normalized().

Before computing it, check if target.global_position == enemy.global_position to avoid null vectors. This cause a problem because you hit with some non zero strength, but direction is undefined. There are quite different ways to manage this. Since it should be a very rare event, a simple specific flag like direction_undefined should be sufficient.

I’d also suggest to add a bit of vertical component for collision convenience.

Also, knockback influences target’s velocity. You surely want to decay it to avoid infinite knockback.

can you explain in a 2d setting (where the rest of the code is focused on) and with code visuals because a wall of directional text does nothing for me and my brain

I do this for knockback in a 2D project:

# Calculate the amount on hit
var knockback = (body.global_position - global_position).normalized()
knockback *= 10000

# Apply it
if knockback != Vector2.ZERO:
		velocity = knockback
		move_and_slide()

Important to note though that this is knockback for a player. Their velocity gets reset almost immediately, so I don’t have to worry about the knockback effect going on until it is stopped.

Do you want to give the knockback in the opposite direction of where the tackle is coming from? Like to the upper left if from the bottom right? Or always just left or right?

Do you want it to be instant or over a period of time?

Do you have a state machine for your enemy?