From e8524920707b5072875b4f8b08b205ce59bac4be Mon Sep 17 00:00:00 2001 From: CoreTaxxe <47256718+CoreTaxxe@users.noreply.github.com> Date: Wed, 2 Oct 2024 11:22:23 +0200 Subject: [PATCH 1/7] Implement Frame option for Timer node --- doc/classes/Timer.xml | 20 +++++++++++++----- scene/main/timer.cpp | 49 +++++++++++++++++++++++++++++++++++++++---- scene/main/timer.h | 17 ++++++++++++++- 3 files changed, 76 insertions(+), 10 deletions(-) diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml index f8f9393847c..484e28a5f22 100644 --- a/doc/classes/Timer.xml +++ b/doc/classes/Timer.xml @@ -9,10 +9,10 @@ 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!") + print("Time to attack!") [/codeblock] [b]Note:[/b] To create a one-shot timer without instantiating a node, use [method SceneTree.create_timer]. - [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]. + [b]Note:[/b] Timers in Time mode 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]. https://godotengine.org/asset-library/asset/2712 @@ -53,13 +53,17 @@ Specifies when the timer is updated during the main loop (see [enum TimerProcessCallback]). + + Specifies which units the timer uses to count. (see [enum TimerProcessType]). + The timer's remaining time in seconds. This is always [code]0[/code] if the timer is stopped. [b]Note:[/b] This property is read-only and cannot be modified. It is based on [member wait_time]. - - The time required for the timer to end, in seconds. This property can also be set every time [method 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 [member Engine.time_scale]. + + The time required for the timer to end, in seconds or frames. This property can also be set every time [method start] is called. + Depending on the selected mode either floats or integers are required. + [b]Note:[/b] Timers can only process once per physics or process frame (depending on the [member process_callback]). In Time mode, 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 [member Engine.time_scale]. @@ -76,5 +80,11 @@ Update the timer every process (rendered) frame (see [constant Node.NOTIFICATION_INTERNAL_PROCESS]). + + Timer works with seconds. In this mode the timer is affected by [member Engine.time_scale]. + + + Timer works with frames. Speed depends on the framerate. Accepts integer values only. + diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp index 0f4f18b495e..bae2535b234 100644 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -48,7 +48,15 @@ void Timer::_notification(int p_what) { if (!processing || timer_process_callback == TIMER_PROCESS_PHYSICS || !is_processing_internal()) { return; } - time_left -= get_process_delta_time(); + + switch (timer_process_type) { + case TIMER_PROCESS_TYPE_TIME: { + time_left -= get_process_delta_time(); + }; break; + case TIMER_PROCESS_TYPE_FRAMES: { + time_left -= 1; + }; break; + } if (time_left < 0) { if (!one_shot) { @@ -60,7 +68,6 @@ void Timer::_notification(int p_what) { emit_signal(SNAME("timeout")); } } break; - case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (!processing || timer_process_callback == TIMER_PROCESS_IDLE || !is_physics_processing_internal()) { return; @@ -152,13 +159,13 @@ void Timer::set_timer_process_callback(TimerProcessCallback p_callback) { if (is_physics_processing_internal()) { set_physics_process_internal(false); set_process_internal(true); - } + }; break; case TIMER_PROCESS_IDLE: if (is_processing_internal()) { set_process_internal(false); set_physics_process_internal(true); - } + }; break; } timer_process_callback = p_callback; @@ -168,6 +175,25 @@ Timer::TimerProcessCallback Timer::get_timer_process_callback() const { return timer_process_callback; } +void Timer::set_timer_process_type(TimerProcessType p_type) { + if (timer_process_type == p_type) { + return; + } + switch (timer_process_type) { + case TIMER_PROCESS_TYPE_FRAMES: { + wait_time = (int)wait_time; + }; break; + case TIMER_PROCESS_TYPE_TIME: + break; + } + timer_process_type = p_type; + notify_property_list_changed(); +} + +Timer::TimerProcessType Timer::get_timer_process_type() const { + return timer_process_type; +} + void Timer::_set_process(bool p_process, bool p_force) { switch (timer_process_callback) { case TIMER_PROCESS_PHYSICS: @@ -190,6 +216,14 @@ PackedStringArray Timer::get_configuration_warnings() const { return warnings; } +void Timer::_validate_property(PropertyInfo &p_property) const { + if (timer_process_type == TIMER_PROCESS_TYPE_FRAMES && p_property.name == "wait_time") { + p_property.type = Variant::INT; + p_property.hint = PROPERTY_HINT_NONE; + p_property.hint_string = "suffix:f"; + } +} + void Timer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_wait_time", "time_sec"), &Timer::set_wait_time); ClassDB::bind_method(D_METHOD("get_wait_time"), &Timer::get_wait_time); @@ -213,9 +247,13 @@ void Timer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_timer_process_callback", "callback"), &Timer::set_timer_process_callback); ClassDB::bind_method(D_METHOD("get_timer_process_callback"), &Timer::get_timer_process_callback); + ClassDB::bind_method(D_METHOD("set_timer_process_type", "type"), &Timer::set_timer_process_type); + ClassDB::bind_method(D_METHOD("get_timer_process_type"), &Timer::get_timer_process_type); + ADD_SIGNAL(MethodInfo("timeout")); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_timer_process_callback", "get_timer_process_callback"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "process_type", PROPERTY_HINT_ENUM, "Time,Frames"), "set_timer_process_type", "get_timer_process_type"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wait_time", PROPERTY_HINT_RANGE, "0.001,4096,0.001,or_greater,exp,suffix:s"), "set_wait_time", "get_wait_time"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "is_one_shot"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autostart"), "set_autostart", "has_autostart"); @@ -224,6 +262,9 @@ void Timer::_bind_methods() { BIND_ENUM_CONSTANT(TIMER_PROCESS_PHYSICS); BIND_ENUM_CONSTANT(TIMER_PROCESS_IDLE); + + BIND_ENUM_CONSTANT(TIMER_PROCESS_TYPE_TIME); + BIND_ENUM_CONSTANT(TIMER_PROCESS_TYPE_FRAMES); } Timer::Timer() {} diff --git a/scene/main/timer.h b/scene/main/timer.h index d16e49793dd..1ad5d72c8ba 100644 --- a/scene/main/timer.h +++ b/scene/main/timer.h @@ -47,6 +47,7 @@ class Timer : public Node { protected: void _notification(int p_what); static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const; public: enum TimerProcessCallback { @@ -54,6 +55,13 @@ class Timer : public Node { TIMER_PROCESS_IDLE, }; + // Type instead of Mode to prevent name collisions when upgrading from 3 to 4 + // Godot 3 used TimerProcessMode which is now TimerProcessCallback + enum TimerProcessType { + TIMER_PROCESS_TYPE_TIME, + TIMER_PROCESS_TYPE_FRAMES, + }; + void set_wait_time(double p_time); double get_wait_time() const; @@ -77,13 +85,20 @@ class Timer : public Node { void set_timer_process_callback(TimerProcessCallback p_callback); TimerProcessCallback get_timer_process_callback() const; + + void set_timer_process_type(TimerProcessType p_type); + TimerProcessType get_timer_process_type() const; + Timer(); private: TimerProcessCallback timer_process_callback = TIMER_PROCESS_IDLE; + TimerProcessType timer_process_type = TIMER_PROCESS_TYPE_TIME; void _set_process(bool p_process, bool p_force = false); + }; VARIANT_ENUM_CAST(Timer::TimerProcessCallback); +VARIANT_ENUM_CAST(Timer::TimerProcessType); -#endif // TIMER_H +#endif // TIMER_H \ No newline at end of file From f3470b01ef25bacf522dab9535767b5df9ef9fbe Mon Sep 17 00:00:00 2001 From: CoreTaxxe <47256718+CoreTaxxe@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:59:25 +0200 Subject: [PATCH 2/7] Fix Docs and add counter to physics frames as well as proper type sanitization --- doc/classes/Timer.xml | 8 ++++---- scene/main/timer.cpp | 18 ++++++++++++++++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml index 484e28a5f22..14055cddd12 100644 --- a/doc/classes/Timer.xml +++ b/doc/classes/Timer.xml @@ -53,16 +53,16 @@ Specifies when the timer is updated during the main loop (see [enum TimerProcessCallback]). - + Specifies which units the timer uses to count. (see [enum TimerProcessType]). The timer's remaining time in seconds. This is always [code]0[/code] if the timer is stopped. [b]Note:[/b] This property is read-only and cannot be modified. It is based on [member wait_time]. - + The time required for the timer to end, in seconds or frames. This property can also be set every time [method start] is called. - Depending on the selected mode either floats or integers are required. + Time mode uses floats while Frame mode only accepts and returns non-fractional numbers. [b]Note:[/b] Timers can only process once per physics or process frame (depending on the [member process_callback]). In Time mode, 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 [member Engine.time_scale]. @@ -84,7 +84,7 @@ Timer works with seconds. In this mode the timer is affected by [member Engine.time_scale]. - Timer works with frames. Speed depends on the framerate. Accepts integer values only. + Timer works with frames. Speed depends on the framerate. Accepts non-fractional values only. diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp index bae2535b234..6dbcc4f6dc1 100644 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -72,7 +72,15 @@ void Timer::_notification(int p_what) { if (!processing || timer_process_callback == TIMER_PROCESS_IDLE || !is_physics_processing_internal()) { return; } - time_left -= get_physics_process_delta_time(); + + switch (timer_process_type) { + case TIMER_PROCESS_TYPE_TIME: { + time_left -= get_physics_process_delta_time(); + }; break; + case TIMER_PROCESS_TYPE_FRAMES: { + time_left -= 1; + }; break; + } if (time_left < 0) { if (!one_shot) { @@ -88,7 +96,12 @@ void Timer::_notification(int p_what) { void Timer::set_wait_time(double p_time) { ERR_FAIL_COND_MSG(p_time <= 0, "Time should be greater than zero."); - wait_time = p_time; + if (timer_process_type == TIMER_PROCESS_TYPE_FRAMES) { + ERR_FAIL_COND_MSG(p_time != (int)p_time, "Frame mode only accepts non-fractional values. (Frames will be rounded.)"); + wait_time = (int)p_time; + } else { + wait_time = p_time; + } update_configuration_warnings(); } @@ -182,6 +195,7 @@ void Timer::set_timer_process_type(TimerProcessType p_type) { switch (timer_process_type) { case TIMER_PROCESS_TYPE_FRAMES: { wait_time = (int)wait_time; + time_left = (int)time_left; }; break; case TIMER_PROCESS_TYPE_TIME: break; From 9e084e93093fdd5dcb4691ef4359a02eb9595552 Mon Sep 17 00:00:00 2001 From: CoreTaxxe <47256718+CoreTaxxe@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:33:13 +0200 Subject: [PATCH 3/7] Fix documentation example indentation --- doc/classes/Timer.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml index 14055cddd12..b1acd511e3f 100644 --- a/doc/classes/Timer.xml +++ b/doc/classes/Timer.xml @@ -9,7 +9,7 @@ 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!") + print("Time to attack!") [/codeblock] [b]Note:[/b] To create a one-shot timer without instantiating a node, use [method SceneTree.create_timer]. [b]Note:[/b] Timers in Time mode 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]. From 365f502a59302ce2b47a7e437555b5e660cdbd01 Mon Sep 17 00:00:00 2001 From: CoreTaxxe <47256718+CoreTaxxe@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:37:51 +0200 Subject: [PATCH 4/7] Replace tab with 4 spaces. --- doc/classes/Timer.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml index b1acd511e3f..c337e13cdb2 100644 --- a/doc/classes/Timer.xml +++ b/doc/classes/Timer.xml @@ -9,7 +9,7 @@ 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!") + print("Time to attack!") [/codeblock] [b]Note:[/b] To create a one-shot timer without instantiating a node, use [method SceneTree.create_timer]. [b]Note:[/b] Timers in Time mode 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]. From a9b431e78d641b85dc398a5cb7c72ac8532e59fa Mon Sep 17 00:00:00 2001 From: CoreTaxxe <47256718+CoreTaxxe@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:47:48 +0200 Subject: [PATCH 5/7] Doctool is not reporting any issues .... so another try we do --- doc/classes/Timer.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml index c337e13cdb2..665b1d5ad7d 100644 --- a/doc/classes/Timer.xml +++ b/doc/classes/Timer.xml @@ -9,7 +9,7 @@ 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!") + print("Time to attack!") [/codeblock] [b]Note:[/b] To create a one-shot timer without instantiating a node, use [method SceneTree.create_timer]. [b]Note:[/b] Timers in Time mode 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]. From 87cca48bb13126edc040adc98374f559388b1677 Mon Sep 17 00:00:00 2001 From: CoreTaxxe <47256718+CoreTaxxe@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:18:28 +0200 Subject: [PATCH 6/7] Add new line --- scene/main/timer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scene/main/timer.h b/scene/main/timer.h index 1ad5d72c8ba..9ac106ac179 100644 --- a/scene/main/timer.h +++ b/scene/main/timer.h @@ -101,4 +101,4 @@ class Timer : public Node { VARIANT_ENUM_CAST(Timer::TimerProcessCallback); VARIANT_ENUM_CAST(Timer::TimerProcessType); -#endif // TIMER_H \ No newline at end of file +#endif // TIMER_H From 7b3235e191f6dde02c466ce121c83fe52b72cd3e Mon Sep 17 00:00:00 2001 From: CoreTaxxe <47256718+CoreTaxxe@users.noreply.github.com> Date: Wed, 2 Oct 2024 15:28:07 +0200 Subject: [PATCH 7/7] Remove new line --- scene/main/timer.h | 1 - 1 file changed, 1 deletion(-) diff --git a/scene/main/timer.h b/scene/main/timer.h index 9ac106ac179..5eb3842af56 100644 --- a/scene/main/timer.h +++ b/scene/main/timer.h @@ -95,7 +95,6 @@ class Timer : public Node { TimerProcessCallback timer_process_callback = TIMER_PROCESS_IDLE; TimerProcessType timer_process_type = TIMER_PROCESS_TYPE_TIME; void _set_process(bool p_process, bool p_force = false); - }; VARIANT_ENUM_CAST(Timer::TimerProcessCallback);