public void DrawActorDebugGUI(VoosActor actor) { using (new Util.GUILayoutFrobArea(actor.transform.position, 100, 500)) { PhotonView photonView = PhotonView.Get(actor); if (photonView == null) { return; } PlayerBody playerBody = actor.GetPlayerBody(); //string pbodyInfo = playerBody == null ? "" : $"Claimed? {playerBody.IsClaimed()} ..play mode? {playerBody.GetIsClaimerPlaying()}"; string color = photonView.isMine ? "yellow" : "grey"; string hash = actor.GetName().Substring(0, 9); bool locked = actor.IsLockedByAnother() || actor.IsLockWantedLocally(); string lockingString = locked ? " LOCKED" : ""; string lastPos = actor.unrel == null ? "" : actor.unrel.lastPosition.x.ToFourDecimalPlaces(); GUILayout.Label($@"<color={color}>{actor.GetDisplayName()} rot: {actor.GetRotation().ToFourDecimalPlaces()} last unrel: {actor.lastUnreliableUpdateTime} lastPX: {lastPos}</color>".Trim()); GUILayout.Toggle(actor.GetReplicantCatchUpMode(), "Catchup?"); actor.debug = GUILayout.Toggle(actor.debug, "Debug"); // owner: {photonView.ownerId}{lockingString} // {hash} view {actor.reliablePhotonView.viewID} // X: {actor.transform.position.x.ToFourDecimalPlaces()} // lastRBMPX: {actor.lastRBMovedPos.x.ToFourDecimalPlaces()} } }
void FixedUpdate() { foreach (KeyValuePair <short, NetworkedActor> na in actors) { VoosActor va = na.Value.actor; if (va != null && !va.reliablePhotonView.isMine && !va.IsParented()) { Rigidbody rb = va.GetComponent <Rigidbody>(); bool isDynamic = rb != null && va.GetEnablePhysics(); Vector3 pos = rb != null ? rb.position : va.transform.position; Quaternion rot = rb != null ? rb.rotation : va.transform.rotation; if (!isDynamic || va.GetReplicantCatchUpMode()) { // Kinematic or non-RB. Lerp it. Quaternion lerpedRot = Quaternion.Lerp(rot, na.Value.lastRotation, Mathf.Clamp01(Time.deltaTime * 5)); var damped = Vector3.SmoothDamp(pos, na.Value.lastPosition, ref na.Value.posDampVelocity, 0.2f); va.ForceReplicantPosition(damped); va.ForceReplicantRotation(lerpedRot); } else { rb.angularVelocity *= 0.9f; if (!SnapRigidbodyOnRecv) { // Let physics simulate the position with our corrective velocity, // but we still lerp the rotation. Quaternion lerpedRot = Quaternion.Lerp(rot, na.Value.lastRotation, Mathf.Clamp01(Time.deltaTime * 5)); rb.rotation = lerpedRot; } } } } }
void Deserialize(byte[] bytes, int senderId) { VoosNetworkTypes.ShortToBytes stb = new VoosNetworkTypes.ShortToBytes(); VoosNetworkTypes.UintToBytes utb = new VoosNetworkTypes.UintToBytes(); if (bytes.Length < 6) { return; } stb.byte0 = bytes[0]; stb.byte1 = bytes[1]; short positionCount = stb.data; utb.byte0 = bytes[1]; utb.byte1 = bytes[2]; utb.byte2 = bytes[3]; utb.byte3 = 0; float timestamp = VoosNetworkTypes.DecompressFloatFromUint24(utb.data); if (bytes.Length < 5 + positionCount * size) { return; } for (int i = 0; i < positionCount; i++) { int offset = 5 + i * size; /// View Id stb.byte0 = bytes[offset]; stb.byte1 = bytes[offset + 1]; offset += 2; short viewId = stb.data; if (actors.ContainsKey(viewId)) { NetworkedActor na = actors[viewId]; VoosActor va = na.actor; if (va == null) { diag.numNullActorEntries++; } if (va != null && va.reliablePhotonView.ownerId != senderId) { diag.numSenderOwnerMismatches++; } if (va != null && va.reliablePhotonView.ownerId == senderId) { va.lastUnreliableUpdateTime = Time.realtimeSinceStartup; va.unrel = na; float timeDelta = Mathf.Clamp(timestamp - na.lastRecvTimestamp, 0.01f, maxTimeDelta); na.lastRecvTimestamp = timestamp; UnreliableBitmask mask = (UnreliableBitmask)bytes[offset]; offset += 1; Vector3 oldP = na.lastPosition; /// Local Position utb.byte0 = bytes[offset]; utb.byte1 = bytes[offset + 1]; utb.byte2 = bytes[offset + 2]; utb.byte3 = 0; na.lastPosition.x = VoosNetworkTypes.DecompressFloatFromUint24(utb.data); utb.byte0 = bytes[offset + 3]; utb.byte1 = bytes[offset + 4]; utb.byte2 = bytes[offset + 5]; utb.byte3 = 0; na.lastPosition.y = VoosNetworkTypes.DecompressFloatFromUint24(utb.data); utb.byte0 = bytes[offset + 6]; utb.byte1 = bytes[offset + 7]; utb.byte2 = bytes[offset + 8]; utb.byte3 = 0; offset += 9; na.lastPosition.z = VoosNetworkTypes.DecompressFloatFromUint24(utb.data); Vector3 predictionError = na.lastPosition - va.transform.position; Quaternion oldQ = na.lastRotation; // Full precision rotation for static var ftb = new VoosNetworkTypes.FloatToBytes(); float x = ftb.DeserializeFrom(bytes, ref offset); float y = ftb.DeserializeFrom(bytes, ref offset); float z = ftb.DeserializeFrom(bytes, ref offset); float w = ftb.DeserializeFrom(bytes, ref offset); na.lastRotation = new Quaternion(x, y, z, w); /// Color Color32 c = new Color32(); c.r = bytes[offset]; c.g = bytes[offset + 1]; c.b = bytes[offset + 2]; c.a = bytes[offset + 3]; offset += 4; va.SetTint(c); /// Linear V utb.byte0 = bytes[offset]; utb.byte1 = bytes[offset + 1]; utb.byte2 = bytes[offset + 2]; utb.byte3 = 0; na.linearVelocity.x = VoosNetworkTypes.DecompressFloatFromUint24(utb.data); utb.byte0 = bytes[offset + 3]; utb.byte1 = bytes[offset + 4]; utb.byte2 = bytes[offset + 5]; utb.byte3 = 0; na.linearVelocity.y = VoosNetworkTypes.DecompressFloatFromUint24(utb.data); utb.byte0 = bytes[offset + 6]; utb.byte1 = bytes[offset + 7]; utb.byte2 = bytes[offset + 8]; utb.byte3 = 0; na.linearVelocity.z = VoosNetworkTypes.DecompressFloatFromUint24(utb.data); Rigidbody rb = va.GetComponent <Rigidbody>(); bool isDynamic = rb != null && va.GetEnablePhysics(); // Hard Snap if the teleport flag is on. Otherwise we smoothly interpolate. if ((mask & UnreliableBitmask.Teleport) == UnreliableBitmask.Teleport) { predictionError = Vector3.zero; va.transform.position = na.lastPosition; va.transform.rotation = na.lastRotation; if (rb != null) { rb.velocity = Vector3.zero; rb.angularVelocity = Vector3.zero; } } if (va.IsParented()) { continue; } if (isDynamic) { if (SnapRigidbodyOnRecv) { rb.velocity = na.linearVelocity; rb.MovePosition(na.lastPosition); rb.MoveRotation(na.lastRotation); } else { const float catchUpEnterThresh = 1f; const float catchUpExitThresh = 0.5f; float errorDist = predictionError.magnitude; if (!va.GetReplicantCatchUpMode()) { if (errorDist > catchUpEnterThresh) { va.SetReplicantCatchUpMode(true); } } else { if (va.debug) { Util.Log($"in catch up mode... err dist: {errorDist}"); } if (errorDist < catchUpExitThresh) { va.SetReplicantCatchUpMode(false); } } if (va.GetReplicantCatchUpMode()) { // Let the fixed update directly lerp this. } else { // Best-effort corrective velocity only. // TODO expose some tunable error -> correction velocity parameter? rb.velocity = na.linearVelocity + predictionError; } } } } } else { diag.numUnknownViewIds++; } } }