Open krazyjakee opened 2 months ago
RidigBodies don't directly support being moved the same way most other nodes do. They're expected to be driven almost exclusively by the physics engine.
TickInterpolator which is interpolating position between ticks is probably not the right tool to use.
The closest we can probably get is to sync physic states from the authoritative server and copy and apply them to the clients
This probably needs it's own dedicated Node. RigidBodySynchronizer?
It won't be able to participate in rollback either since there's no current way to run more than a single physics simulation per frame in Godot.
There's an implementation here of what a physics synchronizer can look like:
Copying the code to here as things tend to disappear on the Internet over time.
extends MultiplayerSynchronizer
class_name PhysicsSynchronizer
@export var sync_bstate_array : Array = \
[0, Vector3.ZERO, Quaternion.IDENTITY, Vector3.ZERO, Vector3.ZERO]
@onready var sync_object : RigidBody3D = get_node(root_path)
@onready var body_state : PhysicsDirectBodyState3D = \
PhysicsServer3D.body_get_direct_state( sync_object.get_rid() )
var frame : int = 0
var last_frame : int = 0
enum {
FRAME,
ORIGIN,
QUAT, # the quaternion is used for an optimized rotation state
LIN_VEL,
ANG_VEL,
}
#copy state to array
func get_state( state, array ):
array[ORIGIN] = state.transform.origin
array[QUAT] = state.transform.basis.get_rotation_quaternion()
array[LIN_VEL] = state.linear_velocity
array[ANG_VEL] = state.angular_velocity
#copy array to state
func set_state( array, state ):
state.transform.origin = array[ORIGIN]
state.transform.basis = Basis( array[QUAT] )
state.linear_velocity = array[LIN_VEL]
state.angular_velocity = array[ANG_VEL]
func get_physics_body_info():
# server copy for sync
get_state( body_state, sync_bstate_array )
func set_physics_body_info():
# client rpc set from server
set_state( sync_bstate_array, body_state )
func _physics_process(_delta):
if is_multiplayer_authority() and sync_object.visible:
frame += 1
sync_bstate_array[FRAME] = frame
get_physics_body_info()
# make sure to wire the "synchronized" signal to this function
func _on_synchronized():
correct_error()
# is this necessary?
if is_previouse_frame():
return
set_physics_body_info()
# very basic network jitter reduction
func correct_error():
var diff :Vector3= body_state.transform.origin - sync_bstate_array[ORIGIN]
# print(name,": diff origin ", diff.length())
# correct minor error, but snap to incoming state if too far from reality
if diff.length() < 3.0:
sync_bstate_array[ORIGIN] = body_state.transform.origin.lerp(sync_bstate_array[ORIGIN],0.05)
func is_previouse_frame() -> bool:
if sync_bstate_array[FRAME] <= last_frame:
return true
else:
last_frame = sync_bstate_array[FRAME]
return false
Actually looking at the correct_error section in the code above maybe TickInterpolator can work if its set to transform.origin
A few comments in other places mention its a hack that lets you mess with the position of a RigidBody
Actually looking at the correct_error section in the code above maybe TickInterpolator can work if its set to transform.origin
A few comments in other places mention its a hack that lets you mess with the position of a RigidBody
what would be the property path of transform.origin, because my netfox gives warnings that it's an invalid path
also if I put that code above on a player rigidbody it desyncs even on localhost and honestly I'm kinda at a loss as to why
@homhomhomhomhom
netfox gives warnings that it's an invalid path
Are you typing it like :transform.origin
? It works for me.
@albertok
maybe TickInterpolator can work if its set to transform.origin
This technically fixes the issue and while there's a definite improvement over no interpolation, it is still quite jumpy and unstable.
https://github.com/user-attachments/assets/0a04cdb2-2d69-4ddf-9a42-daeb79aa3e76
There's an implementation here
Using this implementation, the physics are silky smooth on the client.
https://github.com/user-attachments/assets/6f2c8f1b-6cd3-4f0a-b30a-f05e77acb9e7
Adding this _ready function to the code also removes any need for configuration...
func _ready():
connect("synchronized", _on_synchronized)
replication_config.add_property("%s:sync_bstate_array" % self.get_path())
new-game-project.zip
Netfox: 1.8.0 Godot 4.3