I'm a rookie making my first 2d game in Godot, which I'm drafting from a much smaller JS,CSS, & HTML version called Storie. I am attempting to create a Point-click-and move function into my game.
All was well in my main sprites scene, but when I tried to use it in my game scene, there was a huge offset frome where I clicked and where the sprite went
extends CharacterBody2D
@export var speed = 400
var target = position
func _input(event):
if event.is_action_pressed("click"):
target = get_global_mouse_position()
func _physics_process(_delta):
velocity = position.direction_to(target) * speed
# look_at(target)
if position.distance_to(target) > 10:
move_and_slide()
Oh, and also, I mistakenly switched to Godot 4 without realising the repercussions
Thank you, Dimitri
There are global coordinates, which are relative to the origin of the world. And there are local coordinates, which are relative to the parent node.
Now, position
is local coordinates, so this is local coordinates:
var target = position
And this get_global_mouse_position()
is global coordinates. Thus, this is global coordinates:
target = get_global_mouse_position()
Therefore, target
is switching coordinates system. Once it has the global coordinates of the mouse position, then this is mixing coordinates system:
velocity = position.direction_to(target) * speed
Thus, the result will result in an incorrect behavior. The same happens here:
position.distance_to(target)
You could convert to local coordinates (e.g. using to_local
), however the velocity
should be based on global coordinates (also look_at
, we will get to that).
Thus, instead of using position
which is in local coordinates, use global_position
which is in global coordinates:
extends CharacterBody2D
@export var speed = 400
var target = global_position
func _input(event):
if event.is_action_pressed("click"):
target = get_global_mouse_position()
func _physics_process(_delta):
velocity = global_position.direction_to(target) * speed
# look_at(target)
if global_position.distance_to(target) > 10:
move_and_slide()
Using look_at
By the way, look_at
also wants global coordinates.
Perhaps you want to move to inside the conditional (as there would be no direction to look at, if you are already at the position):
func _physics_process(_delta):
velocity = global_position.direction_to(target) * speed
if global_position.distance_to(target) > 10:
look_at(target)
move_and_slide()
You can compute how much the character would move.
Do you remember speed
is distance over time?
Well, delta
is the time. If speed = distance / delta
and we solve for distance
we have:
var distance = speed * delta
You could, for example, use in your check:
func _physics_process(delta:float) -> void:
velocity = global_position.direction_to(target) * speed
var distance := speed * delta
if global_position.distance_to(target) > distance:
look_at(target)
move_and_slide()
However, I suggest you figure out which is the maximum speed the character can move this frame without overshooting the target:
func _physics_process(delta:float) -> void:
var distance_to_target := global_position.distance_to(target)
var direction_to_target := global_position.direction_to(target)
var max_speed := distance_to_target / delta
velocity = direction_to_target * clampf(speed, 0.0, max_speed)
look_at(target)
move_and_slide()
Note: clampf(speed, 0.0, max_speed)
would be equivalent to minf(speed, max_speed
assuming speed
is positive to begin with. However, I believe clampf(speed, 0.0, max_speed)
is more readable.
The above should stop at the target. However, if you want to have a check, use is_zero_approx
:
func _physics_process(delta:float) -> void:
var distance_to_target := global_position.distance_to(target)
var direction_to_target := global_position.direction_to(target)
if not is_zero_approx(distance_to_target):
var max_speed := distance_to_target / delta
velocity = direction_to_target * clampf(speed, 0.0, max_speed)
look_at(target)
move_and_slide()