rope maybe working pls??
This commit is contained in:
parent
0a521b51c8
commit
acca896c7b
|
@ -473,7 +473,7 @@ MonoBehaviour:
|
||||||
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
|
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
GlobalObjectIdHash: 2899378164
|
GlobalObjectIdHash: 4009941625
|
||||||
InScenePlacedSourceGlobalObjectIdHash: 0
|
InScenePlacedSourceGlobalObjectIdHash: 0
|
||||||
AlwaysReplicateAsRoot: 0
|
AlwaysReplicateAsRoot: 0
|
||||||
SynchronizeTransform: 1
|
SynchronizeTransform: 1
|
||||||
|
|
|
@ -48,7 +48,7 @@ MonoBehaviour:
|
||||||
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
|
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
GlobalObjectIdHash: 15502151
|
GlobalObjectIdHash: 168437488
|
||||||
InScenePlacedSourceGlobalObjectIdHash: 0
|
InScenePlacedSourceGlobalObjectIdHash: 0
|
||||||
AlwaysReplicateAsRoot: 0
|
AlwaysReplicateAsRoot: 0
|
||||||
SynchronizeTransform: 1
|
SynchronizeTransform: 1
|
||||||
|
@ -118,6 +118,11 @@ MonoBehaviour:
|
||||||
ropeCollidersParent: {fileID: 144529238244638330}
|
ropeCollidersParent: {fileID: 144529238244638330}
|
||||||
lineRenderer: {fileID: 901761791259710742}
|
lineRenderer: {fileID: 901761791259710742}
|
||||||
pullAnimationOvershootThreshold: 0.2
|
pullAnimationOvershootThreshold: 0.2
|
||||||
|
k_ropeReconciliateThreshold: 2
|
||||||
|
stateBuffer:
|
||||||
|
buffer: []
|
||||||
|
bufferSize: 0
|
||||||
|
reconciliateDuration: 0.3
|
||||||
rope:
|
rope:
|
||||||
points: []
|
points: []
|
||||||
sticks: []
|
sticks: []
|
||||||
|
|
|
@ -133,7 +133,6 @@ public class EnemySpawner : NetworkBehaviour
|
||||||
void SpawnEnemy(GameObject enemyPrefab)
|
void SpawnEnemy(GameObject enemyPrefab)
|
||||||
{
|
{
|
||||||
GameObject enemy = Instantiate(enemyPrefab, GetRandomPointOnCircle(SpawnRadius), Quaternion.identity, SpawnedEnenmyHolder.transform);
|
GameObject enemy = Instantiate(enemyPrefab, GetRandomPointOnCircle(SpawnRadius), Quaternion.identity, SpawnedEnenmyHolder.transform);
|
||||||
enemy.GetComponent<NetworkID>().InitialID = idCounter++;
|
|
||||||
enemy.GetComponent<NetworkObject>().Spawn();
|
enemy.GetComponent<NetworkObject>().Spawn();
|
||||||
|
|
||||||
enemy.GetComponent<EnemyPathFinding>().targets = players.Select(x => x.transform).ToArray();
|
enemy.GetComponent<EnemyPathFinding>().targets = players.Select(x => x.transform).ToArray();
|
||||||
|
|
|
@ -1,12 +1,25 @@
|
||||||
using Unity.Netcode;
|
using Unity.Netcode;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
// FIXME: NetworkObject.NetworkObjectId are ulong but we're sending them as int. potential problem
|
||||||
|
|
||||||
[System.Serializable]
|
[System.Serializable]
|
||||||
public struct GameState : INetworkSerializable
|
public struct GameState : INetworkSerializable
|
||||||
{
|
{
|
||||||
public int tick;
|
public int tick;
|
||||||
public NetworkRope nrope;
|
public NetworkRope nrope;
|
||||||
public Vector2[] enemyPositions;
|
|
||||||
|
/// <summary>
|
||||||
|
/// x & y are location coords. z component is the <see cref="NetworkID.ID"/>
|
||||||
|
/// of the enemy.
|
||||||
|
/// </summary>
|
||||||
|
public Vector3[] enemyPositions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// x & y are location coords. z component is the <see cref="NetworkID.ID"/>
|
||||||
|
/// of the player.
|
||||||
|
/// </summary>
|
||||||
|
public Vector3[] playerPositions;
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
|
@ -23,6 +36,7 @@ public struct GameState : INetworkSerializable
|
||||||
{
|
{
|
||||||
serializer.SerializeValue(ref tick);
|
serializer.SerializeValue(ref tick);
|
||||||
serializer.SerializeValue(ref enemyPositions);
|
serializer.SerializeValue(ref enemyPositions);
|
||||||
|
serializer.SerializeValue(ref playerPositions);
|
||||||
nrope.NetworkSerialize(serializer);
|
nrope.NetworkSerialize(serializer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,12 +67,16 @@ public class RopeSimulator : NetworkBehaviour
|
||||||
[Header("Netcode")]
|
[Header("Netcode")]
|
||||||
private const float k_serverTickRate = 60f;
|
private const float k_serverTickRate = 60f;
|
||||||
private const int k_rngSeed = 6969;
|
private const int k_rngSeed = 6969;
|
||||||
public float k_ropeReconciliateThreshold = 10f;
|
public float k_ropeReconciliateThreshold = 1f;
|
||||||
private System.Random rng = new System.Random(k_rngSeed);
|
private System.Random rng = new System.Random(k_rngSeed);
|
||||||
private int currentTick => NetworkManager.Singleton.NetworkTickSystem.LocalTime.Tick;
|
private int currentTick => NetworkManager.Singleton.NetworkTickSystem.LocalTime.Tick;
|
||||||
[SerializeField] private CircularBuffer<GameState> stateBuffer;
|
[SerializeField] private CircularBuffer<GameState> stateBuffer;
|
||||||
private GameState lastReceivedServerGameState;
|
private GameState lastReceivedServerGameState;
|
||||||
private const int k_bufferSize = 512;
|
private const int k_bufferSize = 512;
|
||||||
|
private bool isReconciliating = false;
|
||||||
|
private GameState currentRewindingState;
|
||||||
|
[SerializeField] private float reconciliateDuration = 0.1f;
|
||||||
|
private float rewindCounter = 0f;
|
||||||
|
|
||||||
private int[] order;
|
private int[] order;
|
||||||
|
|
||||||
|
@ -271,10 +275,10 @@ public class RopeSimulator : NetworkBehaviour
|
||||||
private void DrawRope()
|
private void DrawRope()
|
||||||
{
|
{
|
||||||
// Update line renderer
|
// Update line renderer
|
||||||
List<Vector3> positions = new List<Vector3>(ropeCollidersParent.childCount);
|
List<Vector3> positions = new List<Vector3>(this.rope.points.Length);
|
||||||
for (int i = 0; i < ropeCollidersParent.childCount; i++)
|
for (int i = 0; i < this.rope.points.Length; i++)
|
||||||
{
|
{
|
||||||
positions.Add(ropeCollidersParent.GetChild(i).position);
|
positions.Add(this.rope.points[i].position);
|
||||||
}
|
}
|
||||||
lineRenderer.positionCount = positions.Count;
|
lineRenderer.positionCount = positions.Count;
|
||||||
lineRenderer.SetPositions(positions.ToArray());
|
lineRenderer.SetPositions(positions.ToArray());
|
||||||
|
@ -297,8 +301,76 @@ public class RopeSimulator : NetworkBehaviour
|
||||||
Rope serverRope = Rope.FromNetworkRope(serverState.nrope, distBetweenRopePoints);
|
Rope serverRope = Rope.FromNetworkRope(serverState.nrope, distBetweenRopePoints);
|
||||||
Rope oldLocalRope = Rope.FromNetworkRope(stateBuffer.Get(serverState.tick).nrope, distBetweenRopePoints);
|
Rope oldLocalRope = Rope.FromNetworkRope(stateBuffer.Get(serverState.tick).nrope, distBetweenRopePoints);
|
||||||
|
|
||||||
float serverLocalRopeDiff = Rope.CalcDiff(serverRope, oldLocalRope);
|
float serverLocalRopeError = Rope.CalcDiff(serverRope, oldLocalRope);
|
||||||
Debug.Log($"Server to client sync error: {serverLocalRopeDiff}");
|
|
||||||
|
if (serverLocalRopeError >= k_ropeReconciliateThreshold)
|
||||||
|
{
|
||||||
|
SimpleReconcile(serverState);
|
||||||
|
}
|
||||||
|
Debug.Log($"Server to client sync error: {serverLocalRopeError}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SimpleReconcile(GameState rewindState)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Reconciliate");
|
||||||
|
this.rewindCounter = 0f;
|
||||||
|
this.isReconciliating = true;
|
||||||
|
this.currentRewindingState = rewindState;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Reconciliate()
|
||||||
|
{
|
||||||
|
Physics2D.simulationMode = SimulationMode2D.Script;
|
||||||
|
|
||||||
|
Debug.LogWarning("Reconciliate");
|
||||||
|
Debug.Break();
|
||||||
|
// Reset client to server state
|
||||||
|
GameState serverState = this.lastReceivedServerGameState;
|
||||||
|
this.rope = Rope.FromNetworkRope(serverState.nrope, distBetweenRopePoints);
|
||||||
|
Dictionary<ulong, GameObject> enemyByIds = new();
|
||||||
|
Dictionary<ulong, GameObject> playerByIds = new();
|
||||||
|
IEnumerable<EnemyPathFinding> enemies = GameObject.FindObjectsByType<EnemyPathFinding>(FindObjectsSortMode.None);
|
||||||
|
IEnumerable<PlayerMovement> players = GameObject.FindObjectsByType<PlayerMovement>(FindObjectsSortMode.None);
|
||||||
|
|
||||||
|
foreach (var enemy in enemies)
|
||||||
|
enemyByIds.Add(enemy.GetComponent<NetworkObject>().NetworkObjectId, enemy.gameObject);
|
||||||
|
|
||||||
|
foreach (var player in players)
|
||||||
|
playerByIds.Add(player.GetComponent<NetworkObject>().NetworkObjectId, player.gameObject);
|
||||||
|
|
||||||
|
|
||||||
|
// Re-simulate up to this client's tick
|
||||||
|
Debug.Log($"Needs to re-sim {currentTick - serverState.tick} steps");
|
||||||
|
for (int tick = serverState.tick; tick <= currentTick; tick++)
|
||||||
|
{
|
||||||
|
GameState intermediateState = tick == serverState.tick ? serverState : stateBuffer.Get(tick);
|
||||||
|
|
||||||
|
foreach (var enemyPos in intermediateState.enemyPositions)
|
||||||
|
{
|
||||||
|
// Find corresponding client enemy with id (z-component)
|
||||||
|
ulong enemyID = (ulong) enemyPos.z;
|
||||||
|
GameObject enemy = enemyByIds.GetValueOrDefault(enemyID);
|
||||||
|
Assert.IsNotNull(enemy, $"Server enemy with id: {enemyID} could not be found on client!");
|
||||||
|
|
||||||
|
enemy.transform.position = new Vector2(enemyPos.x, enemyPos.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var playerPos in intermediateState.playerPositions)
|
||||||
|
{
|
||||||
|
// Find corresponding client player with id (z-component)
|
||||||
|
ulong playerID = (ulong) playerPos.z;
|
||||||
|
GameObject player = playerByIds.GetValueOrDefault(playerID);
|
||||||
|
Assert.IsNotNull(player, $"Server player with id: {playerID} could not be found on client!");
|
||||||
|
|
||||||
|
player.transform.position = new Vector2(playerPos.x, playerPos.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Simulate(Time.fixedDeltaTime);
|
||||||
|
|
||||||
|
Physics2D.Simulate(Time.fixedDeltaTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
Physics2D.simulationMode = SimulationMode2D.FixedUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NetworkTick()
|
private void NetworkTick()
|
||||||
|
@ -315,7 +387,8 @@ public class RopeSimulator : NetworkBehaviour
|
||||||
GameState localState = new() {
|
GameState localState = new() {
|
||||||
tick = currentTick,
|
tick = currentTick,
|
||||||
nrope = Rope.ToNetworkRope(this.rope),
|
nrope = Rope.ToNetworkRope(this.rope),
|
||||||
enemyPositions = GameObject.FindObjectsByType<EnemyPathFinding>(FindObjectsSortMode.None).Select(e => new Vector2(e.transform.position.x, e.transform.position.y)).ToArray()
|
enemyPositions = GameObject.FindObjectsByType<EnemyPathFinding>(FindObjectsSortMode.None).Select(e => new Vector3(e.transform.position.x, e.transform.position.y, e.GetComponent<NetworkObject>().NetworkObjectId)).ToArray(),
|
||||||
|
playerPositions = GameObject.FindObjectsByType<PlayerMovement>(FindObjectsSortMode.None).Select(p => new Vector3(p.transform.position.x, p.transform.position.y, p.GetComponent<NetworkObject>().NetworkObjectId)).ToArray()
|
||||||
};
|
};
|
||||||
|
|
||||||
return localState;
|
return localState;
|
||||||
|
@ -326,6 +399,42 @@ public class RopeSimulator : NetworkBehaviour
|
||||||
if (!IsInitialized)
|
if (!IsInitialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (isReconciliating)
|
||||||
|
{
|
||||||
|
this.rope.points.First().position = start.position;
|
||||||
|
this.rope.points.First().prevPosition = start.position;
|
||||||
|
|
||||||
|
this.rope.points.Last().position = end.position;
|
||||||
|
this.rope.points.Last().prevPosition = end.position;
|
||||||
|
|
||||||
|
Rope rewindRope = Rope.FromNetworkRope(currentRewindingState.nrope, distBetweenRopePoints);
|
||||||
|
rewindCounter += Time.deltaTime;
|
||||||
|
float t = rewindCounter / reconciliateDuration;
|
||||||
|
for (int point = 1; point < rope.points.Length - 1; point++)
|
||||||
|
{
|
||||||
|
Vector3 newPos = Vector3.Slerp(this.rope.points[point].position, rewindRope.points[point].position, t);
|
||||||
|
|
||||||
|
this.rope.points[point].position = newPos;
|
||||||
|
this.rope.points[point].prevPosition = newPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
Simulate(Time.deltaTime);
|
||||||
|
foreach (var point in rope.points)
|
||||||
|
{
|
||||||
|
// point.prevPosition = point.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawRope();
|
||||||
|
|
||||||
|
if (t >= 1f)
|
||||||
|
{
|
||||||
|
rewindCounter = 0f;
|
||||||
|
isReconciliating = false;
|
||||||
|
currentRewindingState = default(GameState);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
colliderToSquezeForce.Clear();
|
colliderToSquezeForce.Clear();
|
||||||
|
|
||||||
rope.points.First().position = start.position;
|
rope.points.First().position = start.position;
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
using Unity.Netcode;
|
|
||||||
|
|
||||||
public class NetworkID : NetworkBehaviour
|
|
||||||
{
|
|
||||||
// Set from enemy spawner
|
|
||||||
public int InitialID = -1;
|
|
||||||
|
|
||||||
private NetworkVariable<int> nID = new();
|
|
||||||
|
|
||||||
public int ID => nID.Value;
|
|
||||||
|
|
||||||
public override void OnNetworkSpawn()
|
|
||||||
{
|
|
||||||
if (IsServer)
|
|
||||||
nID.Value = InitialID;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: e071dd818c5a905209e31c2d66ea6390
|
|
|
@ -1,16 +0,0 @@
|
||||||
#if UNITY_EDITOR
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEditor;
|
|
||||||
|
|
||||||
[CustomEditor(typeof(NetworkID))]
|
|
||||||
public class NetworkID_Inspector : Editor
|
|
||||||
{
|
|
||||||
public override void OnInspectorGUI()
|
|
||||||
{
|
|
||||||
base.OnInspectorGUI();
|
|
||||||
|
|
||||||
EditorGUILayout.LabelField($"ID: {(target as NetworkID).ID}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,2 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: e70a0a80d88c1620c955d7bdc1efe3ee
|
|
Loading…
Reference in New Issue