Can't apply a 'knockback' effect in x direction

Godot Version

4.5

Question

Hi all. I'm having an odd problem with my code: I cannot get the combatant to move in the x direction under a knockback effect.

I say x direction, because I tried adding a small amount to y, and it DID move in the y. I know eventually I”ll need to figure out the proper knockback math (right now its pretty simple) but I want to make sure the combatant can move in the x direction first.

combatant = character that can enter combat. in this case, the player and the enemies.

the knockback function itself is:

func knocked_back(antagonist):
	self.can_move = false
	if antagonist.global_position.x > self.global_position.x:
		#we are to the left of them
		#knockback further left. 
		velocity.x -= knocked_back_amount
		#velocity.y += 5
	elif antagonist.global_position.x < self.global_position.x: 
		#we are to teh right of them 
		#knockback further right
		velocity.x += knocked_back_amount
	#	velocity.y += 5
	await get_tree().create_timer(.2).timeout #magic number
	self.can_move = true

and the whole combatant function is:

extends CharacterBody3D

var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
@export var max_health = 100.0
@export var current_health = max_health
#weir dt hing whre when we export the max health to a different (lower?) number, 
#it keeps using 100 for current health?
@export var max_mana = 100.0
@export var current_mana = max_mana
@export var max_stamina = 100.0 #stamina may be only used for hte player but
#dont watn to have to go back and add it here if i DO use it for an enemy :P
@export var current_stamina = max_stamina
@export var mana_regen_amount = 5.0
@export var mana_regen_rate = 2.5 #in seconds
@export var health_regen_amount = 2.0
@export var health_regen_rate = 5.0 #in seconds
var mana_timer = Timer.new()
var health_timer = Timer.new()
#add helaht regen and stamina regen soon.
enum teams{FRIENDLY, ENEMY}
@export var current_team = teams.FRIENDLY
@export var speed = 5.0
var dir = 1 #1 is right, -1 is left. set in hcild's script - they dont move in combatant
enum damage_types{MAGIC, PHYSICAL}
var current_damage_type = damage_types.PHYSICAL #set it to a default
@export var mag_resist = 0
@export var phys_resist = 0
@export var knocked_back_amount = 400 #guessed a number
var can_move = true
var can_attack = true
var is_attacking = false
var is_fighting = false
#maybe a timer for how lon til we are done 'fighting\'?
var in_combat_timer = Timer.new()
var combat_time = 1 #in seconds
#signals
signal health_changed
signal mana_changed
signal stamina_changed

func _ready():
	add_child(mana_timer)
	add_child(health_timer)
	health_timer.start(health_regen_rate)
	health_timer.timeout.connect(_on_health_timer_timeout)
	mana_timer.start(mana_regen_rate)
	mana_timer.timeout.connect(_on_mana_timer_timeout)
	add_child(in_combat_timer)
	#we dont need to start it  untilw e enter combat!!!!
	health_timer.timeout.connect(_on_combat_timer_timeout)

func _physics_process(delta: float) -> void:
	velocity.y += -gravity * delta
	var current_x_pos = self.global_position.x  #this works for gettin current positoin for x.
	var current_y_pos = self.global_position.y #ditto but y
	pass_move_args(current_x_pos, current_y_pos, delta)
	#hym could THIS be messin with our knockback effect????
	#but t hen y does it still wokr in y dir
	#nvm putting it on can move didnt' work to fix our knockbac :C

func pass_move_args(current_x, current_y, delta):
	#so this code just copies over our current poses, besides z
	global_position = global_position.move_toward(Vector3(current_x, current_y, 0.0), delta * speed)
	#AND player can still fly off cliffs forward/ back ward wen colliding iwth antoher 
	#object mid aid

func take_damage(damage_type, damage):
	#print ("Damage taken")
	is_fighting = true
	if current_health > 0:
		if damage_type == damage_types.MAGIC:
			if damage > mag_resist: # IF we CAN get past magic resistance....
				current_health = current_health - (damage - mag_resist)
		elif damage_type == damage_types.PHYSICAL:
			if damage > phys_resist:
				current_health = current_health - (damage - phys_resist)
		else: 
			print ("Invalid damage type") #unless i add 'spiritual' magic to it
	elif current_health <= 0: 
		print ("so and so died") 
		on_death()
	health_changed.emit()

