On asset store there is only paid solution and googling didn’t yield anything useful. For now I’m doing it oldschool way (rope “segments” and lot of character joints), but obviously it doesn’t look too good.
The best ones are the ones you make yourself! I suggest searching Google for how to create them. They are rather tricky but you gotta know your unity
This may be of some help to you.
http://wiki.unity3d.com/index.php/3D_Physics_Based_Rope
Or this. This is acceptable. But I also recommend looking through the code and learning it.
No need to learn what you don’t need to know
I found the wiki rope example great but a bit of a performance hog, so I’ve optimised the code a bit to increase it’s performance and reduce it’s memory footprint.
Don’t have wiki access to update it so thought you might like it here.
PS if you can improve the performance further please do so and show us the improvements.
//============================//== 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.
//EDIT: OPTIMISED BY ALLAN ROWNTREE - Increased Performance and Reduced Memory Usage
//http://www.arowx.com
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;
var damper = 1f;
var spring = 1f;
// 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+2];
for (var p:int = 0; p < line.vertices.length; p++) {
line.vertices[p] = TubeVertex(Vector3.zero, 0f, Color.white);
}
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.swingAxis = swingAxis;
end.lowTwistLimit.limit = lowTwistLimit;
end.highTwistLimit.limit = highTwistLimit;
end.swing1Limit.limit = swing1Limit;
end.lowTwistLimit.damper = damper;
end.highTwistLimit.damper = damper;
end.lowTwistLimit.spring = spring;
end.highTwistLimit.spring = spring;
*/
var end : SpringJoint = target.gameObject.AddComponent("SpringJoint");
end.maxDistance = highTwistLimit;
end.minDistance = lowTwistLimit;
end.spring = spring;
end.damper = damper;
end.connectedBody = joints[joints.length-1].transform.rigidbody;
end.rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
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].layer = gameObject.layer;
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 : SpringJoint = joints[n].AddComponent("SpringJoint");
ph.maxDistance = highTwistLimit;
ph.minDistance = lowTwistLimit;
ph.spring = spring;
ph.damper = damper;
/*
var ph : CharacterJoint = joints[n].AddComponent("CharacterJoint");
ph.swingAxis = swingAxis;
ph.lowTwistLimit.limit = lowTwistLimit;
ph.highTwistLimit.limit = highTwistLimit;
ph.swing1Limit.limit = swing1Limit;
ph.lowTwistLimit.damper = damper;
ph.highTwistLimit.damper = damper;
ph.lowTwistLimit.spring = spring;
ph.highTwistLimit.spring = spring;
*/
ph.rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
//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;
}
}
//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
//EDIT: OPTIMISED BY ALLAN ROWNTREE - Increased Performance and Reduced Memory Usage
//http://www.arowx.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;
private var meshVertices : Vector3[];
private var uvs : Vector2[];
private var colors : Color[];
private var tris : int[];
private var lastVertices : int[];
private var theseVertices : int[];
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;
}
//Debug.Log("Start Vertices Length = "+vertices.length);
var v : int = vertices.length+2;
meshVertices = new Vector3[v*crossSegments];
uvs = new Vector2[v*crossSegments];
colors = new Color[v*crossSegments];
tris = new int[v*crossSegments*6];
lastVertices = new int[crossSegments];
theseVertices = new int[crossSegments];
}
function LateUpdate () {
if (!vertices || vertices.length <= 1) {
renderer.enabled=false;
return;
}
//Debug.Log("LateUpdate Vertices Length = "+vertices.length);
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;
var vlen : int = vertices.length;
var invvlen : float = 1f/vlen;
var invCross : float = 1f/crossSegments;
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);
var cross : int = p*crossSegments;
//Debug.Log("p = "+p+" vlen = "+vertices.length);
var v : TubeVertex = vertices[p];
var m : Matrix4x4;
m.SetTRS(v.point, rotation, new Vector3(1f,1f,1f) * v.radius);
for (c=0;c<crossSegments;c++) {
var vertexIndex : int = cross+c;
//Debug.Log("p = "+p+",vi = "+vertexIndex);
//meshVertices[vertexIndex] = v.point + rotation * crossPoints[c] * v.radius;
meshVertices[vertexIndex] = m.MultiplyPoint3x4(crossPoints[c]);
uvs[vertexIndex] = Vector2((0.0+c) * invCross,(0.0+p) * invvlen);
colors[vertexIndex] = v.color;
lastVertices[c]=theseVertices[c];
theseVertices[c] = vertexIndex;
}
//make triangles
if (p>0) {
for (c=0;c<crossSegments;c++) {
var start : int= (cross+c)*6;
var crossSeg : int = (c+1)%crossSegments;
tris[start] = lastVertices[c];
tris[start+1] = lastVertices[crossSeg];
tris[start+2] = theseVertices[c];
tris[start+3] = tris[start+2];
tris[start+4] = tris[start+1];
tris[start+5] = theseVertices[crossSeg];
}
}
}
//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 p0 : Vector3 = points[0];
var v0offset : Vector3 = (p0-points[1])*0.01;
vertices[0] = TubeVertex(v0offset+p0, 0.0, col);
var p1 : Vector3 = points[points.length-1];
var v1offset : Vector3 = (p1 - points[points.length-2])*0.01;
vertices[vertices.length-1] = TubeVertex(v1offset+p1, 0.0, col);
for (var p:int=0;p<points.length;p++) {
//vertices[p+1] = TubeVertex(points[p], radius, col);
var i : int = p+1;
vertices[i].point = points[p];
vertices[i].radius = radius;
vertices[i].color = col;
}
}
Hello!
Maybe you remember why TubeRenderer left the Reset()? I do not see where it is used, can it be removed?
Just get Obi Rope, its 40 USD, but time costs money too
These are three decent solutions depending on your needs (all three are tutorials on his site)