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\$

3 Answers 3

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);
\$\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.

\$\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.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .