3D Physics Based Rope

Description and Assembly

This script makes 3D physics based ropes for things like powerlines blowing in the wind, hoses flopping around, ropes you can swing on, and more. Basically all you have to do is drop this script into one object, then add the second into the "target" variable and your ready to go!

Rope_Line.js

```//============================
//==  Physics Based 3D Rope ==
//==  File: Rope_Tube.js    ==
//==  By: Jacob Fletcher    ==
//==  Use and alter Freely  ==
//============================

//To see other things I have created, visit me at www.reverieinterative.com

//How To Use:
// ( BASIC )
// 1. Simply add this script to the object you want a rope teathered to
// 3. Assign the other end of the rope as the "Target" object in this script
// 4. Play and enjoy!

// Sometimes your rope needs to be very limp and by that I mean NO SPRINGY EFFECT.
// In order to do this, you must loosen it up using the swingAxis and twist limits.
// For example, On my joints in my drawing app, I set the swingAxis to (0,0,1) sense
// the only axis I want to swing is the Z axis (facing the camera) and the other settings to around -100 or 100.

var target : Transform;
var material : Material;
var ropeWidth = 0.5;
var resolution = 0.5;
var ropeDrag = 0.1;
var ropeMass = 0.5;
var startRestrained = true;
var endRestrained = false;
var useMeshCollision = false;

// Private Variables (Only change if you know what your doing)
private var segmentPos : Vector3[];
private var joints : GameObject[];
private var tubeRenderer : GameObject;
private var line;
private var segments = 4;
private var rope = false;

//Joint Settings
var swingAxis = Vector3(0,1,0);
var lowTwistLimit = 0.0;
var highTwistLimit = 0.0;
var swing1Limit  = 20.0;

// Require a Rigidbody
@script RequireComponent(Rigidbody)

function OnDrawGizmos() {
if(target) {
Gizmos.color = Color.yellow;
Gizmos.DrawLine (transform.position, target.position);
Gizmos.DrawWireSphere ((transform.position+target.position)/2,ropeWidth);
Gizmos.color = Color.green;
Gizmos.DrawWireSphere (transform.position, ropeWidth);
Gizmos.color = Color.red;
Gizmos.DrawWireSphere (target.position, ropeWidth);
} else {
Gizmos.color = Color.green;
Gizmos.DrawWireSphere (transform.position, ropeWidth);
}
}

function Awake()
{
if(target) {
BuildRope();
} else {
Debug.LogError("You must have a gameobject attached to target: " + this.name,this);
}
}

function LateUpdate()
{
if(target) {
// Does rope exist? If so, update its position
if(rope) {
line.SetPoints(segmentPos, ropeWidth, Color.white);

line.enabled = true;
segmentPos[0] = transform.position;

for(s=1;s<segments;s++)
{
segmentPos[s] = joints[s].transform.position;
}
}
}
}

function BuildRope()
{
tubeRenderer = new GameObject("TubeRenderer_" + gameObject.name);
line.useMeshCollision = useMeshCollision;

// Find the amount of segments based on the distance and resolution
// Example: [resolution of 1.0 = 1 joint per unit of distance]
segments = Vector3.Distance(transform.position,target.position)*resolution;
if(material) {
material.SetTextureScale("_MainTex",Vector2(1,segments+2));
if(material.GetTexture("_BumpMap"))
material.SetTextureScale("_BumpMap",Vector2(1,segments+2));
}
line.vertices = new TubeVertex[segments];
line.material = material;
segmentPos = new Vector3[segments];
joints = new GameObject[segments];
segmentPos[0] = transform.position;
segmentPos[segments-1] = target.position;

// Find the distance between each segment
var segs = segments-1;
var seperation = ((target.position - transform.position)/segs);

for(s=0;s < segments;s++)
{
// Find the each segments position using the slope from above
vector = (seperation*s) + transform.position;
segmentPos[s] = vector;

}

// Attach the joints to the target object and parent it to this object
end.connectedBody = joints[joints.length-1].transform.rigidbody;
end.swingAxis = swingAxis;
end.lowTwistLimit.limit = lowTwistLimit;
end.highTwistLimit.limit = highTwistLimit;
end.swing1Limit.limit  = swing1Limit;
target.parent = transform;

if(endRestrained)
{
end.rigidbody.isKinematic = true;
}

if(startRestrained)
{
transform.rigidbody.isKinematic = true;
}

// Rope = true, The rope now exists in the scene!
rope = true;
}

{
joints[n] = new GameObject("Joint_" + n);
joints[n].transform.parent = transform;
if(!useMeshCollision) {
}
ph.swingAxis = swingAxis;
ph.lowTwistLimit.limit = lowTwistLimit;
ph.highTwistLimit.limit = highTwistLimit;
ph.swing1Limit.limit  = swing1Limit;
//ph.breakForce = ropeBreakForce; <--------------- TODO

joints[n].transform.position = segmentPos[n];

rigid.drag = ropeDrag;
rigid.mass = ropeMass;

if(n==0){
ph.connectedBody = transform.rigidbody;
} else
{
ph.connectedBody = joints[n-1].rigidbody;
}

}
*/```

MODIFIED TubeRenderer.js

