On occasion I give back to the community some. In looking for something… I can’t remember what right now, I thought… oh, a rope would do the exact same thing. So I went looking up ropes in the wiki and found the rope that was done a while back… It wasn’t very good.
I even saw where someone created a swinging tether game and the physics on the rope were so bad that the player jerked around in mid air for no reason. I went off looking for a better solution.
The old rope used CharacterJoint’s. Where I thought we could use some improvement was to use springs instead. I searched some on Google and ran across many different ways to look at rope. Some were processor heavy, some were easy and very doable in game physics. I settled on a simple implementation of a spring rope and hacked up the code to make it work. I found out that the concept was really just a bunch of balls sprung together. So instead of using the Unity spring, I used the coded spring.
So here is my demonstration. (not complete, really just working with the balls and seeing how the physics work on it.
http://lod3dx.net/Unity/Rope/index.html
left mouse button moves the start point
right mouse button moves the end point
Shift left, unhooks the start point (or hooks it back)
Shift right, unhooks the end point (or hooks it back)
R reloads… just in case you need it.
And here is the free code that makes the actual rope work:
//RopeSimulator.cs
using UnityEngine;
using System.Collections;
public class RopeSimulator : MonoBehaviour {
public Transform endObject;
public float radius=0.1f;
public float mass=10.0f;
public bool lockStart=true;
public bool lockEnd=false;
public float drag=1.0f;
private float resoloution=1.8f;
private float springConstant=100.0f;
private float springFrictionConstant=0.5f;
private RopeSpring[] springs;
private GameObject[] knots;
private int numKnots=0;
void Start () {
if(! endObject) return;
Destroy(collider);
if(gameObject.GetComponent<Renderer>())gameObject.GetComponent<Renderer>().enabled=false;
Destroy(endObject.collider);
if(endObject.gameObject.GetComponent<Renderer>())endObject.gameObject.GetComponent<Renderer>().enabled=false;
numKnots=(int)(Vector3.Distance(transform.position, endObject.position) * resoloution);
Debug.Log(endObject.position);
float springLength=Vector3.Distance(transform.position, endObject.position) / (float)(numKnots-1);
float knotMass=mass/(float)numKnots;
knots=new GameObject[numKnots];
springs=new RopeSpring[numKnots-1];
for (int i = 0; i < numKnots; i++){
GameObject knot = GameObject.CreatePrimitive(PrimitiveType.Sphere);
knot.name="Knot-" + i + "-" + gameObject.name;
//knot.transform.parent=transform;
knot.transform.localScale=Vector3.one * radius * 2;
knot.AddComponent<Rigidbody>();
knot.rigidbody.mass=knotMass;
if(i==0 lockStart)knot.rigidbody.isKinematic=true;
if(i==numKnots-1 lockEnd) knot.rigidbody.isKinematic=true;
knot.transform.position=Vector3.Lerp(transform.position, endObject.position, (float)i/(float)(numKnots-1));
knots[i]=knot;
}
//knots[0].transform.parent=transform;
for (int i = 0; i < numKnots - 1; i++)
{
springs[i] = new RopeSpring(knots[i], knots[i + 1],
springConstant, springLength, springFrictionConstant);
}
}
// Update is called once per frame
void FixedUpdate () {
if(Input.GetButton("Fire1") ! Input.GetKey(KeyCode.LeftShift))
MoveKnot(knots[0]);
if(Input.GetButton("Fire2") ! Input.GetKey(KeyCode.LeftShift))
MoveKnot(knots[numKnots-1]);
if(Input.GetButtonDown("Fire1") Input.GetKey(KeyCode.LeftShift))
knots[0].rigidbody.isKinematic=!knots[0].rigidbody.isKinematic;
if(Input.GetButtonDown("Fire2") Input.GetKey(KeyCode.LeftShift))
knots[numKnots-1].rigidbody.isKinematic=!knots[numKnots-1].rigidbody.isKinematic;
if(! endObject) return;
for(int i=0; i<numKnots-1; i++){
springs[i].solve(drag, Time.deltaTime);
}
}
void MoveKnot(GameObject knot){
Ray ray;
float dist;
Plane plane;
ray=Camera.main.ScreenPointToRay(Input.mousePosition);
plane=new Plane(-Camera.main.transform.forward, knot.transform.position);
if(plane.Raycast(ray, out dist)){
knot.transform.position=ray.GetPoint(dist);
}
}
}
//RopeSpring.cs
using UnityEngine;
using System.Collections;
public class RopeSpring{
public Rigidbody mass1;
public Rigidbody mass2;
private float springConstant;
private float springLength;
private float frictionConstant;
public RopeSpring(GameObject m1, GameObject m2, float sc, float sl, float fc){
springConstant = sc;
springLength = sl;
frictionConstant = fc;
mass1 = m1.rigidbody;
mass2 = m2.rigidbody;
}
public void solve(float drag, float timestep)
{
Vector3 springVector = // Vector Between The Two Masses
mass1.transform.TransformPoint(mass1.centerOfMass) -
mass2.transform.TransformPoint(mass2.centerOfMass);
float r = springVector.magnitude; // Distance Between The Two Masses
Vector3 d = Vector3.one * drag;
Vector3 force=Vector3.zero; // Force Initially Has A Zero Value
if (r != 0) // To Avoid A Division By Zero... Check If r Is Zero The Spring Force Is Added To The Force
force += -(springVector / r) * (r - springLength) * springConstant;
// The Friction Force Is Added To The force With This Addition We Obtain The Net Force Of The Spring
force += -(mass1.velocity - mass2.velocity) * frictionConstant;
mass1.AddForce(force * drag); // Force Is Applied To mass1
mass2.AddForce(-force * drag); // The Opposite Of Force Is Applied To mass2
}
}
Reference: http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=40