2024-02-03 00:15:07 +01:00
|
|
|
using System;
|
2024-02-02 21:03:44 +01:00
|
|
|
using System.Collections;
|
|
|
|
using System.Collections.Generic;
|
2024-02-03 00:15:07 +01:00
|
|
|
using System.Linq;
|
2024-02-02 21:03:44 +01:00
|
|
|
using UnityEngine;
|
2024-02-27 19:54:10 +01:00
|
|
|
using UnityEngine.Assertions;
|
2024-02-02 21:03:44 +01:00
|
|
|
|
|
|
|
public class RopeSimulator : MonoBehaviour
|
|
|
|
{
|
|
|
|
[SerializeField]
|
|
|
|
private float gravity = 10;
|
|
|
|
|
|
|
|
[SerializeField]
|
2024-02-03 00:15:07 +01:00
|
|
|
private int solveIterations = 10;
|
2024-02-02 21:03:44 +01:00
|
|
|
|
|
|
|
[SerializeField]
|
2024-02-03 00:15:07 +01:00
|
|
|
private bool constrainStickMinLength;
|
|
|
|
|
|
|
|
[SerializeField]
|
2024-02-14 05:37:52 +01:00
|
|
|
public RopeJoint start, end;
|
2024-02-03 00:15:07 +01:00
|
|
|
|
|
|
|
[SerializeField]
|
2024-02-03 16:21:32 +01:00
|
|
|
float subDivision = 50f;
|
2024-02-03 00:15:07 +01:00
|
|
|
|
|
|
|
[SerializeField]
|
|
|
|
float collisionCheckDist = 0.5f;
|
|
|
|
|
|
|
|
[SerializeField, Range(0f, 1f)]
|
|
|
|
float distBetweenRopePoints = 0.1f;
|
|
|
|
|
|
|
|
[SerializeField, Range(0.01f, 1f)]
|
|
|
|
float ropeRadius;
|
|
|
|
|
|
|
|
[SerializeField]
|
|
|
|
float ignoreResolveThreshold = 0.08f;
|
|
|
|
|
|
|
|
[SerializeField]
|
|
|
|
LayerMask staticColliderMask;
|
|
|
|
|
|
|
|
[SerializeField, Range(0f, 100f)]
|
|
|
|
float pullForce = 20f;
|
2024-02-02 21:03:44 +01:00
|
|
|
|
2024-02-03 02:47:40 +01:00
|
|
|
[SerializeField]
|
|
|
|
float xyGravityDampScalor = 1f;
|
|
|
|
|
2024-02-03 16:58:14 +01:00
|
|
|
[SerializeField, Range(0f, 20f)]
|
2024-02-03 16:21:32 +01:00
|
|
|
public float ropeExtendSpeed, ropeShrinkSpeed;
|
|
|
|
|
2024-02-03 20:15:38 +01:00
|
|
|
public float squezeDamage = 1f;
|
2024-02-04 07:15:21 +01:00
|
|
|
public AnimationCurve swingSpeedToDamageMultiplier;
|
2024-02-03 20:15:38 +01:00
|
|
|
|
2024-02-03 16:21:32 +01:00
|
|
|
[SerializeField]
|
2024-02-03 16:58:14 +01:00
|
|
|
public float ropeMaxLength, ropeMinLength;
|
2024-02-03 16:21:32 +01:00
|
|
|
|
2024-02-27 19:54:10 +01:00
|
|
|
[Header("Rope Colliders")]
|
|
|
|
[SerializeField] string colliderTag = "Rope";
|
|
|
|
[SerializeField] Transform ropeCollidersParent;
|
2024-02-03 20:15:38 +01:00
|
|
|
|
2024-02-03 01:25:34 +01:00
|
|
|
[Header("Rendering")]
|
|
|
|
[SerializeField] LineRenderer lineRenderer;
|
|
|
|
|
2024-02-04 06:27:35 +01:00
|
|
|
[Header("Animaion")]
|
|
|
|
[SerializeField] float pullAnimationOvershootThreshold = 0.2f;
|
|
|
|
|
2024-02-02 21:03:44 +01:00
|
|
|
int[] order;
|
|
|
|
|
2024-02-04 02:40:16 +01:00
|
|
|
public float Overshoot => rope.CalculateLengthOvershoot();
|
2024-02-04 07:15:21 +01:00
|
|
|
public bool InSwingMode => start.locked || end.locked;
|
2024-02-04 02:40:16 +01:00
|
|
|
|
2024-02-04 04:03:19 +01:00
|
|
|
public Rope rope;
|
2024-02-02 21:03:44 +01:00
|
|
|
|
2024-02-03 01:14:34 +01:00
|
|
|
Dictionary<Collider2D, float> colliderToSquezeForce = new();
|
|
|
|
|
2024-02-15 01:32:08 +01:00
|
|
|
public static RopeSimulator instance;
|
|
|
|
|
2024-02-28 14:50:14 +01:00
|
|
|
private bool IsInitialized => !(start == null || end == null);
|
2024-02-27 19:54:10 +01:00
|
|
|
|
2024-02-15 01:32:08 +01:00
|
|
|
private void Awake()
|
|
|
|
{
|
|
|
|
if (instance == null)
|
|
|
|
{
|
|
|
|
instance = this;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Destroy(instance);
|
|
|
|
}
|
2024-02-27 21:17:44 +01:00
|
|
|
GameManager.OnPlayersReady += PlayersReady;
|
2024-02-15 01:32:08 +01:00
|
|
|
}
|
|
|
|
|
2024-02-28 14:50:14 +01:00
|
|
|
private void OnEnable()
|
2024-02-02 21:03:44 +01:00
|
|
|
{
|
2024-02-14 14:37:29 +01:00
|
|
|
// Example of using rope builder
|
2024-02-03 00:15:07 +01:00
|
|
|
//rope = new RopeBuilder()
|
|
|
|
// .AddPoint(new Point(testPos, locked: true))
|
|
|
|
// .AddPoint(new Point(testPos.Add(x:5f)))
|
|
|
|
// .AddPoint(new Point(testPos.Add(x: 10f)))
|
|
|
|
// .AddPoint(new Point(testPos.Add(x: 15f)))
|
|
|
|
// .AddPoint(new Point(testPos.Add(x: 20f)))
|
|
|
|
// .ConnectPoints(0, 1)
|
|
|
|
// .ConnectPoints(1, 2)
|
|
|
|
// .ConnectPoints(2, 3)
|
|
|
|
// .ConnectPoints(3, 4)
|
|
|
|
// .Build();
|
2024-02-27 21:17:44 +01:00
|
|
|
|
|
|
|
}
|
2024-02-14 14:37:29 +01:00
|
|
|
|
2024-02-27 21:17:44 +01:00
|
|
|
private void OnDisable()
|
|
|
|
{
|
|
|
|
GameManager.OnPlayersReady -= PlayersReady;
|
2024-02-27 20:37:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public void PlayersReady(GameObject[] players)
|
|
|
|
{
|
2024-02-27 21:17:44 +01:00
|
|
|
//if (IsInitialized) return;
|
2024-02-27 20:37:15 +01:00
|
|
|
|
2024-02-27 21:17:44 +01:00
|
|
|
Debug.Log(players[0].name);
|
2024-02-27 20:37:15 +01:00
|
|
|
BuildRope(players[0].GetComponent<RopeJoint>(), players[1].GetComponent<RopeJoint>());
|
2024-02-14 14:37:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public void BuildRope(RopeJoint start, RopeJoint end)
|
|
|
|
{
|
2024-02-27 19:54:10 +01:00
|
|
|
Assert.IsNotNull(start);
|
|
|
|
Assert.IsNotNull(end);
|
|
|
|
|
|
|
|
// Sanity check if rope simulator was initialized before - we are re-building the rope
|
2024-02-27 21:17:44 +01:00
|
|
|
if (this.start != null)
|
2024-02-14 14:37:29 +01:00
|
|
|
{
|
2024-02-27 19:54:10 +01:00
|
|
|
this.start.playerInput.ropeLengthExtend -= ExtendRope;
|
|
|
|
this.start.playerInput.ropeLengthShrinken -= ShrinkenRope;
|
2024-02-27 21:17:44 +01:00
|
|
|
}
|
|
|
|
if (this.end != null)
|
|
|
|
{
|
2024-02-27 19:54:10 +01:00
|
|
|
this.end.playerInput.ropeLengthExtend -= ExtendRope;
|
|
|
|
this.end.playerInput.ropeLengthShrinken -= ShrinkenRope;
|
2024-02-14 14:37:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
this.start = start;
|
|
|
|
this.end = end;
|
|
|
|
|
2024-02-03 16:21:32 +01:00
|
|
|
Rebuild();
|
|
|
|
|
2024-02-14 14:37:29 +01:00
|
|
|
this.start.playerInput.ropeLengthShrinken += ShrinkenRope;
|
|
|
|
this.end.playerInput.ropeLengthShrinken += ShrinkenRope;
|
2024-02-03 16:21:32 +01:00
|
|
|
|
2024-02-14 14:37:29 +01:00
|
|
|
this.start.playerInput.ropeLengthExtend += ExtendRope;
|
|
|
|
this.end.playerInput.ropeLengthExtend += ExtendRope;
|
2024-02-03 16:21:32 +01:00
|
|
|
}
|
|
|
|
|
2024-02-03 16:58:14 +01:00
|
|
|
void ShrinkenRope(int playerNumber)
|
2024-02-03 16:21:32 +01:00
|
|
|
{
|
2024-02-03 16:58:14 +01:00
|
|
|
int prevSubDivision = (int) subDivision;
|
|
|
|
subDivision -= ropeShrinkSpeed * Time.deltaTime;
|
2024-02-27 19:54:10 +01:00
|
|
|
subDivision = Mathf.Clamp(subDivision, ropeMinLength, ropeMaxLength);
|
2024-02-03 16:58:14 +01:00
|
|
|
|
2024-02-27 19:54:10 +01:00
|
|
|
// Only shrinken if the numeric value has changed
|
|
|
|
if (prevSubDivision - (int) subDivision <= 0) return;
|
2024-02-03 16:58:14 +01:00
|
|
|
|
2024-02-27 19:54:10 +01:00
|
|
|
// Shrink from rope point after start rope joint
|
2024-02-03 20:01:12 +01:00
|
|
|
rope.sticks.Clear();
|
|
|
|
rope.points.RemoveAt(1);
|
2024-02-03 16:58:14 +01:00
|
|
|
|
2024-02-03 20:01:12 +01:00
|
|
|
var builder = new RopeBuilder(rope.points, rope.sticks);
|
2024-02-03 16:58:14 +01:00
|
|
|
|
2024-02-03 20:01:12 +01:00
|
|
|
// Re-gen sticks
|
|
|
|
for (int i = 0; i < (int) subDivision; i++)
|
2024-02-03 16:58:14 +01:00
|
|
|
{
|
2024-02-03 20:01:12 +01:00
|
|
|
builder.ConnectPointsWithDesiredLength(i, i + 1, distBetweenRopePoints);
|
2024-02-03 16:58:14 +01:00
|
|
|
}
|
2024-02-03 20:01:12 +01:00
|
|
|
rope = builder.Build();
|
|
|
|
|
|
|
|
RebuildRopeColliders();
|
2024-02-03 16:58:14 +01:00
|
|
|
CreateOrderArray();
|
2024-02-03 16:21:32 +01:00
|
|
|
}
|
|
|
|
|
2024-02-03 16:58:14 +01:00
|
|
|
void ExtendRope(int playerNumber)
|
2024-02-03 16:21:32 +01:00
|
|
|
{
|
2024-02-03 20:01:12 +01:00
|
|
|
int prevSubDivision = (int)subDivision;
|
2024-02-03 16:58:14 +01:00
|
|
|
subDivision += ropeExtendSpeed * Time.deltaTime;
|
2024-02-27 19:54:10 +01:00
|
|
|
subDivision = Mathf.Clamp(subDivision, ropeMinLength, ropeMaxLength);
|
2024-02-03 16:58:14 +01:00
|
|
|
|
2024-02-27 19:54:10 +01:00
|
|
|
// Only extend if the numeric value has changed
|
2024-02-03 20:01:12 +01:00
|
|
|
if (prevSubDivision - (int) subDivision >= 0) return;
|
|
|
|
|
2024-02-27 19:54:10 +01:00
|
|
|
// Extend from rope point after start rope point
|
2024-02-03 20:01:12 +01:00
|
|
|
rope.sticks.Clear();
|
2024-02-27 19:54:10 +01:00
|
|
|
rope.points.Insert(1, new Point(rope.points[1].position));
|
2024-02-03 20:01:12 +01:00
|
|
|
|
|
|
|
var builder = new RopeBuilder(rope.points, rope.sticks);
|
|
|
|
|
|
|
|
// Re-gen sticks
|
|
|
|
for (int i = 0; i < (int)subDivision; i++)
|
|
|
|
{
|
|
|
|
//Debug.Log($"Reg-gen stick. from: {i} to {i + 1}, with dist: {distBetweenRopePoints}");
|
|
|
|
builder.ConnectPointsWithDesiredLength(i, i + 1, distBetweenRopePoints);
|
|
|
|
}
|
|
|
|
rope = builder.Build();
|
|
|
|
|
|
|
|
RebuildRopeColliders();
|
|
|
|
CreateOrderArray();
|
2024-02-03 16:21:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void OnDestroy()
|
|
|
|
{
|
2024-02-27 19:54:10 +01:00
|
|
|
// May never have been initialized
|
|
|
|
if (!IsInitialized) return;
|
2024-02-15 01:32:08 +01:00
|
|
|
|
2024-02-03 16:21:32 +01:00
|
|
|
start.playerInput.ropeLengthShrinken -= ShrinkenRope;
|
|
|
|
end.playerInput.ropeLengthShrinken -= ShrinkenRope;
|
|
|
|
|
|
|
|
start.playerInput.ropeLengthExtend -= ExtendRope;
|
|
|
|
end.playerInput.ropeLengthExtend -= ExtendRope;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void Rebuild()
|
|
|
|
{
|
|
|
|
Debug.Log("rebuild");
|
|
|
|
|
2024-02-03 00:15:07 +01:00
|
|
|
RopeBuilder builder = new RopeBuilder();
|
|
|
|
builder.AddPoint(new Point(start.position, locked: true));
|
2024-02-03 16:21:32 +01:00
|
|
|
|
2024-02-27 19:54:10 +01:00
|
|
|
// Build rope points
|
2024-02-03 16:21:32 +01:00
|
|
|
for (int i = 1; i < (int) subDivision; i++)
|
2024-02-03 00:15:07 +01:00
|
|
|
{
|
2024-02-27 19:54:10 +01:00
|
|
|
Vector3 pointPos = Vector3.Lerp(start.position, end.position, (float)i / Mathf.Floor(subDivision));
|
2024-02-03 00:15:07 +01:00
|
|
|
Debug.DrawRay(pointPos, (end.position - start.position).normalized);
|
|
|
|
builder.AddPoint(new Point(pointPos));
|
|
|
|
}
|
|
|
|
|
|
|
|
builder.AddPoint(new Point(end.position, locked: true));
|
|
|
|
|
2024-02-27 19:54:10 +01:00
|
|
|
// Connect rope points
|
2024-02-03 16:21:32 +01:00
|
|
|
for (int i = 0; i < (int) subDivision; i++)
|
2024-02-03 00:15:07 +01:00
|
|
|
{
|
|
|
|
builder.ConnectPointsWithDesiredLength(i, i + 1, desiredLength: distBetweenRopePoints);
|
|
|
|
}
|
|
|
|
rope = builder.Build();
|
|
|
|
|
2024-02-03 20:01:12 +01:00
|
|
|
RebuildRopeColliders();
|
|
|
|
|
|
|
|
CreateOrderArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void RebuildRopeColliders()
|
|
|
|
{
|
2024-02-28 14:50:14 +01:00
|
|
|
// Benjamin forgor... to assign
|
|
|
|
if (ropeCollidersParent == null)
|
|
|
|
{
|
|
|
|
var holder = new GameObject("RopeHolder");
|
|
|
|
ropeCollidersParent = holder.transform;
|
|
|
|
}
|
|
|
|
|
2024-02-14 04:30:38 +01:00
|
|
|
for (int i = 0; i < ropeCollidersParent.childCount; i++)
|
|
|
|
{
|
|
|
|
Destroy(ropeCollidersParent.GetChild(i));
|
|
|
|
}
|
|
|
|
|
2024-02-03 00:15:07 +01:00
|
|
|
foreach (var point in rope.points)
|
|
|
|
{
|
|
|
|
GameObject ropeCollider = new GameObject("Rope Collider");
|
2024-02-27 19:54:10 +01:00
|
|
|
ropeCollider.tag = colliderTag;
|
2024-02-03 00:15:07 +01:00
|
|
|
ropeCollider.transform.parent = ropeCollidersParent;
|
2024-02-03 02:47:40 +01:00
|
|
|
ropeCollider.transform.position = point.position;
|
2024-02-27 21:17:44 +01:00
|
|
|
ropeCollider.layer = LayerMask.NameToLayer("Rope");
|
2024-02-03 00:15:07 +01:00
|
|
|
|
|
|
|
var colliderComponent = ropeCollider.AddComponent<CircleCollider2D>();
|
|
|
|
colliderComponent.radius = ropeRadius;
|
2024-02-03 10:57:18 +01:00
|
|
|
|
|
|
|
var rigidBody = ropeCollider.AddComponent<Rigidbody2D>();
|
|
|
|
rigidBody.isKinematic = true;
|
2024-02-03 00:15:07 +01:00
|
|
|
}
|
2024-02-02 21:03:44 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void Update()
|
|
|
|
{
|
2024-02-28 14:50:14 +01:00
|
|
|
if (!IsInitialized)
|
2024-02-14 14:37:29 +01:00
|
|
|
return;
|
|
|
|
|
2024-02-03 01:14:34 +01:00
|
|
|
colliderToSquezeForce.Clear();
|
|
|
|
|
2024-02-03 00:15:07 +01:00
|
|
|
rope.points.First().position = start.position;
|
|
|
|
rope.points.Last().position = end.position;
|
|
|
|
|
2024-02-02 21:03:44 +01:00
|
|
|
Simulate();
|
2024-02-03 00:15:07 +01:00
|
|
|
|
|
|
|
// Update the rope collider positions
|
|
|
|
for (int i = 0; i < rope.points.Count; i++)
|
|
|
|
{
|
|
|
|
ropeCollidersParent.GetChild(i).position = rope.points[i].position;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle static colliders
|
|
|
|
foreach (var point in rope.points)
|
|
|
|
{
|
|
|
|
if (point.locked) continue;
|
|
|
|
|
|
|
|
HandleStaticCollidersOfPoint(point);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constrain start transform based on overshoot
|
|
|
|
float overshoot = rope.CalculateLengthOvershoot();
|
2024-02-27 19:54:10 +01:00
|
|
|
PlayerPullAnimation(overshoot);
|
|
|
|
PullPlayers(overshoot);
|
2024-02-03 00:15:07 +01:00
|
|
|
|
2024-02-03 01:14:34 +01:00
|
|
|
// Handle squeze kills
|
|
|
|
foreach (var collider in colliderToSquezeForce)
|
|
|
|
{
|
2024-02-03 21:38:42 +01:00
|
|
|
ISquezeDamageReceiver squezeDamageReceiver = collider.Key.transform.root.GetComponent<ISquezeDamageReceiver>();
|
|
|
|
if (squezeDamageReceiver == null)
|
|
|
|
squezeDamageReceiver = collider.Key.GetComponent<ISquezeDamageReceiver>();
|
|
|
|
|
2024-02-03 01:14:34 +01:00
|
|
|
if (squezeDamageReceiver == null) continue;
|
|
|
|
|
2024-02-04 07:15:21 +01:00
|
|
|
float swingMultiplier = InSwingMode ? swingSpeedToDamageMultiplier.Evaluate((start.locked ? end : start).body.velocity.magnitude) : 1f;
|
|
|
|
squezeDamageReceiver.TakeSquezeDamage(collider.Value * squezeDamage * swingMultiplier);
|
2024-02-03 01:14:34 +01:00
|
|
|
}
|
2024-02-03 01:25:34 +01:00
|
|
|
|
|
|
|
// Update line renderer
|
|
|
|
var positions = rope.points.Select(p => new Vector3(p.position.x, p.position.y, 0f)).ToArray();
|
|
|
|
lineRenderer.positionCount = positions.Length;
|
|
|
|
lineRenderer.SetPositions(positions);
|
2024-02-03 02:47:40 +01:00
|
|
|
|
|
|
|
// Handle xy dampening on z gravity
|
|
|
|
foreach (var point in rope.points)
|
|
|
|
{
|
|
|
|
if (point.position.z >= 0f) continue;
|
|
|
|
|
|
|
|
Vector2 newXYPos = Vector2.MoveTowards(new Vector2(point.position.x, point.position.y), new Vector2(point.prevPosition.x, point.prevPosition.y), Mathf.Abs(point.position.z * xyGravityDampScalor));
|
|
|
|
point.position.Set(newXYPos.x, newXYPos.y, 0f);
|
|
|
|
}
|
2024-02-02 21:03:44 +01:00
|
|
|
}
|
|
|
|
|
2024-02-27 19:54:10 +01:00
|
|
|
private void PlayerPullAnimation(float overshoot)
|
|
|
|
{
|
|
|
|
if (overshoot > pullAnimationOvershootThreshold)
|
|
|
|
{
|
|
|
|
float startDot = Vector2.Dot((start.position - rope.points[1].position).normalized, start.playerInput.movement);
|
|
|
|
if (startDot > 0.35f)
|
|
|
|
{
|
|
|
|
start.playerAnimationHandler?.animator.SetBool("IsPulling", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
float endDot = Vector2.Dot((end.position - rope.points[rope.points.Count - 2].position).normalized, end.playerInput.movement);
|
|
|
|
if (endDot > 0.35f)
|
|
|
|
{
|
|
|
|
end.playerAnimationHandler?.animator.SetBool("IsPulling", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
start.playerAnimationHandler?.animator.SetBool("IsPulling", false);
|
|
|
|
end.playerAnimationHandler?.animator.SetBool("IsPulling", false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void PullPlayers(float overshoot)
|
|
|
|
{
|
|
|
|
if (overshoot <= 0f) return;
|
|
|
|
|
|
|
|
//start.position = prevStartPos;
|
|
|
|
float divider = !start.locked && !end.locked ? 2f : 1f;
|
|
|
|
|
|
|
|
if (!start.locked)
|
|
|
|
{
|
|
|
|
Vector2 pullDirection = (rope.points[1].position - start.position).normalized;
|
|
|
|
Vector2 force = pullDirection * overshoot * (pullForce / divider);
|
|
|
|
start.body.AddForce(force);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
start.body.velocity *= 0;
|
|
|
|
}
|
|
|
|
if (!end.locked)
|
|
|
|
{
|
|
|
|
Vector2 pullDirection = (rope.points[rope.points.Count - 2].position - end.position).normalized;
|
|
|
|
Vector2 force = pullDirection * overshoot * (pullForce / divider);
|
|
|
|
end.body.AddForce(force);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
end.body.velocity *= 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-02 21:03:44 +01:00
|
|
|
private void OnDrawGizmos()
|
|
|
|
{
|
2024-02-28 14:50:14 +01:00
|
|
|
return;
|
|
|
|
|
2024-02-27 19:54:10 +01:00
|
|
|
if (IsInitialized) return;
|
2024-02-02 21:03:44 +01:00
|
|
|
if (!Application.isPlaying) return;
|
|
|
|
|
|
|
|
foreach (var point in rope.points)
|
|
|
|
{
|
2024-02-03 00:15:07 +01:00
|
|
|
//Debug.Log($"pos: {point.position}");
|
|
|
|
Gizmos.DrawSphere(point.position, ropeRadius);
|
2024-02-02 21:03:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Simulate()
|
|
|
|
{
|
|
|
|
foreach (Point p in rope.points)
|
|
|
|
{
|
|
|
|
if (!p.locked)
|
|
|
|
{
|
2024-02-03 02:47:40 +01:00
|
|
|
Vector3 positionBeforeUpdate = p.position;
|
2024-02-02 21:03:44 +01:00
|
|
|
p.position += p.position - p.prevPosition;
|
2024-02-03 02:47:40 +01:00
|
|
|
p.position.z -= gravity * Time.deltaTime * Time.deltaTime;
|
2024-02-02 21:03:44 +01:00
|
|
|
p.prevPosition = positionBeforeUpdate;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < solveIterations; i++)
|
|
|
|
{
|
|
|
|
for (int s = 0; s < rope.sticks.Count; s++)
|
|
|
|
{
|
|
|
|
Stick stick = rope.sticks[order[s]];
|
|
|
|
if (stick.dead)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-02-03 02:47:40 +01:00
|
|
|
Vector3 stickCentre = (stick.A.position + stick.B.position) / 2;
|
|
|
|
Vector3 stickDir = (stick.A.position - stick.B.position).normalized;
|
2024-02-03 20:01:12 +01:00
|
|
|
float length = Vector2.Distance(stick.A.position, stick.B.position);
|
2024-02-02 21:03:44 +01:00
|
|
|
|
2024-02-03 00:15:07 +01:00
|
|
|
if (length > stick.desiredLength || constrainStickMinLength)
|
2024-02-02 21:03:44 +01:00
|
|
|
{
|
|
|
|
if (!stick.A.locked)
|
|
|
|
{
|
2024-02-03 00:15:07 +01:00
|
|
|
TryMovePointToPosition(stick.A, stickCentre + stickDir * stick.desiredLength / 2);
|
2024-02-02 21:03:44 +01:00
|
|
|
}
|
|
|
|
if (!stick.B.locked)
|
|
|
|
{
|
2024-02-03 00:15:07 +01:00
|
|
|
TryMovePointToPosition(stick.B, stickCentre - stickDir * stick.desiredLength / 2);
|
2024-02-02 21:03:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-03 02:47:40 +01:00
|
|
|
private void TryMovePointToPosition(Point point, Vector3 position)
|
2024-02-03 00:15:07 +01:00
|
|
|
{
|
2024-02-03 02:47:40 +01:00
|
|
|
Vector2 moveDir = new Vector2(position.x, position.y) - new Vector2(point.position.x, point.position.y);
|
2024-02-03 00:15:07 +01:00
|
|
|
int stepsRequired = (int) Mathf.Ceil(moveDir.magnitude / collisionCheckDist);
|
|
|
|
moveDir.Normalize();
|
2024-02-03 02:47:40 +01:00
|
|
|
|
|
|
|
Vector2 initialPos = new Vector2(point.position.x, point.position.y);
|
2024-02-03 10:57:18 +01:00
|
|
|
bool shouldBreak = false;
|
2024-02-03 00:15:07 +01:00
|
|
|
for (int i = 0 ; i < stepsRequired; i++)
|
|
|
|
{
|
2024-02-03 02:47:40 +01:00
|
|
|
Vector2 newPos = Vector2.MoveTowards(new Vector2(point.position.x, point.position.y), new Vector2(position.x, position.y), collisionCheckDist);
|
2024-02-03 10:57:18 +01:00
|
|
|
point.position.Set(newPos.x, newPos.y, point.position.z);
|
|
|
|
|
|
|
|
foreach (var collider in Physics2D.OverlapCircleAll(point.position, ropeRadius, staticColliderMask))
|
|
|
|
{
|
|
|
|
if (collider == null) continue;
|
|
|
|
if (collider.isTrigger) continue;
|
2024-02-03 00:15:07 +01:00
|
|
|
|
2024-02-03 10:57:18 +01:00
|
|
|
// A static collider was met, dont move any further
|
|
|
|
Vector2 resolvedPos = collider.ClosestPoint(initialPos);
|
2024-02-03 00:15:07 +01:00
|
|
|
|
2024-02-03 10:57:18 +01:00
|
|
|
if (Vector2.Distance(initialPos, resolvedPos) < ignoreResolveThreshold) continue;
|
2024-02-03 00:15:07 +01:00
|
|
|
|
2024-02-03 10:57:18 +01:00
|
|
|
Vector2 penetrationDir = (resolvedPos - new Vector2(point.position.x, point.position.y)).normalized;
|
|
|
|
Vector2 finalPos = resolvedPos - penetrationDir * ropeRadius;
|
|
|
|
//Debug.Log($"resolved pos: {point.position}->{finalPos}");
|
|
|
|
point.position.Set(finalPos.x, finalPos.y, point.position.z);
|
|
|
|
shouldBreak = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (shouldBreak)
|
|
|
|
break;
|
2024-02-03 00:15:07 +01:00
|
|
|
}
|
2024-02-03 02:47:40 +01:00
|
|
|
|
|
|
|
// Move z position
|
|
|
|
point.position.z = position.z;
|
2024-02-03 00:15:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void HandleStaticCollidersOfPoint(Point p)
|
|
|
|
{
|
2024-02-09 11:25:50 +01:00
|
|
|
foreach (var hitCollider in Physics2D.OverlapCircleAll(p.position, ropeRadius*1.1f, staticColliderMask))
|
2024-02-03 10:57:18 +01:00
|
|
|
{
|
|
|
|
if (hitCollider == null) continue;
|
|
|
|
if (hitCollider.isTrigger) continue;
|
|
|
|
|
|
|
|
// Register the squeze force this rope particle is squezing the collider
|
|
|
|
Vector2 pointPos = new Vector2(p.position.x, p.position.y);
|
|
|
|
Vector2 resolvedPos = hitCollider.ClosestPoint(pointPos);
|
|
|
|
Vector2 penetration = resolvedPos - pointPos;
|
|
|
|
Vector2 finalPos = resolvedPos - penetration.normalized * ropeRadius;
|
|
|
|
|
|
|
|
float squezeForce;
|
|
|
|
if (!colliderToSquezeForce.TryGetValue(hitCollider, out squezeForce))
|
|
|
|
colliderToSquezeForce.Add(hitCollider, squezeForce + penetration.magnitude);
|
|
|
|
else
|
|
|
|
colliderToSquezeForce[hitCollider] = squezeForce + penetration.magnitude;
|
|
|
|
|
|
|
|
p.position.Set(finalPos.x, finalPos.y, p.position.z);
|
|
|
|
}
|
2024-02-03 00:15:07 +01:00
|
|
|
}
|
|
|
|
|
2024-02-02 21:03:44 +01:00
|
|
|
void CreateOrderArray()
|
|
|
|
{
|
|
|
|
order = new int[rope.sticks.Count];
|
|
|
|
for (int i = 0; i < order.Length; i++)
|
|
|
|
{
|
|
|
|
order[i] = i;
|
|
|
|
}
|
|
|
|
ShuffleArray(order, new System.Random());
|
|
|
|
}
|
|
|
|
|
|
|
|
public static T[] ShuffleArray<T>(T[] array, System.Random prng)
|
|
|
|
{
|
|
|
|
|
|
|
|
int elementsRemainingToShuffle = array.Length;
|
|
|
|
int randomIndex = 0;
|
|
|
|
|
|
|
|
while (elementsRemainingToShuffle > 1)
|
|
|
|
{
|
|
|
|
|
|
|
|
// Choose a random element from array
|
|
|
|
randomIndex = prng.Next(0, elementsRemainingToShuffle);
|
|
|
|
T chosenElement = array[randomIndex];
|
|
|
|
|
|
|
|
// Swap the randomly chosen element with the last unshuffled element in the array
|
|
|
|
elementsRemainingToShuffle--;
|
|
|
|
array[randomIndex] = array[elementsRemainingToShuffle];
|
|
|
|
array[elementsRemainingToShuffle] = chosenElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
}
|