```//TubeRenderer.js

//This script is created by Ray Nothnagel of Last Bastion Games. It is
//free for use and available on the Unify Wiki.

//For other components I've created, see:
//http://lastbastiongames.com/middleware/

//(C) 2008 Last Bastion Games

//--------------------------------------------------------------

//EDIT: MODIFIED BY JACOB FLETCHER FOR USE WITH THE ROPE SCRIPT
//http://www.reverieinteractive.com

class TubeVertex {
var point : Vector3 = Vector3.zero;
var radius : float = 1.0;
var color : Color = Color.white;

function TubeVertex(pt : Vector3, r : float, c : Color) {
point=pt;
color=c;
}
}

var vertices : TubeVertex[];
var material : Material;
var crossSegments : int = 3;
var flatAtDistance : float=-1;
var movePixelsForRebuild = 6;
var maxRebuildTime = 0.1;
var useMeshCollision = false;

private var lastCameraPosition1 : Vector3;
private var lastCameraPosition2 : Vector3;
private var crossPoints : Vector3[];
private var lastCrossSegments : int;
private var lastRebuildTime = 0.00;
private var mesh;
private var colliderExists = false;
private var usingBumpmap = false;

function Reset() {
vertices = [TubeVertex(Vector3.zero, 1.0, Color.white), TubeVertex(Vector3(1,0,0), 1.0, Color.white)];
}

function Start() {
Reset();
mesh = new Mesh();
var mr : MeshRenderer = gameObject.AddComponent(MeshRenderer);
mr.material = material;
if(material) {
if(material.GetTexture("_BumpMap")) usingBumpmap = true;
}
}

function LateUpdate () {
if (!vertices || vertices.length <= 1) {
renderer.enabled=false;
return;
}

renderer.enabled=true;
if (crossSegments != lastCrossSegments) {
crossPoints = new Vector3[crossSegments];
var theta : float = 2.0*Mathf.PI/crossSegments;
for (c=0;c<crossSegments;c++) {
crossPoints[c] = Vector3(Mathf.Cos(theta*c), Mathf.Sin(theta*c), 0);
}
lastCrossSegments = crossSegments;
}

var meshVertices : Vector3[] = new Vector3[vertices.length*crossSegments];
var uvs : Vector2[] = new Vector2[vertices.length*crossSegments];
var colors : Color[] = new Color[vertices.length*crossSegments];
var tris : int[] = new int[vertices.length*crossSegments*6];
var lastVertices : int[] = new int[crossSegments];
var theseVertices : int[] = new int[crossSegments];
var rotation : Quaternion;

for (p=0;p<vertices.length;p++) {
if(p<vertices.length-1)
rotation = Quaternion.FromToRotation(Vector3.forward,vertices[p+1].point-vertices[p].point);

for (c=0;c<crossSegments;c++) {
var vertexIndex : int = p*crossSegments+c;
meshVertices[vertexIndex] = vertices[p].point + rotation * crossPoints[c] * vertices[p].radius;
uvs[vertexIndex] = Vector2((0.0+c)/crossSegments,(0.0+p)/vertices.length);
colors[vertexIndex] = vertices[p].color;

lastVertices[c]=theseVertices[c];
theseVertices[c] = p*crossSegments+c;
}

//make triangles
if (p>0) {
for (c=0;c<crossSegments;c++) {
var start : int= (p*crossSegments+c)*6;
tris[start] = lastVertices[c];
tris[start+1] = lastVertices[(c+1)%crossSegments];
tris[start+2] = theseVertices[c];
tris[start+3] = tris[start+2];
tris[start+4] = tris[start+1];
tris[start+5] = theseVertices[(c+1)%crossSegments];
}
}
}

//Clear mesh for new build  (jf)
mesh.Clear();
mesh.vertices = meshVertices;
mesh.triangles = tris;
mesh.RecalculateNormals();
if(usingBumpmap)
mesh.tangents = CalculateTangents(meshVertices);
mesh.uv = uvs;

if(useMeshCollision)
if(colliderExists) {
gameObject.GetComponent(MeshCollider).sharedMesh = mesh;
} else {
colliderExists = true;
}
GetComponent(MeshFilter).mesh = mesh;
}

function CalculateTangents(verts)
{
var tangents = new Vector4[verts.length];

for(i=0;i<tangents.length;i++)
{
var vertex1 = i > 0 ? verts[i-1] : verts[i];
var vertex2 = i < tangents.length - 1 ? verts[i+1] : verts[i];
var tan = (vertex1 - vertex2).normalized;
tangents[i] = Vector4( tan.x, tan.y, tan.z, 1.0 );
}
return tangents;
}

//sets all the points to points of a Vector3 array, as well as capping the ends.
function SetPoints(points : Vector3[], radius : float, col : Color) {
if (points.length < 2) return;
vertices = new TubeVertex[points.length+2];

var v0offset : Vector3 = (points[0]-points[1])*0.01;
vertices[0] = TubeVertex(v0offset+points[0], 0.0, col);
var v1offset : Vector3 = (points[points.length-1] - points[points.length-2])*0.01;
vertices[vertices.length-1] = TubeVertex(v1offset+points[points.length-1], 0.0, col);

for (p=0;p<points.length;p++) {