@icon("res://components/ui/hud/timer/stopwatch.svg") class_name TimescalableTimer extends Node ## A timescalable-countdown timer. ## ## The [b]Timer[/b] node is a countdown timer and is the simplest way to handle time-based logic in the engine. ## When a timer reaches the end of its [member wait_time], it will emit the [signal timeout] signal.[br][br] ## After a timer enters the tree, it can be manually started with [method start]. ## A timer node is also started automatically if [member autostart] is [code]true[/code].[br][br] ## Without requiring much code, a timer node can be added and configured in the editor. ## The [signal timeout] signal it emits can also be connected through the Node dock in the editor: ## ## [codeblock] ## func _on_timer_timeout(): ## print("Time to attack!") ## [/codeblock] ## [b]Note:[/b] To create a one-shot timer without instantiating a node, use [method SceneTree.create_timer].[br][br] ## [b]Note:[/b] Timers are affected by [member Engine.time_scale]. ## The higher the time scale, the sooner timers will end. ## How often a timer processes may depend on the framerate or [member Engine.physics_ticks_per_second]. ## Emitted when the timer reaches the end. signal timeout ## See documentation for [param process_callback] in [Timer] @export var process_callback: Timer.TimerProcessCallback ## The time required for the timer to end, in seconds. This property can also be set every time [member start] is called. ## ## [b]Note[/b]: Timers can only process once per physics or process frame (depending on the [member process_callback]). ## An unstable framerate may cause the timer to end inconsistently, ## which is especially noticeable if the wait time is lower than roughly [code]0.05[/code] seconds. ## For very short timers, it is recommended to write your own code instead of using a [Timer] node. ## Timers are also affected by [Engine.time_scale]. @export_range(0.001, 4096.0, 0.001, "suffix:s") var wait_time: float = 1.0: get = get_wait_time, set = set_wait_time ## The maximum allowed elapsed time we can have.[br]If this is under [code]0[/code] ## we don't have any limit and we count towards infinity. @export var one_shot: bool = false ## If [code]true[/code], the stopwatch will call [method start] when entering the scene tree. @export var autostart: bool = false ## Manipulates, how fast or slow the timer will count down. @export_range(-10.0, 10.0, 0.01, "or_greater", "or_less") var time_scale: float = 1.0 var time_left: float = -1.0: get = get_time_left var paused: bool = false: set = set_paused, get = is_paused var _stopped: bool = true func _ready() -> void: if autostart: start() func _process(delta: float) -> void: if process_callback == Timer.TIMER_PROCESS_IDLE: _process_timer(delta) func _physics_process(delta: float) -> void: if process_callback == Timer.TIMER_PROCESS_PHYSICS: _process_timer(delta) func _process_timer(delta: float) -> void: if is_stopped() or is_paused(): return time_left = maxf(time_left - delta * time_scale, 0.0) if time_left == 0.0: timeout.emit() if one_shot: stop() else: time_left += wait_time #region Starting/Stopping ## Starts the stopwatch. [param custom_start_time] will set the elapsed time to it instead of ## using [param start_time]. func start(time_sec: float = -1.0) -> void: if time_sec >= 0.0: wait_time = time_sec time_left = wait_time _stopped = false paused = false ## Stops the stopwatch. func stop() -> float: var final_time: float = time_left time_left = -1.0 _stopped = true return final_time #endregion func get_wait_time() -> float: return wait_time func set_wait_time(value: float) -> void: wait_time = value ## Returns the elapsed time since this stopwatch was started. func get_time_left() -> float: return time_left if not is_stopped() else 0.0 ## Returns [code]true[/code] if the timer is stopped or has not started. func is_stopped() -> bool: return _stopped func set_paused(value: bool) -> void: paused = value func is_paused() -> bool: return paused