Need help with knockback direction

Creating knockback for my character when they get hit by an enemy although it seems to always register enemy position as Vector2(0,0).

Knockback direction is a Vector2 that’s defined by:

if (character.position.x - enemy_position.x > 0):

Knockback currently only works in one direction since player position is correct but enemy position is always 0
(print() is just for debugging)

How would you properly call enemy position?

full script:

@export var enemy_position: Vector2 
#-------------------------------------------
func _on_knockback_collision_body_entered(body):
	if (body.is_in_group("enemy")):
		enemy_position = body.global_position

func knockback(force: float, x_pos: float, up_force: float):
	if (character.position.x - enemy_position.x > 0):
		character.velocity = Vector2(-force * 2 * x_pos, -force * up_force)
		print(enemy_position.x)
		print(character.global_position.x)
	else: 
		character.velocity = Vector2(force * 2 * x_pos, -force * up_force)

This should be character.global_position.x

fair, but character position already works. Enemy_position.x is always 0
Do you know how to get enemy position because that’s what seems to mess up the knockback

I think you are doing enemy position right. But if you haven’t collided that vector2 will initialize to zero before the collision.

What is this initial value?

Also when is knock back called? I think it should happen during the collision detection. But I don’t see it there.

I’m using a finite state machine so knockback is called whenever the player is hurt e.g whenever the player hits the enemy

Full script:

class_name hurtstate
extends Player_State
#-------------------------------------------
@export var fall_state : Player_State
@export var ground_state : Player_State
@export var hurt_animation : String = "Hurt"
@export var run_animation_node : String = "Run"
@export var fall_animation_node : String = "Fall"
@export var enemy_position: Vector2 
@onready var timer: Timer = $Hurt_Timer


#-------------------------------------------
func _on_knockback_collision_body_entered(body):
	if (body.is_in_group("enemy")):
		enemy_position = body.global_position

func knockback(force: float, x_pos: float, up_force: float):
	if (character.position.x - enemy_position.x > 0):
		character.velocity = Vector2(-force * 2 * x_pos, -force * up_force)
		print(enemy_position.x)
		print(character.global_position.x)
	else: 
		character.velocity = Vector2(force * 2 * x_pos, -force * up_force)


func on_enter():
	timer.start()
	playback.travel(hurt_animation)
	character.startslowmo()
	knockback(1,1500,300)

func _on_hurt_timer_timeout():
	Game.hurt = false
	character.endslowmo()
	if (character.is_on_floor()):
		character.velocity = Vector2(0, 0)
		playback.travel(run_animation_node)
		next_state = ground_state
	elif(character.is_on_floor() == false):
		character.velocity = Vector2(0, 0)
		playback.travel(fall_animation_node)
		next_state = fall_state

Where @export var fall_state : Player_State is manually set

func on_enter(): is when the player enters the state (when they lose health)

I think the initial value is just (0,0), but it should be the enemy_position

I’m still relatively new to godot what do you by “colliding a vector2 so that it doesn’t initialize to zero before the collision.”?

Oh, I think there could be some issues with collision detection if you instantiated a collision body dynamically. My bet is _on_knockback_collision_body_entered is never being called in time. It may need to run a frame or two before the collision is called back.

I would probably pass the collision point or enemy position into the hurt state rather then collecting the information again.

Or you could query the collision manually. This is for area2d

For character body
KinematicCollision2D get_last_slide_collision ( )

For rigid body
Node2D get_colliding_bodies ( )

Btw godot AnimationtTree has a built in state machine.

I’m still pretty new to godot should i just write func get_overlapping_bodies() and insert my code?

Yes, at least give it a try. It should be one or two lines because it will give you an array and you need to get to the body in the data structure.

var bodies = $KnockBackArea.get_overlapping_bodies()
for body in bodies:
  if (body.is_in_group("enemy")):
		enemy_position = body.global_position
       break

The result was the same as before. Knockback worked in 1 direction and enemy x position still returns 0

Did you do something like this in on_enter

var bodies = $KnockBackArea.get_overlapping_bodies()
for body in bodies:
  if (body.is_in_group("enemy")):
		enemy_position = body.global_position
        break
breakpoint

And did you update the character.global_position.x ?

It’s this here

1 Like

It still returned the same

I have this code now:

func get_overlapping_bodies(body):
	if (body.is_in_group("enemy")):
		enemy_position = body.global_position

#-------------------------------------------


func knockback(force: float, x_pos: float, up_force: float):
	if (character.position.x - enemy_position.x > 0):
		character.velocity = Vector2(-force * 2 * x_pos, -force * up_force)
		print(enemy_position.x)
		print(character.global_position.x)
	else: 
		character.velocity = Vector2(force * 2 * x_pos, -force * up_force)

func on_enter():
	var bodies = $"../../knockback_collision".get_overlapping_bodies()
	for body in bodies:
		if (body.is_in_group("enemy")):
			enemy_position = body.global_position

	timer.start()
	playback.travel(hurt_animation)
	character.startslowmo()
	knockback(1,1500,300)

func get_overlapping_bodies(body): needed to insert body into this function to remove red warnings

? What is this for ?

Anyway you should add a breakpoint after the for loop and inspect the stack to see what’s in the array at that time.

Also this is still using local position use global_position on the character

After messing around with it i found that i had to delete some other code that was interfering with it
Thank you so much for the help :slight_smile:

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