Skip to content

Instantly share code, notes, and snippets.

@belzecue
Forked from staggartcreations/Rope.cs
Created March 6, 2021 05:38
HingeJoint-based rope building component
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
[RequireComponent(typeof(Rigidbody))]
public class Rope : MonoBehaviour
{
public LineRenderer lineRenderer;
public Transform endPoint;
[Range(0.25f, 1f)]
public float jointDistance = 1f;
public float width = 0.05f;
[SerializeField]
private HingeJoint[] hinges;
private Rigidbody startRigidBody;
private Rigidbody endRigidBody;
private HingeJoint endJoint;
private void LateUpdate()
{
UpdateLineRenderer();
}
public void Rebuild()
{
if (!endPoint) return;
if (!startRigidBody) startRigidBody = GetComponent<Rigidbody>();
startRigidBody.isKinematic = true;
float length = (transform.position - endPoint.position).magnitude;
int segmentCount = Mathf.RoundToInt(length / jointDistance);
if (hinges != null)
{
for (int i = 0; i < hinges.Length; i++)
{
DestroyImmediate(hinges[i].gameObject);
}
hinges = null;
}
hinges = new HingeJoint[segmentCount];
Vector3 direction = (endPoint.position - transform.position).normalized;
Vector3 localDirection = transform.TransformDirection(direction);
for (int i = 0; i < hinges.Length; i++)
{
GameObject hingeObj = new GameObject("Hinge_" + i);
hingeObj.transform.up = direction;
hingeObj.transform.parent = this.transform;
hingeObj.transform.localPosition = localDirection * (i * jointDistance);
HingeJoint joint = hingeObj.AddComponent<HingeJoint>();
CapsuleCollider collider = joint.gameObject.AddComponent<CapsuleCollider>();
collider.radius = width;
collider.center = new Vector3(0f, jointDistance * 0.5f, 0f);
collider.height = jointDistance;
if (i == 0) joint.connectedBody = startRigidBody;
if (i > 0) joint.connectedBody = hinges[i - 1].GetComponent<Rigidbody>();
hinges[i] = joint;
}
if (endJoint == null) endJoint = endPoint.GetComponent<HingeJoint>();
if (endJoint == null) endJoint = endPoint.gameObject.AddComponent<HingeJoint>();
if (endRigidBody == null) endRigidBody = endPoint.GetComponent<Rigidbody>();
endJoint.connectedBody = hinges[hinges.Length - 1].GetComponent<Rigidbody>();
RebuildLineRenderer();
}
private void UpdateLineRenderer()
{
if (!lineRenderer) return;
for (int i = 0; i < hinges.Length; i++)
{
lineRenderer.SetPosition(i, hinges[i].transform.position);
}
lineRenderer.SetPosition(lineRenderer.positionCount-1, endPoint.position);
}
private void RebuildLineRenderer()
{
if (!lineRenderer) return;
lineRenderer.positionCount = hinges.Length+1;
lineRenderer.useWorldSpace = true;
lineRenderer.widthMultiplier = width*2f;
for (int i = 0; i < hinges.Length; i++)
{
lineRenderer.SetPosition(i, hinges[i].transform.position);
}
lineRenderer.SetPosition(lineRenderer.positionCount-1, endPoint.position);
}
}
#if UNITY_EDITOR
[CustomEditor(typeof(Rope))]
public class RopeBuilderInspector : Editor
{
private Rope script;
private void OnEnable()
{
script = (Rope)target;
}
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
base.OnInspectorGUI();
//Can't destroy objects in OnValidate, so do it here
if (EditorGUI.EndChangeCheck())
{
script.Rebuild();
}
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment