Skip to content

Instantly share code, notes, and snippets.

@mstevenson
Created January 17, 2013 00:47
Show Gist options
  • Save mstevenson/4552515 to your computer and use it in GitHub Desktop.
Save mstevenson/4552515 to your computer and use it in GitHub Desktop.
A simple solution for grabbing and dragging physics objects in Unity. Attach a DragRigidbody component to an object that has a both collider and a rigidbody. Add the object to a layer named "Interactive".
using UnityEngine;
using System.Collections;
/// <summary>
/// Utility class for working with planes relative to a camera.
/// </summary>
public static class CameraPlane
{
/// <summary>
/// Returns world space position at a given viewport coordinate for a given depth.
/// </summary>
public static Vector3 ViewportToWorldPlanePoint (Camera theCamera, float zDepth, Vector2 viewportCord)
{
Vector2 angles = ViewportPointToAngle (theCamera, viewportCord);
float xOffset = Mathf.Tan (angles.x) * zDepth;
float yOffset = Mathf.Tan (angles.y) * zDepth;
Vector3 cameraPlanePosition = new Vector3 (xOffset, yOffset, zDepth);
cameraPlanePosition = theCamera.transform.TransformPoint (cameraPlanePosition);
return cameraPlanePosition;
}
public static Vector3 ScreenToWorldPlanePoint (Camera camera, float zDepth, Vector3 screenCoord)
{
var point = Camera.main.ScreenToViewportPoint (screenCoord);
return ViewportToWorldPlanePoint (camera, zDepth, point);
}
/// <summary>
/// Returns X and Y frustum angle for the given camera representing the given viewport space coordinate.
/// </summary>
public static Vector2 ViewportPointToAngle (Camera cam, Vector2 ViewportCord)
{
float adjustedAngle = AngleProportion(cam.fieldOfView/2, cam.aspect) * 2;
float xProportion = ((ViewportCord.x - .5f)/.5f);
float yProportion = ((ViewportCord.y - .5f)/.5f);
float xAngle = AngleProportion(adjustedAngle/2, xProportion) * Mathf.Deg2Rad;
float yAngle = AngleProportion(cam.fieldOfView/2, yProportion) * Mathf.Deg2Rad;
return new UnityEngine.Vector2 (xAngle, yAngle);
}
/// <summary>
/// Distance between the camera and a plane parallel to the viewport that passes through a given point.
/// </summary>
public static float CameraToPointDepth (Camera cam, Vector3 point)
{
Vector3 localPosition = cam.transform.InverseTransformPoint (point);
return localPosition.z;
}
public static float AngleProportion (float angle, float proportion)
{
float oppisite = Mathf.Tan (angle * Mathf.Deg2Rad);
float oppisiteProportion = oppisite * proportion;
return Mathf.Atan (oppisiteProportion) * Mathf.Rad2Deg;
}
}
using UnityEngine;
/// <summary>
/// Drag a rigidbody with the mouse using a spring joint.
/// </summary>
[RequireComponent(typeof(Rigidbody))]
public class DragRigidbody : MonoBehaviour
{
public float force = 600;
public float damping = 6;
Transform jointTrans;
float dragDepth;
void OnMouseDown ()
{
HandleInputBegin (Input.mousePosition);
}
void OnMouseUp ()
{
HandleInputEnd (Input.mousePosition);
}
void OnMouseDrag ()
{
HandleInput (Input.mousePosition);
}
public void HandleInputBegin (Vector3 screenPosition)
{
var ray = Camera.main.ScreenPointToRay (screenPosition);
RaycastHit hit;
if (Physics.Raycast (ray, out hit)) {
if (hit.transform.gameObject.layer == LayerMask.NameToLayer ("Interactive")) {
dragDepth = CameraPlane.CameraToPointDepth (Camera.main, hit.point);
jointTrans = AttachJoint (hit.rigidbody, hit.point);
}
}
}
public void HandleInput (Vector3 screenPosition)
{
if (jointTrans == null)
return;
var worldPos = Camera.main.ScreenToWorldPoint (screenPosition);
jointTrans.position = CameraPlane.ScreenToWorldPlanePoint (Camera.main, dragDepth, screenPosition);
}
public void HandleInputEnd (Vector3 screenPosition)
{
Destroy (jointTrans.gameObject);
}
Transform AttachJoint (Rigidbody rb, Vector3 attachmentPosition)
{
GameObject go = new GameObject ("Attachment Point");
go.hideFlags = HideFlags.HideInHierarchy;
go.transform.position = attachmentPosition;
var newRb = go.AddComponent<Rigidbody> ();
newRb.isKinematic = true;
var joint = go.AddComponent<ConfigurableJoint> ();
joint.connectedBody = rb;
joint.configuredInWorldSpace = true;
joint.xDrive = NewJointDrive (force, damping);
joint.yDrive = NewJointDrive (force, damping);
joint.zDrive = NewJointDrive (force, damping);
joint.slerpDrive = NewJointDrive (force, damping);
joint.rotationDriveMode = RotationDriveMode.Slerp;
return go.transform;
}
private JointDrive NewJointDrive (float force, float damping)
{
JointDrive drive = new JointDrive ();
drive.mode = JointDriveMode.Position;
drive.positionSpring = force;
drive.positionDamper = damping;
drive.maximumForce = Mathf.Infinity;
return drive;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment