Compare commits

...

2 Commits

Author SHA1 Message Date
Sveske_Juice 3364a2785d SMOOTH rope reconciliation
No collisions or enemies tho
2024-05-27 13:40:48 +02:00
Sveske_Juice 15d0079a2d Save ropestate and send RPC 2024-05-27 12:19:16 +02:00
6 changed files with 138 additions and 11 deletions

View File

@ -179,7 +179,7 @@ LineRenderer:
m_Curve: m_Curve:
- serializedVersion: 3 - serializedVersion: 3
time: 0 time: 0
value: 0.2280693 value: 0.2160211
inSlope: 0 inSlope: 0
outSlope: 0 outSlope: 0
tangentMode: 0 tangentMode: 0

View File

@ -2238,6 +2238,7 @@ GameObject:
serializedVersion: 6 serializedVersion: 6
m_Component: m_Component:
- component: {fileID: 34894283} - component: {fileID: 34894283}
- component: {fileID: 34894286}
- component: {fileID: 34894284} - component: {fileID: 34894284}
- component: {fileID: 34894285} - component: {fileID: 34894285}
m_Layer: 0 m_Layer: 0
@ -2275,14 +2276,14 @@ MonoBehaviour:
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
solveIterations: 10 solveIterations: 10
ropeStart: {fileID: 0} subDivision: 30
ropeEnd: {fileID: 0} distBetweenRopePoints: 0.75
subDivision: 20
distBetweenRopePoints: 1.5
lineRenderer: {fileID: 34894285} lineRenderer: {fileID: 34894285}
rope: rope:
points: [] points: []
sticks: [] sticks: []
reconciliateThreshold: 2
reconciliateLerpFrameDuration: 9
stateBuffer: stateBuffer:
buffer: [] buffer: []
bufferSize: 0 bufferSize: 0
@ -2295,7 +2296,7 @@ LineRenderer:
m_PrefabAsset: {fileID: 0} m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 34894282} m_GameObject: {fileID: 34894282}
m_Enabled: 1 m_Enabled: 1
m_CastShadows: 1 m_CastShadows: 0
m_ReceiveShadows: 1 m_ReceiveShadows: 1
m_DynamicOccludee: 1 m_DynamicOccludee: 1
m_StaticShadowCaster: 0 m_StaticShadowCaster: 0
@ -2309,7 +2310,7 @@ LineRenderer:
m_RenderingLayerMask: 1 m_RenderingLayerMask: 1
m_RendererPriority: 0 m_RendererPriority: 0
m_Materials: m_Materials:
- {fileID: 10306, guid: 0000000000000000f000000000000000, type: 0} - {fileID: 2100000, guid: 62e10f2f26f232f49affb7663a8064fb, type: 2}
m_StaticBatchInfo: m_StaticBatchInfo:
firstSubMesh: 0 firstSubMesh: 0
subMeshCount: 0 subMeshCount: 0
@ -2339,7 +2340,7 @@ LineRenderer:
m_Curve: m_Curve:
- serializedVersion: 3 - serializedVersion: 3
time: 0 time: 0
value: 0.10041046 value: 0.21601537
inSlope: 0 inSlope: 0
outSlope: 0 outSlope: 0
tangentMode: 0 tangentMode: 0
@ -2382,7 +2383,7 @@ LineRenderer:
numCornerVertices: 0 numCornerVertices: 0
numCapVertices: 0 numCapVertices: 0
alignment: 0 alignment: 0
textureMode: 0 textureMode: 1
textureScale: {x: 1, y: 1} textureScale: {x: 1, y: 1}
shadowBias: 0.5 shadowBias: 0.5
generateLightingData: 0 generateLightingData: 0
@ -2390,6 +2391,27 @@ LineRenderer:
m_UseWorldSpace: 1 m_UseWorldSpace: 1
m_Loop: 0 m_Loop: 0
m_ApplyActiveColorSpace: 1 m_ApplyActiveColorSpace: 1
--- !u!114 &34894286
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 34894282}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier:
GlobalObjectIdHash: 1869519831
InScenePlacedSourceGlobalObjectIdHash: 0
AlwaysReplicateAsRoot: 0
SynchronizeTransform: 1
ActiveSceneSynchronization: 0
SceneMigrationSynchronization: 1
SpawnWithObservers: 1
DontDestroyWithOwner: 0
AutoObjectParentSync: 1
--- !u!1001 &37294573 --- !u!1001 &37294573
PrefabInstance: PrefabInstance:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0

View File

