0
\$\begingroup\$

I am trying to get an object to slowly look at another object, that is, rotate slowly so its forward points towards the target position. Here is my code:

using UnityEngine;

public class LookAtTheDamnThing: MonoBehaviour 
{
    public Transform target;
    public float speed;

    void FixedUpdate()
    {
        AimAtTarget();
    }

    public void AimAtTarget()
    {
        Vector3 direction = target.position - transform.position;
        Quaternion toRotation = Quaternion.FromToRotation(transform.forward, direction);
        transform.rotation = Quaternion.Lerp(transform.rotation, toRotation, speed * Time.time);
        Vector3 fwd = this.transform.TransformDirection(Vector3.forward);
        Debug.DrawRay(this.transform.position, fwd * 50, Color.green);
    } 
}

This results in the object looking kind of towards the target but never actually looking at it. I cannot for the life of me see my mistake. Can anyone help me? enter image description here

\$\endgroup\$
1
\$\begingroup\$

There are a few different issues here.

First, you're constructing toRotation as a relative rotation (some amount of change in rotation)

Quaternion toRotation = Quaternion.FromToRotation(transform.forward, direction);

But then you're applying toRotation as a destination (a final rotation to ease toward)

transform.rotation = Quaternion.Lerp(transform.rotation, toRotation, speed * Time.time);

Imagine if we did this with position, starting with an object at x=8 that wants to move to x=10. The change in x is +2, but if we lerp toward that change value like so:

 Mathf.Lerp(startPosition, endPosition - startPosition, weight)

...then as the interpolation weight gets closer to 1, our x gets closer to 2 - not to 10 where we want it to be!

(When applied in a compounding way frame over frame, this will seek out an equilibrium point where x = lerp(x, 10 - x, weight), x = (1 - weight) * x + weight * 10 - weight * x, 2 weight * x = weight * 10, 2 x = 10, x = 5... still not where we wanted it to be!)

So, first we need to describe the destination orientation we want to have. It looks like you probably want something like this:

// Calculate direction as before.
Vector3 direction = target.position - transform.position;

// Use LookRotation to form a rotation that looks along that vector.
// (You can optionally pass a second vector as "up" to control the twist).
Quaternion toRotation = Quaternion.LookRotation(direction);

Then, if you want to rotate toward this location at a constant rate, as John Hamilton says, you can use the Quaternion.RotateTowards method:

float step = speed * Time.deltaTime;
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, step);

Or, if you want the aim to be faster the further off-target it is, and gradually ease-in as it locks on, you can keep the exponential ease you were using before, but with the destination as calculated above, and correcting the time adjustment like so:

// Here "speed" is replaced by "sharpness," a parameter between 0 & 1
// that controls how smooth vs fast the aim is.
// referenceFramerate is a constant like, say, 30.0f. It adjusts the 
// "units" of sharpness to be the same as you'd use at that framerate
blend = 1f - Mathf.Pow(1f - sharpness, Time.deltaTime * referenceFramerate);

transform.rotation = Quaternion.Lerp(transform.rotation, toRotation, blend);
| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

Any of the Lerp (linear interpolation) functions (Mathf, Vector3, Quaternion [ok, maybe quaternions don't apply since it's a matrix], etc.) works following this formula: P = P0 + (t * (P1 - P0)), 0 < t < 1, where, as the formula suggests, t which is time goes between 0 and 1. So what this function gives you is a point of a delta between two points multiplied by a normalized time.

What I do is to use coroutines to handle such interpolations, but since you are using an update, maybe you could try with Quaternion.LookRotation(Vector3 _direction), instead of using Quaternion.FromToRotation.

As a side node, you could use transform.forward, instead of using TransformDirection(Vector3 _normal). Transform.forward, transform.up and transform.right gives you already TransformDirection with (0,0,1), (0,1,0) & (1,0,0) respectively.

| improve this answer | |
\$\endgroup\$
0
\$\begingroup\$

There's already a Unity built-in doing what you need to do. You shouldn't need to re-invent the wheel, unless you want it to do very specific things other than just rotating towards another game object. Example from the link:

using UnityEngine;
using System.Collections;

public class ExampleClass : MonoBehaviour {
    public Transform target;
    public float speed;
    void Update() {
        float step = speed * Time.deltaTime;
        transform.rotation = Quaternion.RotateTowards(transform.rotation, target.rotation, step);
    }
}

Also, I suggest using Update() instead of FixedUpdate() for this kind of movement (well, Unity examples also seem to suggest that), since the time-step is usually faster in the Update() function, you should get a smoother movement overall.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.