how to make kinematicbody2d jump along circular arc?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By amaou310

I am trying to make enemy like a slime which jump towards direction in 2d platformer.
following code is a current enemy script.
I want enemy to jump once in every 3 seconds, so in func _on_Timer_timeout(), I put enemy_state = state.JUMP to make it jump along circula arc.
However, it goes upward continuously. any solution?

extends KinematicBody2D

var speed = 0
var gravity = 800
var jump_speed = 100
var jump_height = 350
var direction = 1
var velocity = Vector2.ZERO


enum state {IDLE, JUMP}

var enemy_state = state.IDLE

func _ready():
	enemy_state = state.IDLE
	$Jump_Timer.start()
	pass

func _physics_process(delta):
	
	match enemy_state:
		state.IDLE:
			if velocity.x>0:
				$Position2D.scale.x=1
			elif velocity.x<0:
				$Position2D.scale.x=-1
			
			velocity.y += gravity * delta
			velocity.x = speed * direction
			velocity = move_and_slide(velocity, Vector2.UP)
			
			if is_on_wall() or not $Position2D/FloorRay.is_colliding() and is_on_floor():
				direction = direction * -1


		state.JUMP:
			if velocity.x>0:
				$Position2D.scale.x=1
			elif velocity.x<0:
				$Position2D.scale.x=-1	
	
			velocity.x = jump_speed * direction
			velocity.y = -jump_height
			velocity.y += gravity * delta
			velocity = move_and_slide(velocity, Vector2.UP)
			if is_on_wall() or not $Position2D/FloorRay.is_colliding() and is_on_floor():
				direction = direction * -1

func _on_Timer_timeout():
	#velocity.y = -jump_height
	#velocity.x = jump_speed * direction
	#velocity = move_and_slide(velocity, Vector2.UP)
	enemy_state = state.JUMP
	print("Do Jump")
	$Jump_Timer.start()
:bust_in_silhouette: Reply From: zhyrin

Your enemy starts in IDLE state, but once it’s in JUMP state, there is nothing setting it back to being IDLE.

If you are alright with parabolic jump arcs, here is an excellent talk about it: https://www.youtube.com/watch?v=hG9SzQxaCm8
I’ve implemented this in my games, you can use this if you like:
https://github.com/zhyrin0/through-the-looking-glass/blob/main/src/character/character_base.gd ← defines the parameters of the jump
https://github.com/zhyrin0/through-the-looking-glass/blob/main/src/character/player/player.gd ← implements jump physics.

Another cheeky way you could do it is have a Path2D with a circular path, and when the enemy jumps, a PathFollow2D goes along the path and you translate your enemy based on that.

Hi zhrin. Thank you for great sources for jump!
the following code I have made works complete fine, as the jump is only triggered when the key is pressed. but enemy doesnt press the key, and i have no idea how to make it jump once in every three seconds.

extends KinematicBody2D
var speed = 50
var gravity = 800
var direction = 1

var move_speed = 50
export var jump_height : float
export var jump_time_to_peak : float
export var jump_time_to_descent : float

onready var jump_velocity : float = ((2.0 * jump_height) / jump_time_to_peak) * -1.0
onready var jump_gravity : float = ((-2.0 * jump_height) / (jump_time_to_peak * jump_time_to_peak)) * -1.0
onready var fall_gravity : float = ((-2.0 * jump_height) / (jump_time_to_descent * jump_time_to_descent)) * -1.0

var velocity = Vector2.ZERO

func get_gravity() -> float:
	return jump_gravity if velocity.y < 0.0 else fall_gravity

func _physics_process(delta):
	if velocity.x>0:
		$Position2D.scale.x=1
	elif velocity.x<0:
		$Position2D.scale.x=-1
	
	velocity.y += get_gravity() * delta
	velocity.x = speed * direction
	velocity = move_and_slide(velocity, Vector2.UP)
	
	if is_on_wall() or not $Position2D/FloorRay.is_colliding() and is_on_floor():
		direction = direction * -1
	
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = jump_velocity

amaou310 | 2023-02-11 04:52

Since your enemy won’t be listening to input events from the player, the last two lines that are responsible for starting a jump should be refactored.
Try adding a Timer node as a child, and you should run the jump logic when it emits the timeout signal.
You’ll still need to check if the monster is_on_floor(), if so, you can set the vertical velocity to the jump velocity.

zhyrin | 2023-02-11 10:18

thanks for the further advice.
Yes it did work!

func _ready():
	#enemy_state = state.IDLE
	$Jump_Timer.start()
	pass

func get_gravity() -> float:
	return jump_gravity if velocity.y < 0.0 else fall_gravity

func _physics_process(delta):
	if velocity.x>0:
		$Position2D.scale.x=1
	elif velocity.x<0:
		$Position2D.scale.x=-1
	
	velocity.y += get_gravity() * delta
	velocity.x = speed * direction
	velocity = move_and_slide(velocity, Vector2.UP)
	
	if is_on_wall() or not $Position2D/FloorRay.is_colliding() and is_on_floor():
		direction = direction * -1

 func _on_Timer_timeout():
  	if is_on_floor():
    	velocity.y = jump_velocity
    	print("Do Jump")
    $Jump_Timer.start()

I wanted make it move only while jumping, therefore, I changed speed variable to zero, and added velocity.x = jump_speed * direction. but it does not jump forward as I expected.

   func _on_Timer_timeout():
      	if is_on_floor():
        	velocity.y = jump_velocity
        	velocity.x = jump_speed * direction
        	print("Do Jump")
        $Jump_Timer.start()

amaou310 | 2023-02-11 12:36

:bust_in_silhouette: Reply From: exuin

It doesn’t look like you ever set the state back to idle?