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.
protected GameObject remoteCopy;
// joints
Vector3 initialPosition;
Quaternion initialRotation;
Vector3 initialScale;
void Awake()
{
tf = transform;
@ -111,6 +116,11 @@ void Awake()
// cache some threshold to avoid calculating them in LateUpdate
float colliderSize = GetComponentInChildren<Collider>().bounds.size.magnitude;
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)
@ -164,9 +174,6 @@ protected virtual void CreateGhosts()
// if we copy localScale then the copy has scale=0.5, where as the
// original would have a global scale of ~1.0.
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.
// games may use a custom physics collision matrix, layer matters.
@ -176,8 +183,25 @@ protected virtual void CreateGhosts()
PredictedRigidbodyPhysicsGhost physicsGhostRigidbody = physicsCopy.AddComponent<PredictedRigidbodyPhysicsGhost>();
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);
// => 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
if (showGhost)
@ -213,7 +237,27 @@ protected virtual void DestroyGhosts()
// otherwise next time they wouldn't have a collider anymore.
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);
// => 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);
// reassign our Rigidbody reference

View File

@ -30,8 +30,12 @@ public static void MoveRigidbody(GameObject source, GameObject destination)
rigidbodyCopy.constraints = original.constraints;
rigidbodyCopy.sleepThreshold = original.sleepThreshold;
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
if (!original.isKinematic)
@ -250,10 +254,10 @@ public static void MoveConfigurableJoints(GameObject source, GameObject destinat
jointCopy.connectedMassScale = sourceJoint.connectedMassScale;
jointCopy.enableCollision = sourceJoint.enableCollision;
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.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.projectionAngle = sourceJoint.projectionAngle;
jointCopy.projectionDistance = sourceJoint.projectionDistance;