using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
[RequireComponent(typeof(Animator))]
public class TestingIK : MonoBehaviour
{
public List<Transform> lookObjects = new List<Transform>();
public float weightDamping = 1.5f;
private Animator animator;
private Transform lastPrimaryTarget;
private float lerpEndDistance = 0.1f;
private float finalLookWeight = 0;
private bool transitionToNextTarget = false;
void Start()
{
animator = GetComponent<Animator>();
}
// Callback for calculating IK
void OnAnimatorIK()
{
if (lookObjects != null)
{
Transform primaryTarget = null;
float closestLookWeight = 0;
// Here we find the target which is closest (by angle) to the players view line
foreach (Transform target in lookObjects)
{
Vector3 lookAt = target.position - transform.position;
lookAt.y = 0f;
float dotProduct = Vector3.Dot(new Vector3(transform.forward.x, 0f, transform.forward.z).normalized, lookAt.normalized);
float lookWeight = Mathf.Clamp(dotProduct, 0f, 1f);
if (lookWeight > closestLookWeight)
{
closestLookWeight = lookWeight;
primaryTarget = target;
}
}
if (primaryTarget != null)
{
if ((lastPrimaryTarget != null) && (lastPrimaryTarget != primaryTarget) && (finalLookWeight > 0f))
{
// Here we start a new transition because the player looks already to a target but
// we have found another target the player should look at
transitionToNextTarget = true;
}
}
// The player is in a neutral look position but has found a new target
if ((primaryTarget != null) && !transitionToNextTarget)
{
lastPrimaryTarget = primaryTarget;
finalLookWeight = Mathf.Lerp(finalLookWeight, closestLookWeight, Time.deltaTime * weightDamping);
float bodyWeight = finalLookWeight * .75f;
animator.SetLookAtWeight(finalLookWeight, bodyWeight, 1f);
animator.SetLookAtPosition(primaryTarget.position);
}
// Let the player smoothly look away from the last target to the neutral look position
if ((primaryTarget == null && lastPrimaryTarget != null) || transitionToNextTarget)
{
finalLookWeight = Mathf.Lerp(finalLookWeight, 0f, Time.deltaTime * weightDamping);
float bodyWeight = finalLookWeight * .75f;
animator.SetLookAtWeight(finalLookWeight, bodyWeight, 1f);
animator.SetLookAtPosition(lastPrimaryTarget.position);
if (finalLookWeight < lerpEndDistance)
{
transitionToNextTarget = false;
finalLookWeight = 0f;
lastPrimaryTarget = null;
}
}
}
}
}
I have a cube that move in circles around the player.
When the cube is getting behind the player from the left side the player is looking at it a bit behind and then return the head back but then when the cube is coming back from the right side the player is not looking at it at all he start looking at the cube only when the cube is almost directly in front of him.
The right side is not active when the cube is coming back from behind to front from the right side.
Update :
I found that if i change this line
float dotProduct = Vector3.Dot(new Vector3(transform.forward.x, 0f, transform.forward.z).normalized, lookAt.normalized);
Adding minus before the forward in both on x and z to this :
float dotProduct = Vector3.Dot(new Vector3(-transform.forward.x, 0f, -transform.forward.z).normalized, lookAt.normalized);
Now it will work fine when the object is coming from the back from the right side but now the left side is not working.
So it's either the left side from the front to behind without the minus or the right side from the back to front with the minus.
How can i solve it so it will work for both sides front/behind equal ?
CodePudding user response:
Vector3.Dot
will return a negative when the target is behind the player, unfortunately you clamp the result to [0, 1] and the minimal weight is 0, that the reason why the behind targets are ignored.
To fix it, use the dot product value directly and use an initial weight small enough.
float closestLookWeight = float.MinValue;
float lookWeight = dotProduct;