@ -87,6 +87,7 @@ public class HealthComponent : MonoBehaviour, ISquezeDamageReceiver
void Update() void Update()
{ {
return;
// blod regen // blod regen
if (bloodRegen) if (bloodRegen)
{ {

View File

@ -93,6 +93,22 @@ public class Rope
return builder.Build(); 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<T>(BufferSerializer<T> serializer) where T : IReaderWriter /* public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{ {
int pLen = 0; int pLen = 0;

View File

@ -2,8 +2,9 @@ using UnityEngine;
using UnityEngine.Assertions; using UnityEngine.Assertions;
using System.Linq; using System.Linq;
using Unity.Netcode; using Unity.Netcode;
using System.Collections;
public class RopeSimulator : MonoBehaviour public class RopeSimulator : NetworkBehaviour
{ {
[Header("Solver")] [Header("Solver")]
[SerializeField] int solveIterations = 10; [SerializeField] int solveIterations = 10;
@ -19,14 +20,20 @@ public class RopeSimulator : MonoBehaviour
[SerializeField] Rope rope; [SerializeField] Rope rope;
[Header("Networking")] [Header("Networking")]
[SerializeField] float reconciliateThreshold = 1f;
[SerializeField] int reconciliateLerpFrameDuration = 5;
[SerializeField] CircularBuffer<RopeState> stateBuffer; [SerializeField] CircularBuffer<RopeState> 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 const int k_stateBufferSize = 128;
private int currentTick => NetworkManager.Singleton.NetworkTickSystem.LocalTime.Tick; private int currentTick => NetworkManager.Singleton.NetworkTickSystem.LocalTime.Tick;
bool initted = false;
private void Awake() private void Awake()
{ {
this.rope = null;
stateBuffer = new CircularBuffer<RopeState>(k_stateBufferSize); stateBuffer = new CircularBuffer<RopeState>(k_stateBufferSize);
} }
private void OnEnable() private void OnEnable()
@ -37,10 +44,16 @@ public class RopeSimulator : MonoBehaviour
private void OnDisable() private void OnDisable()
{ {
GameManager.OnPlayersReady += Init; GameManager.OnPlayersReady += Init;
if (NetworkManager.Singleton != null)
NetworkManager.Singleton.NetworkTickSystem.Tick -= NetworkTick;
} }
private void Update() private void Update()
{ {
if (!initted) return;
reconciliateCooldown += Time.deltaTime;
rope.points.First().position = ropeStart.position; rope.points.First().position = ropeStart.position;
rope.points.Last().position = ropeEnd.position; rope.points.Last().position = ropeEnd.position;
Simulate(this.rope, Time.deltaTime, this.distBetweenRopePoints, this.solveIterations); Simulate(this.rope, Time.deltaTime, this.distBetweenRopePoints, this.solveIterations);
@ -54,6 +67,80 @@ public class RopeSimulator : MonoBehaviour
this.ropeEnd = players[1].transform; this.ropeEnd = players[1].transform;
this.rope = RopeSimulator.BuildRope(this.ropeStart, this.ropeEnd, this.subDivision, this.distBetweenRopePoints); this.rope = RopeSimulator.BuildRope(this.ropeStart, this.ropeEnd, this.subDivision, this.distBetweenRopePoints);
NetworkManager.Singleton.NetworkTickSystem.Tick += NetworkTick;
initted = true;
}
private void NetworkTick()
{
RopeState ropeState = new()
{
tick = currentTick,
nrope = Rope.ToNetworkRope(this.rope),
playerPositions = GameObject.FindObjectsByType<PlayerMovement>(FindObjectsSortMode.None).Select(p => new Vector3(p.transform.position.x, p.transform.position.y, p.GetComponent<NetworkObject>().NetworkObjectId)).ToArray()
};
stateBuffer.Add(ropeState, currentTick);
if (IsServer)
{
ServerToClientRopeStateRpc(ropeState);
}
}
[Rpc(SendTo.NotServer)]
private void ServerToClientRopeStateRpc(RopeState serverState)
{
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})/({currentTick})");
if (diff > reconciliateThreshold && reconciliateCooldown >= reconciliateCooldownDuration)
{
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();
}
} }
private void DisplayRope() private void DisplayRope()

View File

@ -6,6 +6,7 @@ public struct RopeState : INetworkSerializable
{ {
public int tick; public int tick;
public NetworkRope nrope; public NetworkRope nrope;
public Vector3[] playerPositions;
public override int GetHashCode() public override int GetHashCode()
{ {