This commit is contained in:
Sveske_Juice 2024-03-04 17:40:23 +01:00
parent 026d427dc0
commit f0f56c5b70
8 changed files with 302 additions and 155 deletions

View File

@ -11,8 +11,6 @@ GameObject:
- component: {fileID: 3817028170077760731}
- component: {fileID: 4590407122831754579}
- component: {fileID: 8963967235500975272}
- component: {fileID: -5174249022758144916}
- component: {fileID: -7862229988660046446}
m_Layer: 0
m_Name: RopePoint
m_TagString: Untagged
@ -98,55 +96,3 @@ Rigidbody2D:
m_SleepingMode: 1
m_CollisionDetection: 0
m_Constraints: 0
--- !u!114 &-5174249022758144916
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4736158799711156794}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier:
GlobalObjectIdHash: 1634323294
InScenePlacedSourceGlobalObjectIdHash: 0
AlwaysReplicateAsRoot: 0
SynchronizeTransform: 1
ActiveSceneSynchronization: 0
SceneMigrationSynchronization: 1
SpawnWithObservers: 1
DontDestroyWithOwner: 0
AutoObjectParentSync: 1
--- !u!114 &-7862229988660046446
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4736158799711156794}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e96cb6065543e43c4a752faaa1468eb1, type: 3}
m_Name:
m_EditorClassIdentifier:
UseUnreliableDeltas: 0
SyncPositionX: 1
SyncPositionY: 1
SyncPositionZ: 1
SyncRotAngleX: 0
SyncRotAngleY: 0
SyncRotAngleZ: 0
SyncScaleX: 0
SyncScaleY: 0
SyncScaleZ: 0
PositionThreshold: 0.001
RotAngleThreshold: 0.01
ScaleThreshold: 0.01
UseQuaternionSynchronization: 0
UseQuaternionCompression: 0
UseHalfFloatPrecision: 0
InLocalSpace: 0
Interpolate: 1
SlerpPosition: 0

View File

@ -9,12 +9,11 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 144529238244638330}
- component: {fileID: 2894273998966960381}
- component: {fileID: 6056950010778645813}
- component: {fileID: 7717684785049474632}
- component: {fileID: 901761791259710742}
- component: {fileID: 4976294692568481572}
- component: {fileID: 2066655509941542230}
- component: {fileID: 7670104141912458081}
m_Layer: 0
m_Name: Rope
m_TagString: Untagged
@ -37,7 +36,7 @@ Transform:
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &2894273998966960381
--- !u!114 &6056950010778645813
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
@ -49,15 +48,15 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3}
m_Name:
m_EditorClassIdentifier:
GlobalObjectIdHash: 3972580189
InScenePlacedSourceGlobalObjectIdHash: 1619090677
GlobalObjectIdHash: 15502151
InScenePlacedSourceGlobalObjectIdHash: 0
AlwaysReplicateAsRoot: 0
SynchronizeTransform: 1
ActiveSceneSynchronization: 0
SceneMigrationSynchronization: 1
SpawnWithObservers: 1
DontDestroyWithOwner: 0
AutoObjectParentSync: 0
AutoObjectParentSync: 1
--- !u!114 &7717684785049474632
MonoBehaviour:
m_ObjectHideFlags: 0
@ -119,6 +118,9 @@ MonoBehaviour:
ropeCollidersParent: {fileID: 144529238244638330}
lineRenderer: {fileID: 901761791259710742}
pullAnimationOvershootThreshold: 0.2
rope:
points: []
sticks: []
--- !u!120 &901761791259710742
LineRenderer:
serializedVersion: 2
@ -333,34 +335,3 @@ AudioSource:
m_PreInfinity: 2
m_PostInfinity: 2
m_RotationOrder: 4
--- !u!114 &7670104141912458081
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5991265243222894942}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: e96cb6065543e43c4a752faaa1468eb1, type: 3}
m_Name:
m_EditorClassIdentifier:
UseUnreliableDeltas: 0
SyncPositionX: 1
SyncPositionY: 1
SyncPositionZ: 1
SyncRotAngleX: 0
SyncRotAngleY: 0
SyncRotAngleZ: 0
SyncScaleX: 0
SyncScaleY: 0
SyncScaleZ: 0
PositionThreshold: 0.001
RotAngleThreshold: 0.01
ScaleThreshold: 0.01
UseQuaternionSynchronization: 0
UseQuaternionCompression: 0
UseHalfFloatPrecision: 0
InLocalSpace: 0
Interpolate: 1
SlerpPosition: 0

View File

@ -3722,7 +3722,6 @@ MonoBehaviour:
difficulty: 1
difficultyIncreasePerWave: 3
WaveTime: 20
enemyDifficulties: []
SpawnRadius: 22.6
NumEnemies: 6
initialSpawnDelay: 5
@ -3902,14 +3901,18 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 2894273998966960381, guid: 0248db69242a3dd47898c6742b6c9f60, type: 3}
propertyPath: GlobalObjectIdHash
value: 3972580189
objectReference: {fileID: 0}
- target: {fileID: 5991265243222894942, guid: 0248db69242a3dd47898c6742b6c9f60, type: 3}
propertyPath: m_Name
value: Rope
objectReference: {fileID: 0}
- target: {fileID: 6056950010778645813, guid: 0248db69242a3dd47898c6742b6c9f60, type: 3}
propertyPath: GlobalObjectIdHash
value: 1448608317
objectReference: {fileID: 0}
- target: {fileID: 6056950010778645813, guid: 0248db69242a3dd47898c6742b6c9f60, type: 3}
propertyPath: InScenePlacedSourceGlobalObjectIdHash
value: 331293026
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []

View File

@ -1,15 +1,32 @@
using UnityEngine;
using Unity.Netcode;
[System.Serializable]
public class Point
public class Point : INetworkSerializable
{
public Vector3 position, prevPosition;
public bool locked;
public Point() {}
public Point(Vector3 position, bool locked = false)
{
this.position = position;
this.prevPosition = position;
this.locked = locked;
}
public Point(Vector3 position, Vector3 prevPosition, bool locked = false)
{
this.position = position;
this.prevPosition = prevPosition;
this.locked = locked;
}
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
serializer.SerializeValue(ref position);
serializer.SerializeValue(ref prevPosition);
serializer.SerializeValue(ref locked);
}
}

View File