#add healht and stamina regens later
func mana_regen():
	if current_mana < max_mana:
		current_mana += mana_regen_amount
		mana_changed.emit()
func health_regen():
	#okay the values here are beign read WRONG
	#current as 102 and max as 999
	if current_health < max_health:
		current_health += health_regen_amount
		health_changed.emit()

#we need to kncok back hwen we take damage so we are not always on top of each other
func knocked_back(antagonist):
	self.can_move = false
	if antagonist.global_position.x > self.global_position.x:
		#we are to the left of them
		#knockback further left. 
		velocity.x -= knocked_back_amount
		#velocity.y += 5
	elif antagonist.global_position.x < self.global_position.x: 
		#we are to teh right of them 
		#knockback further right
		velocity.x += knocked_back_amount
	#	velocity.y += 5
	await get_tree().create_timer(.2).timeout #magic number
	self.can_move = true

func _on_mana_timer_timeout():
#	if current_mana < max_mana: #this is readin as 100 even though its not :C (current mana)
	mana_regen()
	mana_timer.start()
	
func _on_health_timer_timeout():
	health_regen()
	health_timer.start()
	
func _on_combat_timer_timeout():
	is_fighting = false

func on_death():
	is_fighting = false
	#we need to stop the character from acting

there are also two scripts that both inherit from the combatant script. unsure if those are relevant or not, they each have the exact same bug.

ty for anyone who can help, or that took the time to read t his

If you take the vector difference between the two and normalize it, that gives you the direction of knockback in both x and y:

func knocked_back(antagonist: Node, power: float) -> void:
    self.can_move = false
    # Raw vector between self & antagonist.
    var kbvec: Vector2 = self.global_position - antagonist.global_position
    # Normalized vector (length 1).
    var kbnorm: Vector2 = kbvec.normalized()
    # Scaled by power.
    var knockback = kbnorm * power
    velocity += knockback

I’d advise against the await timer thing; it’ll do unexpected things if you take a second hit while already in knockback. My advice would be make can_move into a stunned float var, and do something like:

var stunned:  float = 0.0
var can_move: bool  = true

func knocked_back(...):
    stunned = power * 2 # Or something...
    can_move = false

func _process(delta: float) -> void:
    # [...]
    if stunned > 0.0:
        stunned -= delta
    else:
        can_move = true
    # [...]

oh a stunned variable is a good idea.

i’ll take your changes. Its possible it still won’t wokr - idk if i have osmeting else messing with my velocity in x that i missed. But your hcnages seem sensible .

ill let you know later if it works :3 TY

hi sorry for the late reply, holidays are making it busy atm ^^;

i did implement it, it seemed like good solution, but i think i must have something else overriding the movement or something, because the character still does not move when knocked back.

Do you want to see my other code (maybe in a few days or whenever you have time :stuck_out_tongue: )
I’ll keep trying to debug in the meanwhile

1 Like

Hi there,

Looking through your code I see you are changing velocity for both gravity and the knockback, but are never actually using it. You don’t use it yourself or call move_and_slide(), so these changes are doing nothing.

What I can’t figure out is how you can move at all. The movement code you do have is just setting global_position to the result of calling move_toward() on it, with a first parameter that is the same as global_position (but with z zeroed). So you should be stuck in place permanently (assuming z was zero to begin with, if not the movement will be strictly ‘to the ground’, x and y should not change).

1 Like

thank you for your time and help.

I use “move and slide” in th player’s script, which inherits from the combatant script
HOWEVER, I only used it when the player CAN move, which might be the problem, since it still needs to move with move and slide when the player can’t control themself, is that what you are saying?

I’ll investigate this.t y for the reply

Yep. You are use velocity for the knockback, so you need move_and_slide(), to stop the character from moving under user input, you want to check can_move wherever you are adding that into velocity and not do so if true.

1 Like

thanks! taht fixed it :smiley: