0
\$\begingroup\$
using System.Collections;
using UnityEngine;

[RequireComponent(typeof(Animator))]
public class Test : MonoBehaviour
{
    public Transform lookAtTarget;
    public AnimationCurve curve = AnimationCurve.EaseInOut(0.0f, 0.0f, 1.0f, 1.0f);
    public float duration;
    public float duration1;

    private float t;
    private float t1;
    private Quaternion rot;

    private void Start()
    {
        t = 0;
        t1 = 0;
        rot = transform.rotation;
    }

    void LateUpdate()
    {
        Vector3 forward = transform.TransformDirection(Vector3.forward);
        Vector3 toOther = lookAtTarget.transform.position - transform.position;

        if (Vector3.Dot(forward, toOther) < 0)
        {
            t1 += Time.deltaTime;
            float s = t1 / duration1;

            

            transform.rotation = Quaternion.Lerp(transform.rotation, rot, curve.Evaluate(s));
        }
        else
        {
            t1 = 0;

            t += Time.deltaTime;
            float s = t / duration;

            var targetRotation = Quaternion.LookRotation(lookAtTarget.transform.position - transform.position);
            transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, curve.Evaluate(s));
        }
    }
}

In the if part the transform is like "jumping" at once to the original rotation and there make some lerping or not doing at all and I want that it will lerp smooth slowly from it's current rotation back to the original rotation(rot).

Same effect when turret is cool down when there is no target so the turret smoothly return to his original rotation.

I tried also :

transform.rotation = Quaternion.Lerp(transform.rotation, rot, s);

But the same problem remains.

Tried this :

using System.Collections;
using UnityEngine;

[RequireComponent(typeof(Animator))]
public class Test : MonoBehaviour
{
    public Transform lookAtTarget;
    public AnimationCurve curve = AnimationCurve.EaseInOut(0.0f, 0.0f, 1.0f, 1.0f);
    public float duration;

    private float t;

    private void Start()
    {
        t = 0;
    }

    void LateUpdate()
    {
        t += Time.deltaTime;
        float s = t / duration;

        Vector3 forward = transform.TransformDirection(Vector3.forward);
        Vector3 toOther = lookAtTarget.transform.position - transform.position;

        if (Vector3.Dot(forward, toOther) < 0)
        {
            transform.rotation = Quaternion.Lerp(, transform.rotation, curve.Evaluate(s));
        }
        else
        {
            

            var targetRotation = Quaternion.LookRotation(lookAtTarget.transform.position - transform.position);
            transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, curve.Evaluate(s));
        }
    }
}

Update :

my player object name is kid_from_space this screenshot show the player settings in the inspector :

Player settings in inspector

The player have a Rigidbody and also animtor components too.

That is why the script use LateUpdate instead Update.

The script is attached to the player head child object :

The player head child object with the script attached

This object have the script that make object rotate around the player :

The object with the script that make object to rotate around the player

This screenshot is of the child of the object Rotate Around the Target is the basketball in the screenshot the basketball is rotating aorund the player :

The Target(basketball) settings

This is the script that is attached to the player head :

using System.Collections;
using UnityEngine;

public class LookAtTarget : MonoBehaviour
{
    public Transform target;
    // Keep this low to make the tracking tight for small target movements.
    public float lookChangeDuration = 0.1f;

    // Use this to limit our top rotation speed when making a big turn.
    // It's measured in "approximately" radians per second.
    public float maxLookSpeed = 5f;

    Vector3 lookVectorVelocity;
    Vector3 originalLookDirection;

    private void Start()
    {
        originalLookDirection = transform.forward;
    }

    void LateUpdate()
    {
        Vector3 desiredLook = (target.position - transform.position).normalized;

        if (Vector3.Dot(desiredLook, transform.forward) < 0)
        {
            desiredLook = originalLookDirection;
        }

        var look = Vector3.SmoothDamp(transform.forward,
                                      desiredLook,
                                      ref lookVectorVelocity,
                                      lookChangeDuration,
                                      maxLookSpeed);

        transform.rotation = Quaternion.LookRotation(look);
    }
}

This script is attached to the Rotate Around object :

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

[ExecuteAlways]
public class SimpleRotate : MonoBehaviour
{
    public List<TextMeshProUGUI> uiText = new List<TextMeshProUGUI>();

    public GameObject objToRotateAround;
    [Header("The axis by which it will rotate around")]
    public Vector3 axis;
    [Header("Angle covered per update")]
    public float angle;
    public float upperLimit, lowerLimit, delay;

    private float height, prevHeight, time;

    // Update is called once per frame
    void Update()
    {
        transform.RotateAround(objToRotateAround.transform.position, axis.normalized, angle);
        time += Time.deltaTime;
        if (time > delay)
        {
            prevHeight = height;
            height = Random.Range(lowerLimit, upperLimit);
            time = 0;
        }
        
        transform.position = new Vector3(transform.position.x, Mathf.Lerp(prevHeight, height, time), transform.position.z);
    }
}

The result is that the player head is not rotating at all not looking at the target at all. The player head stay all the time in natural original rotation view.

I found that if I disable the Animator that attached to the player the head will keep rotating nonstop 360 degrees looking at the target. With the Animator turned on enabled true the head is not rotating at all.

This is the player animator controller settings. Grounded is a blend tree :

Animator controller

This screenshot is of the Grounded blend tree the top animation is the HumanoidIdle and this is also the default animation play when running the game :

Player animator ocntroller Grounded blend tree settings

\$\endgroup\$
4
  • \$\begingroup\$ Are you resetting your time (t1) after the first rotation? \$\endgroup\$
    – Zibelas
    Sep 12, 2021 at 8:05
  • \$\begingroup\$ @Zibelas I thought I put the whole script. I edited my question with the full script. \$\endgroup\$
    – Daniel Lip
    Sep 12, 2021 at 8:26
  • 1
    \$\begingroup\$ In your first condition you use rot, which is set once in start. Your else branch is using targetRotation, which every time the else branch gets executed is updated. Instead of having it duplicated, you want to rotate from a to b, use the same code but update your start and end \$\endgroup\$
    – Zibelas
    Sep 12, 2021 at 8:52
  • \$\begingroup\$ @Zibelas Can you show me please how to do it ? I updated my question with what I thought to try but not sure what to do. \$\endgroup\$
    – Daniel Lip
    Sep 12, 2021 at 9:48

1 Answer 1

2
\$\begingroup\$

Apart from the obvious where you never reset your t1 value to zero at the start of a new motion...

You're still using the exponential ease-out lerp I told you about previously. Whenever you use the pattern:

current = lerp(current, target, sharpness);

...you create a negative feedback loop. (See how "current" appears as both the input AND the output?)

That makes the lerp behave fastest when the object first starts moving, then slow to a crawl as it gets closer to its target. Combined with an ease-in-out curve being passed to the sharpness parameter (which is more like a rate of change, not a measure of progress), and you get a very strange motion that is not at all what you intended, leaping up to the target value much faster than your provided duration:

Graph of different Lerp behaviours

One way you can solve this is by storing some state about when you started this motion and which way you were facing at the time:

if (Vector3.Dot(forward, toOther) < 0)
{
    // Snapshot our initial rotation only when we *begin* our return journey.
    if (returnProgress < 0f) {
        returnProgress = 0f;
        rotationBeforeReturning = transform.rotation;
    }

    returnProgress = Mathf.Clamp01(returnProgress + Time.deltaTime/returnDuration);

    // Note that "transform.rotation" does not appear on the right-hand side.
    // That means we're no longer using an exponential ease-out lerp, and the
    // last parameter really is a measure of progress, not a diminishing rate of change.
    transform.rotation = Quaternion.Lerp(rotationBeforeRetuning,
                                         originalRotation,
                                         curve.Evaluate(returnProgress));
} else {
    // Terminate the attempt to return to the original orientation,
    // so that we re-start it from scratch the next time we lose sight.
    returnProgress = -1f;

    // The rest of your else case goes here...
}

