4 using System.Collections.Generic;
9 public delegate
void ObjectDespawnImminentHandler (GameObject go);
10 public delegate
void ObjectDespawnedHandler (GameObject go);
12 [AddComponentMenu(
"Liberi/Sync")]
13 public partial class Sync : MonoBehaviour
15 public event ObjectDespawnImminentHandler DespawnImminent;
16 public event ObjectDespawnedHandler Despawned;
20 get {
return _rootSync; }
25 get {
return _rootSync ==
this; }
28 public int OwnerPeerIndex
30 get {
return _rootSync._ownerPeerIndex; }
33 public int ObjectIndex
35 get {
return _rootSync._objectIndex; }
40 get {
return _rootSync._spawnInfo; }
43 public TimelineBase[] Timelines
45 get {
return _rootSync._timelines.ToArray(); }
48 public TimelineBase[] EssentialTimelines
50 get {
return _rootSync._essentialTimelines.ToArray(); }
53 public bool SyncPosition =
false;
54 public bool SyncDirection =
false;
55 public bool SyncColliderState =
false;
56 public bool SyncColliderSize =
false;
57 public bool SyncAnimator =
false;
58 public DirectionMode DirectionMode = DirectionMode.None;
59 public float PositionCorrectionStrength = 25f;
60 public float DirectionCorrectionStrength = 25f;
61 public float SetTime = 0f;
62 public float GetTime = 0f;
63 public float SendRate = 10f;
64 public bool DrawDebugGizmos;
66 [TimelineTags(
"Essential")]
67 public Timeline<Vector3> Position;
68 [TimelineTags(
"Essential")]
69 public Timeline<float> Angle;
70 [TimelineTags(
"Essential")]
71 public Timeline<bool> Flipped;
73 public Timeline<GameMessage> AnimationMessages;
75 Timeline<GameMessage> _teleports;
76 Timeline<bool> _colliderState;
77 Timeline<Vector3> _colliderSize;
79 MonoBehaviour[] _originalBehaviours;
81 int _objectIndex = -1;
82 int _ownerPeerIndex = -1;
83 List<TimelineBase> _timelines =
new List<TimelineBase>();
84 List<TimelineBase> _essentialTimelines =
new List<TimelineBase>();
87 bool _isWaitingForTeleportConfirmation;
88 BoxCollider _boxCollider;
89 SphereCollider _sphereCollider;
90 CapsuleCollider _capsuleCollider;
93 private float GetSendRate ()
102 while (_rootSync.transform.parent != null && _rootSync.transform.parent.GetComponent<
Sync>() != null)
104 _rootSync = _rootSync.transform.parent.GetComponent<
Sync>();
109 _originalBehaviours = GetComponentsInChildren<MonoBehaviour>().Where(mb => mb.enabled).ToArray();
111 foreach (var mb
in _originalBehaviours)
122 var positionSendFilter = TimelineUtils.BuildRateFilter<Vector3>(GetSendRate);
124 Position = CreateContinuousState<Vector3>(positionSendFilter);
131 if (DirectionMode == DirectionMode.Radial)
133 var angleSendFilter = TimelineUtils.BuildRateFilter<
float>(GetSendRate);
135 Angle = CreateContinuousState<float>(angleSendFilter);
136 Angle.Interpolate = UTimelineUtils.InterpolateAngleSlerp;
138 else if (DirectionMode == DirectionMode.Flip)
140 Flipped = CreateDiscreteState<bool>();
144 if (collider == null)
146 SyncColliderState =
false;
147 SyncColliderSize =
false;
150 if (SyncColliderState)
152 _colliderState = CreateDiscreteState<bool>();
155 if (SyncColliderSize)
157 if (collider is MeshCollider)
159 SyncColliderSize =
false;
163 _boxCollider = GetComponent<BoxCollider>();
164 _sphereCollider = GetComponent<SphereCollider>();
165 _capsuleCollider = GetComponent<CapsuleCollider>();
167 _colliderSize = CreateContinuousState<Vector3>();
168 _colliderSize.AddSendFilter(TimelineUtils.BuildInequalityFilter<Vector3>());
174 _animator = GetComponent<Animator>();
175 AnimationMessages = CreateDiscreteState<GameMessage>();
181 if (!DrawDebugGizmos)
184 if (Position != null)
186 if (Position.NumEntries >= 1)
188 Gizmos.color =
new Color(0f, 0f, 1f, .75f);
189 Gizmos.DrawWireSphere(Position.PreviousValue, .1f);
192 if (Position.NumEntries >= 2)
194 Gizmos.color =
new Color(0f, 0f, 1f, .5f);
195 Gizmos.DrawWireSphere(Position.PreviousEntry.Prev.Value, .1f);
198 Gizmos.color = Color.white;
206 _teleports.EntryPassed += OnTeleportEvent;
209 if (_rootSync._ownerPeerIndex == _localPeerIndex)
216 if (rigidbody != null)
218 this.rigidbody.useGravity =
false;
221 this.rigidbody.constraints |= RigidbodyConstraints.FreezePosition;
225 if (DirectionMode == DirectionMode.Radial)
226 this.rigidbody.constraints |= RigidbodyConstraints.FreezeRotation;
227 else if (DirectionMode == DirectionMode.Flip)
229 Flipped.EntryPassed += OnFlippedChanged;
230 transform.SetFlipDirection(Flipped.LastValue ? Vector3.left : Vector3.right);
238 if (SyncColliderState)
239 _colliderState.EntryPassed += OnColliderStateChanged;
243 AnimationMessages.EntryPassed += OnAnimationMessagePassed;
248 if (DespawnImminent != null)
249 DespawnImminent(gameObject);
254 if (Despawned != null)
255 Despawned(gameObject);
258 public Vector3 GetDirection ()
260 if (DirectionMode == DirectionMode.Flip)
261 return transform.GetFlipDirection();
262 else return transform.GetRadialDirection();
265 public void SetDirection (Vector3 direction,
bool ignoreRigidbody =
false)
267 if (DirectionMode == DirectionMode.Flip)
269 transform.SetFlipDirection(direction);
271 else if (DirectionMode == DirectionMode.Radial)
273 if (!ignoreRigidbody && rigidbody != null)
274 rigidbody.SetRadialDirection(direction);
275 else transform.SetRadialDirection(direction);
281 if (_rootSync._ownerPeerIndex == _localPeerIndex)
293 private void PushTransform ()
297 Position[SetTime] = transform.position;
302 if (DirectionMode == DirectionMode.Radial)
304 Angle[SetTime] = transform.rotation.eulerAngles.z;
306 else if (DirectionMode == DirectionMode.Flip)
308 Flipped[SetTime] = transform.localScale.x < 0;
313 private void PullTransform ()
315 if (_isWaitingForTeleportConfirmation)
318 if (SyncPosition && !Position.IsEmpty)
320 var position = transform.position;
321 var targetPosition = Position[GetTime];
322 var sqrDistToTarget = (targetPosition - position).sqrMagnitude;
323 bool shouldSnap =
false;
333 _errorTime += Time.deltaTime;
344 transform.position = Position[GetTime];
349 transform.position = Vector3.Lerp(position,
350 targetPosition, Mathf.Min(1f, PositionCorrectionStrength * Time.deltaTime));
356 if (DirectionMode == DirectionMode.Radial && !Angle.IsEmpty)
358 var rotation = transform.rotation;
359 float currentAngle = rotation.eulerAngles.z;
360 float interpAngle = Mathf.LerpAngle(
361 currentAngle, Angle[GetTime], DirectionCorrectionStrength * Time.deltaTime);
363 transform.rotation = Quaternion.Euler(0f, 0f, interpAngle);
370 if (SyncColliderState)
372 if (_colliderState.LastValue != collider.enabled)
373 _colliderState[0] = collider.enabled;
376 if (SyncColliderSize)
378 if (_sphereCollider != null)
380 _colliderSize[SetTime] =
new Vector3(_sphereCollider.radius, 0f, 0f);
382 else if (_capsuleCollider != null)
384 _colliderSize[SetTime] =
new Vector3(_capsuleCollider.radius, _capsuleCollider.height, 0f);
386 else if (_boxCollider != null)
388 _colliderSize[SetTime] = _boxCollider.size;
395 if (SyncColliderSize && !_colliderSize.IsEmpty)
397 var size = _colliderSize[GetTime];
399 if (_sphereCollider != null)
401 _sphereCollider.radius = size.x;
403 else if (_capsuleCollider != null)
405 _capsuleCollider.radius = size.x;
406 _capsuleCollider.height = size.y;
408 else if (_boxCollider != null)
410 _boxCollider.size = size;
415 void OnFlippedChanged (Timeline<bool> timeline, TimelineEntry<bool> entry)
417 transform.SetFlipDirection(entry.Value ? Vector3.left : Vector3.right);
420 void OnColliderStateChanged (Timeline<bool> timeline, TimelineEntry<bool> entry)
422 collider.enabled = entry.Value;
425 void OnAnimationMessagePassed (Timeline<GameMessage> timeline, TimelineEntry<GameMessage> entry)
429 int stateNameHash = msg.ReadInt32();
430 float transitionDuration = msg.ReadFloat();
431 int layer = msg.ReadInt32();
435 _animator.CrossFade(stateNameHash, transitionDuration, layer);
438 void OnTeleportEvent (Timeline<GameMessage> timeline, TimelineEntry<GameMessage> entry)
440 var msg = entry.Value;
442 bool isConfirmation = msg.ReadBoolean();
443 Vector3 target = msg.ReadVector3();
446 transform.position = target;
447 Position.RemoveRange(Position.FirstTime,
true, Position.LastTime,
true,
true);
448 Position.Insert(0f, target);
454 if (_rootSync._ownerPeerIndex != _localPeerIndex)
455 _isWaitingForTeleportConfirmation =
false;
459 if (_rootSync._ownerPeerIndex == _localPeerIndex)
462 confirmMsg.Write(
true);
463 confirmMsg.Write(target);
465 _teleports[0] = confirmMsg;
467 else _isWaitingForTeleportConfirmation =
true;
static float MaxSmoothDistance
This is the distance between the current and correct positions of an object beyond which smooth corre...
static void SendEnabledMessage(GameObject go, string message, params object[] args)
Invokes a method on all enabled scripts in the GameObject.
void FinishReading()
Set the read / write position to 0 so the next user won't run over.
static float MaxErrorTime
This is the length of time an object must be in the error state before a positional snap happens...
static float ErrorDistance
This is the distance between the current and correct positions of an object beyond which the position...
A class for serializing game data into a stream for propagation between objects and peers...
static float MinSmoothDistance
This is the distance between the current and correct positions of an object below which smooth correc...
This class server two main functions: 1) As a MonoBehaviour, it allows for network synchronization of...
The spawn-time information of a spawned object.