Skip to content

Instantly share code, notes, and snippets.

@mandarinx
Forked from LordNed/GearRotator.cs
Last active August 29, 2015 14:28
Show Gist options
  • Save mandarinx/2e2691ff14fdc753fa73 to your computer and use it in GitHub Desktop.
Save mandarinx/2e2691ff14fdc753fa73 to your computer and use it in GitHub Desktop.
A quick example of creating a gear or object that can spin with mouse cursor. Expects object to rotate along its Forward vector.
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
public class GearRotator : MonoBehaviour
{
[SerializeField] private float m_radiusThreshold = 4f;
private float m_startingAngle;
private Quaternion m_startingRotation;
private bool m_isBeingRotated;
private void Update()
{
if (Input.GetMouseButtonUp(0))
{
m_isBeingRotated = false;
}
if (Input.GetMouseButton(0))
{
// Create a Ray using the mouse's position into the world.
Ray mouseRay = Camera.main.ScreenPointToRay(Input.mousePosition);
// Construct a plane using our current point. This could probably be cached
// on first frame of GetMouseButton but meh.
Plane plane = new Plane(transform.forward, transform.position);
float intersectDist;
bool bIntersected = plane.Raycast(mouseRay, out intersectDist);
// Calculate an intersection point by seeing how far awa from our mouseRay origin
// the plane lies.
Vector3 intersectPoint = mouseRay.origin + (mouseRay.direction * intersectDist);
// When they first press the mouse, check if they're within range. If they are, mark us as
// being rotated, otherwise, we ignore it.
if (Input.GetMouseButtonDown(0))
{
// Early out if this intersection point is too far away from our gear.
if (Vector3.Distance(transform.position, intersectPoint) > m_radiusThreshold)
return;
m_isBeingRotated = true;
}
// Early out if we're not currently being rotated.
if (!m_isBeingRotated)
return;
if (bIntersected)
{
// Get a angle between the intersect point and us.
float angleDegrees = PointPairToBearingDegrees(Camera.main.transform.forward, transform.position, intersectPoint);
// Store the angle if the mouse was just pressed so we can subtract it.
// This lets us know the delta on each frame (in degrees), allowing us to
// smoothly rotate from current position instead of snapping.
if(Input.GetMouseButtonDown(0))
{
m_startingAngle = angleDegrees;
m_startingRotation = transform.rotation;
Debug.Log("Set Starting Angle: " + m_startingAngle);
}
float deltaAngle = angleDegrees - m_startingAngle;
Debug.Log("deltaAngle: " + deltaAngle);
// We use Vector3.forward here and not transform.forward, otehrwise we add weird off-axis
// rotations and it breaks everything. Quaternions!
Quaternion additiveRot = Quaternion.AngleAxis(deltaAngle, Vector3.forward);
// Finally add the rotation.
transform.rotation = m_startingRotation * additiveRot;
}
Debug.DrawLine(transform.position, intersectPoint, Color.green);
Debug.Log("Intersect Dist: " + intersectDist);
}
}
private float PointPairToBearingDegrees(Vector3 cameraNormal, Vector3 startPoint, Vector3 endPoint)
{
Vector3 direction = endPoint - startPoint;
// Pick the correct pair of coordinates to run our Mathf.Atan2 on. If we assume X and Y, then it won't
// work along the X plane, as X will always be zero, etc. Oof.
Vector2 anglePair = FindPairForDirection(cameraNormal, direction);
float dirRadians = Mathf.Atan2(anglePair.y, anglePair.x); // Atan2 = Direction -> Radians
float dirDegrees = dirRadians * (180.0f / Mathf.PI); // To degrees - this has a discontinuity at -180/180.
dirDegrees = (dirDegrees > 0.0f ? dirDegrees : (360.0f + dirDegrees)); // Correct for 0-360.
return dirDegrees;
}
private Vector2 FindPairForDirection(Vector3 cameraNormal, Vector3 direction)
{
// Find the closest unit plane.
Dictionary<int, float> planes = new Dictionary<int, float>();
planes[0] = Vector3.Dot(transform.forward, Vector3.right); // X axis plane
planes[1] = Vector3.Dot(transform.forward, Vector3.up); // Y axis plane
planes[2] = Vector3.Dot(transform.forward, Vector3.forward); // Z axis plane
// Sort them to find the closest one.
var sortedPlanes = planes.OrderBy(i => i.Value).Reverse().ToArray();
Debug.Log("Closest Plane: " + sortedPlanes[0].Key);
switch(sortedPlanes[0].Key)
{
// X Axis, return y and z.
case 0:
return new Vector2(direction.y, direction.z);
// Y Axis, return x and z
case 1:
return new Vector2(direction.z, direction.x);
// Z axis, return y and x
case 2:
return new Vector2(direction.x, direction.y);
}
return Vector2.zero;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment