godot
godot copied to clipboard
Add `PhysicsServer2/3D::space_step()` to step physics simulation manually
Implement #1373 In #2821, it seems like the topic is quite big, but I think this pr can solve it.
Here is a simple demonstrate video of rollbackable/recordable physics simulation create by this pr:
https://user-images.githubusercontent.com/61624558/234483154-004bf831-9a7a-4e16-b1e8-a3f723fa49c9.mp4
But this pr is not perfect, there has two points which confuse me (I'm not familiar with multi-threads and GodotPhysics), and I mark them at below.
Here is rollback demo: physics_rollback_test.zip
Hello @Daylily-Zeleen,
for thread-safety, you might want to call the functions sync() and end_sync() of the PhysicsServer classes
an example of usage is in the function Main::iteration() in file main/main.cpp
Saw your PR when making mine ; yours has more general usage but might need some tweaks.
for thread-safety, you might want to call the functions
sync()andend_sync()of the PhysicsServer classes an example of usage is in the functionMain::iteration()in filemain/main.cpp
In my opinion, the PhysicsSpace which be stepped by step() is handled by developers, to run in which thread, sync or not, are controled by developers themselves.
Instead of call sync() and end_sync() in step(), expose spcas_sync(RID p_space) and space_end_sync(RID p_space) may be better.
I need people, who familiar with godot physics and server design, to instruct me.
When will this feature be merged? I need this functionality to assist me in implementing my network synchronization.
@Az-qianchen it hasn't been reviewed yet, so there's no real timeframe for this being merged at the moment, the questions of the OP about some details would need to be resolved as well by someone experienced with the physics
If you can test it and give your results and comments it would help the process
https://github.com/godotengine/godot/assets/52212525/ab00512a-ca3e-412f-9812-a8404d5e5e7a
I am not sure if the way I use is correct, but when I simulation is positive, I can get the correct result. When the value is negative, the result seems to have an error.
When the value is negative, the result seems to have an error.
How to step a physics space is not this pr's work.
I guess your purpose of passing a negative time to space_step() is to roll the physics space back to the previous state (for example, 0.1 second before).
This cannot be achieved through space_step(), this method is to simulate a physical space forward, you can passing a negative delta time to simulate, but it just simulate forward by a negative delta time.
The right way to implement rollback feature is store your physics objects' state, and just restore them when you want to roll back.
Typically, you should create a independent physics space to realize your physics simulation instead of using default space, then use space_step() to setp this space, to make your physics simulation under your control fully.
you should create an independent physics space to realize
I encountered some obstacles when creating a physical space. Do I need to copy all PhysicsBody3D objects to the new physical space, and all objects must be StaticBody3D to perform space_step() on a single object and obtain the correct collision? Is there other shortcuts?
Do I need to copy all PhysicsBody3D objects to the new physical space,
If your physics object are created as a node (CollisionObject2/3D or Joint2/3D) and want to move them from default space to your own space, please use PhysicsServer2/3D.xxx_set_space(), you don't need to copy them.
and all objects must be StaticBody3D to perform space_step() on a single object and obtain the correct collision?
There has not limit the type of physics objet.
I uploaded a testing project for reference.
Here is a rollback demo: physics_rollback_test.zip
There has not limit the type of physics objet.
I may not be very clear. In fact, I hope to make a Step Physics Simulation in a certain object in the scene, but in order to get the correct interaction collision, I seem to need to copy a static scene in the simulation space.
Or turn PhysicsBody into staticbody in the original scene to prevent being simulated together
The following is the code that I tried to transfer the copy to the new physical space, but I still did not think about how to deal with the problem of retaining PhysicsBody and avoiding being simulated together.
class_name SubSpace3D
extends Node
@export var node_mirror: Node
var space_rid: RID
var space_rid_new: RID
func _ready() -> void:
# Body is to obtain the default space RID,maybe there is a better way?
var Body = StaticBody3D.new()
add_child(Body)
space_rid = PhysicsServer3D.body_get_space(Body.get_rid())
space_rid_new = PhysicsServer3D.space_create()
# Configure is a singleton to facilitate data calls from other locations
Configure.space_rid = space_rid
Configure.space_rid_new = space_rid_new
# Copy the basic data of the scene
create(space_rid, space_rid_new)
PhysicsServer3D.space_set_active(space_rid_new, true)
Body.free()
# Nodes that need to be copied
_update_space(node_mirror)
func _update_space(node:Node):
var node_array: Array[Node]
if node is RigidBody3D:
node_array.push_back(node)
for child in node.get_children():
_update_space(child)
for collisionobject in node_array:
var node_new := collisionobject.duplicate()
node.add_child(node_new)
PhysicsServer3D.body_set_space(node_new.get_rid(),space_rid_new)
func _enter_tree():
get_tree().connect("node_added",_node_added)
func _exit_tree():
get_tree().disconnect("node_added",_node_added)
func _node_added(node:Node):
await ready
if is_ancestor_of(node) and node is CollisionObject3D:
PhysicsServer3D.body_set_space(node.get_rid(),space_rid_new)
func set_gravity(space, new_space):
var gravity: float = PhysicsServer3D.area_get_param(space, PhysicsServer3D.AREA_PARAM_GRAVITY)
PhysicsServer3D.area_set_param(new_space, PhysicsServer3D.AREA_PARAM_GRAVITY, gravity)
func set_gravity_vector(space, new_space):
var gravity_vector: Vector3 = PhysicsServer3D.area_get_param(space, PhysicsServer3D.AREA_PARAM_GRAVITY_VECTOR)
PhysicsServer3D.area_set_param(new_space, PhysicsServer3D.AREA_PARAM_GRAVITY_VECTOR, gravity_vector)
func set_linear_damp(space, new_space):
var linear_damp: float = PhysicsServer3D.area_get_param(space, PhysicsServer3D.AREA_PARAM_LINEAR_DAMP)
PhysicsServer3D.area_set_param(new_space, PhysicsServer3D.AREA_PARAM_LINEAR_DAMP, linear_damp)
func set_angular_damp(space, new_space):
var angular_damp:float = PhysicsServer3D.area_get_param(space, PhysicsServer3D.AREA_PARAM_ANGULAR_DAMP)
PhysicsServer3D.area_set_param(new_space, PhysicsServer3D.AREA_PARAM_ANGULAR_DAMP, angular_damp)
func get_direct_space_state(_active):
return PhysicsServer3D.space_get_direct_state(_active)
func create(space, new_space):
set_gravity(space, new_space)
set_gravity_vector(space, new_space)
set_linear_damp(space, new_space)
set_angular_damp(space, new_space)
@Az-qianchen Sorry, I don't think I can help you solve this specific application scenarios.
After reading your code, I found that you set the space_rid_new to actived. I'm not sure about your situation, but I think I need to clearify something here:
A actived physics space will be stepped automatically by the main loop, if you want to control by yourself completely, you should ensure the physics space is Inactivated.
A actived physics space will be stepped automatically by the main loop, if you want to control by yourself completely, you should ensure the physics space is Inactivated.
Oh, I was wrong about this
but it seems that rolling back for individual objects isn't that straightforward. Perhaps avoiding the approach of duplicating the space is to collectively roll back all PhysicsBody .
Before merging this PR into the main branch, maybe I'll try implementing the desired functionality by using a custom integrator along with manual calculations.
Thank you for your help.
Sorry, I will ask the progress of this PR again
I used it for rigid body synchronization in my project Can this PR merge before the frozen 4.2?
https://github.com/godotengine/godot/assets/52212525/02765254-c593-4573-98ac-1e09956e8231
I want predict projectile path in 3d with code like this:
static func predict_projectile_path(start_pos:Vector3, launch_velocity:Vector3,delta:float,stop_func:Callable,gravity:=Vector3.DOWN)->PackedVector3Array:
var path := PackedVector3Array()
var t := 0.0
var index := 0
var pos := start_pos
var vel := launch_velocity
var stop := false
while not stop:
stop = stop_func.call(index,pos,vel)
if stop:
break
path.append(pos)
index += 1
t += delta
vel = launch_velocity + gravity * t
pos = start_pos + launch_velocity*t + gravity*pow(t,2)*0.5
return path
but it not work well and I don't know why. This pr like can solved my problem.
@CsloudX
vel = launch_velocity + gravity * t
pos = start_pos + launch_velocity*t + gravity*pow(t,2)*0.5
pos does not take the changing vel into account. I think it could be something like
vel += gravity * delta
pos += vel * delta
But anyway, I think this is off-topic :P Using physics simulation for this is likely an overkill.
but it not work well and I don't know why.
It seems that you have not consider the damping.
vel += gravity * delta pos += vel * delta
Thanks, but the result was same.
It seems that you have not consider the damping.
Thanks, and my linear damp was set to 0.
PR progress? The only thing keeping it back is one doc change @Daylily-Zeleen
I will say that the butterfly effect still counts for the rollbacking nature of this update, also since godot isnt deterministic i guess.
@RadiantUwU A space which stepped by user should be deactivated (by space_set_activate()), otherwise it will be stepped by main loop again and bring this issue ( it means that query again).
If step a space both by main loop and by user is expected, you should call space_flush_queries() again after space_step().
This is not elegent, and it seems that flush queries is Godot Physics specific, I'm not sure about this.
NOTE: Collisions broken for rollbacks
@RadiantUwU A space which stepped by user should be deactivated (by
space_set_activate()), otherwise it will be stepped by main loop again and bring this issue ( it means that query again). If step a space both by main loop and by user is expected, you should callspace_flush_queries()again afterspace_step().This is not elegent, and it seems that flush queries is Godot Physics specific, I'm not sure about this.
Thank you!
NOTE: Collisions broken for rollbacks
Can you provide a minimum demo for this issue?
NOTE: Collisions broken for rollbacks
Can you provide a minimum demo for this issue?
Here minimum.zip use W to step forward, S to step backward Simulation runs at 7680 Hz btw
NOTE: Collisions broken for rollbacks
Can you provide a minimum demo for this issue?
Here minimum.zip use W to step forward, S to step backward Simulation runs at 7680 Hz btw
Uh... you have not rollback anything at this demo. I need to clearify again. this pr just implements step physics simulation manually. Please refer this comment.
If you need to implement rollback feature, please refer my demo project (at the last of my first comment in this pr).
Uh... you have not rollback anything at this demo. I need to clearify again. this pr just implements step physics simulation manually. Please refer this comment.
If you need to implement rollback feature, please refer my demo project (at the last of my first comment in this pr).
Thanks!
Offtopic-ish - Ignorable: I am strongly interested in this feature (I am porting Rollback-Netcode from Unity to Godot). I see:
🔗 GHA / 🐧 Linux / Editor w/ Mono (target=editor) (pull_request) Failing after 20m
These are tests, right? Or is it about the review not yet done on this individual part? The linked log doesn't tell and I wonder why tests and review are combined here. I would expect that review is relevant at first when the tests are green.
These are tests, right? Or is it about the review not yet done on this individual part? The linked log doesn't tell and I wonder why tests and review are combined here. I would expect that review is relevant at first when the tests are green.
It's occuring generally even for completely trivial PRs.
Strictly speaking, this pr just provide a way for simulating a physics space manully, and help you to realize rollback feature.
Note that this pr is not implement deterministic physic (Godot Physics is not deterministic), deterministic is a topic of physic engine and is not included in this pr (I mean that deterministic is not a duty of PhysicsServer).
@Daylily-Zeleen Thanks for the elaboration. Is it a yes to the questions whether tests are failing? This is would be reason for blocking the merge, right?
The class reference XML is outdated – @Daylily-Zeleen needs to run --doctool on a locally compiled build to update it, then push the changes.