Skip to content

Instantly share code, notes, and snippets.

@LordNed
Last active October 21, 2022 23:49
Show Gist options
  • Save LordNed/cf9c87477b548be3af37 to your computer and use it in GitHub Desktop.
Save LordNed/cf9c87477b548be3af37 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 around its Forward vector.
using UnityEngine;
public class GearRotator : MonoBehaviour
{
[SerializeField] private float m_radiusThreshold = 4f;
private float m_startingAngle;
private bool m_isBeingRotated;
private void OnDrawGizmos()
{
Gizmos.DrawLine(transform.position, transform.position + (transform.right * 2f));
Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, Vector3.one);
Gizmos.DrawWireCube(Vector3.zero, new Vector3(m_radiusThreshold, m_radiusThreshold, 0f));
}
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;
}
// Draw a green line to our mouse so we know where it thinks the intersection is.
Debug.DrawLine(transform.position, intersectPoint, Color.green);
// Early out if we're not currently being rotated.
if (!m_isBeingRotated || !bIntersected)
return;
// Get a angle between the intersect point and us.
float deltaAngleDegrees = PointPairToBearingDegrees(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 = deltaAngleDegrees;
Debug.Log("Set Starting Angle: " + m_startingAngle);
}
float deltaAngle = deltaAngleDegrees - 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 = transform.rotation * additiveRot;
}
}
private float PointPairToBearingDegrees(Vector3 endPoint)
{
// Convert this into local space so we can always use the same pair of coordinates, regardless of world orientation of the gear.
// endPointLocal is now also already a direction, since it's in local space, so we can use it directly.
Vector3 endPointLocal = transform.InverseTransformPoint(endPoint);
float dirRadians = Mathf.Atan2(endPointLocal.y, endPointLocal.x); // Atan2 = Direction -> Radians
float dirDegrees = dirRadians * (180.0f / Mathf.PI); // To degrees - this returns -180/180.
// dirDegrees = (dirDegrees > 0.0f ? dirDegrees : (360.0f + dirDegrees)); // Correct for 0-360 if desired (unneeded in this case)
return dirDegrees;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment