mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 11:00:32 +00:00
fix recursive chain activations 'soft deadlock'
This commit is contained in:
parent
e2ab34a55e
commit
96af64f436
@ -48,6 +48,9 @@ public class ForecastRigidbody : NetworkBehaviour
|
|||||||
[Header("Collision Chaining")]
|
[Header("Collision Chaining")]
|
||||||
[Tooltip("Enable to have actively predicted Rigidbodies activate other Rigidbodies they collide with.")]
|
[Tooltip("Enable to have actively predicted Rigidbodies activate other Rigidbodies they collide with.")]
|
||||||
public bool collisionChaining = true;
|
public bool collisionChaining = true;
|
||||||
|
[Tooltip("If a player interacts with an object, it can recursively activate other objects it collides with.\nDepth is the chain link from A->B->C->etc., it doesn't mean the amount of A->B, A->C, etc.\nNeeds to be finite to avoid chain activations going on forever like A->B->C->A (easy to notice in the stacked prediction example.")]
|
||||||
|
public int maxCollisionChainingDepth = 2; // A->B->C is enough!
|
||||||
|
int remainingCollisionChainDepth;
|
||||||
|
|
||||||
// motion smoothing happen on-demand, because it requires moving physics components to another GameObject.
|
// motion smoothing happen on-demand, because it requires moving physics components to another GameObject.
|
||||||
// this only starts at a given velocity and ends when stopped moving.
|
// this only starts at a given velocity and ends when stopped moving.
|
||||||
@ -102,8 +105,7 @@ public override void OnStartClient()
|
|||||||
predictedRigidbody.isKinematic = true;
|
predictedRigidbody.isKinematic = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// client prediction API
|
void AddPredictedForceInternal(Vector3 force, ForceMode mode)
|
||||||
public void AddPredictedForce(Vector3 force, ForceMode mode)
|
|
||||||
{
|
{
|
||||||
// apply local force dampening.
|
// apply local force dampening.
|
||||||
// client applies a bit less force than the server, so that
|
// client applies a bit less force than the server, so that
|
||||||
@ -116,6 +118,26 @@ public void AddPredictedForce(Vector3 force, ForceMode mode)
|
|||||||
predictedRigidbody.AddForce(force, mode);
|
predictedRigidbody.AddForce(force, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// client prediction API
|
||||||
|
public void AddPredictedForce(Vector3 force, ForceMode mode)
|
||||||
|
{
|
||||||
|
// player interacted with this object explicitly.
|
||||||
|
// restart the collision chain at max.
|
||||||
|
remainingCollisionChainDepth = maxCollisionChainingDepth;
|
||||||
|
|
||||||
|
// add the predicted force
|
||||||
|
AddPredictedForceInternal(force, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddPredictedForceChain(Vector3 force, ForceMode mode, int newChainDepth)
|
||||||
|
{
|
||||||
|
// apply the collision chain depth
|
||||||
|
remainingCollisionChainDepth = newChainDepth;
|
||||||
|
|
||||||
|
// add the predicted force
|
||||||
|
AddPredictedForceInternal(force, mode);
|
||||||
|
}
|
||||||
|
|
||||||
protected void BeginPredicting()
|
protected void BeginPredicting()
|
||||||
{
|
{
|
||||||
predictedRigidbody.isKinematic = false; // full physics sync
|
predictedRigidbody.isKinematic = false; // full physics sync
|
||||||
@ -144,6 +166,8 @@ protected void BeginFollowing()
|
|||||||
predictedRigidbody.isKinematic = true; // full transform sync
|
predictedRigidbody.isKinematic = true; // full transform sync
|
||||||
state = ForecastState.FOLLOWING;
|
state = ForecastState.FOLLOWING;
|
||||||
if (debugColors) rend.material.color = originalColor;
|
if (debugColors) rend.material.color = originalColor;
|
||||||
|
// reset the collision chain depth so it starts at 0 again next time
|
||||||
|
remainingCollisionChainDepth = 0;
|
||||||
OnBeginFollow();
|
OnBeginFollow();
|
||||||
// Debug.Log($"{name} BEGIN FOLLOW");
|
// Debug.Log($"{name} BEGIN FOLLOW");
|
||||||
}
|
}
|
||||||
@ -317,6 +341,14 @@ void OnCollisionEnter(Collision collision)
|
|||||||
// is the other object already predicting? then don't call events again.
|
// is the other object already predicting? then don't call events again.
|
||||||
if (other.state != ForecastState.FOLLOWING) return;
|
if (other.state != ForecastState.FOLLOWING) return;
|
||||||
|
|
||||||
|
// collided with an object that has not yet been activate.
|
||||||
|
// should this object still activate other objects?
|
||||||
|
// for example, chain depth = 3 means: A->B->C.
|
||||||
|
// so C->D would not activate anymore.
|
||||||
|
// (we always need to check this only for inactivate objects,
|
||||||
|
// otherwise A->B->A->B etc. would reduce the chain depth forever).
|
||||||
|
if (remainingCollisionChainDepth <= 0) return;
|
||||||
|
|
||||||
// the other object is in FOLLOWING mode (kinematic).
|
// the other object is in FOLLOWING mode (kinematic).
|
||||||
// PhysX will register the collision, but not add the collision force while kinematic.
|
// PhysX will register the collision, but not add the collision force while kinematic.
|
||||||
// we need to add the force manually, which will also begin predicting it.
|
// we need to add the force manually, which will also begin predicting it.
|
||||||
@ -324,7 +356,9 @@ void OnCollisionEnter(Collision collision)
|
|||||||
// => we need to calculate the direction manually to always be A->B.
|
// => we need to calculate the direction manually to always be A->B.
|
||||||
Vector3 direction = other.transform.position - transform.position;
|
Vector3 direction = other.transform.position - transform.position;
|
||||||
Vector3 impulse = direction.normalized * collision.impulse.magnitude;
|
Vector3 impulse = direction.normalized * collision.impulse.magnitude;
|
||||||
other.AddPredictedForce(impulse, ForceMode.Impulse);
|
|
||||||
|
|
||||||
|
other.AddPredictedForceChain(impulse, ForceMode.Impulse, remainingCollisionChainDepth - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// optional user callbacks, in case people need to know about events.
|
// optional user callbacks, in case people need to know about events.
|
||||||
|
Loading…
Reference in New Issue
Block a user