DeruDeru
DeruDeru

Reputation: 75

【godot 2d】jumping enemy jumps opposite direction of where player is at

I am in the process of developing a 2D enemy that emulates the behavior of a slime, specifically jumping towards the player. I have been able to successfully implement this feature. However, upon further testing, I have observed that while the first jump executes in accordance with the intended behavior, subsequent jumps inexplicably occur in a direction opposite to the player's location. I would appreciate if you could help me solve this problem.

enter image description here

Jump Code here:

if is_on_floor():
        var player_pos = get_node("../Player").get_global_position()
        var self_pos = get_global_position()        
        var jump_distance = (player_pos - self_pos).normalized()
        var distance_to_player = player_pos.distance_to(self_pos)
        
        var horizontal_speed = min_speed + (distance_to_player / max_distance) * (max_speed - min_speed)
        horizontal_speed = min(max_speed, max(min_speed, horizontal_speed))
        
        velocity = jump_distance 
        velocity.x *= horizontal_speed * direction
        velocity.y = -jumpmove_height #-jump_height
    
        velocity = move_and_slide(velocity, Vector2.UP)
        
        $Position2D/SlimeSkin/AnimationPlayer.play("SlimePrototype Attack")
        $Jump_Cooldown.start()
    print("Do Jump")

All Code here: extends KinematicBody2D

var speed = 20

var min_speed = 0 #min_speed for jump horizontal speed
var max_speed = 250 #max_speed for jump horizontal speed
var max_distance = 300 #max distance for jump distance

var direction = 1

export var jumpmove_height : float
export var jump_time_to_peak : float
export var jump_time_to_descent : float

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

var velocity = Vector2.ZERO
var is_idle = true

func _ready():
    is_idle = true
    $Idle_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
    
    if is_on_wall() or not $Position2D/FloorRay.is_colliding() and is_on_floor():
        direction = direction * -1
    
    velocity.y += get_gravity() * delta
    if is_on_floor():
        velocity.x = 0
    velocity = move_and_slide(velocity, Vector2.UP)

    if is_idle:
        velocity.x = speed * direction
        if is_on_wall() or not $Position2D/FloorRay.is_colliding() and is_on_floor():
            direction = direction * -1
        velocity = move_and_slide(velocity, Vector2.UP)
        $Position2D/SlimeSkin/AnimationPlayer.play("SlimePrototype Idle")

func _on_Timer_timeout():
    if is_on_floor():
        var player_pos = get_node("../Player").get_global_position()
        var self_pos = get_global_position()        
        var jump_distance = (player_pos - self_pos).normalized()
        var distance_to_player = player_pos.distance_to(self_pos)
        
        var horizontal_speed = min_speed + (distance_to_player / max_distance) * (max_speed - min_speed)
        horizontal_speed = min(max_speed, max(min_speed, horizontal_speed))
        
        velocity = jump_distance 
        velocity.x *= horizontal_speed * direction
        velocity.y = -jumpmove_height #-jump_height
    
        velocity = move_and_slide(velocity, Vector2.UP)
        
        $Position2D/SlimeSkin/AnimationPlayer.play("SlimePrototype Attack")
        $Jump_Cooldown.start()
    print("Do Jump")

func _on_Idle_Timer_timeout():
    is_idle = false
    $Timer.start()
    $Position2D/SlimeSkin/AnimationPlayer.play("SlimePrototype Charge")
    print("IDLE TIMER TIMEOUT")
    pass # Replace with function body.

func _on_Jump_Cooldown_timeout():
    yield(get_tree().create_timer(0.5), "timeout")
    is_idle = true
    pass # Replace with function body.

Upvotes: 3

Views: 300

Answers (1)

Theraot
Theraot

Reputation: 40295

The issue is direction. You are encoding twice the direction of the jump. Once in jump_distance (which, as I note below is actually a direction) and the other in direction. And you multiply them… So we have four cases:

+-------------------+--------------------------+------------------+
| moving direction  | direction to player      | result           |
+-------------------+--------------------------+------------------+
| (-1) moving left  | (-1) player to the left  | (+1) jumps right |
| (-1) moving left  | (+1) player to the right | (-1) jumps left  |
| (+1) moving right | (-1) player to the left  | (-1) jumps left  |
| (+1) moving right | (+1) player to the right | (+1) jumps right |
+-------------------+--------------------------+------------------+

So it only jumps in the direction to the player when it was moving to the right to begin with.

Use only one of the two directions. Either make it jump in in the direction it is moving (direction) or in the direction to the player.


Code review

I'm going to go over the jump code, and write what I notice, in the order I read it. Ok?


First:

var jump_distance = (player_pos - self_pos).normalized()

That jump_distance is not a distance. It is a unit vector (it is normalized) in the direction to the player. You can also write it like this:

var jump_distance = self_pos.direction_to(player_pos)

In fact, direction_to_player or jump_direction would be better names. However, given that you only use it here:

velocity = jump_distance

You can probably get rid of the variable:

velocity = self_pos.direction_to(player_pos)

Second:

var horizontal_speed = min_speed + (distance_to_player / max_distance) * (max_speed - min_speed)

That is a lerp. We can write it as such:

var horizontal_speed = lerp(min_speed, max_speed, distance_to_player / max_distance)

Third:

horizontal_speed = min(max_speed, max(min_speed, horizontal_speed))

That is a clamp. We can write it as such:

horizontal_speed = clamp(horizontal_speed, min_speed, max_speed)

Fourth:

velocity = self_pos.direction_to(player_pos) # this was jump_distance
velocity.x *= horizontal_speed * direction

This is the problem. You had already set the direction to the player in the first line. And then you multiply by direction in the second, which could flip the direction away from the player.

If you want to always jump in the direction it is moving, then do this:

velocity.x = horizontal_speed * direction

Alternatively, if you want it to be in the direction of the player, then do this:

velocity.x = sign(self_pos.direction_to(player_pos).x) * horizontal_speed

The reason why I'm taking the sign here, is because the direction also has a vertical component (it is a vector in the unit circle, its x component will only be 1 or -1 when the y component is 0).

Of course, you can make it move towards the player:

direction = sign(self_pos.direction_to(player_pos).x)
velocity.x = horizontal_speed * direction

Upvotes: 3

Related Questions