From 3364a2785d16e8074658d041349af3b47becc696 Mon Sep 17 00:00:00 2001 From: Sveske_Juice Date: Mon, 27 May 2024 13:40:48 +0200 Subject: [PATCH] SMOOTH rope reconciliation No collisions or enemies tho --- Assets/Prefabs/Rope.prefab | 2 +- Assets/Scenes/RopeOverhaulGame.unity | 16 ++++---- Assets/Scripts/Rope/Rope.cs | 16 ++++++++ Assets/Scripts/Rope/RopeSimulator.cs | 61 +++++++++++++++++++++++++--- Assets/Scripts/Rope/RopeState.cs | 1 + 5 files changed, 82 insertions(+), 14 deletions(-) diff --git a/Assets/Prefabs/Rope.prefab b/Assets/Prefabs/Rope.prefab index 2f9e97c..206a988 100644 --- a/Assets/Prefabs/Rope.prefab +++ b/Assets/Prefabs/Rope.prefab @@ -179,7 +179,7 @@ LineRenderer: m_Curve: - serializedVersion: 3 time: 0 - value: 0.2280693 + value: 0.2160211 inSlope: 0 outSlope: 0 tangentMode: 0 diff --git a/Assets/Scenes/RopeOverhaulGame.unity b/Assets/Scenes/RopeOverhaulGame.unity index 76318a0..9ff5b97 100644 --- a/Assets/Scenes/RopeOverhaulGame.unity +++ b/Assets/Scenes/RopeOverhaulGame.unity @@ -2238,9 +2238,9 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 34894283} + - component: {fileID: 34894286} - component: {fileID: 34894284} - component: {fileID: 34894285} - - component: {fileID: 34894286} m_Layer: 0 m_Name: Rope m_TagString: Untagged @@ -2276,12 +2276,14 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: solveIterations: 10 - subDivision: 20 - distBetweenRopePoints: 1.5 + subDivision: 30 + distBetweenRopePoints: 0.75 lineRenderer: {fileID: 34894285} rope: points: [] sticks: [] + reconciliateThreshold: 2 + reconciliateLerpFrameDuration: 9 stateBuffer: buffer: [] bufferSize: 0 @@ -2294,7 +2296,7 @@ LineRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 34894282} m_Enabled: 1 - m_CastShadows: 1 + m_CastShadows: 0 m_ReceiveShadows: 1 m_DynamicOccludee: 1 m_StaticShadowCaster: 0 @@ -2308,7 +2310,7 @@ LineRenderer: m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: - - {fileID: 10306, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 2100000, guid: 62e10f2f26f232f49affb7663a8064fb, type: 2} m_StaticBatchInfo: firstSubMesh: 0 subMeshCount: 0 @@ -2338,7 +2340,7 @@ LineRenderer: m_Curve: - serializedVersion: 3 time: 0 - value: 0.10041046 + value: 0.21601537 inSlope: 0 outSlope: 0 tangentMode: 0 @@ -2381,7 +2383,7 @@ LineRenderer: numCornerVertices: 0 numCapVertices: 0 alignment: 0 - textureMode: 0 + textureMode: 1 textureScale: {x: 1, y: 1} shadowBias: 0.5 generateLightingData: 0 diff --git a/Assets/Scripts/Rope/Rope.cs b/Assets/Scripts/Rope/Rope.cs index e89eaa7..5e91a2b 100644 --- a/Assets/Scripts/Rope/Rope.cs +++ b/Assets/Scripts/Rope/Rope.cs @@ -93,6 +93,22 @@ public class Rope return builder.Build(); } + public Rope Copy(float stickLength) + { + RopeBuilder builder = new(); + + foreach (var point in points) + { + builder.AddPoint(new Point(point.position, point.locked)); + } + for (int i = 0; i < points.Length - 1; i++) + { + builder.ConnectPointsWithDesiredLength(i, i + 1, stickLength); + } + + return builder.Build(); + } + /* public void NetworkSerialize(BufferSerializer serializer) where T : IReaderWriter { int pLen = 0; diff --git a/Assets/Scripts/Rope/RopeSimulator.cs b/Assets/Scripts/Rope/RopeSimulator.cs index 1795af7..1f1f50e 100644 --- a/Assets/Scripts/Rope/RopeSimulator.cs +++ b/Assets/Scripts/Rope/RopeSimulator.cs @@ -2,6 +2,7 @@ using UnityEngine; using UnityEngine.Assertions; using System.Linq; using Unity.Netcode; +using System.Collections; public class RopeSimulator : NetworkBehaviour { @@ -19,11 +20,16 @@ public class RopeSimulator : NetworkBehaviour [SerializeField] Rope rope; [Header("Networking")] - [SerializeField] CircularBuffer stateBuffer; [SerializeField] float reconciliateThreshold = 1f; + [SerializeField] int reconciliateLerpFrameDuration = 5; + [SerializeField] CircularBuffer stateBuffer; + [SerializeField] float reconciliateCooldownDuration = 1f; + float reconciliateCooldown = -3f; // First reconciliate after n seconds after its begun to populate stateBuffer private const int k_stateBufferSize = 128; private int currentTick => NetworkManager.Singleton.NetworkTickSystem.LocalTime.Tick; + bool initted = false; + private void Awake() { this.rope = null; @@ -45,8 +51,9 @@ public class RopeSimulator : NetworkBehaviour private void Update() { - if (rope == null) return; + if (!initted) return; + reconciliateCooldown += Time.deltaTime; rope.points.First().position = ropeStart.position; rope.points.Last().position = ropeEnd.position; Simulate(this.rope, Time.deltaTime, this.distBetweenRopePoints, this.solveIterations); @@ -61,6 +68,8 @@ public class RopeSimulator : NetworkBehaviour this.rope = RopeSimulator.BuildRope(this.ropeStart, this.ropeEnd, this.subDivision, this.distBetweenRopePoints); NetworkManager.Singleton.NetworkTickSystem.Tick += NetworkTick; + + initted = true; } private void NetworkTick() @@ -68,7 +77,8 @@ public class RopeSimulator : NetworkBehaviour RopeState ropeState = new() { tick = currentTick, - nrope = Rope.ToNetworkRope(this.rope) + nrope = Rope.ToNetworkRope(this.rope), + playerPositions = GameObject.FindObjectsByType(FindObjectsSortMode.None).Select(p => new Vector3(p.transform.position.x, p.transform.position.y, p.GetComponent().NetworkObjectId)).ToArray() }; stateBuffer.Add(ropeState, currentTick); @@ -84,13 +94,52 @@ public class RopeSimulator : NetworkBehaviour { RopeState clientState = this.stateBuffer.Get(serverState.tick); Rope serverRope = Rope.FromNetworkRope(serverState.nrope, this.distBetweenRopePoints); + + // Not enough information on client to reconcile + if (clientState.nrope.Equals(default(NetworkRope))) + return; + Rope previousClientRope = Rope.FromNetworkRope(clientState.nrope, this.distBetweenRopePoints); float diff = Rope.CalcDiff(previousClientRope, serverRope); - Debug.Log($"Diff: {diff}, ({serverState.tick})/({clientState.tick})"); + // Debug.Log($"Diff: {diff}, ({serverState.tick})/({currentTick})"); - if (diff > reconciliateThreshold) + if (diff > reconciliateThreshold && reconciliateCooldown >= reconciliateCooldownDuration) { - this.rope = serverRope; + reconciliateCooldown = 0f; + // StopCoroutine("ReconciliateLerp"); + // StartCoroutine(ReconciliateLerp(serverRope)); + Rope localCopy = serverRope.Copy(this.distBetweenRopePoints); + int ticksToResimulate = currentTick - serverState.tick; + Debug.Log($"Resimulating {ticksToResimulate} ticks"); + for (int i = 1; i <= ticksToResimulate; i++) + { + RopeState intermediateState = stateBuffer.Get(serverState.tick + i); + Vector3 intermediateRopeStart = new Vector3(intermediateState.playerPositions[0].x, intermediateState.playerPositions[0].y, ropeStart.position.z); + Vector3 intermediateRopeEnd = new Vector3(intermediateState.playerPositions[1].x, intermediateState.playerPositions[1].y, ropeEnd.position.z); + rope.points.First().position = intermediateRopeStart; + rope.points.Last().position = intermediateRopeEnd; + + Simulate(localCopy, 1f / (float) NetworkManager.Singleton.NetworkTickSystem.TickRate, this.distBetweenRopePoints, this.solveIterations); + } + localCopy.points.First().position = ropeStart.position; + localCopy.points.Last().position = ropeEnd.position; + + StopCoroutine("LerpRope"); + LerpRope(this.rope, localCopy); + } + } + + IEnumerator LerpRope(Rope from, Rope to) + { + for (int i = 1; i <= reconciliateLerpFrameDuration; i++) + { + float t = (float) i / (float) reconciliateLerpFrameDuration; + for (int j = 0; j < this.rope.points.Length; j++) + { + from.points[j].position = Vector3.Lerp(this.rope.points[j].position, to.points[j].position, t); + from.points[j].prevPosition = Vector3.Lerp(this.rope.points[j].prevPosition, to.points[j].prevPosition, t); + } + yield return new WaitForEndOfFrame(); } } diff --git a/Assets/Scripts/Rope/RopeState.cs b/Assets/Scripts/Rope/RopeState.cs index f13a387..308bc20 100644 --- a/Assets/Scripts/Rope/RopeState.cs +++ b/Assets/Scripts/Rope/RopeState.cs @@ -6,6 +6,7 @@ public struct RopeState : INetworkSerializable { public int tick; public NetworkRope nrope; + public Vector3[] playerPositions; public override int GetHashCode() {