CharacterBody2D knockback not being applied in the correct direction

Godot Version

4.3

Question

I’m creating a game where enemies (characterBody2D) can get hurt and get knocked back by Projectiles (also characterBody2D), I thought I implemented this correctly but then I realized that the knock-back sometimes pushes the enemy in the wrong direction like this :

bandicam 2024-08-24 22-02-40-316

The enemies usually only get knocked to the right, down and everything in between, a projectile could hit them from their right side and should push them to the left but instead pushes them to the right which makes no sense.

Here’s the projectile code :

extends CharacterBody2D
class_name projectile

func _on_area_entered(area: Area2D) -> void: #the bullet enters the enemy's hurtbox
	area.get_parent().take_damage(damage_amount) #damage the enemy
	area.get_parent().knockback(knockback_force, get_velocity()) #pass the strength of the knockback and then the current Velocity of the projectile to the enemy so they can know where to get knocked back to 
	queue_free() 

enemy code :

extends CharacterBody2D
class_name enemy

func _physics_process(delta: float) -> void:
	move_and_slide()

func knockback(knockback_force: int, knockback_source_pos: Vector2): #takes the strength and the source of the knockback
	var knockback_source_pos_corrected = knockback_source_pos * Vector2(-1, -1) # multiplying it by (-1, -1) makes the knockback direction less broken for some reason ?
	var knockback_direction = knockback_source_pos_corrected.direction_to(self.global_position) #this is what calculates where the enemy gets knocked to
	var knockback = knockback_direction * knockback_force #applies the strength
	
	velocity = knockback #what moves the enemy
	await get_tree().create_timer(0.2).timeout 
	velocity = Vector2.ZERO #stops the enemy after 0.2 seconds 

I’ve tried multiplying the knockback_source_pos by (-1, 1) and (1, -1) or just leaving it as is but the knockback direction is never right

please tell me if this whole system is just straight up bad so I could just rewrite it from the ground up instead of patching a bad system, and I’d appreciate any advice on how to do so ! I really wanna learn how to create knockback for my games.

Passing the velocity of the projectile as the source position argument of your knockback function doesn’t seem right. Either way, you can probably simplify your code like this:

@export var knockback_force: float

func _on_area_entered(area: Area2D) -> void:
	area.get_parent().take_damage(damage_amount)
	area.get_parent().knockback(get_velocity().normalized() * knockback_force)
	queue_free() 

By letting the projectile calculate the knockback itself, it can simply be passed to the enemies like this:

func knockback(direction: Vector2) -> void:
	velocity = direction
	await get_tree().create_timer(0.2).timeout 
	velocity = Vector2.ZERO

Note here that direction isn’t just the direction of the knockback. It also uses the length of the vector as its strength. If you prefer the two to be seperate tho, you can change the function to accept the two arguments separately. Really just depends on preference.

I tested the code real quick and there doesn’t seem to be any need for to_local() or to_global() convertions. Let me know if it works for you. :>

Thank you so much you solved it !

This exact solution also popped into my head while I was falling asleep last night XD

#--projectile code

func _on_area_entered(area: Area2D) -> void:
	area.get_parent().take_damage(damage_amount)
	area.get_parent().knockback(get_velocity().normalized() * knockback_force)
	queue_free()
#--enemy code

func knockback(direction: Vector2):
	velocity = direction
	await get_tree().create_timer(0.2).timeout 
	velocity = Vector2.ZERO

no need for no any .direction_to(self.global_position or * (-1, -1)

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.