godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
89.55k stars 20.65k forks source link

Inconsistent execution order of _integrate_forces #11925

Open vpellen opened 6 years ago

vpellen commented 6 years ago

Operating system or device, Godot version, GPU Model and driver (if graphics related): v2.1.4 stable

Issue description: The standard 2d physics step performs force integration before velocity integration. Rigid Bodies have a method binding called _integrate_forces that's designed to replace force integration with custom logic. However, within a standard step, the _integrate_forces method executes after the velocity integration stage, rather than before.

This is mainly a consistency issue. It can partially be worked around by inserting integration code into the _fixed_process method, but this can result in minor race conditions if other nodes attempt to modify a rigid body's physics parameters during the fixed physics stage.

bojidar-bg commented 6 years ago

Not sure if I understand the issue correctly, but can't it be worked around by turning "Custom Integrator" on, and calling state.integrate_forces() manually?

vpellen commented 6 years ago

In spite of its name, state.integrate_forces() does not actually integrate forces. At least it doesn't integrate all the forces that the default integration does. But that's a separate issue entirely.

The problem is execution order.

By default, each physics cycle, the engine will integrate forces (i.e. calculate rigid body velocities from their respective forces) and then integrate velocities (i.e. calculate the displacement of rigid bodies based on their respective velocities). This is all fine and dandy.

There's a virtual method for rigid bodies called _integrate_forces(). It's designed to replace the built-in force integration code with your own code. Theoretically, you should be able to call state.integrate_forces() within the _integrate_forces() method, and the program should run exactly like default.

It does not.

The problem is that _integrate_forces() is called after the engine integrates velocities, whereas the default force integration is called before. This seems minor, and it is, but it still throws off the physics simulation slightly.

This is something I may have to dig into myself and I'm likely one of the only people unreasonable enough to care about it honestly.

bojidar-bg commented 6 years ago

Ok, makes sense, looking at the code also confirms your theory. Basically, there are a few steps the physics engine goes through:

akien-mga commented 6 years ago

Is this still valid in the current master branch?

KoBeWi commented 5 years ago

This is the code I used for testing (in 2.1.5):

extends RigidBody2D

func _ready():
    print("ready ", get_pos())
    set_fixed_process(true)

func _fixed_process(delta):
    print("process ", get_pos())

func _integrate_forces(state):
    print("integrate ", get_pos())

If I understand the issue correctly, these 3 should output the same position at the start. b2b06dd4a with equivalent code behaves the same, so I guess it's not fixed.

Unless I tested wrongly.

ghost commented 4 years ago

I think my problem might be related to this issue but I'm not sure.

I have the following scenario: two RigidBody2Ds (player and the ball). Player's velocity is controlled by the keyboard in _integrate_forces, collisions with the ball work as expected.

I wanted to implement a kick - player can kick the ball which means setting its velocity right after the collision. For that purpose I use body_entered player's signal to detect the collision, calculate the target velocity of the ball and then set it in _integrate_forces next time when it's called. I put some print functions in ball's script to see how it goes:

_integrate_forces
_physics_process: velocity = (0, 1219.997925)
kick: current velocity: (0, 1219.997925), target: (1191.503906, -911.218201)
_integrate_forces
_integrate_forces: collision with Player1
_integrate_forces: setting velocity
_integrate_forces: before: (2537.907959, -1889.9375)
_integrate_forces: after: (1191.503906, -911.218201)
_physics_process: velocity = (2537.907959, -1889.9375)
_integrate_forces
_physics_process: velocity = (1189.518066, -893.032837)
_integrate_forces
_physics_process: velocity = (1187.535522, -874.877747)
_integrate_forces
_physics_process: velocity = (1185.556274, -856.75293)

I can't apply new velocity right after the collision because by the time I want do that, physics engine calculates its own velocity after collision and instantly applies it to the object. This results in a noticeable jitter when the initial ball's velocity is high but the target velocity is small.

Let me know if this could be an engine thing or me doing things in a wrong way.