From f2df31cb5edf08d7ec258ad1a2ea14795403d2dc Mon Sep 17 00:00:00 2001 From: Hugo Locurcio Date: Wed, 3 Jan 2024 23:34:06 +0100 Subject: [PATCH] Add `Engine.get_{process,physics}_step()` to get unscaled time delta This can be used for manual UI animations or code whose behavior shouldn't vary depending on `Engine.time_scale`. --- core/config/engine.h | 2 ++ core/core_bind.cpp | 10 ++++++++++ core/core_bind.h | 2 ++ doc/classes/Engine.xml | 15 ++++++++++++++- doc/classes/Node.xml | 6 +++--- doc/classes/ProjectSettings.xml | 1 + main/main.cpp | 1 + 7 files changed, 33 insertions(+), 4 deletions(-) diff --git a/core/config/engine.h b/core/config/engine.h index be7cd62f663e..62364d5dade3 100644 --- a/core/config/engine.h +++ b/core/config/engine.h @@ -59,6 +59,7 @@ class Engine { uint32_t _frame_delay = 0; uint64_t _frame_ticks = 0; double _process_step = 0; + double _physics_step = 0; int ips = 60; double physics_jitter_fix = 0.5; @@ -118,6 +119,7 @@ class Engine { bool is_in_physics_frame() const { return _in_physics; } uint64_t get_frame_ticks() const { return _frame_ticks; } double get_process_step() const { return _process_step; } + double get_physics_step() const { return _physics_step; } double get_physics_interpolation_fraction() const { return _physics_interpolation_fraction; } void set_time_scale(double p_scale); diff --git a/core/core_bind.cpp b/core/core_bind.cpp index f5c69b9b9802..e7d90feaaae9 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -1579,6 +1579,14 @@ double Engine::get_physics_jitter_fix() const { return ::Engine::get_singleton()->get_physics_jitter_fix(); } +double Engine::get_process_step() const { + return ::Engine::get_singleton()->get_process_step(); +} + +double Engine::get_physics_step() const { + return ::Engine::get_singleton()->get_physics_step(); +} + double Engine::get_physics_interpolation_fraction() const { return ::Engine::get_singleton()->get_physics_interpolation_fraction(); } @@ -1740,6 +1748,8 @@ void Engine::_bind_methods() { ClassDB::bind_method(D_METHOD("get_max_physics_steps_per_frame"), &Engine::get_max_physics_steps_per_frame); ClassDB::bind_method(D_METHOD("set_physics_jitter_fix", "physics_jitter_fix"), &Engine::set_physics_jitter_fix); ClassDB::bind_method(D_METHOD("get_physics_jitter_fix"), &Engine::get_physics_jitter_fix); + ClassDB::bind_method(D_METHOD("get_process_step"), &Engine::get_process_step); + ClassDB::bind_method(D_METHOD("get_physics_step"), &Engine::get_physics_step); ClassDB::bind_method(D_METHOD("get_physics_interpolation_fraction"), &Engine::get_physics_interpolation_fraction); ClassDB::bind_method(D_METHOD("set_max_fps", "max_fps"), &Engine::set_max_fps); ClassDB::bind_method(D_METHOD("get_max_fps"), &Engine::get_max_fps); diff --git a/core/core_bind.h b/core/core_bind.h index f884426881ef..dab1ad3abfa1 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -481,6 +481,8 @@ class Engine : public Object { void set_physics_jitter_fix(double p_threshold); double get_physics_jitter_fix() const; + double get_process_step() const; + double get_physics_step() const; double get_physics_interpolation_fraction() const; void set_max_fps(int p_fps); diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml index f9c9b72ed730..9249b8107f2d 100644 --- a/doc/classes/Engine.xml +++ b/doc/classes/Engine.xml @@ -117,6 +117,12 @@ Returns the fraction through the current physics tick we are at the time of rendering the frame. This can be used to implement fixed timestep interpolation. + + + + Returns the time spent between the last two physics ticks in seconds. Unlike the [code]delta[/code] parameter in [method Node._physics_process], this value is not affected by [member time_scale]. See also [method get_process_step]. + + @@ -142,6 +148,12 @@ [/codeblocks] + + + + Returns the time spent between the last two rendered frames in seconds. Unlike the [code]delta[/code] parameter in [method Node._process], this value is not affected by [member time_scale]. See also [method get_physics_step]. + + @@ -261,7 +273,7 @@ func _enter_tree(): # Depending on when the node is added to the tree, # prints either "true" or "false". - print(Engine.is_in_physics_frame()) + print(Engine.is_in_physics_frame()) func _process(delta): print(Engine.is_in_physics_frame()) # Prints false @@ -321,6 +333,7 @@ The maximum number of physics steps that can be simulated each rendered frame. [b]Note:[/b] The default value is tuned to prevent expensive physics simulations from triggering even more expensive simulations indefinitely. However, the game will appear to slow down if the rendering FPS is less than [code]1 / max_physics_steps_per_frame[/code] of [member physics_ticks_per_second]. This occurs even if [code]delta[/code] is consistently used in physics calculations. To avoid this, increase [member max_physics_steps_per_frame] if you have increased [member physics_ticks_per_second] significantly above its default value. + [b]Note:[/b] [member max_physics_steps_per_frame] is ignored if the [code]--fixed-fps <fps>[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url] is used with a positive value, which also occurs when using Movie Maker mode. Simulation will always run at full speed in this scenario. How much physics ticks are synchronized with real time. If [code]0[/code] or less, the ticks are fully synchronized. Higher values cause the in-game clock to deviate more from the real clock, but they smooth out framerate jitters. diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 29e144e4b95f..20a649bdb82f 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -71,7 +71,7 @@ - Called during the physics processing step of the main loop. Physics processing means that the frame rate is synced to the physics, i.e. the [param delta] variable should be constant. [param delta] is in seconds. + Called during the physics processing step of the main loop. Physics processing means that the frame rate is synced to the physics, i.e. the [param delta] variable should be constant. [param delta] is in seconds and is multiplied by [member Engine.time_scale]. To get the [param delta] that isn't scaled by [member Engine.time_scale], use [method Engine.get_physics_step]. It is only called if physics processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_physics_process]. Corresponds to the [constant NOTIFICATION_PHYSICS_PROCESS] notification in [method Object._notification]. [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not an orphan). @@ -81,7 +81,7 @@ - Called during the processing step of the main loop. Processing happens at every frame and as fast as possible, so the [param delta] time since the previous frame is not constant. [param delta] is in seconds. + Called during the processing step of the main loop. Processing happens at every frame and as fast as possible, so the [param delta] time since the previous frame is not constant. [param delta] is in seconds and is multiplied by [member Engine.time_scale]. To get the [param delta] that isn't scaled by [member Engine.time_scale], use [method Engine.get_process_step]. It is only called if processing is enabled, which is done automatically if this method is overridden, and can be toggled with [method set_process]. Corresponds to the [constant NOTIFICATION_PROCESS] notification in [method Object._notification]. [b]Note:[/b] This method is only called if the node is present in the scene tree (i.e. if it's not an orphan). @@ -961,7 +961,7 @@ [b]Note:[/b] When changing the name, the following characters will be replaced with an underscore: ([code].[/code] [code]:[/code] [code]@[/code] [code]/[/code] [code]"[/code] [code]%[/code]). In particular, the [code]@[/code] character is reserved for auto-generated names. See also [method String.validate_node_name]. - The owner of this node. The owner must be an ancestor of this node. When packing the owner node in a [PackedScene], all the nodes it owns are also saved with it. + The owner of this node. The owner must be an ancestor of this node. When packing the owner node in a [PackedScene], all the nodes it owns are also saved with it. [b]Note:[/b] In the editor, nodes not owned by the scene root are usually not displayed in the Scene dock, and will [b]not[/b] be saved. To prevent this, remember to set the owner after calling [method add_child]. See also (see [member unique_name_in_owner]) diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 8bf299e3e781..bdbbfae8490b 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2260,6 +2260,7 @@ Controls the maximum number of physics steps that can be simulated each rendered frame. The default value is tuned to avoid "spiral of death" situations where expensive physics simulations trigger more expensive simulations indefinitely. However, the game will appear to slow down if the rendering FPS is less than [code]1 / max_physics_steps_per_frame[/code] of [member physics/common/physics_ticks_per_second]. This occurs even if [code]delta[/code] is consistently used in physics calculations. To avoid this, increase [member physics/common/max_physics_steps_per_frame] if you have increased [member physics/common/physics_ticks_per_second] significantly above its default value. + [b]Note:[/b] [member physics/common/max_physics_steps_per_frame] is ignored if the [code]--fixed-fps <fps>[/code] [url=$DOCS_URL/tutorials/editor/command_line_tutorial.html]command line argument[/url] is used with a positive value, which also occurs when using Movie Maker mode. Simulation will always run at full speed in this scenario. [b]Note:[/b] This property is only read when the project starts. To change the maximum number of simulated physics steps per frame at runtime, set [member Engine.max_physics_steps_per_frame] instead. diff --git a/main/main.cpp b/main/main.cpp index c520ebecd379..7851e351d297 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -3906,6 +3906,7 @@ bool Main::iteration() { double scaled_step = process_step * time_scale; Engine::get_singleton()->_process_step = process_step; + Engine::get_singleton()->_physics_step = physics_step; Engine::get_singleton()->_physics_interpolation_fraction = advance.interpolation_fraction; uint64_t physics_process_ticks = 0;