@ -1,13 +1,18 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Assertions;
using Unity.Netcode;
using System.Collections.Generic;
[System.Serializable]
public class Rope
{
public List<Point> points { get ; private set; }
public List<Stick> sticks { get; private set; }
public Point[] points;
public Stick[] sticks;
public Rope(List<Point> points, List<Stick> sticks)
public Rope() {}
public Rope(Point[] points, Stick[] sticks)
{
this.points = points;
this.sticks = sticks;
@ -25,4 +30,200 @@ public class Rope
return sum;
}
public static float CalcDiff(Rope r1, Rope r2)
{
if (r1.points.Length != r2.points.Length)
{
throw new System.ArgumentException("Ropes are not the same length");
}
float diff = 0;
for (int i = 0; i < r1.points.Length; i++)
{
diff += Vector3.Distance(r1.points[i].position, r2.points[i].position);
}
return diff;
}
public static NetworkRope ToNetworkRope(Rope rope)
{
return new NetworkRope
{
positions = rope.points.Select(p => p.position).ToArray(),
prevPositions = rope.points.Select(p => p.prevPosition).ToArray(),
locked = rope.points.Select(p => p.locked).ToArray(),
sPosA = rope.sticks.Select(s => s.A.position).ToArray(),
sPrevPosA = rope.sticks.Select(s => s.A.prevPosition).ToArray(),
sPosB = rope.sticks.Select(s => s.B.position).ToArray(),
sPrevPosB = rope.sticks.Select(s => s.B.prevPosition).ToArray(),
dead = rope.sticks.Select(s => s.dead).ToArray(),
};
}
public static Rope FromNetworkRope(NetworkRope nrope, float stickLength)
{
Assert.IsTrue(nrope.positions.Length == nrope.prevPositions.Length);
Point[] points = new Point[nrope.positions.Length];
for (int i = 0; i < nrope.positions.Length; i++)
{
points[i] = new Point(nrope.positions[i], nrope.prevPositions[i], nrope.locked[i]);
}
RopeBuilder builder = new RopeBuilder(new List<Point>(points), new List<Stick>());
for (int i = 0; i < (points.Length - 1); i++)
{
builder.ConnectPointsWithDesiredLength(i, i + 1, desiredLength: stickLength);
}
return builder.Build();
}
/* public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
int pLen = 0;
int sLen = 0;
if (serializer.IsWriter)
{
pLen = points.Length;
sLen = sticks.Length;
}
serializer.SerializeValue(ref pLen);
serializer.SerializeValue(ref sLen);
if (serializer.IsReader)
{
points = new Point[pLen];
sticks = new Stick[sLen];
for (int i = 0; i < pLen; i++)
{
serializer.GetFastBufferReader().ReadValueSafe(out points[i]);
}
for (int i = 0; i < sLen; i++)
{
serializer.GetFastBufferReader().ReadValueSafe(out sticks[i]);
}
}
else
{
for (int i = 0; i < pLen; i++)
{
points[i].NetworkSerialize(serializer);
}
for (int i = 0; i < sLen; i++)
{
sticks[i].NetworkSerialize(serializer);
}
}
} */
}
public struct NetworkRope : INetworkSerializable
{
// For rope points
public Vector3[] positions;
public Vector3[] prevPositions;
public bool[] locked;
// For rope sticks
public Vector3[] sPosA;
public Vector3[] sPrevPosA;
public Vector3[] sPosB;
public Vector3[] sPrevPosB;
public bool[] dead;
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
int positionsLen = 0;
int prevPositionsLen = 0;
int lockedLen = 0;
int sPosALen = 0;
int sPrevPosALen = 0;
int sPosBLen = 0;
int sPrevPosBLen = 0;
int deadLen = 0;
if (!serializer.IsReader)
{
positionsLen = positions.Length;
prevPositionsLen = prevPositions.Length;
lockedLen = locked.Length;
sPosALen = sPosA.Length;
sPrevPosALen = sPrevPosA.Length;
sPosBLen = sPosB.Length;
sPrevPosBLen = sPrevPosB.Length;
deadLen = dead.Length;
}
serializer.SerializeValue(ref positionsLen);
serializer.SerializeValue(ref prevPositionsLen);
serializer.SerializeValue(ref lockedLen);
serializer.SerializeValue(ref sPosALen);
serializer.SerializeValue(ref sPrevPosALen);
serializer.SerializeValue(ref sPosBLen);
serializer.SerializeValue(ref sPrevPosBLen);
serializer.SerializeValue(ref deadLen);
if (serializer.IsReader)
{
positions = new Vector3[positionsLen];
prevPositions = new Vector3[prevPositionsLen];
locked = new bool[lockedLen];
sPosA = new Vector3[sPosALen];
sPrevPosA = new Vector3[sPrevPosALen];
sPosB = new Vector3[sPosBLen];
sPrevPosB = new Vector3[sPrevPosBLen];
dead = new bool[deadLen];
}
for (int i = 0; i < positionsLen; i++)
{
serializer.SerializeValue(ref positions[i]);
}
for (int i = 0; i < prevPositionsLen; i++)
{
serializer.SerializeValue(ref prevPositions[i]);
}
for (int i = 0; i < lockedLen; i++)
{
serializer.SerializeValue(ref locked[i]);
}
for (int i = 0; i < sPosALen; i++)
{
serializer.SerializeValue(ref sPosA[i]);
}
for (int i = 0; i < sPrevPosALen; i++)
{
serializer.SerializeValue(ref sPrevPosA[i]);
}
for (int i = 0; i < sPosBLen; i++)
{
serializer.SerializeValue(ref sPosB[i]);
}
for (int i = 0; i < sPrevPosBLen; i++)
{
serializer.SerializeValue(ref sPrevPosB[i]);
}
for (int i = 0; i < deadLen; i++)
{
serializer.SerializeValue(ref dead[i]);
}
}
}

View File

@ -40,6 +40,6 @@ public class RopeBuilder
public Rope Build()
{
return new Rope(points: points, sticks: sticks);
return new Rope(points: points.ToArray(), sticks: sticks.ToArray());
}
}

View File

