-
Notifications
You must be signed in to change notification settings - Fork 447
How do I force an update with ClientNetworkTransform or NetworkTransform? #3455
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
You can use
It really requires two components: /// <summary>
/// Placed on object with trigger
/// </summary>
public class TriggerObject : NetworkBehaviour
{
private TagHandle m_TagHandle;
private void Awake()
{
// Add a "Player" tag to your players to help with filtering out
// unwanted triggers.
m_TagHandle = TagHandle.GetExistingTag("Player");
}
private void OnTriggerEnter(Collider other)
{
if (!other.CompareTag(m_TagHandle))
{
return;
}
var playerNetworkTransform = other.gameObject.GetComponent<PlayerNetworkTransform>();
if (playerNetworkTransform)
{
playerNetworkTransform.HandleTrigger(other, this);
}
}
}
/// <summary>
/// Placed on the Player prefab.
/// Will delay trigger until the non-authority instance(s) have
/// interpolated to the point where the authority entered the
/// trigger.
/// </summary>
public class PlayerNetworkTransform : NetworkTransform
{
public float DeferTriggerTimeout = 5.0f;
public bool TriggerOnTimeout = false;
public float PositionPrecision = 1E-04F;
private struct DeferredTrigger
{
public ulong ClientThatTriggered;
public float TimeOut;
public TriggerObject TriggerObject;
public Vector3 TriggerPosition;
}
private List<DeferredTrigger> m_DeferredTriggers = new List<DeferredTrigger>();
/// <summary>
/// One way to make this owner/client authoritative
/// </summary>
protected override bool OnIsServerAuthoritative()
{
return false;
}
public void HandleTrigger(Collider other, TriggerObject triggerObject)
{
// Non-authority exits early
if (!CanCommitToTransform)
{
return;
}
// Send notification to all non-authority instances
OnTriggerEnterRpc(new NetworkBehaviourReference(triggerObject), transform.position);
// Handle trigger locally
OnTriggerEntered(triggerObject, NetworkManager.LocalClientId);
}
/// <summary>
/// The script that normally you would place within the OnTriggerEnter method
/// </summary>
/// <param name="triggerObject">object that triggered </param>
/// <param name="clientThatTriggered"></param>
protected virtual void OnTriggerEntered(TriggerObject triggerObject, ulong clientThatTriggered)
{
// Place the script you would normally have within OnTriggerEnter here
}
[Rpc(SendTo.NotMe)]
private void OnTriggerEnterRpc(NetworkBehaviourReference triggerObjectReference,
Vector3 triggerPosition, RpcParams rpcParams = default)
{
// Add to the list of deferred triggers
if (triggerObjectReference.TryGet<TriggerObject>(out var triggerObject))
{
m_DeferredTriggers.Add(new DeferredTrigger()
{
TimeOut = Time.realtimeSinceStartup + DeferTriggerTimeout,
TriggerObject = triggerObject,
TriggerPosition = triggerPosition,
ClientThatTriggered = rpcParams.Receive.SenderClientId
});
}
}
/// <summary>
/// Handles processing the deferred triggers
/// </summary>
private void ProcessDeferredTriggers()
{
if (m_DeferredTriggers.Count == 0)
{
return;
}
for (int i = m_DeferredTriggers.Count - 1; i >= 0; i--)
{
var deferredTrigger = m_DeferredTriggers[i];
var isApproximately = IsApproximately(transform.position, deferredTrigger.TriggerPosition);
var timedOut = deferredTrigger.TimeOut < Time.realtimeSinceStartup;
if (isApproximately || timedOut)
{
if (isApproximately || (!isApproximately && TriggerOnTimeout && timedOut))
{
OnTriggerEntered(deferredTrigger.TriggerObject, deferredTrigger.ClientThatTriggered);
}
m_DeferredTriggers.RemoveAt(i);
}
}
}
/// <summary>
/// Will be continuously invoked on non-authority instances
/// </summary>
public override void OnUpdate()
{
base.OnUpdate();
ProcessDeferredTriggers();
}
/// <summary>
/// Generic Vector3 approximately method
/// </summary>
private bool IsApproximately(Vector3 first, Vector3 second)
{
return System.Math.Round(Mathf.Abs(first.x - second.x), 2) <= PositionPrecision &&
System.Math.Round(Mathf.Abs(first.y - second.y), 2) <= PositionPrecision &&
System.Math.Round(Mathf.Abs(first.z - second.z), 2) <= PositionPrecision;
}
} The TriggerObject is what has the trigger on it (unless it is on the player...then you would just migrate the TriggerObject .OnTriggerEntered into the PlayerNetworkTransform...but you still want the TriggerObject in order to know "what thing" was causing the trigger) and the PlayerNetworkTransform handles deferring the trigger on non-authority instances until they "catch-up" (i.e. finish interpolating) to the authority's position when it caused the trigger...to trigger. Something like the above should assure that the trigger logic is invoked on all instances when they reach the point where the trigger should "trigger". To get a "perfect" synchronized triggering (and be visually correct on all clients without the delay due to latency) requires prediction of movement...which that is a much more complex problem to solve for...I would start with something like the above and see if it works for you. Obviously, add to and/or modify as you need... but something like the above should do the trick. I am going to close this support ticket out as I believe this should give you a reasonable starting point. |
@NoelStephensUnity Please consider adding a sample that involves this to the SDK. |
Uh oh!
There was an error while loading. Please reload this page.
I have the following situation. I am using the Update loop to do some stuff, and I'm moving a network object with a clientnetworktransform. The states are supposed to synchronize by moving the transform itself, because the movement hits colliders and triggers behaviour.
My problem is that the ClientNetworkTransform is not always synchronized across clients. Sometimes a trigger doesn't fire on a client because the object is not on the place it's supposed to be. It can be slightly off.
My question is: Is there a way to force the transform synchronization? Say, I hit a collider with the transform. As the owner of the networkobject, I want to send an rpc to all clients to move the tranform to that position. The owner keeps moving the transform, but I want to guarantee that a specific position happens across clients, so they all trigger the collider.
What is the proper way of dealing with this?
The text was updated successfully, but these errors were encountered: