How to make smooth transition with SetLookAtPosition ?

Hi there!
I am trying to implement next behavior : when player sees somebody he must look at him. and i can do smooth transition between two states : “player sees nothing” and “player see somebody”. but i can’t do it between “player sees somebody” and “player see another guy”. it’s has sharp transition. I’ve tried use temp for prev player, lerp for SetLookAtPosition, but it’s did not work.

#pragma warning disable 0219
#pragma warning disable 0414
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using BeardedManStudios.Network;

public class RotateHeadToPlayer : MonoBehaviour
{

    public List<Animator> animators = new List<Animator>();

    public float lookIKWeight;
    public float bodyWeight = 0.3f;
    public float headWeight;
    public float eyesWeight;
    public float clampWeight;

    public Transform lookPos;
    public LayerMask layerMaskForHeadLook;
    public Collider[] headLookColliders;

    private PlayerMovement PM;
    private Transform lookPosHead = null;
    private Transform myHead = null;
    [SerializeField]
    private Transform tempLookPos = null;

    //[SerializeField]
    //private float timeToSetWeight = 3f;

    // Use this for initialization
    void Awake()
    {
        animators = GetComponentsInChildren<Animator>(true).ToList();
        PM = GetComponent<PlayerMovement>();
    }

    // Update is called once per frame
    void Update()
    {
        if (!PM.alive)
            return;

        bodyWeight = PM.defence || !PM.canAttack ? RealLerpFloat(bodyWeight, 0, 2f) : RealLerpFloat(bodyWeight, 0.3f, 2f);

        FindPlayerToLook();
    }

    void OnAnimatorIK()
    {
        if (!PM.alive)
            return;

        if (lookPos != null)
        {
            lookPosHead = lookPos.GetComponent<Animator>().GetBoneTransform(HumanBodyBones.Head);

            animators[0].SetLookAtWeight(lookIKWeight, bodyWeight, headWeight, eyesWeight, clampWeight);
            animators[0].SetLookAtPosition(lookPosHead.position);

            lookIKWeight = Mathf.Lerp(lookIKWeight, 1f, Time.deltaTime * 0.1f);
        }
        else
        {
            animators[0].SetLookAtWeight(lookIKWeight, bodyWeight, headWeight, eyesWeight, clampWeight);
            lookIKWeight = Mathf.Lerp(lookIKWeight, 0, Time.deltaTime * 0.4f);
        }

    }

    private float RealLerpFloat(float changableValue, float goalValue, float time)
    {
        float i = 0f;
        float rate = 1f / time;

        while (i < 1f)
        {
            i += Time.deltaTime * rate;
            changableValue = Mathf.Lerp(changableValue, goalValue, i);
            return changableValue;
        }
        return changableValue;
    }


    void FindPlayerToLook()
    {
        headLookColliders = Physics.OverlapSphere(transform.position, 20f, layerMaskForHeadLook, QueryTriggerInteraction.Ignore);
        float minDistance = float.PositiveInfinity;
        Transform closestPlayer = null;

        foreach (var collider in headLookColliders)
        {
            if (collider.transform.GetInstanceID() == transform.GetInstanceID() || collider.GetComponentInParent<Animator>() == null || Vector3.Dot(transform.forward, collider.transform.root.position - transform.position) < 0)
                continue;

            float distance = Vector3.Distance(collider.transform.root.position, transform.position);
            if (distance < minDistance)
            {
                minDistance = distance;
                closestPlayer = collider.transform.root;
            }
        }

        lookPos = closestPlayer != null ? closestPlayer : null;
    }
}

Hey there!
Let’s walk together trough your code, in order to find what the problem is.

So first, you are finding a player to look at, if there is none, closestPlayer is null.

Then in OnAnimatorIK you are dooing one of two things:

  • you either have no closestPlayer and you are lerping current lookIKWeight to 0 ( that means that the value of lookIKWeight will slowley go to 0, and when it reaches 0 it stays there.

  • if you do have a closestPlayer you are lerping from current lookIKWeight to 1.

When lookIKWeight is 1, the head will move extremely fast, when lookIKWeight is 0, the head will not move.

Now because you switch between a target and no target you keep changing lookIkWeight from 0 to 1 to 0. That is why you get a smooth transition.

The question is, what happens when you have a new closestPlayer, but your lookIkWeight already reached 1?

2 Likes