# Create a 2D rope without hinge joints breaking!

Discussion in 'Physics' started by DroidifyDevs, Jun 22, 2017.

1. ### DroidifyDevs

Joined:
Jun 24, 2015
Posts:
1,661
Hello!

So I'm trying to make a simply 2D rope grappling game to show to someone, and I'm creating a rope with a script. I'm spawning objects between 2 points and hooking their anchors together; all that is done by 1 script.

However, if the rope is too long, the hinges break and cause the physics engine to glitch out.
Here's a video:

And here's the script:
Code (CSharp):
1. using System.Collections;
2. using System.Collections.Generic;
3. using System.Linq;
5. using UnityEngine;
6. using UnityEngine.SceneManagement;
7. using UnityEditor;
8.
9. public class RopeMaker : MonoBehaviour {
10.     public Transform Player;
12.     public Transform RopeParent;
15.     public Vector3 StartPoint;
16.     public Vector3 EndPoint;
17.     public float DistanceBetweenPoints;
18.     public float AngleBetweenPoints;
19.     public Vector3 Offset;
21.     Ray ray;
22.     RaycastHit hit;
23.
24.     void GenerateRope()
25.     {
26.         DistanceBetweenPoints = Vector3.Distance(StartPoint, EndPoint);
27.         GetAngle();
28.         //AngleBetweenPoints = Vector3.Angle(StartPoint, EndPoint);
30.         //FIX! Offset MUST take into account angle
31.         //Offset = new Vector3((DistanceBetweenPoints / AmountOfLinksNeeded) * Mathf.Cos(AngleBetweenPoints), ((DistanceBetweenPoints / AmountOfLinksNeeded) * Mathf.Sin(AngleBetweenPoints)), 0);
33.         {
34.             var ropelink = Instantiate(RopeLinkPrefab, StartPoint, Quaternion.Euler(0, 0, AngleBetweenPoints - 90), RopeParent);
37.             Player.GetComponent<HingeJoint2D>().enabled = true;
40.         }
41.         for(int i = 1; i < AmountOfLinksNeeded; i++)
42.         {
47.             if (i != (AmountOfLinksNeeded - 1))
49.             else
50.             {
54.             }
57.         }
58.     }
59.
60.     //gets angle between start and end clicks so we can spawn rope links in correct direction
61.     void GetAngle()
62.     {
64.         if (StartPoint.x < EndPoint.x)
65.         {
66.             if (StartPoint.y < EndPoint.y)
67.             {
69.                 AngleBetweenPoints = (Mathf.Asin((EndPoint.y - StartPoint.y) / DistanceBetweenPoints));
70.                 Debug.Log("Angle in radians: " + AngleBetweenPoints);
71.                 //to degrees
72.                 AngleBetweenPoints = (AngleBetweenPoints * 180) / Mathf.PI;
73.                 Debug.Log("Angle in degrees: " + AngleBetweenPoints);
75.             }
76.             if (StartPoint.y > EndPoint.y)
77.             {
78.                 //ASin(Y)/Distance - to radians, this gives small angle down from right X axis
79.                 AngleBetweenPoints = (Mathf.Asin((StartPoint.y - EndPoint.y) / DistanceBetweenPoints));
80.                 Debug.Log("Angle in radians: " + AngleBetweenPoints);
81.                 //to degrees
82.                 AngleBetweenPoints = 360 - (AngleBetweenPoints * 180) / Mathf.PI;
83.                 Debug.Log("Angle in degrees: " + AngleBetweenPoints);
85.             }
86.         }
88.         if (StartPoint.x > EndPoint.x)
89.         {
90.             if (StartPoint.y > EndPoint.y)
91.             {
93.                 AngleBetweenPoints = (Mathf.Asin((StartPoint.y - EndPoint.y) / DistanceBetweenPoints));
94.                 Debug.Log("Angle in radians: " + AngleBetweenPoints);
95.                 //to degrees
96.                 AngleBetweenPoints = 180 + ((AngleBetweenPoints * 180) / Mathf.PI);
97.                 Debug.Log("Angle in degrees: " + AngleBetweenPoints);
99.             }
100.             if (StartPoint.y < EndPoint.y)
101.             {
102.                 //ASin(Y)/Distance - to radians, this gives small angle down from right X axis
103.                 AngleBetweenPoints = (Mathf.Asin((EndPoint.y - StartPoint.y) / DistanceBetweenPoints));
104.                 Debug.Log("Angle in radians: " + AngleBetweenPoints);
105.                 //to degrees
106.                 AngleBetweenPoints = 180 - (AngleBetweenPoints * 180) / Mathf.PI;
107.                 Debug.Log("Angle in degrees: " + AngleBetweenPoints);
109.             }
110.         }
111.     }
112.
113.     // Update is called once per frame
114.     void Update ()
115.     {
116.         if (Input.GetMouseButtonDown(0))
117.         {
118.             ray = Camera.main.ScreenPointToRay(Input.mousePosition);
119.             if (Physics.Raycast(ray, out hit))
120.             {
122.                 StartPoint = new Vector2(Player.transform.position.x + Player.GetComponent<HingeJoint2D>().anchor.x,
123.                     Player.transform.position.y + Player.GetComponent<HingeJoint2D>().anchor.y);
124.                 EndPoint = new Vector3(hit.point.x, hit.point.y, 0);
125.             }
126.             GenerateRope();
127.             Debug.Log("<color=green>Generating rope between</color> " + StartPoint + " & " + EndPoint);
128.             Debug.Log("<color=green>Rope start point: </color>" + StartPoint);
129.             Debug.Log("<color=red>Rope end point: </color>" + EndPoint);
130.         }
131.     }
132. }
133.
Is using 2D Hinge Joints the best way to make a 2D rope? If not, what can I do?

Thank you!

### Unity Technologies

Joined:
May 24, 2013
Posts:
2,012
You can use HingeJoint2D to connect each rope segment but to keep things stable you should connect the start and end parts of the rope/chain with a DistanceJoint2D with its MaxDistanceOnly property set to true.

You can see an example of this here, specifically this scene which simulates a chain and shows the difference in stablility.

meyerjoer5 and bobkingstone like this.
3. ### DroidifyDevs

Joined:
Jun 24, 2015
Posts:
1,661
Thanks for the link! That's a very nice collection of 2D examples that should be more easily visible on Unity's website!!

I did as you said and now I'm adding a distance joint between the 1st and last links in the chain (that's also how you did it in your scene).

This is my result:

As you see, when spawning the rope, the 2D box colliders on each of the rope's "links" create an initial bounce which the distance joint contains by curving the rope. Without the distance joint, the rope stretches.

Here's the slightly modified script:
Code (CSharp):
1. using System.Collections;
2. using System.Collections.Generic;
3. using System.Linq;
5. using UnityEngine;
6. using UnityEngine.SceneManagement;
7. using UnityEditor;
8. public class RopeMaker : MonoBehaviour {
9.     public Transform Player;
11.     public Transform RopeParent;
14.     public Vector3 StartPoint;
15.     public Vector3 EndPoint;
16.     public float DistanceBetweenPoints;
17.     public float AngleBetweenPoints;
18.     public Vector3 Offset;
20.     Ray ray;
21.     RaycastHit hit;
22.     //generates the rope
23.     void GenerateRope()
24.     {
25.         DistanceBetweenPoints = Vector3.Distance(StartPoint, EndPoint);
26.         GetAngle();
28.         //this happens for 1st rope link (green one)
30.         {
31.             var ropelink = Instantiate(RopeLinkPrefab, StartPoint, Quaternion.Euler(0, 0, AngleBetweenPoints - 90), RopeParent);
35.             Player.GetComponent<HingeJoint2D>().enabled = true;
38.         }
39.         for(int i = 1; i < AmountOfLinksNeeded; i++)
40.         {
43.             if (i != (AmountOfLinksNeeded - 1))
45.             else
46.             {
47.                 //this happens for last rope link (red one)
52.                 Debug.Break();
53.             }
56.         }
57.     }
58.     //gets angle between start and end clicks so we can spawn rope links in correct direction
59.     void GetAngle()
60.     {
62.         if (StartPoint.x < EndPoint.x)
63.         {
64.             if (StartPoint.y < EndPoint.y)
65.             {
67.                 AngleBetweenPoints = (Mathf.Asin((EndPoint.y - StartPoint.y) / DistanceBetweenPoints));
68.                 Debug.Log("Angle in radians: " + AngleBetweenPoints);
69.                 //to degrees
70.                 AngleBetweenPoints = (AngleBetweenPoints * 180) / Mathf.PI;
71.                 Debug.Log("Angle in degrees: " + AngleBetweenPoints);
73.             }
74.             if (StartPoint.y > EndPoint.y)
75.             {
76.                 //ASin(Y)/Distance - to radians, this gives small angle down from right X axis
77.                 AngleBetweenPoints = (Mathf.Asin((StartPoint.y - EndPoint.y) / DistanceBetweenPoints));
78.                 Debug.Log("Angle in radians: " + AngleBetweenPoints);
79.                 //to degrees
80.                 AngleBetweenPoints = 360 - (AngleBetweenPoints * 180) / Mathf.PI;
81.                 Debug.Log("Angle in degrees: " + AngleBetweenPoints);
83.             }
84.         }
86.         if (StartPoint.x > EndPoint.x)
87.         {
88.             if (StartPoint.y > EndPoint.y)
89.             {
91.                 AngleBetweenPoints = (Mathf.Asin((StartPoint.y - EndPoint.y) / DistanceBetweenPoints));
92.                 Debug.Log("Angle in radians: " + AngleBetweenPoints);
93.                 //to degrees
94.                 AngleBetweenPoints = 180 + ((AngleBetweenPoints * 180) / Mathf.PI);
95.                 Debug.Log("Angle in degrees: " + AngleBetweenPoints);
97.             }
98.             if (StartPoint.y < EndPoint.y)
99.             {
100.                 //ASin(Y)/Distance - to radians, this gives small angle down from right X axis
101.                 AngleBetweenPoints = (Mathf.Asin((EndPoint.y - StartPoint.y) / DistanceBetweenPoints));
102.                 Debug.Log("Angle in radians: " + AngleBetweenPoints);
103.                 //to degrees
104.                 AngleBetweenPoints = 180 - (AngleBetweenPoints * 180) / Mathf.PI;
105.                 Debug.Log("Angle in degrees: " + AngleBetweenPoints);
107.             }
108.         }
109.     }
110.
111.  // Update is called once per frame
112.  void Update ()
113.     {
114.         if (Input.GetMouseButtonDown(0))
115.         {
116.             ray = Camera.main.ScreenPointToRay(Input.mousePosition);
117.             if (Physics.Raycast(ray, out hit))
118.             {
120.                 StartPoint = new Vector2(Player.transform.position.x + Player.GetComponent<HingeJoint2D>().anchor.x,
121.                     Player.transform.position.y + Player.GetComponent<HingeJoint2D>().anchor.y);
122.                 EndPoint = new Vector3(hit.point.x, hit.point.y, 0);
123.             }
124.             GenerateRope();
125.             Debug.Log("<color=green>Generating rope between</color> " + StartPoint + " & " + EndPoint);
126.             Debug.Log("<color=green>Rope start point: </color>" + StartPoint);
127.             Debug.Log("<color=red>Rope end point: </color>" + EndPoint);
128.         }
129.  }
130. }
131.
Now here's a 2nd video, this time without the 2D box colliders on each rope link:

The 1st play is with distance joint. It prevents stretch but curves the rope. I'm not moving the ball with my controls.

The 2nd play is without the distance joint. Because there are no colliders, the rope doesn't break, but it stretches. Since there's no distance joint, it doesn't curve either.

I need the colliders though to make the rope bend properly, as in your example Do you want me to share a link to the project?

I've spent almost all my time in 3D, so I wanted to get my 2D skills up to shape with this project but I'm stuck on generating a rope Thank you again for your help!

Last edited: Jun 23, 2017
4. ### layinka

Joined:
Jun 26, 2013
Posts:
6
Hi DroidifyDevs,
Please Can you help with how the prefabs and the joints are setup?

I am trying to replicate a rope exactly like this in a game i am working on, but the joints are sacttering a lot, once they are moved.

Or if you can help with a sample scene like the ones Melvmay sent

5. ### DroidifyDevs

Joined:
Jun 24, 2015
Posts:
1,661
I didn't find a solution for my use case (maybe if I spent more time on it I would have). So I dumped that project indefinitely.