How to make animated animal's head smoothly look at player instead of instantly snapping?

Hi, I’m working on a game, where the player collects trash, from Trash cans, and dumps them into a Trash Truck. There will be a random chance of there being a racoon, sitting on top of a Trash can. I am going to make them be a sort of enemy. If the player approaches them, and gets within a certain radius, an animation will play, of them eating, then noticing the player. When they notice the player, I’m trying to make it be so that they will quickly turn their head to look at the player, and have their head / look follow the player. This is where I’m having the issues. I can’t seem to be able to get the racoon to smoothly turn it’s head. I’ve tried several different methods, and what seems to happen is it will either,:

  1. Properly plays through the first part of the animation (eating, before noticing player,), then, once the racoon looks up, it’s head instantly snaps to the position of the player.
  2. Properly plays through the first part of the animation (eating, before noticing player,), then, once the racoon looks up, it’s head seems to want to move smoothly, but jitters, it seems like it will constantly snap back to look just forwards, which is what happens right before it should look at the player, it should look up, and straight forwards.

I have no idea what’s going wrong, I feel like I’ve tried every possible combination, nothing seems to work. Please, someone help!.

Here’s my current Script / Code:

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

[RequireComponent (typeof (Animator))]
public class RacoonLook : MonoBehaviour
{
    public bool ikActive = false;
    public Transform headObj = null;
    public Transform lookTarget = null;

    public Transform togTarget;

    Animator anim;

    public Vector3 lookPos;
    public Quaternion rotation;

    public float rotationSpeed = 0.0f;

    public bool seesPlayer = false;

    private Quaternion initialRotation;

    // Use this for initialization
    void Start()
    {
        anim = GetComponent<Animator>();

        initialRotation = headObj.rotation;

        togTarget = lookTarget;
    }
    void Update()
    {
        lookPos = headObj.position - lookTarget.position;

        rotation = Quaternion.LookRotation(lookPos);

        rotation *= Quaternion.Euler(-90, 0, 0);
    }

    void LateUpdate()
    {
        if (seesPlayer == true)
        {
            Debug.Log("Sees Player!");
            rotationSpeed = 2.0f;
            lookTarget = togTarget;
        }
        else if(seesPlayer == false)
        {
            Debug.Log("Doesn't See Player!");
            rotationSpeed = 0.0f;
            lookTarget = null;
        }

        //This one seems to act like the bottom two below this one, the Slerp ones.
        //headObj.rotation = Quaternion.Slerp(headObj.rotation, rotation, Time.deltaTime * rotationSpeed);

        //This one seems to work, and doesn't jitter, but it will not smoothly transition to the Target Position / Rotation.
        Quaternion lookRotation = Quaternion.LookRotation(headObj.position - lookTarget.position);
        headObj.rotation = lookRotation * initialRotation;

        Quaternion newRot = lookRotation * initialRotation;

        //This appears like it wants to work, but jitters, after a very short period of time, so I don't know if it actually works.
        //headObj.rotation = Quaternion.Slerp(headObj.rotation, rotation, Time.deltaTime * rotationSpeed);

        //Same with this one, as the one above.
        //headObj.rotation = Quaternion.Slerp(headObj.rotation, newRot, Time.deltaTime * rotationSpeed);
    }
    
    void NoticePlayer(string seeplayer)
    {
        if(seeplayer == "yes")
        {
            seesPlayer = true;
        }
        else if(seeplayer == "no")
        {
            seesPlayer = false;
        }
    }
}

If anyone needs any more information, please, let me know!
Thanks!

I would think your problem is with the time scale you are passing the slerp function. rotationSpeed is always either 2 or 0 so the function will never make it to the end rotation. The time scale needs to be a value from 0 to 1 and ‘Time.deltaTime * 2‘ will be just a small fraction of the journey and the stuttering is potentially caused by variation in the frame rate. I would suggest to think about how many degrees per second the rotation should be and get the angle between the current forward direction and the desired forward direction. So let’s say 180 degrees should be done in 1 second, if we need to rotate 90 degrees, we need to a rotation speed of 2, but we also need to save the time scale and update it every frame until we reach our target.

float timeScale = 1.0f;
float degreesPerSecond = 180;
Vector3 targetDir = Vector3.zero;
Quaternion fromRot;

void LateUpdate()
{
    if(targetDir != lookTarget.position - headObj.position)
    {
        targetDir = lookTarget.position - headObj.position;
        timeScale = 0.0f;
        fromRot = headObj.rotation;
    }
    
    if(timeScale < 1.0f)
    {
        float angle = Vector3.Angle(transform.forward, targetDir);
        float speed = 1 / ( angle / degreesPerSecond );

        timeScale += speed * Time.deltaTime;
        headObj.rotation = Quaternion.Slerp(fromRot, rotation, timeScale);
    }
}

This is just an example, you would have to fit it in with your logic, but as you can see, I’m adding to the time scale every frame because 0 equals the start rot and 1 equals the end rot that you pass. You were passing in almost the same value every frame, so almost no change was occurring


Hope that helps