Effectively animating/representing a looping timer and timeline track with dynamically resizable UI elements

Godot Version



Hi everyone. I am new to Godot and this community, so apologies if anything is amiss.

I am designing a prototype which utilises a timeline system which maps inputs on a 4 second timer loop. I am storing this information in a sorted array with a timestamp and the input button pressed. When the inputs are confirmed by the player, I use this data to create signalling nodes which signal the appropriate game objects at the right time on a loop. This backend implementation seems to be doing the trick for me, and I’m relatively happy with it.

My issue is representing it visually. I am aiming to represent them on a looping timeline system (e.g., like a looping Zelda musical instrument player). Currently I am stamping UI elements down on a panel container based on the position/progress of a PathFollow2D along a Path2D node. This is a real spaghetti implementation and obviously doesn’t work once you resize any elements as the Path2D does not scale like a control node, and is not as precise at visually representing the timings as I’d like.

timeline GIF

My question is: how could I visually represent the progression of a timer using a dynamically sized moving indicator like my current example above, and stamp UI elements down on a UI container, with their position reflecting their point in time out of 4 seconds?

I have looked into using a texture progress bar, but to my knowledge it fills the bar up with multiple textures up until its max range, when I want a single moving indicator to demonstrate progression through the timeline loop.

Many thanks for any help that can be provided. Happy to give more context/detail if needed, this is my first post so I am learning :slight_smile:

You could use @GlobalScope.remap() to remap the Timer.time_left value from Timer.wait_time to 0 to your start and end position.

Something like:

extends Node

@onready var timer: Timer = $Timer
@onready var sprite_2d: Sprite2D = $Sprite2D

func _ready() -> void:

func _process(delta: float) -> void:
	sprite_2d.position.x = remap(timer.time_left, timer.wait_time, 0, 100, 500)



This is great, thank you very much. I’ll try implementing this soon. I’ve also tried using a tween and moving it between a starting position and the panel container’s size to try and ensure it scales with the size of the UI. Can you foresee any issues with this when scaling UI up and down? From my testing it seems to be working so far, but I’m still new to CanvasUI and control nodes and have had some weird surprises before when I think I’ve finally fool proofed UI.

func _on_timer_timeout():
	if tween: tween.kill()
	panel.position.x = 5
	tween = get_tree().create_tween()
	tween.tween_property(panel, "position:x", panel_container.size.x - 8, 4)

A tween should work fine too.

1 Like