But actually I think you can reimplement your whole script more simply using the built-in SmoothDamp function, which handles an ease-in-out movement toward a target, maintaining a continuous velocity (so eg. a character's head doesn't suddenly lurch or snap to an abrupt stop).

There's no Quaternion.SmoothDamp to damp rotations directly, but we can use Vector3.SmoothDamp to damp our look direction as it moves around the unit sphere, then construct a quaternion from that.

public class EasedLookAt : MonoBehaviour {

    public Transform target;    
    // Keep this low to make the tracking tight for small target movements.
    public float lookChangeDuration = 0.1f;

    // Use this to limit our top rotation speed when making a big turn.
    // It's measured in "approximately" radians per second.
    public float maxLookSpeed = 5f;

    Vector3 lookVectorVelocity;
    Vector3 originalLookDirection;

    private void Start() {
        originalLookDirection = transform.forward;
    }

    void LateUpdate() {
        Vector3 desiredLook = (target.position - transform.position).normalized;

        if (Vector3.Dot(desiredLook, transform.forward) < 0) {
            desiredLook = originalLookDirection;
        }

        var look = Vector3.SmoothDamp(transform.forward, 
                                      desiredLook,
                                      ref lookVectorVelocity, 
                                      lookChangeDuration,
                                      maxLookSpeed);

        transform.rotation = Quaternion.LookRotation(look);
    }
}

In terms of your use of the site, I notice you've left a long chain of zero-vote, unanswered, or deleted questions - quite a few struggling with different aspects of this look mechanic.

I would recommend asking fewer questions that say "my code is broken, why / how do I fix it?" - if your code is broken, it might not be the best starting point for accomplishing your goal. Fixing one problem might just reveal another, and focusing on individual bugs can distract from the opportunity to find a better overall strategy.

Instead, I would recommend trying to ask goal-focused questions. For example:

How to look at a target and return to neutral when it's out of sight, with easing?

As you can see above, an answer to such a question can actually be much simpler than playing whack-a-mole trying to fix all the problems with a broken script.

And they're usually more pleasant to answer, too, so you might find you get better engagement from other users with this strategy.

\$\endgroup\$
7
  • \$\begingroup\$ I tried the solution for the whole script EasedLookAt but now the transform is not rotating at all. The target object to look at keep rotating around the transfer but now the transfer is not looking at the target just stay at natural normal view. \$\endgroup\$
    – Daniel Lip
    Sep 13, 2021 at 18:04
  • 1
    \$\begingroup\$ I've tested this script and I'm not able to reproduce that problem. Please edit your question to include a Minimal Complete Verifiable Example: every step we'd need to follow to reproduce this problem in a new, empty project. \$\endgroup\$
    – DMGregory
    Sep 13, 2021 at 19:23
  • \$\begingroup\$ Updated my question. \$\endgroup\$
    – Daniel Lip
    Sep 13, 2021 at 19:56
  • 1
    \$\begingroup\$ Did you want to stop looking at the object when it moves behind the original orientation, rather than the current orientation? If so, that's obvious: replace transform.forward in the dot product with originalLookDirection. That wasn't the behaviour of your original code, so I replicated the original behaviour you showed instead. \$\endgroup\$
    – DMGregory
    Sep 13, 2021 at 21:08
  • 1
    \$\begingroup\$ You can control the max speed for big moves (like returning to the original orientation) using the maxLookSpeed parameter. Or you can can extend the if statement into an if/else, and call SmoothDamp with different arguments in both cases. If using if statements to control the behaviour for two distinct cases like this is unfamiliar to you, then you might want to work your way through a few more beginner C# tutorials to ensure you have a thorough understanding of the basics of using the language. \$\endgroup\$
    – DMGregory
    Sep 13, 2021 at 21:37

You must log in to answer this question.

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