Hi, thanks for answering, this is my current implementation, they are very similar, but I used an animation_player node instead of a Tween to animate it a bit, and I call it directly when required using the focus_entered signal in every screen, so that I can spawn multiples cursors (For submenus), or for picking multiple enemy targets (massive skills)
attack_button.focus_entered.connect(_on_button_focused.bind(attack_button))
func _on_button_focused(control: Control) -> void:
pointer.change_focus(control, true)
class_name Pointer
extends Control
@export var animation_player: AnimationPlayer
@export var pointer_ui_audio: AudioStream
@export var pointer_texture: TextureRect
# Current control focused in the UI
var current_control: Control
# Hide it until is requested by the interface
func _ready() -> void:
hide()
# Move pointer to the position of current focused element in UI
func change_focus(new_control: Control, center: bool = true) -> void:
if current_control == new_control:
return
# Workaround, if the position hasn't been updated in the tree
await get_tree().process_frame
current_control = new_control
var target_position := current_control.global_position
# Center in the Y axis relative to the current focused control
if center:
target_position.y -= (current_control.size.y / 2)
# Center the pointer relative to the pointer sprite size
target_position = target_position + (pointer_texture.size / 2)
# Left offset from the control focused [] -> Focused Control
target_position = target_position - Vector2(24, 0)
global_position = target_position
I updated my code to try to use parts of your approach, like reassigning the parent itself instead of global_position, and using a tween, which seems to help a bit with the position delay, but there are times when moving fast across menus, that the cursor will appear in non desired positions, it’d also get clipped in a Scroll Container, but I can workaround that scroll later.
Did you run out on any of these problems down the road? I think that my problem might come from the fact that I used anchor points rather than specific positions in the inspector. Thanks again for your time!
New approach:
class_name Pointer
extends Control
#@export var animation_player: AnimationPlayer
@export var pointer_ui_audio: AudioStream
@export var pointer_texture: TextureRect
# Current control focused in the UI
var current_control: Control
# Hide it until is requested by the interface
func _ready() -> void:
hide()
# Move pointer to the position of current focused element in UI
func change_focus(new_control: Control, center: bool = true) -> void:
if current_control == new_control:
return
# Workaround, if the position hasn't been updated in the tree
#await get_tree().process_frame
current_control = new_control
var target_position := current_control.position
## New solution, also tried using the texture_rect rather than the control object
if pointer_texture.get_parent() != current_control.get_parent():
pointer_texture.get_parent().remove_child(pointer_texture)
current_control.get_parent().add_child(pointer_texture)
# Center in the Y axis relative to the current focused control
if center:
target_position.y -= (current_control.size.y / 2)
# Center the pointer relative to the pointer sprite size (No longer needed)
# target_position = target_position + (pointer_texture.size / 2)
# Left offset from the control focused [] -> Focused Control
target_position = target_position - Vector2(24, 0)
var tween := create_tween().set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_CUBIC)
tween.tween_property(pointer_texture, "position", target_position, 0.5)