diff --git a/Assets/CONTENT/Game/Scripts/Interaction/NetworkHeldInteractable.cs b/Assets/CONTENT/Game/Scripts/Interaction/NetworkHeldInteractable.cs index 291f268..8874e20 100644 --- a/Assets/CONTENT/Game/Scripts/Interaction/NetworkHeldInteractable.cs +++ b/Assets/CONTENT/Game/Scripts/Interaction/NetworkHeldInteractable.cs @@ -15,12 +15,15 @@ public abstract class NetworkHeldInteractable : NetworkInteractable, IWorkstatio [SerializeField, Min(0.05f)] private float collisionRefreshInterval = 0.25f; [Header("Holding Follow")] - [SerializeField, Min(0.01f)] private float positionSmoothTime = 0.12f; [SerializeField, Min(0.1f)] private float maximumFollowSpeed = 12f; - [SerializeField, Min(0.1f)] private float maximumFollowDistance = 1.25f; [SerializeField, Min(0.5f)] private float maximumHoldDistance = 4.5f; [SerializeField, Min(0.1f)] private float rotationFollowSpeed = 14f; + [Header("Local Presentation")] + [Tooltip("Removes the server round-trip delay for the player currently carrying the item.")] + [SerializeField] private bool correctLocalHolderPresentation = true; + [SerializeField, Min(0.1f)] private float maximumLocalCorrectionDistance = 2f; + [Header("Throw")] [SerializeField, Min(0f)] private float throwForce = 6f; [SerializeField, Min(0f)] private float throwUpwardForce = 0.5f; @@ -38,6 +41,7 @@ public abstract class NetworkHeldInteractable : NetworkInteractable, IWorkstatio private readonly HashSet ignoredPlayerColliders = new(); private readonly HashSet desiredIgnoredColliders = new(); private readonly List collidersToRestore = new(); + private readonly RaycastHit[] localCorrectionHits = new RaycastHit[24]; private float nextCollisionRefreshTime; private bool initialUseGravity; private CollisionDetectionMode initialCollisionDetectionMode; @@ -125,6 +129,28 @@ public abstract class NetworkHeldInteractable : NetworkInteractable, IWorkstatio } + private void LateUpdate() + { + if (!correctLocalHolderPresentation || IsServer || !IsSpawned || + !IsHeldByLocalPlayer || IsExternallyControlled || physicsBody == null || + !TryGetPlayerObject(holderClientId.Value, out NetworkObject holder)) + { + return; + } + + Transform anchor = ResolveHoldingAnchor(holder); + Vector3 targetPosition = anchor.TransformPoint(HoldingOffset); + Vector3 targetDelta = targetPosition - physicsBody.position; + if (targetDelta.sqrMagnitude > + maximumLocalCorrectionDistance * maximumLocalCorrectionDistance || + HasBlockingColliderForLocalCorrection(targetDelta)) + { + return; + } + + transform.SetPositionAndRotation(targetPosition, anchor.rotation); + } + public void RequestThrow() { if (IsSpawned && IsHeldByLocalPlayer) @@ -266,17 +292,10 @@ public abstract class NetworkHeldInteractable : NetworkInteractable, IWorkstatio return false; } - float distance = targetDelta.magnitude; - float speedDistance = Mathf.Max(maximumFollowDistance, 0.01f); - float desiredSpeed = maximumFollowSpeed * Mathf.Clamp01(distance / speedDistance); - Vector3 desiredVelocity = distance > 0.0001f - ? targetDelta / distance * desiredSpeed - : Vector3.zero; - float maximumAcceleration = maximumFollowSpeed / Mathf.Max(positionSmoothTime, 0.01f); - Vector3 velocityChange = Vector3.ClampMagnitude( - desiredVelocity - physicsBody.linearVelocity, - maximumAcceleration * Time.fixedDeltaTime); - physicsBody.AddForce(velocityChange, ForceMode.VelocityChange); + Vector3 targetVelocity = targetDelta / Mathf.Max(Time.fixedDeltaTime, 0.001f); + physicsBody.linearVelocity = Vector3.ClampMagnitude( + targetVelocity, + maximumFollowSpeed); DriveRotation(anchor.rotation); return true; @@ -292,15 +311,52 @@ public abstract class NetworkHeldInteractable : NetworkInteractable, IWorkstatio if (angle > 180f) angle -= 360f; - Vector3 desiredAngularVelocity = axis.normalized * - (angle * Mathf.Deg2Rad * rotationFollowSpeed); - desiredAngularVelocity = Vector3.ClampMagnitude( - desiredAngularVelocity, + Vector3 targetAngularVelocity = axis.normalized * + (angle * Mathf.Deg2Rad / Mathf.Max(Time.fixedDeltaTime, 0.001f)); + physicsBody.angularVelocity = Vector3.ClampMagnitude( + targetAngularVelocity, rotationFollowSpeed); - Vector3 angularVelocityChange = Vector3.ClampMagnitude( - desiredAngularVelocity - physicsBody.angularVelocity, - rotationFollowSpeed * 4f * Time.fixedDeltaTime); - physicsBody.AddTorque(angularVelocityChange, ForceMode.VelocityChange); + } + + private bool HasBlockingColliderForLocalCorrection(Vector3 targetDelta) + { + float distance = targetDelta.magnitude; + if (distance <= 0.0001f) + return false; + + int hitCount = Physics.RaycastNonAlloc( + physicsBody.position, + targetDelta / distance, + localCorrectionHits, + distance, + ~0, + QueryTriggerInteraction.Ignore); + + for (int hitIndex = 0; hitIndex < hitCount; hitIndex++) + { + Collider hitCollider = localCorrectionHits[hitIndex].collider; + if (hitCollider == null || + ignoredPlayerColliders.Contains(hitCollider) || + IsObjectCollider(hitCollider)) + { + continue; + } + + return true; + } + + return false; + } + + private bool IsObjectCollider(Collider candidate) + { + foreach (Collider objectCollider in objectColliders) + { + if (objectCollider == candidate) + return true; + } + + return false; } private void HandleHolderChanged(ulong _, ulong __) => ApplyHoldingState(); diff --git a/Assets/CONTENT/Scenes/Factory.unity b/Assets/CONTENT/Scenes/Factory.unity index 4668974..a840f92 100644 --- a/Assets/CONTENT/Scenes/Factory.unity +++ b/Assets/CONTENT/Scenes/Factory.unity @@ -341314,7 +341314,7 @@ PrefabInstance: objectReference: {fileID: 2055952141} - target: {fileID: 1187608763751665767, guid: 8cc9c4d7d9e04d640bbd0616e7926670, type: 3} propertyPath: m_VersionIndex - value: 1189 + value: 1192 objectReference: {fileID: 0} - target: {fileID: 2057801181638564846, guid: 8cc9c4d7d9e04d640bbd0616e7926670, type: 3} propertyPath: m_Mesh @@ -363686,7 +363686,7 @@ PrefabInstance: objectReference: {fileID: 1230761045} - target: {fileID: 5333406013407316476, guid: 6eee6df5dab550446b41008fa6fff89a, type: 3} propertyPath: m_VersionIndex - value: 3432 + value: 3435 objectReference: {fileID: 0} - target: {fileID: 5521966715159789732, guid: 6eee6df5dab550446b41008fa6fff89a, type: 3} propertyPath: m_LocalPosition.x