godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.13k stars 69 forks source link

Add a way to set a separate time scale for the physics simulation #1243

Open kilojool opened 4 years ago

kilojool commented 4 years ago

Describe the project you are working on: An action game with bullet time features

Describe the problem or limitation you are having in your project: Changing the Engine.time_scale effects all time-based processes such as UI, while I only want it to effect the gameplay. While most things can be worked around by multiplying the delta parameter in process or physics_process, there does not seem to be any other way to change the time_scale for the physics simulation.

Describe the feature / enhancement and how it helps to overcome the problem or limitation: Add a new variable called physics_time_scale_factor which is multiplied by Engine.time_scale to be used in the physics engine simulation.

Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams: I have tried an implementation which adds a physics_time_scale_factor variable to engine.h which is multiplied by the time_scale. This value is then passed to the PhysicsServer instead. In my implementation I pass it to every function call inside the physics_frame, which means that _physics_process uses the new new physics_time_scale, while _process still uses the old time_scale.

This might cause some confusion of course, but I still think it is worth it if documented properly. For example, move_and_slide works as intended inside _physics_process, while you can still use UI or camera with the unmodified delta values inside _process or in animations.

in main.cpp

bool Main::iteration() {
...
float physics_time_scale = time_scale * physics_time_scale_factor;

if (OS::get_singleton()->get_main_loop()->iteration(frame_slice * physics_time_scale)) {
    exit = true;
    break;
}

message_queue->flush();

NavigationServer3D::get_singleton_mut()->process(frame_slice * physics_time_scale);

PhysicsServer3D::get_singleton()->step(frame_slice * physics_time_scale);

PhysicsServer2D::get_singleton()->end_sync();
PhysicsServer2D::get_singleton()->step(frame_slice * physics_time_scale);
...

Letting physics_time_scale_factor have a default value of 1 also means that the old workflow still works as intended.

If this enhancement will not be used often, can it be worked around with a few lines of script?: The physics engine simulation time scale can not be changed except for changing Engine.time_scale, which also effects everything else.

Is there a reason why this should be core and not an add-on in the asset library?: The implementation is in core.

TheDuriel commented 4 years ago

There is an open PR which adds time_scale as a property of Node, not on the engine level. Which probably covers this adequately. https://github.com/godotengine/godot/pull/29861

kilojool commented 4 years ago

Cool! yes, that should do it! =) I guess I'll close this then?

Calinou commented 4 years ago

@kilojool This proposal can be kept open until we reach an agreement on the pull request (upon which it'll be merged or closed). Therefore, I reopened it as it's still relevant.

huhund commented 3 years ago

Thanks for making this feature request. Something like this would be very useful. It would also be cool to have an option to keep the delta time fixed while while changing time step. A fixed delta is important to keep physics stable. See this PR as well: https://github.com/lawnjelly/godot/commit/55ddf46628ad310175b3e41a55784c186d087c83

jitspoe commented 1 year ago

There is an open PR which adds time_scale as a property of Node, not on the engine level. Which probably covers this adequately. godotengine/godot#29861

Sadly, this PR was closed. I would love to have a per-rigid body time scale.

It seems like we could do something like:

    while (b) {
        b->self()->integrate_forces(p_delta * b->time_scale);
        b = b->next();
        active_count++;
    }

in GodotStep3D::step().