rigidbody2D dragable script,

Hi, Is there a rigidbody2D dragable script, for easy use on chains of sprites? I still crave one that is multi-touch savvy…
Kind regards Ian

Here, in answer to my own question, are two scripts (one js one c#) that reproduce the behaviour of the standard asset DragRidgidbody.js script but on 2D sprites.

I hope they are of help to someone else. My initial posts seemed not to generate a response so I spent the day solving the problem (that I’m sure is quite simple to the experienced).

If anyone has suggestions or improvements (especially around the SpringJoint2D settings as conversion between the SpringJoint 3D and the 2D are not one-to-one.

Notes: Ray / Hit test
I used a layer mask. See the comments in the script. I set ‘touchable’ sprites to that layer. That lets me create non-selectedable collision objects and blend 2D and 3D collisions. So be sure to set one and check the layer mask index matches yours.
Centre of Mass Setting Currently Disabled
Currently ‘centerOfMass’ isn’t reported for 2D physics like 3D Physics yet - it will be added in a future Unity release. See code comments for a URL to the info.
2D/3D Interaction
A useful thing for me: if the 3D DragRigidbody.js script is in the scene, a single click can drag both objects. If you do that you can see the spring settings / control feel are very different 2D snappy, 3D lazy.

Anyone who can help convert this to multitouch - or indicate the schematic of how that could work - that would be fab. I get the principles of how I can do it. But would love to check the methodology before beginning…

I also attach an image of the script working on a 2D sprite chain colliding with a 3D sphere on a spring (the sphere has a child Circle Collider 2D).

C# DragRigidbody2D.cs

// Conversion of standard DragRigidbody.js to DragRigidbody2D.cs
// Ian Grant v001
using UnityEngine;
using System.Collections;

public class DragRigidBody2D : MonoBehaviour
{

// Class Variables
		public float distance = 0.2f;
		public float damper = 0.5f; // damping ration in SpringJoint2D (0.0.- 1.0)
		public float frequency = 8.0f;
		public float drag = 1.0f; // this doesn't exist on 2D Spring...
		public float angularDrag = 5.0f;
		//var distance = 0.2;
		public bool attachToCenterOfMass = false;
		private SpringJoint2D springJoint;
	

// Update
		void Update ()
		{
	

				if (!Input.GetMouseButtonDown (0))
						return;
		
				Camera mainCamera = FindCamera ();
				int layerMask = 1 << 8;
				RaycastHit2D hit = Physics2D.Raycast (mainCamera.ScreenToWorldPoint (Input.mousePosition), Vector2.zero, Mathf.Infinity, layerMask);
				Debug.Log ("Layermask: " + LayerMask.LayerToName (8));
				// I have proxy collider objects (empty gameobjects with a 2D Collider) as a child of a 3D rigidbody - simulating collisions between 2D and 3D objects
				// I therefore set any 'touchable' object to layer 8 and use the layerMask above for all touchable items

				if (hit.collider != null  hit.rigidbody.isKinematic == true) {
						return;
				} 

				if (hit.collider != null  hit.rigidbody.isKinematic == false) {
			
			
						if (!springJoint) {
								GameObject go = new GameObject ("Rigidbody2D Dragger");
								Rigidbody2D body = go.AddComponent ("Rigidbody2D") as Rigidbody2D;
								springJoint = go.AddComponent ("SpringJoint2D") as SpringJoint2D;
				
								body.isKinematic = true;
						}

						springJoint.transform.position = hit.point;


						if (attachToCenterOfMass) {
			
								Debug.Log ("Currently 'centerOfMass' isn't reported for 2D physics like 3D Physics - it will be added in a future release.");
								// Currently 'centerOfMass' isn't reported for 2D physics like 3D Physics yet - it will be added in a future release.
				
								//Vector3 anchor = transform.TransformDirection(hit.rigidbody.centerOfMass) + hit.rigidbody.transform.position; in c# might be Vector2?
				
								//anchor = springJoint.transform.InverseTransformPoint(anchor);
								//springJoint.anchor = anchor;
						} else {
				
								//springJoint.anchor = Vector3.zero;
						}

						springJoint.distance = distance; // there is no distance in SpringJoint2D
						springJoint.dampingRatio = damper;// there is no damper in SpringJoint2D but there is a dampingRatio
						//springJoint.maxDistance = distance;  // there is no MaxDistance in the SpringJoint2D - but there is a 'distance' field 
						//	see http://docs.unity3d.com/Documentation/ScriptReference/SpringJoint2D.html
						//springJoint.maxDistance = distance;
						springJoint.connectedBody = hit.rigidbody;
			
			
						// maybe check if the 'fraction' is normalised. See http://docs.unity3d.com/Documentation/ScriptReference/RaycastHit2D-fraction.html
						StartCoroutine ("DragObject", hit.fraction);



				} // end of hit true condition

		} // end of update


		IEnumerator DragObject (float distance)
		{	
		
				float oldDrag = springJoint.connectedBody.drag;
				float oldAngularDrag = springJoint.connectedBody.angularDrag;

				springJoint.connectedBody.drag = drag;
				springJoint.connectedBody.angularDrag = angularDrag;

				Camera mainCamera = FindCamera ();

				while (Input.GetMouseButton (0)) {
						Ray ray = mainCamera.ScreenPointToRay (Input.mousePosition);
						springJoint.transform.position = ray.GetPoint (distance);
						yield return null;
				}
		
		
		
				if (springJoint.connectedBody) {	
						springJoint.connectedBody.drag = oldDrag;
						springJoint.connectedBody.angularDrag = oldAngularDrag;
						springJoint.connectedBody = null;
				}

		}

		Camera FindCamera ()
		{
				if (camera)
						return camera;
				else
						return Camera.main;
		}
}

JS DragRigidbody2D.js

// Conversion of standard DragRigidbody.js to DragRigidbody2D.js
// Ian Grant v001

var distance =0.2;
var damper = 0.5; // damping ration in SpringJoint2D (0.0.- 1.0)
var frequency = 8.0;
var drag = 1.0; // this doesn't exist on 2D Spring...
var angularDrag = 5.0;
//var distance = 0.2;
var attachToCenterOfMass = false;


private var springJoint : SpringJoint2D;

function Update ()
{
	// Make sure the user pressed the mouse down
	if (!Input.GetMouseButtonDown (0))
		return;

	var mainCamera = FindCamera();
	var layerMask = 1 << 8;
	var hit : RaycastHit2D = Physics2D.Raycast(mainCamera.ScreenToWorldPoint(Input.mousePosition), Vector2.zero, Mathf.Infinity, layerMask);
	Debug.Log("Layermask: "+LayerMask.LayerToName(8));
	// I have proxy collider objects (empty gameobjects with a 2D Collider) as a child of a 3D rigidbody - simulating collisions between 2D and 3D objects
	// I therefore set any 'touchable' object to layer 8 and use the layerMask above for all touchable items
	
    if (hit.collider != null   hit.rigidbody.isKinematic==true)
        {
            return;
        } 
        
    if (hit.collider != null   hit.rigidbody.isKinematic==false) {
    	

		if (!springJoint)
		{
			var go = new GameObject("Rigidbody2D Dragger");
			var body : Rigidbody2D = go.AddComponent ("Rigidbody2D") as Rigidbody2D;
			springJoint = go.AddComponent ("SpringJoint2D");
			
			body.isKinematic = true;
		}
		
		springJoint.transform.position = hit.point;
		
		if (attachToCenterOfMass)
		{
			Debug.Log("Currently 'centerOfMass' isn't reported for 2D physics like 3D Physics - it will be added in a future release.");
			// Currently 'centerOfMass' isn't reported for 2D physics like 3D Physics yet - it will be added in a future release.
			
			//var anchor = transform.TransformDirection(hit.rigidbody.centerOfMass) + hit.rigidbody.transform.position;
			
			//anchor = springJoint.transform.InverseTransformPoint(anchor);
			//springJoint.anchor = anchor;
		} else{
		
			//springJoint.anchor = Vector3.zero;
			
		}
		
		springJoint.distance = distance; // there is no distance in SpringJoint2D
		springJoint.dampingRatio = damper;// there is no damper in SpringJoint2D but there is a dampingRatio
		//springJoint.maxDistance = distance;  // there is no MaxDistance in the SpringJoint2D - but there is a 'distance' field 
											//	see http://docs.unity3d.com/Documentation/ScriptReference/SpringJoint2D.html
		//springJoint.maxDistance = distance;
		springJoint.connectedBody = hit.rigidbody;
		
		
		// maybe check if the 'fraction' is normalised. See http://docs.unity3d.com/Documentation/ScriptReference/RaycastHit2D-fraction.html
		StartCoroutine ("DragObject", hit.fraction);
		
		} // end of hit true condition
		
	} // end of update

function DragObject (distance : float)
{	

	var oldDrag = springJoint.connectedBody.drag;
	var oldAngularDrag = springJoint.connectedBody.angularDrag;
	springJoint.connectedBody.drag = drag;
	springJoint.connectedBody.angularDrag = angularDrag;
	var mainCamera = FindCamera();
	while (Input.GetMouseButton (0))
	{
		var ray = mainCamera.ScreenPointToRay (Input.mousePosition);
		springJoint.transform.position = ray.GetPoint(distance);
		yield;
	}
	
	
	
	if (springJoint.connectedBody)
	{	
		springJoint.connectedBody.drag = oldDrag;
		springJoint.connectedBody.angularDrag = oldAngularDrag;
		springJoint.connectedBody = null;
	}
}

function FindCamera ()
{
	if (camera)
		return camera;
	else
		return Camera.main;
}

1427781--75583--$screen_2D_dragable_dragon_.png

Kind regards,

Ian

This script works great, except it always seems to “pick up” the object on it’s center point. So if you have say a hockey stick, and try to grab it by the top of the handle, it jerks to the center. I can’t figure out for the life of me why it’s doing this. Any one know?

Thanks!

Low and behold, I answered my own question.

The problem was that SpringJoint2D doesn’t have the ability to autoConfigureConnectedAnchor that SpringJoint does (and it’s enabled by default). As a result, the connectedAnchor is always 0,0 in local space unless you specifically set it.

After:

springJoint.connectedBody = hit.rigidbody;

Add:

Vector3 localPoint = transform.InverseTransformPoint (hit.point);
springJoint.connectedAnchor = localPoint;

We have to transform the hit.point back into location space and apply that to connectedAnchor.

That’s it!

I just wanted to mention that there is a public float distance and IEnumerator DragObject (float distance). You might want to change the name of distance in DragObject.

Also, You probably don’t need to do the check at line 28 since you’re doing the same thing in 33.

Thanks a lot for this script. I’ve used it many times now.

Hi, how do I implement this script ?

Hi, it is used in the same way as the DragRigidBody script in the standard assets: drag a single instance of it onto an empty game object or your main camera and then any 2D rigidbody will be dragable. Might be a good idea to make the changes slek120 and FamerJoe mention… kind regards Ian

don’t forget to add a BoxCollider 2D or nothing will work

Hi,
I’m new here. I try to test it with a simple sprite.
I create a sprite. In the inspector, I have : transform, sprite renderer, Rigidbody2D, BoxCollider2D and the DragRididBody2d (script).
When I try to drag’n’drop, in my debug, I have : Layermask: UnityEngine.Debug:Log(Object).

I add a debug of : hit.rigidbody.isKinematic
and it’s throw a NullReferenceException :frowning:

Do you have the same problem ? How to resolve it ?

Anyway, thanks a lot for this script

Hey, I’m new to unity, and am looking for a script to drag a 2D Rigidbody by touch. I plan to develop the game for android. So far this script seems to be exactly what I’m looking for, but would anyone be willing to modify the script to allow for touch input instead of mouse drag and drop? Thanks, I really appreciate it!

Here is my multitouch version of this script.
I use a couple new features in Unity 4.5 (at least, I think they’re new):
rigidbody.centerOfMass and Physics2D.GetRayIntersection.

using UnityEngine;
using System.Collections;

public class MultiDragRigidbody2D : MonoBehaviour
{
    public int maxTouch = 2;
    [Range(0,31)]
    public int layerMask = 0;
    public float distance = 0.2f;
    public float dampingRatio = 1;
    public float frequency = 1.8f;
    public float linearDrag = 1.0f;
    public float angularDrag = 5.0f;
    public bool centerOfMass = false;
    private SpringJoint2D[] springJoints;

    void Start ()
    {
        springJoints = new SpringJoint2D[maxTouch];

        for (int i = 0; i < maxTouch; i++) {
            GameObject go = new GameObject ("Dragger" + (i + 1));
            go.transform.parent = this.transform;
       
            Rigidbody2D body = go.AddComponent ("Rigidbody2D") as Rigidbody2D;
            springJoints [i] = go.AddComponent ("SpringJoint2D") as SpringJoint2D;
            body.isKinematic = true;
        }
    }

    void Update ()
    {
        foreach (Touch touch in Input.touches) {
            int Id = touch.fingerId;
       
            if (Id < maxTouch && touch.phase == TouchPhase.Began) {
                Camera mainCamera = FindCamera ();
                Ray ray = mainCamera.ScreenPointToRay (touch.position);
                RaycastHit2D hit = Physics2D.GetRayIntersection (ray, Mathf.Infinity, 1 << layerMask);
           
                if (hit.rigidbody != null && hit.rigidbody.isKinematic == false) {
                    springJoints [Id].transform.position = hit.point;
                    springJoints [Id].connectedBody = hit.rigidbody;
               
                    if (centerOfMass)
                        springJoints [Id].connectedAnchor = hit.rigidbody.centerOfMass;
                    else
                        springJoints [Id].connectedAnchor = hit.transform.InverseTransformPoint (hit.point);
               
                    float length = (hit.transform.position - mainCamera.transform.position).magnitude;
                    StartCoroutine (DragObject (Id, length));
                }
            }
        }
    }

    IEnumerator DragObject (int Id, float length)
    {
        float oldDrag = springJoints [Id].connectedBody.drag;
        float oldAngularDrag = springJoints [Id].connectedBody.angularDrag;
        springJoints [Id].distance = distance;
        springJoints [Id].dampingRatio = dampingRatio;
        springJoints [Id].frequency = frequency;
        springJoints [Id].connectedBody.drag = linearDrag;
        springJoints [Id].connectedBody.angularDrag = angularDrag;
        Camera mainCamera = FindCamera ();
   
        while (true) {
            bool touchExists = false;
            foreach (Touch touch in Input.touches) {
                if (touch.fingerId == Id) {
                    touchExists = true;
                    Ray ray = mainCamera.ScreenPointToRay (touch.position);
                    springJoints [Id].transform.position = ray.GetPoint (length);
                }
            }
            if (touchExists)
                yield return null;
            else
                break;
        }
   
        if (springJoints [Id].connectedBody) {
            springJoints [Id].connectedBody.drag = oldDrag;
            springJoints [Id].connectedBody.angularDrag = oldAngularDrag;
            springJoints [Id].connectedBody = null;
        }   
    }

    Camera FindCamera ()
    {
        if (camera)
            return camera;
        else
            return Camera.main;
    }
}

Can’t get this new script to work… any idea?
I have 2d sprites in my scene with 2d rigidbodies and polygon 2d colliders.
The script is attached to main camera.
Also, I believe the code has some error:

    public int maxTouch = 2;
    [Range(0,31)]
    public int [SIZE=14px]layerMask = 0;[/SIZE]

please assist, thanks.

Remove the [SIZE=14px][/SIZE].
I don’t know how that got in there.
I’ll edit my post.

Make sure that your GameObject that you want to move is on default layer if layerMask = 0.
If you don’t want to use layer masking, you can change line 40 to this:

RaycastHit2D hit = Physics2D.GetRayIntersection (ray);

The code doesn’t work with mouse.
If anyone wants to fix that, it would be much appreciated.

Hey guys. If anyone is looking for an out-of-the-box solution that just works on all platforms you can check out DragRigidbody2D on the asset store: Unity Asset Store - The Best Assets for Game Making

Hi guys,

Thank you for your great support, this script has helped me alot.
Another question if one of you knows the answer (relating to the MultiDrag script).
I’m trying to do it, so when I drag my object it will slowly (physics wise) rotate back to it’s original rotation point (0 degrees).

I have tried adding the following line but it only snaps the object to rotation 0.

springJoints [Id].transform.eulerAngles = new Vector3(0,0,0);

Thanks,
Syrul

I don’t really understand what you need but maybe this will help

using UnityEngine;
using System.Collections;

public class RotateToNeutral : MonoBehaviour
{
    public enum Curve
    {
        Linear = 0,
        Exponential = 1
    }

    public Quaternion neutralRotation = Quaternion.identity;
    public Curve curve = Curve.Linear;
    public float rotateSpeed = 2;

    void FixedUpdate ()
    {
        if (curve == Curve.Linear)
            this.rigidbody.MoveRotation (Quaternion.RotateTowards (this.rigidbody.rotation, neutralRotation, rotateSpeed));
        else if (curve == Curve.Exponential)
            this.rigidbody.MoveRotation (Quaternion.Slerp (this.rigidbody.rotation, neutralRotation, Time.deltaTime * rotateSpeed));
    }
}

If you are using 2D, make sure to change all the rigidbody to rigidbody2D

If you want to do it in one line try this

float speed = 2;
springJoints [Id].transform.rotation = Quaternion.Slerp(springJoints [Id].transform.rotation, Quaternion.identity, Time.deltaTime * speed);

Hi slek120,

Thank you for your answer… obviously Unity has a simple answer :stuck_out_tongue:
Anyway, tried your code but it didn’t work, I understood the logic was right but it didn’t point the actual body.
I’ve done some changes and now it works.
Adding snippet below if anyone wants to use this.
(Perhaps add it to the original script).

        while (true) {
            bool touchExists = false;
            foreach (Touch touch in Input.touches) {
                if (touch.fingerId == Id) {
                    touchExists = true;
                    Ray ray = mainCamera.ScreenPointToRay (touch.position);
                    springJoints [Id].transform.position = ray.GetPoint (length);
                    springJoints [Id].connectedBody.transform.rotation = Quaternion.Slerp(springJoints [Id].connectedBody.transform.rotation, Quaternion.identity, Time.deltaTime * speed);
                }
            }

I don’t understand why you need to rotate the spring joint. Can you explain?

Im getting errors on this line:
if (hit.collider != null hit.rigidbody.isKinematic == true)

Error: (31,31): Error BCE0044: expecting ), found ‘hit’. (BCE0044) (Assembly-UnityScript)

Looks like a missing operator, but I’m not sure what should go here. Can someone help?