perf(Prediction): cache .transform getter because this is performance critical

This commit is contained in:
mischa 2024-01-28 12:12:12 +01:00
parent 5e56b96f43
commit f926514a6e

View File

@ -23,6 +23,7 @@ public enum CorrectionMode
// [RequireComponent(typeof(Rigidbody))] <- RB is moved out at runtime, can't require it. // [RequireComponent(typeof(Rigidbody))] <- RB is moved out at runtime, can't require it.
public class PredictedRigidbody : NetworkBehaviour public class PredictedRigidbody : NetworkBehaviour
{ {
Transform tf; // this component is performance critical. cache .transform getter!
Rigidbody rb; // own rigidbody on server. this is never moved to a physics copy. Rigidbody rb; // own rigidbody on server. this is never moved to a physics copy.
Vector3 lastPosition; Vector3 lastPosition;
@ -87,6 +88,7 @@ public class PredictedRigidbody : NetworkBehaviour
void Awake() void Awake()
{ {
tf = transform;
rb = GetComponent<Rigidbody>(); rb = GetComponent<Rigidbody>();
if (rb == null) throw new InvalidOperationException($"Prediction: {name} is missing a Rigidbody component."); if (rb == null) throw new InvalidOperationException($"Prediction: {name} is missing a Rigidbody component.");
} }
@ -142,9 +144,9 @@ 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 = transform.position; // world position! physicsCopy.transform.position = tf.position; // world position!
physicsCopy.transform.rotation = transform.rotation; // world rotation! physicsCopy.transform.rotation = tf.rotation; // world rotation!
physicsCopy.transform.localScale = transform.lossyScale; // world scale! 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.
@ -178,9 +180,9 @@ 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.
remoteCopy = new GameObject($"{name}_Remote"); remoteCopy = new GameObject($"{name}_Remote");
remoteCopy.transform.position = transform.position; // world position! remoteCopy.transform.position = tf.position; // world position!
remoteCopy.transform.rotation = transform.rotation; // world rotation! remoteCopy.transform.rotation = tf.rotation; // world rotation!
remoteCopy.transform.localScale = transform.lossyScale; // world scale! remoteCopy.transform.localScale = tf.lossyScale; // world scale!
PredictedRigidbodyRemoteGhost predictedGhost = remoteCopy.AddComponent<PredictedRigidbodyRemoteGhost>(); PredictedRigidbodyRemoteGhost predictedGhost = remoteCopy.AddComponent<PredictedRigidbodyRemoteGhost>();
predictedGhost.target = this; predictedGhost.target = this;
predictedGhost.ghostDistanceThreshold = ghostDistanceThreshold; predictedGhost.ghostDistanceThreshold = ghostDistanceThreshold;
@ -213,17 +215,17 @@ protected virtual void DestroyGhosts()
protected virtual void SmoothFollowPhysicsCopy() protected virtual void SmoothFollowPhysicsCopy()
{ {
// hard follow: // hard follow:
// transform.position = physicsCopyCollider.position; // tf.position = physicsCopyCollider.position;
// transform.rotation = physicsCopyCollider.rotation; // tf.rotation = physicsCopyCollider.rotation;
// if we are further than N colliders sizes behind, then teleport // if we are further than N colliders sizes behind, then teleport
float colliderSize = physicsCopyCollider.bounds.size.magnitude; float colliderSize = physicsCopyCollider.bounds.size.magnitude;
float threshold = colliderSize * teleportDistanceMultiplier; float threshold = colliderSize * teleportDistanceMultiplier;
float distance = Vector3.Distance(transform.position, physicsCopyRigidbody.position); float distance = Vector3.Distance(tf.position, physicsCopyRigidbody.position);
if (distance > threshold) if (distance > threshold)
{ {
transform.position = physicsCopyRigidbody.position; tf.position = physicsCopyRigidbody.position;
transform.rotation = physicsCopyRigidbody.rotation; tf.rotation = physicsCopyRigidbody.rotation;
Debug.Log($"[PredictedRigidbody] Teleported because distance to physics copy = {distance:F2} > threshold {threshold:F2}"); Debug.Log($"[PredictedRigidbody] Teleported because distance to physics copy = {distance:F2} > threshold {threshold:F2}");
return; return;
} }
@ -231,11 +233,11 @@ protected virtual void SmoothFollowPhysicsCopy()
// smoothly interpolate to the target position. // smoothly interpolate to the target position.
// speed relative to how far away we are // speed relative to how far away we are
float positionStep = distance * positionInterpolationSpeed; float positionStep = distance * positionInterpolationSpeed;
transform.position = Vector3.MoveTowards(transform.position, physicsCopyRigidbody.position, positionStep * Time.deltaTime); tf.position = Vector3.MoveTowards(tf.position, physicsCopyRigidbody.position, positionStep * Time.deltaTime);
// smoothly interpolate to the target rotation. // smoothly interpolate to the target rotation.
// Quaternion.RotateTowards doesn't seem to work at all, so let's use SLerp. // Quaternion.RotateTowards doesn't seem to work at all, so let's use SLerp.
transform.rotation = Quaternion.Slerp(transform.rotation, physicsCopyRigidbody.rotation, rotationInterpolationSpeed * Time.deltaTime); tf.rotation = Quaternion.Slerp(tf.rotation, physicsCopyRigidbody.rotation, rotationInterpolationSpeed * Time.deltaTime);
} }
// creater visual copy only on clients, where players are watching. // creater visual copy only on clients, where players are watching.
@ -255,9 +257,9 @@ public override void OnStopClient()
void UpdateServer() void UpdateServer()
{ {
// to save bandwidth, we only serialize when position changed // to save bandwidth, we only serialize when position changed
// if (Vector3.Distance(transform.position, lastPosition) >= positionSensitivity) // if (Vector3.Distance(tf.position, lastPosition) >= positionSensitivity)
// { // {
// lastPosition = transform.position; // lastPosition = tf.position;
// SetDirty(); // SetDirty();
// } // }
@ -405,7 +407,7 @@ void OnReceivedState(double timestamp, RigidbodyState state)
{ {
remoteCopy.transform.position = state.position; remoteCopy.transform.position = state.position;
remoteCopy.transform.rotation = state.rotation; remoteCopy.transform.rotation = state.rotation;
remoteCopy.transform.localScale = transform.lossyScale; // world scale! see CreateGhosts comment. remoteCopy.transform.localScale = tf.lossyScale; // world scale! see CreateGhosts comment.
} }
// OPTIONAL performance optimization when comparing idle objects. // OPTIONAL performance optimization when comparing idle objects.