Knockback Issues

Godot Version

Godot 4

Question

Ask your question here! Try to give as many details as possible.

If you share code, please wrap it inside three backticks or replace the code in the next block:

extends CharacterBody2D

class_name PlayerController


@onready var animation_player: AnimationPlayer = $Animation_Controls/AnimationPlayer
@onready var currenthealth: int = max_health

# Movement Variables
@export var speed : float = 300.0
@export var jump_force : float = -250.0
@export var jump_time : float =  0.25
@export var coyote_time : float = 0.075
@export var gravity_multiplier : float = 3.0
@export var max_health = 3

 (@export var Min_Knockback := 100
@export var Slow_Knockback := 1.3) ------------------> This is knockback

var direction = 0
var is_jumping : bool = false
var jump_timer : float = 0
var coyote_timer : float = 0
var can_control : bool = true

var Knockback : Vector2 = Vector2.ZERO

func _physics_process(delta: float) -> void:
	
	var direction2 = global_position - Knockback
	
	(if Knockback.length() > Min_Knockback:
		Knockback /= Slow_Knockback
		velocity = direction2.normalized() * Knockback
		move_and_slide()
		return) -----------------------------------------------------------------------> This is the issues in this whole line line
	
	if not can_control: return
	# Add the gravity.
	if not is_on_floor() and not is_jumping:
		velocity += get_gravity() * gravity_multiplier * delta
		coyote_timer += delta
	else:
		coyote_timer = 0



	# Handle jump.
	if Input.is_action_just_pressed("Jump") and (is_on_floor() or coyote_timer < coyote_time):
		velocity.y = jump_force 
		is_jumping = true
	elif  Input.is_action_pressed("Jump") and is_jumping:
		velocity.y = jump_force

	if is_jumping and Input.is_action_pressed("Jump") and jump_timer < jump_time:
		jump_timer += delta
	else:
		is_jumping = false
		jump_timer = 0


	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	direction = Input.get_axis("Left", "Right")
	if direction:
		velocity.x = direction * speed 
	else:
		velocity.x = move_toward(velocity.x, 0, speed)

	move_and_slide()


func handle_danger() -> void:
	currenthealth -= 1
	if currenthealth == 0:
		print("You Died")
		visible = false
		can_control = false

		await get_tree().create_timer(1).timeout
		reset_player()


func reset_player() -> void:
	currenthealth = 3
	global_position = Vector2(0,-78.0)
	visible = true
	can_control = true




i also have a seperate scene with an area 2d that i transfer for knockback i drag and drop on entities

[ extends Area2D

@export var Knockback := 1000

func _on_body_entered(body: Node2D) -> void:
	if body is PlayerController:
		body.Knockback = (body.position - position).normalized() * Knockback
		print("KIK")
]







The problem is he always move to the right or left if i negate knockback how do i fix that

This will always instantly reset the player’s speed, the else hints towards how to fix it but the template is flawed

This moves velocity towards zero by speed each frame, though it should be per-second.

If you want to simplify this using the same acceleration value you can remove the if/else, create an acceleration variable 8x the player’s speed should feel pretty snappy

direction = Input.get_axis("Left", "Right")
velocity.x = move_toward(velocity.x, direction * speed, acceleration * delta)

you’re asking me to make a new acceleration value just to be sure ?

You would need one to implement the sample I posted

1 Like

ok so how would i fix the direction that i knockback in i fixed the speed

The direction of your knockback seems influenced by repeating equations, these do similar things to Knockback but their effect stacks; you probably mean to remove direction2 and just use Knockback in your physics_process.

Or with your new acceleration based movement you can avoid the Knockback variables entirely by editing the player’s velocity directly on impact

func _on_body_entered(body: Node2D) -> void:
	if body is PlayerController:
		var direction := position.direction_to(body.position)
		body.velocity = direction * Knockback

This is the improvised player code he still only goes 1 direction

extends CharacterBody2D

class_name PlayerController

@onready var animation_player: AnimationPlayer = $Animation_Controls/AnimationPlayer
@onready var currenthealth: int = max_health

# Movement Variables
@export var acceleration := 2400.0
@export var speed : float = 300.0
@export var jump_force : float = -250.0
@export var jump_time : float =  0.25
@export var coyote_time : float = 0.075
@export var gravity_multiplier : float = 3.0
@export var max_health = 3
@export var Min_Knockback := 100
@export var Slow_Knockback := 1.3

var direction = 0
var is_jumping : bool = false
var jump_timer : float = 0
var coyote_timer : float = 0
var can_control : bool = true
var Knockback : Vector2 = Vector2.ZERO

func _physics_process(delta: float) -> void:
	
	
	if Knockback.length() > Min_Knockback:
		Knockback /= Slow_Knockback
		velocity = direction * Knockback
		move_and_slide()
		return
	
	if not can_control: return
	# Add the gravity.
	if not is_on_floor() and not is_jumping:
		velocity += get_gravity() * gravity_multiplier * delta
		coyote_timer += delta
	else:
		coyote_timer = 0



	# Handle jump.
	if Input.is_action_just_pressed("Jump") and (is_on_floor() or coyote_timer < coyote_time):
		velocity.y = jump_force 
		is_jumping = true
	elif  Input.is_action_pressed("Jump") and is_jumping:
		velocity.y = jump_force

	if is_jumping and Input.is_action_pressed("Jump") and jump_timer < jump_time:
		jump_timer += delta
	else:
		is_jumping = false
		jump_timer = 0


	# Get the input direction and handle the movement/deceleration.
	# As good practice, you should replace UI actions with custom gameplay actions.
	direction = Input.get_axis("Left", "Right")
	velocity.x = move_toward(velocity.x, direction * speed, acceleration * delta)

	move_and_slide()


func handle_danger() -> void:
	currenthealth -= 1
	if currenthealth == 0:
		print("You Died")
		visible = false
		can_control = false

		await get_tree().create_timer(1).timeout
		reset_player()


func reset_player() -> void:
	currenthealth = 3
	global_position = Vector2(0,-78.0)
	visible = true
	can_control = true

Here is the improvised Knockback area code still doesn’t work i tried some other things it didn’t work

extends Area2D

@export var Knockback := 1000

func _on_body_entered(body: Node2D) -> void:
	if body is PlayerController:
		var direction := position.direction_to(body.position)
		body.velocity = direction * Knockback
		print("KIK")

This is overriding the acceleration based movement, should be removed too.

If it still calculates the wrong direction then you should try using global_position instead of position, are the enemies and players siblings in the scene tree? Or do enemies end up children of a different node?

func _on_body_entered(body: Node2D) -> void:
	if body is PlayerController:
		var direction := global_position.direction_to(body.global_position)
		body.velocity = direction * Knockback

Thank you !