diff options
Diffstat (limited to 'Assets/Scripts/Core/Rope.cs')
-rw-r--r-- | Assets/Scripts/Core/Rope.cs | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/Assets/Scripts/Core/Rope.cs b/Assets/Scripts/Core/Rope.cs new file mode 100644 index 0000000..aff295a --- /dev/null +++ b/Assets/Scripts/Core/Rope.cs @@ -0,0 +1,132 @@ +using System.Collections.Generic; +using UnityEngine; + +public class Rope : MonoBehaviour +{ + [SerializeField] protected int constraintIterations = 15; + [SerializeField] protected float gravityMultiplier = 1.0f; + [SerializeField] protected float lineWidth = 0.2f; + [SerializeField] protected float ropeSegmentLength = 0.1f; + [SerializeField] protected int segmentCount = 35; + + protected LineRenderer lr; + protected DistanceJoint2D dj; + protected GameObject player; + protected List<RopeSegment> ropeSegments = new List<RopeSegment>(); + protected Vector3[] ropePositions; + protected bool ropeCreated = false; + + // Start is called before the first frame update + private void Start() + { + player = GameObject.FindGameObjectWithTag("Player"); + dj = GetComponent<DistanceJoint2D>(); + lr = GetComponent<LineRenderer>(); + lr.startWidth = lineWidth; + lr.endWidth = lineWidth; + ropePositions = new Vector3[segmentCount]; + lr.positionCount = segmentCount; + Vector2 ropeLoc = transform.position; + + for (int i = 0; i < segmentCount; i++) + { + ropeSegments.Add(new RopeSegment(ropeLoc)); + ropeLoc.y -= ropeSegmentLength; + } + } + + // Update is called once per frame + private void Update() + { + if (!ropeCreated) + { + CreateRope(); + ropeCreated = true; + } + RenderLine(); + Simulate(); + } + + private void RenderLine() + { + for (int i = 0; i < ropePositions.Length; i++) + { + ropePositions[i] = ropeSegments[i].posNow; + } + lr.SetPositions(ropePositions); + } + + private void CreateRope() + { + dj.connectedBody = player.GetComponent<Rigidbody2D>(); + dj.maxDistanceOnly = true; + dj.distance = Vector2.Distance(player.transform.position, transform.position); + } + + private void Simulate() + { + Vector2 gravityForce = new Vector2(0f, -gravityMultiplier); + + for (int i = 0; i < ropeSegments.Count; i++) + { + RopeSegment segment = ropeSegments[i]; + Vector2 velocity = segment.posNow - segment.posOld; + segment.posOld = segment.posNow; + segment.posNow += velocity; + segment.posNow += gravityForce * Time.deltaTime; + ropeSegments[i] = segment; + } + + for (int i = 0; i < constraintIterations; i++) + { + ApplyContraint(); + } + } + + private void ApplyContraint() + { + RopeSegment endSegment1 = ropeSegments[0]; + endSegment1.posNow = transform.position; + ropeSegments[0] = endSegment1; + + RopeSegment endSegment2 = ropeSegments[ropeSegments.Count - 1]; + endSegment2.posNow = player.transform.position; + ropeSegments[ropeSegments.Count - 1] = endSegment2; + + for (int i = 0; i < ropeSegments.Count - 1; i++) + { + RopeSegment segment1 = ropeSegments[i]; + RopeSegment segment2 = ropeSegments[i + 1]; + + float distance = (segment1.posNow - segment2.posNow).magnitude; + float error = distance - ropeSegmentLength; + Vector2 normalChange = (segment1.posNow - segment2.posNow).normalized; + Vector2 change = normalChange * error; + + if (i != 0) + { + segment1.posNow -= change * 0.5f; + ropeSegments[i] = segment1; + segment2.posNow += change * 0.5f; + ropeSegments[i + 1] = segment2; + } + else + { + segment2.posNow += change; + ropeSegments[i + 1] = segment2; + } + } + } + + public struct RopeSegment + { + public Vector2 posNow; + public Vector2 posOld; + + public RopeSegment(Vector2 pos) + { + this.posNow = pos; + this.posOld = pos; + } + } +} |