@ -65,8 +65,9 @@ public class RopeSimulator : NetworkBehaviour
[Header("Netcode")]
private const int k_rngSeed = 6969;
private const float k_sendRopeDataRPCThreshold = 0.1f;
private const float k_ropeReconciliateThreshold = 0.1f;
private System.Random rng = new System.Random(k_rngSeed);
private NetworkTimer networkTimer;
private int[] order;
@ -81,10 +82,6 @@ public class RopeSimulator : NetworkBehaviour
private bool IsInitialized => start != null || end != null;
// TODO: also true in single player mode
private bool ShouldSimulate => IsHost;
private void Awake()
{
if (instance == null)
@ -109,17 +106,11 @@ public class RopeSimulator : NetworkBehaviour
public void PlayersReady(GameObject[] players)
{
if (ShouldSimulate)
{
Debug.Log(players[0].name);
BuildRope(players[0].GetComponent<RopeJoint>(), players[1].GetComponent<RopeJoint>());
}
}
public void BuildRope(RopeJoint start, RopeJoint end)
{
Assert.IsTrue(ShouldSimulate, "Should not try build rope on client!");
Assert.IsNotNull(start);
Assert.IsNotNull(end);
@ -149,8 +140,6 @@ public class RopeSimulator : NetworkBehaviour
void ShrinkenRope(int playerNumber)
{
Assert.IsTrue(ShouldSimulate, "Should not shrink rope on client!");
int prevSubDivision = (int) subDivision;
subDivision -= ropeShrinkSpeed * Time.deltaTime;
subDivision = Mathf.Clamp(subDivision, ropeMinLength, ropeMaxLength);
@ -159,10 +148,13 @@ public class RopeSimulator : NetworkBehaviour
if (prevSubDivision - (int) subDivision <= 0) return;
// Shrink from rope point after start rope joint
rope.sticks.Clear();
rope.points.RemoveAt(1);
List<Point> newPoints = new(rope.points.Length - 1);
for (int i = 0; i < (rope.points.Length - 1); i++)
{
newPoints.Add(rope.points[i]);
}
var builder = new RopeBuilder(rope.points, rope.sticks);
var builder = new RopeBuilder(newPoints, new List<Stick>());
// Re-gen sticks
for (int i = 0; i < (int) subDivision; i++)
@ -177,8 +169,6 @@ public class RopeSimulator : NetworkBehaviour
void ExtendRope(int playerNumber)
{
Assert.IsTrue(ShouldSimulate, "Should not extend rope on client!");
int prevSubDivision = (int)subDivision;
subDivision += ropeExtendSpeed * Time.deltaTime;
subDivision = Mathf.Clamp(subDivision, ropeMinLength, ropeMaxLength);
@ -187,10 +177,14 @@ public class RopeSimulator : NetworkBehaviour
if (prevSubDivision - (int) subDivision >= 0) return;
// Extend from rope point after start rope point
rope.sticks.Clear();
rope.points.Insert(1, new Point(rope.points[1].position));
List<Point> newPoints = new (rope.points.Length + 1);
newPoints.Add(new Point(rope.points[1].position));
for (int i = 1; i < rope.points.Length; i++)
{
newPoints.Add(rope.points[i]);
}
var builder = new RopeBuilder(rope.points, rope.sticks);
var builder = new RopeBuilder(newPoints, new List<Stick>());
// Re-gen sticks
for (int i = 0; i < (int)subDivision; i++)
@ -220,8 +214,6 @@ public class RopeSimulator : NetworkBehaviour
private void Rebuild()
{
Assert.IsTrue(ShouldSimulate, "Should not re-build on clients!");
Debug.Log("rebuild");
RopeBuilder builder = new RopeBuilder();
@ -251,8 +243,6 @@ public class RopeSimulator : NetworkBehaviour
private void RebuildRopeColliders()
{
Assert.IsTrue(ShouldSimulate, "Should not build rope colliders on client!");
for (int i = 0; i < ropeCollidersParent.childCount; i++)
{
Destroy(ropeCollidersParent.GetChild(i));
@ -261,10 +251,8 @@ public class RopeSimulator : NetworkBehaviour
foreach (var point in rope.points)
{
GameObject ropeCollider = Instantiate(colliderPrefab);
var ropeNO = ropeCollider.GetComponent<NetworkObject>();
ropeNO.Spawn();
ropeCollider.transform.parent = ropeCollidersParent;
ropeNO.TrySetParent(ropeCollidersParent);
ropeCollider.transform.position = point.position;
ropeCollider.tag = colliderTag;
ropeCollider.layer = LayerMask.NameToLayer("Rope");
@ -289,14 +277,21 @@ public class RopeSimulator : NetworkBehaviour
lineRenderer.SetPositions(positions.ToArray());
}
private void Update()
[Rpc(SendTo.NotServer)]
private void ServerRopeDataReceivedRpc(NetworkRope nrope)
{
if (!ShouldSimulate)
Debug.Log($"Received rope data from server: {nrope}");
Debug.Log(nrope.sPosA[1]);
Rope serverRope = Rope.FromNetworkRope(nrope, distBetweenRopePoints);
if (Rope.CalcDiff(this.rope, serverRope) > k_ropeReconciliateThreshold)
{
DrawRope();
return;
Debug.LogWarning("Reconciliating rope!");
this.rope = serverRope;
}
}
private void Update()
{
if (!IsInitialized)
return;
@ -305,10 +300,18 @@ public class RopeSimulator : NetworkBehaviour
rope.points.First().position = start.position;
rope.points.Last().position = end.position;
Simulate(Time.fixedDeltaTime);
float ropeDiff = Simulate(Time.fixedDeltaTime);
if (IsServer && ropeDiff > k_sendRopeDataRPCThreshold)
{
Debug.Log($"Sending rope to client");
NetworkRope nrope = Rope.ToNetworkRope(this.rope);
// Send server rope to client for reconciliation
ServerRopeDataReceivedRpc(nrope);
}
// Update the rope collider positions
for (int i = 0; i < rope.points.Count; i++)
for (int i = 0; i < rope.points.Length; i++)
{
ropeCollidersParent.GetChild(i).position = rope.points[i].position;
}
@ -353,7 +356,6 @@ public class RopeSimulator : NetworkBehaviour
private void PlayerPullAnimation(float overshoot)
{
Assert.IsTrue(ShouldSimulate);
//if (overshoot > pullAnimationOvershootThreshold)
//{
@ -379,8 +381,6 @@ public class RopeSimulator : NetworkBehaviour
private void PullPlayers(float overshoot)
{
Assert.IsTrue(ShouldSimulate);
if (overshoot <= 0f) return;
//start.position = prevStartPos;
@ -398,7 +398,7 @@ public class RopeSimulator : NetworkBehaviour
}
if (!end.locked)
{
Vector2 pullDirection = (rope.points[rope.points.Count - 2].position - end.position).normalized;
Vector2 pullDirection = (rope.points[rope.points.Length - 2].position - end.position).normalized;
Vector2 force = pullDirection * overshoot * (pullForce / divider);
end.body.AddForce(force);
}
@ -422,10 +422,9 @@ public class RopeSimulator : NetworkBehaviour
}
}
void Simulate(float dt)
float Simulate(float dt)
{
Assert.IsTrue(ShouldSimulate, "Should not simulate rope on client!");
float diff = 0f;
foreach (Point p in rope.points)
{
if (!p.locked)
@ -433,13 +432,14 @@ public class RopeSimulator : NetworkBehaviour
Vector3 positionBeforeUpdate = p.position;
p.position += p.position - p.prevPosition;
p.position.z -= gravity * dt * dt;
diff += Mathf.Abs(Vector3.Distance(p.prevPosition, p.position));
p.prevPosition = positionBeforeUpdate;
}
}
for (int i = 0; i < solveIterations; i++)
{
for (int s = 0; s < rope.sticks.Count; s++)
for (int s = 0; s < rope.sticks.Length; s++)
{
Stick stick = rope.sticks[order[s]];
if (stick.dead)
@ -462,12 +462,11 @@ public class RopeSimulator : NetworkBehaviour
}
}
}
return diff;
}
private void TryMovePointToPosition(Point point, Vector3 position)
{
Assert.IsTrue(ShouldSimulate);
Vector2 moveDir = new Vector2(position.x, position.y) - new Vector2(point.position.x, point.position.y);
int stepsRequired = (int) Mathf.Ceil(moveDir.magnitude / collisionCheckDist);
moveDir.Normalize();
@ -506,7 +505,6 @@ public class RopeSimulator : NetworkBehaviour
private void HandleStaticCollidersOfPoint(Point p)
{
Assert.IsTrue(ShouldSimulate);
foreach (var hitCollider in Physics2D.OverlapCircleAll(p.position, ropeRadius*1.1f, staticColliderMask))
{
if (hitCollider == null) continue;
@ -530,7 +528,7 @@ public class RopeSimulator : NetworkBehaviour
void CreateOrderArray()
{
order = new int[rope.sticks.Count];
order = new int[rope.sticks.Length];
for (int i = 0; i < order.Length; i++)
{
order[i] = i;

View File

@ -1,12 +1,15 @@
using UnityEngine;
using Unity.Netcode;
[System.Serializable]
public class Stick
public class Stick : INetworkSerializable
{
public Point A, B;
public float desiredLength;
public bool dead;
public Stick() { }
public Stick(Point pointA, Point pointB)
{
this.A = pointA;
@ -20,4 +23,12 @@ public class Stick
this.B = pointB;
this.desiredLength = desiredLenght;
}
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
A.NetworkSerialize(serializer);
B.NetworkSerialize(serializer);
serializer.SerializeValue(ref desiredLength);
serializer.SerializeValue(ref dead);
}
}