fix(Prediction): ConfigurableJoints' range of motion is now moved correctly

This commit is contained in:
mischa 2024-02-16 10:59:45 +01:00
parent 78cdaef9c4
commit 2ed1d7329c
2 changed files with 56 additions and 8 deletions

View File

@ -102,6 +102,11 @@ public class PredictedRigidbody : NetworkBehaviour
// we also create one extra ghost for the exact known server state. // we also create one extra ghost for the exact known server state.
protected GameObject remoteCopy; protected GameObject remoteCopy;
// joints
Vector3 initialPosition;
Quaternion initialRotation;
Vector3 initialScale;
void Awake() void Awake()
{ {
tf = transform; tf = transform;
@ -111,6 +116,11 @@ void Awake()
// cache some threshold to avoid calculating them in LateUpdate // cache some threshold to avoid calculating them in LateUpdate
float colliderSize = GetComponentInChildren<Collider>().bounds.size.magnitude; float colliderSize = GetComponentInChildren<Collider>().bounds.size.magnitude;
smoothFollowThreshold = colliderSize * teleportDistanceMultiplier; smoothFollowThreshold = colliderSize * teleportDistanceMultiplier;
// cache initial position/rotation/scale to be used when moving physics components (configurable joints' range of motion)
initialPosition = tf.position;
initialRotation = tf.rotation;
initialScale = tf.localScale;
} }
protected virtual void CopyRenderersAsGhost(GameObject destination, Material material) protected virtual void CopyRenderersAsGhost(GameObject destination, Material material)
@ -164,9 +174,6 @@ protected virtual void CreateGhosts()
// if we copy localScale then the copy has scale=0.5, where as the // if we copy localScale then the copy has scale=0.5, where as the
// original would have a global scale of ~1.0. // original would have a global scale of ~1.0.
physicsCopy = new GameObject($"{name}_Physical"); physicsCopy = new GameObject($"{name}_Physical");
physicsCopy.transform.position = tf.position; // world position!
physicsCopy.transform.rotation = tf.rotation; // world rotation!
physicsCopy.transform.localScale = tf.lossyScale; // world scale!
// assign the same Layer for the physics copy. // assign the same Layer for the physics copy.
// games may use a custom physics collision matrix, layer matters. // games may use a custom physics collision matrix, layer matters.
@ -176,8 +183,25 @@ protected virtual void CreateGhosts()
PredictedRigidbodyPhysicsGhost physicsGhostRigidbody = physicsCopy.AddComponent<PredictedRigidbodyPhysicsGhost>(); PredictedRigidbodyPhysicsGhost physicsGhostRigidbody = physicsCopy.AddComponent<PredictedRigidbodyPhysicsGhost>();
physicsGhostRigidbody.target = tf; physicsGhostRigidbody.target = tf;
// move the rigidbody component & all colliders to the physics GameObject // when moving (Configurable)Joints, their range of motion is
// relative to the initial position. if we move them after the
// GameObject rotated, the range of motion is wrong.
// the easiest solution is to move to initial position,
// then move physics components, then move back.
// => remember previous
Vector3 position = tf.position;
Quaternion rotation = tf.rotation;
Vector3 scale = tf.localScale;
// => reset to initial
physicsGhostRigidbody.transform.position = tf.position = initialPosition;
physicsGhostRigidbody.transform.rotation = tf.rotation = initialRotation;
physicsGhostRigidbody.transform.localScale = tf.localScale = initialScale;
// => move physics components
PredictionUtils.MovePhysicsComponents(gameObject, physicsCopy); PredictionUtils.MovePhysicsComponents(gameObject, physicsCopy);
// => reset previous
physicsGhostRigidbody.transform.position = tf.position = position;
physicsGhostRigidbody.transform.rotation = tf.rotation = rotation;
physicsGhostRigidbody.transform.localScale = tf.localScale = scale;
// show ghost by copying all renderers / materials with ghost material applied // show ghost by copying all renderers / materials with ghost material applied
if (showGhost) if (showGhost)
@ -213,7 +237,27 @@ protected virtual void DestroyGhosts()
// otherwise next time they wouldn't have a collider anymore. // otherwise next time they wouldn't have a collider anymore.
if (physicsCopy != null) if (physicsCopy != null)
{ {
// when moving (Configurable)Joints, their range of motion is
// relative to the initial position. if we move them after the
// GameObject rotated, the range of motion is wrong.
// the easiest solution is to move to initial position,
// then move physics components, then move back.
// => remember previous
Vector3 position = tf.position;
Quaternion rotation = tf.rotation;
Vector3 scale = tf.localScale;
// => reset to initial
physicsCopy.transform.position = tf.position = initialPosition;
physicsCopy.transform.rotation = tf.rotation = initialRotation;
physicsCopy.transform.localScale = tf.localScale = initialScale;
// => move physics components
PredictionUtils.MovePhysicsComponents(physicsCopy, gameObject); PredictionUtils.MovePhysicsComponents(physicsCopy, gameObject);
// => reset previous
tf.position = position;
tf.rotation = rotation;
tf.localScale = scale;
// when moving components back, we need to undo the joints initial-delta rotation that we added.
Destroy(physicsCopy); Destroy(physicsCopy);
// reassign our Rigidbody reference // reassign our Rigidbody reference

View File

@ -30,8 +30,12 @@ public static void MoveRigidbody(GameObject source, GameObject destination)
rigidbodyCopy.constraints = original.constraints; rigidbodyCopy.constraints = original.constraints;
rigidbodyCopy.sleepThreshold = original.sleepThreshold; rigidbodyCopy.sleepThreshold = original.sleepThreshold;
rigidbodyCopy.freezeRotation = original.freezeRotation; rigidbodyCopy.freezeRotation = original.freezeRotation;
rigidbodyCopy.position = original.position;
rigidbodyCopy.rotation = original.rotation; // moving (Configurable)Joints messes up their range of motion unless
// we reset to initial position first (we do this in PredictedRigibody.cs).
// so here we don't set the Rigidbody's physics position at all.
// rigidbodyCopy.position = original.position;
// rigidbodyCopy.rotation = original.rotation;
// projects may keep Rigidbodies as kinematic sometimes. in that case, setting velocity would log an error // projects may keep Rigidbodies as kinematic sometimes. in that case, setting velocity would log an error
if (!original.isKinematic) if (!original.isKinematic)
@ -250,10 +254,10 @@ public static void MoveConfigurableJoints(GameObject source, GameObject destinat
jointCopy.connectedMassScale = sourceJoint.connectedMassScale; jointCopy.connectedMassScale = sourceJoint.connectedMassScale;
jointCopy.enableCollision = sourceJoint.enableCollision; jointCopy.enableCollision = sourceJoint.enableCollision;
jointCopy.enablePreprocessing = sourceJoint.enablePreprocessing; jointCopy.enablePreprocessing = sourceJoint.enablePreprocessing;
jointCopy.highAngularXLimit = sourceJoint.highAngularXLimit; jointCopy.highAngularXLimit = sourceJoint.highAngularXLimit; // moving this only works if the object is at initial position/rotation/scale, see PredictedRigidbody.cs
jointCopy.linearLimitSpring = sourceJoint.linearLimitSpring; jointCopy.linearLimitSpring = sourceJoint.linearLimitSpring;
jointCopy.linearLimit = sourceJoint.linearLimit; jointCopy.linearLimit = sourceJoint.linearLimit;
jointCopy.lowAngularXLimit = sourceJoint.lowAngularXLimit; jointCopy.lowAngularXLimit = sourceJoint.lowAngularXLimit; // moving this only works if the object is at initial position/rotation/scale, see PredictedRigidbody.cs
jointCopy.massScale = sourceJoint.massScale; jointCopy.massScale = sourceJoint.massScale;
jointCopy.projectionAngle = sourceJoint.projectionAngle; jointCopy.projectionAngle = sourceJoint.projectionAngle;
jointCopy.projectionDistance = sourceJoint.projectionDistance; jointCopy.projectionDistance = sourceJoint.projectionDistance;