Skip to content

Add PhysicsServer2/3D::space_step() to step physics simulation manually#76462

Open
Daylily-Zeleen wants to merge 1 commit intogodotengine:masterfrom
Daylily-Zeleen:daylily-zeleen/physics_space_step
Open

Add PhysicsServer2/3D::space_step() to step physics simulation manually#76462
Daylily-Zeleen wants to merge 1 commit intogodotengine:masterfrom
Daylily-Zeleen:daylily-zeleen/physics_space_step

Conversation

@Daylily-Zeleen
Copy link
Copy Markdown
Contributor

@Daylily-Zeleen Daylily-Zeleen commented Apr 26, 2023

Closes #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:

rollbackable_simulation.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

@Daylily-Zeleen Daylily-Zeleen changed the title Add space_step() to step physics simulate manually Add PhysicsSrver2/3D::space_step() to step physics simulation manually Apr 26, 2023
@Daylily-Zeleen Daylily-Zeleen force-pushed the daylily-zeleen/physics_space_step branch 2 times, most recently from b769061 to 2419c05 Compare April 26, 2023 05:47
@Daylily-Zeleen Daylily-Zeleen force-pushed the daylily-zeleen/physics_space_step branch from 2419c05 to 48553ed Compare April 26, 2023 05:55
@Daylily-Zeleen Daylily-Zeleen marked this pull request as ready for review April 26, 2023 05:56
@Daylily-Zeleen Daylily-Zeleen requested review from a team as code owners April 26, 2023 05:56
@Chaosus Chaosus added this to the 4.1 milestone Apr 26, 2023
@Daylily-Zeleen Daylily-Zeleen force-pushed the daylily-zeleen/physics_space_step branch 2 times, most recently from ffbacd8 to e6573c3 Compare April 28, 2023 08:56
@Lcbx
Copy link
Copy Markdown
Contributor

Lcbx commented May 14, 2023

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.

@Daylily-Zeleen
Copy link
Copy Markdown
Contributor Author

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

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.

@akien-mga akien-mga changed the title Add PhysicsSrver2/3D::space_step() to step physics simulation manually Add PhysicsServer2/3D::space_step() to step physics simulation manually Jun 19, 2023
@akien-mga akien-mga modified the milestones: 4.1, 4.2 Jun 19, 2023
@Az-qianchen
Copy link
Copy Markdown

When will this feature be merged? I need this functionality to assist me in implementing my network synchronization.

@AThousandShips
Copy link
Copy Markdown
Member

AThousandShips commented Sep 13, 2023

@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

@Az-qianchen
Copy link
Copy Markdown

2023-09-13.180402.mp4

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.

@Daylily-Zeleen Daylily-Zeleen force-pushed the daylily-zeleen/physics_space_step branch from e6573c3 to c9af63e Compare September 13, 2023 12:18
@Daylily-Zeleen
Copy link
Copy Markdown
Contributor Author

Daylily-Zeleen commented Sep 13, 2023

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.

@Az-qianchen
Copy link
Copy Markdown

Az-qianchen commented Sep 14, 2023

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?

@Daylily-Zeleen
Copy link
Copy Markdown
Contributor Author

Daylily-Zeleen commented Sep 14, 2023

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

@Az-qianchen
Copy link
Copy Markdown

Az-qianchen commented Sep 14, 2023

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)

@Daylily-Zeleen
Copy link
Copy Markdown
Contributor Author

@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.

@RickyYCheng
Copy link
Copy Markdown

RickyYCheng commented Aug 12, 2025

If we do not need RigidBody stuffs, then CharacterBody might be a good idea. And there is a move_and_slide function.
If we could add a delta variable to its parameters that would be great to make it steppable.

I create a C# and gdscript example repo to show how to do that. Almost transfer all the code in CharacterBody2D.cpp.

https://github.com/RickyYCheng/Steppable-Godot-Character-Body-2D

@KeyboardDanni
Copy link
Copy Markdown
Contributor

@jrouwe Any insight into the job limit warning when using Jolt? Is there a better way to perform multiple physics steps at once?

@jrouwe
Copy link
Copy Markdown
Contributor

jrouwe commented Sep 18, 2025

I think mihe already answered the question here: jrouwe/JoltPhysics#1701 (comment)

ERR_FAIL_NULL(space);
ERR_FAIL_COND(space->is_stepping());

space->step(p_delta);
Copy link
Copy Markdown

@albertok albertok Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
space->step(p_delta);
space->step(p_delta);
job_system->post_step();

As per @mihe 's suggestion in jrouwe/JoltPhysics#1701 (comment)

This addresses the 'Jolt Physics job system exceeded the maximum number of jobs.' warning

Tested and confirmed in my games.

@monitz87
Copy link
Copy Markdown

monitz87 commented Sep 19, 2025

And there is a move_and_slide function. If we could add a delta variable to its parameters that would be great to make it steppable.

@RickyYCheng there's already a PR for that which has been open almost for as long as this one has

You can work around not being able to pass a custom delta by doing this

func _physics_process(delta: float) -> void:
  var old_velocity := character_body.velocity
  character_body.velocity = old_velocity * custom_delta / delta
  character_body.move_and_slide()
  # set it back to the original value in case you're doing something persistent to the velocity (like acceleration)
  character_body.velocity = old_velocity

@JoNax97
Copy link
Copy Markdown
Contributor

JoNax97 commented Oct 15, 2025

Hi! Is there any way of stopping the engine from automatically calling the physics step? I want to have full control over the default physics space

Edit: I see there's PhysicsEngine3D.Singleton.SetActive(false) but it also prevents manual stepping.

@CREAsTIVE
Copy link
Copy Markdown

Hi! Is there any way of stopping the engine from automatically calling the physics step?

You can check physics_rollback_test.zip (from the first PR message)

lucacicada added a commit to lucacicada/voidine that referenced this pull request Oct 18, 2025
@AR-DEV-1 AR-DEV-1 mentioned this pull request Oct 21, 2025
5 tasks
@AddisonH

This comment was marked as off-topic.

@Togira123
Copy link
Copy Markdown
Contributor

I think the only thing that would need to be done before a merge is the proposed fix for JoltPhysics. I'd love to see this in 4.6 as well!

@AddisonH

This comment was marked as off-topic.

@AddisonH
Copy link
Copy Markdown

@AThousandShips I've been following this PR religiously for some time now. I don't think I've seen any further concerns that should be addressed in this PR. Mostly there have been rollback related conversations that are out of scope. The author has clarified this point about a dozen times. What specifically needs to happen to get this work across the finish line?

@papaIOprog
Copy link
Copy Markdown

@AThousandShips

Please see the conversation, there's several open issues related to this.

Please, help me find which issues block this PR. I looked through history and I didn't found one.
I would be happy to contribute and move this forward.

@clayjohn
Copy link
Copy Markdown
Member

Hi folks, I am writing this comment to provide some clarity on what the Godot maintainers’ view on this PR is and particularly, what the plan is with respect to merging this PR. Obviously the feature freeze for 4.6 has come and gone without an update from us, so I want to provide some clarity on the status.

This PR has been discussed among maintainers extensively. A few of us have carefully read all of the discussion around this PR and evaluated the code. Ultimately however, we have not come to a consensus about what should happen with this PR. Lack of consensus always means that we will not merge a PR.

I could go on at length about why we could not reach a consensus on very specific and technical points, but ultimately the problem is that we do not have a dedicated physics maintainer who can evaluate this kind of risky PR, ensure it fits well with the physics system overall, and maintain the feature going forward.

This is also why it has taken us so long to provide a comprehensive response. No single person is responsible for this and it should come as no surprise that it is difficult to find the time as a volunteer to write up a response to a PR that has gained so much hype, especially when us volunteers have so much pressure to work on other parts of the engine.

Lack of a dedicated maintainer is a huge problem for us, this feature has already led to a huge amount of debate among people who want it merged since everyone in the discussion has a different vision for what it should do. Some commenters want full physics rollback (which this doesn’t provide), others want to use it to implement full physics rollback on their own (which would require very extensive changes in other parts of the engine as well), while others need to it to assist in partial rollback for animations or other systems which are more forgiving to the lack of determinism, or other more limited cases. We need a person who can evaluate user need and arrive at a singular vision for the physics system and act on that vision (i.e. decide on what use-cases are supported and which aren't).

What we need ultimately, is a person who can decide on what use-cases are supported and which aren't and help get the rest of the engine into a state where most users who need manual physics stepping can be accommodated. Doing so would be extremely hard since we need to figure out how to expose this feature in a way that isn’t going to lead to a lot of people being disappointed and claiming that it is broken (see the comments above for several examples of this PR failing to meet expectations for some users).

The fact is, this PR works great for some people, and not for others. But for everyone, it looks like the thing that will solve their problems, and that makes it incredibly risky. Very likely, if we merged this without a proper implementation plan and strong communication, we would be flooded with people claiming it is broken and needs to be overhauled because they had unfair expectations of what manual physics stepping can be used for.

This again is why we need a dedicated physics maintainer who can fit this work into a grand vision for the physics system, prepare proper communication and documentation, ensure that the limitations of the system are easy to discover, and be on standby to provide the necessary support after this is merged.

We don’t have that person, so we can’t go ahead and merge this PR. Our recommendation for anyone who finds it useful for their project is to continue using it. If the code as-is works for you, then please go ahead and use it. We just aren’t prepared to support this in upstream Godot any time soon. But that is the beauty of open source, Godot is yours to modify and tweak however you need.

The good news is we are working as hard as we can to increase funding to the project so we can hire a dedicated physics maintainer. Our vision for the future is that all major areas of the engine will have a dedicated expert assigned who has the time and knowledge to help PRs like this get past the finish line. But we aren’t there yet, so we need to be cautious and play it safe. We would rather lack exciting features than merge features that we aren’t prepared to support properly.

Thank you everyone for your interest and enthusiasm, and thank you especially to Daylily-Zeleen for your work on this PR and for your continued support of it.

@ashtonmeuser
Copy link
Copy Markdown
Contributor

I would love to see this in the Godot Org. priorities. The lack of prioritized physics-related items (none for 2D physics) is discouraging considering the state and the roughly five years since the departure of the sole physics maintainer. Hoping this is the year 🙂

@ashtonmeuser
Copy link
Copy Markdown
Contributor

Hey, folks! I'm a huge fan of this PR and have been using it for some time. While not addressing exactly the same functionality, I've created a related PR here: #116230. It may satisfy some of the use cases highlighted above and also provides some comparison with this PR. Would love to gather feedback. I'm hoping this is a lower impact change and therefore more likely to be approved without huge implications for the external PhysicsServer2/3D API considering the current lack of a Godot physics lead. Thanks!

@Repiteo Repiteo requested a review from a team as a code owner February 17, 2026 20:11
@nonchip
Copy link
Copy Markdown

nonchip commented Feb 20, 2026

@clayjohn yeah sorry i don't get it. you merged whole new physics engines since this PR is open. but a thing everyone agrees works right now and doesnt conflict with anything can't be merged, because it's not documented enough despite being documented in the PR? have you seen half the docs for physics and language extensions? MultiplayerSynchronizer? the stencil buffer?

your vision for the future is nice and well but why on earth would you go "no further development at all in this part of the engine despite there obviously is way more development happening that's way more intrusive, until we have successfully achieved a bus factor of 1 for no reason"?!

if there's no "one person with all the plan" right now, a) what else is new and b) that means right now is the best time to make the thing we already got more usable before someone with a plan can figure out a better way years in the future.

Some commenters want full physics rollback (which this doesn’t provide), others want to use it to implement full physics rollback on their own (which would require very extensive changes in other parts of the engine as well)

this also just seems to be incorrect. i see no reason why this PR should not provide those things. infact it's required to make those things possible. so yes, it very much does provide all those things by being required to make any of those things. nobody's asking for it to already have written the game. since when is that a criteria!

also, that said, who would have been in the position to hire such a person all those years, Mr Secretary of the Board of Directors? because if that's not very much you (either personally, or the vague group of "maintainers" you're citing), then i also doubt your authority to claim "no further physics in godot" while further physics in godot have been happening all along and this PR doesn't even actually change them at all, just adds a few lines of glue code to expose some existing api to the ClassDB.

@clayjohn
Copy link
Copy Markdown
Member

but a thing everyone agrees works right now and doesnt conflict with anything can't be merged, because it's not documented enough despite being documented in the PR? have you seen half the docs for physics and language extensions? MultiplayerSynchronizer? the stencil buffer?

It's pretty clear you didn't read my comment or any of the discussion above. Not everyone agrees that this PR works. There is significant disagreement in fact. Literally just read the comments on this PR, even the people who want this merged can't agree one whether this actually works for their use-cases. Further, documentation is only one small part of what a physics maintainer is needed for, I barely mentioned it in my comment. Assuming that documentation is the only blocker is the silliest straw man argument I have seen on here in a very long time.

I won't respond to the rest of your comment as you take an aggressive and condescending tone over something you clearly have no knowledge about. Further, it is clear you are not making a good faith comment. You are lashing out and resorting to personal attacks over something you clearly don't understand.

You don't have to agree with any decisions made by the project, but you do have to live with them. Although, since this is an open source project, you always have the option to compile the engine yourself with this feature included. It would have taken you less time to do that than to post your misinformed rant.

As a reminder for other readers, we are actively looking for a physics maintainer who can help lead the development of the physics systems in Godot. If you think that could be you, please reach out.

@nonchip
Copy link
Copy Markdown

nonchip commented Feb 20, 2026

disagreeing with your decision while pointing out the facts contradicting your statement makes my point too invalid to even mention? that's a new one for sure.

i went into multiple of your points and showing why they dont add up to me. can you at least give me the benefit of the doubt and do the same instead of deciding and judging my alleged state of mind and literally personal attacks.

because "as far as i know you are (one of) the guy(s) in charge of this thing you say didnt happen" is an argument, not an attack. "it is clear you're acting in bad faith", "silliest thing i've ever read (when i'm literally responding to one of your arguments)" however...?

and no, if i scroll up here i dont see anyone assuming this would allow them to do a thing it doesn't. it doesn't just do that thing for them out of the box, but i already mentioned that in my earlier comment.

@akien-mga
Copy link
Copy Markdown
Member

@nonchip You are clearly breaking the Godot Code of Conduct here. I'm blocking your account for one week, please stay constructive and respectful if you intend to engage again afterwards.

@Daylily-Zeleen Daylily-Zeleen force-pushed the daylily-zeleen/physics_space_step branch from e370ed7 to d99c902 Compare February 22, 2026 07:37
@Daylily-Zeleen Daylily-Zeleen force-pushed the daylily-zeleen/physics_space_step branch from d99c902 to 5b59c33 Compare February 22, 2026 08:13
@Daylily-Zeleen
Copy link
Copy Markdown
Contributor Author

Daylily-Zeleen commented Feb 22, 2026

I found that the flag flushing_queries can be change in flush_queries() and space_flush_queries() currently, it will lead to data racing in threadings.

I have 2 solutions:

  1. Change this flag to a counter with atomic integer, increase and decrease it in flush_queries() and space_flush_queries(), any non-0 value will let is_flushing_queries() return true.
  2. a) Remove flushing_queries from XXXPhysicsServeXD and let it as member of XXXSpaceXD, set this flag when flusing physics space's queries.
    b) Add an argument with default value "RID p_space = RID()" to is_flushing_queries(), pass invalid rid will return true if there have any space is flushing queries, or pass specific space's rid to query its flushing state.

Both of these solutions come with a few performance loss. The first one, because of the atomic value, it should be changed during each iteration. The second plan, the query process will become much more complicated, this only effect on AreaXD::set_monitorable() in godot, but I'm not sure how widely is_flushing_queries() is using by users.

I'm not sure that which one is better. Hope someone can give me some advice.


Additional, I think space_flush_queries() can be integrated to space_step(), because it is always should be called before space_step().
However, even with integration, the aforementioned issues still persist, thus making these two completely different topics.

@albertok
Copy link
Copy Markdown

albertok commented Mar 8, 2026

@Daylily-Zeleen The PR is perfect as it is. It's minimal, flexible and works great.

The fact you can flush at will is a good feature, it means you have the flexibility to do things like trajectory prediction efficiently without thrashing the engine for no reason.

Keep them seperate imo.

You'll note Rapier physics also allowed this with the aim of not constraining performance if you really need to push things.

https://godot.rapier.rs/docs/documentation/performance#disabling-state_sync_callback

In rollback games where doing 30+ ticks of re-simulation is common you need every bit of performance you can get. This might mean not relying on Godot node features like signals and just letting the game play out in the simulation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose PhysicsServer's (and Physics2DServer's) space_step