3D Physics Based Rope

From Unify Community Wiki
Revision as of 16:06, 11 December 2012 by TomoI (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Author: Jacob Fletcher

Contents

Description

This script makes 3D physics based ropes for things like power-lines blowing in the wind, hoses flopping around, ropes you can swing on, and more.

Usage

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!
// (About Character Joints)
// 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 radialSegments = 6;
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 : TubeRenderer;
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(var s:int=1;s<segments;s++)
			{
			 segmentPos[s] = joints[s].transform.position;
			}
		}
	}
}
 
function BuildRope()
{
	tubeRenderer = new GameObject("TubeRenderer_" + gameObject.name);
	line = tubeRenderer.AddComponent(TubeRenderer);
	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.crossSegments = radialSegments;
	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(var s:int=0;s < segments;s++)
	{
		// Find the each segments position using the slope from above
		var vector : Vector3 = (seperation*s) + transform.position;	
		segmentPos[s] = vector;
		//Add Physics to the segments
		AddJointPhysics(s);
	}
	// Attach the joints to the target object and parent it to this object
	var end : CharacterJoint = target.gameObject.AddComponent("CharacterJoint");
	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;
}
 
function AddJointPhysics(n : int)
{
	joints[n] = new GameObject("Joint_" + n);
	joints[n].transform.parent = transform;
	var rigid : Rigidbody = joints[n].AddComponent("Rigidbody");
	if(!useMeshCollision) {
		 var col : SphereCollider = joints[n].AddComponent("SphereCollider");
		 col.radius = ropeWidth;
	}
	var ph : CharacterJoint = joints[n].AddComponent("CharacterJoint");
	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;
		radius=r;
		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 : 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();
	gameObject.AddComponent(MeshFilter);
	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 (var c:int=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 (var p:int=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 {
			gameObject.AddComponent(MeshCollider);
			colliderExists = true;
		}
	GetComponent(MeshFilter).mesh = mesh;
}
 
 
 
function CalculateTangents(verts : Vector3[])
{
	var tangents : Vector4[] = new Vector4[verts.length];
 
	for(var i:int=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 (var p:int=0;p<points.length;p++) {
		vertices[p+1] = TubeVertex(points[p], radius, col);
	}
}
Personal tools
Namespaces

Variants
Actions
Navigation
Extras
Toolbox