Created
July 9, 2018 01:40
-
-
Save nanodeath/46089e944d1984f4e9f6ecd47022b977 to your computer and use it in GitHub Desktop.
Unity: Rotating a RigidBody2D to face the cursor, smoothly
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
public class FaceCursor : MonoBehaviour { | |
// 100 seems to work fine on an object with mass around 1kg. | |
[Tooltip("Amount of torque to apply when turning")] | |
public float turnSpeed; | |
private Rigidbody2D body; | |
void Start() { | |
body = GetComponent<Rigidbody2D>(); | |
} | |
void FixedUpdate() { | |
var gameObjectToMouse = Input.mousePosition - Camera.main.WorldToScreenPoint(transform.position); | |
var targetAngle = wrapAngleAroundZero(Mathf.Atan2(gameObjectToMouse.y, gameObjectToMouse.x) * Mathf.Rad2Deg); | |
var currentAngle = wrapAngleAroundZero(transform.eulerAngles.z); | |
var torque = turnSpeed * Time.fixedDeltaTime; | |
// I have no idea what this actually is or what to call it, but it works... | |
var angularDelta = torque / body.inertia; | |
// How long would it take us to stop? We need this in case we should actually be slamming on the brakes. | |
var timeRequiredToStop = Mathf.Abs(body.angularVelocity / angularDelta * Time.fixedDeltaTime); | |
// Which direction should we go? Depends on which way is faster. | |
// This doesn't factor in current speed or direction, but eh, close enough. | |
var timeUntilDestinationReachedCCW = (targetAngle - currentAngle) / Mathf.Abs(body.angularVelocity); | |
var timeUntilDestinationReachedCW = -timeUntilDestinationReachedCCW; | |
// This is best explained with an example. | |
// If you're at -135°, travelling at -45°/s, and trying to reach -90°, how long will that take? | |
// Since you're going counterclockwise, that's (-90 - -135) / -45 = -1 second, which doesn't make any | |
// sense, except indicates that you overshot it. So you have to go all the way around, which takes | |
// 360 / 45 = 8 seconds. So if you add 8 to -1, you get 7 seconds, which makes sense. | |
var circleRotationTime = 360 / Mathf.Abs(body.angularVelocity); | |
if (timeUntilDestinationReachedCCW < 0) timeUntilDestinationReachedCCW += circleRotationTime; | |
if (timeUntilDestinationReachedCW < 0) timeUntilDestinationReachedCW += circleRotationTime; | |
var timeUntilDestination = Mathf.Min(timeUntilDestinationReachedCCW, timeUntilDestinationReachedCW); | |
if (timeRequiredToStop > timeUntilDestination) { | |
// If we're close, slam on the brakes! | |
// If it takes us 2.5 seconds to stop, but 2.3 seconds until we arrive, well we better try to stop. | |
body.AddTorque(-1 * Mathf.Sign(body.angularVelocity) * torque); | |
} else if (timeUntilDestinationReachedCW < timeUntilDestinationReachedCCW) { | |
body.AddTorque(-torque); | |
} else { | |
body.AddTorque(torque); | |
} | |
} | |
// Clamps to [-180, 180] | |
private static float wrapAngleAroundZero(float a) { | |
if (a >= 0) { | |
float rotation = a % 360; | |
if (rotation > 180) rotation -= 360; | |
return rotation; | |
} else { | |
float rotation = -a % 360; | |
if (rotation > 180) rotation -= 360; | |
return -rotation; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment