Reputation: 75
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.
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
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