关于Unity头部IK的一些分析

时光毁灭记忆、已成空白 提交于 2019-12-10 03:31:20

关于UnityIK控制头部朝向目标的一些研究

(事先声明,本人为编程菜鸡,所使用的方法可能非常笨,不具备任何参考价值,请谅解)
虽然研究动画系统一段时间了,但IK也是最近这段时间才刚刚接触到的,其他部位IK暂时不提,今天我要研究的IK是头部朝向的IK。

让我们开始吧

首先,基本的基本,打开人物的Animaotr面板,添加一层IK层
在这里插入图片描述
接着为你的人物设置IK事件属性就好了
这里用到了两个API,分别是SetLookAtWeightSetLookAtPosition
SetLookAtWeight的作用是设置权重,权重越低,IK所对动画造成的影响越低
SetLookAtPosition的作用是设置IK的朝向位置

主要实现思路有两种:

1、当有目标进入玩家视线时,权重缓慢增长到正常值,当目标离开时,权重又慢慢的回落到0(既IK不对动画产生任何影响)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;

public class IKLookAT : MonoBehaviour
{
    private Animator animator;
    private GameObject minDictObj;
    private List<GameObject> list_LookObj;

    private Vector3 TempPos;

    private float IkanimationSpeed = 0.8f;
    private float all;
    private float body;
    private float head;
    private float eye;

    public List<GameObject> List_LookObj { get { return list_LookObj; } set { list_LookObj = value; } }

    void Awake()
    {
        list_LookObj = new List<GameObject>();
        animator = gameObject.GetComponent<Animator>();
    }

    private void OnAnimatorIK(int layerIndex)
    {
        if (list_LookObj.Count > 0)
        {
            float mindict = 100;
            for (int i = 0; i < list_LookObj.Count; i++)
            {
                float dict = Vector3.Distance(transform.position, list_LookObj[i].transform.position);
                if (dict < mindict) { mindict = dict; minDictObj = list_LookObj[i]; }
            }
        }
        else { minDictObj = null; }

        if (minDictObj != null)
        {
            LockTure();
            TempPos =  Vector3.Lerp(TempPos, minDictObj.transform.position, 1.5f * Time.deltaTime);
            animator.SetLookAtPosition(TempPos); 
        }
        else 
        {
            LockFalse();
            animator.SetLookAtPosition(TempPos);
        }
        animator.SetLookAtWeight(all, body, head, eye, IkanimationSpeed);
    }

    private void LockTure() 
    {
        DOTween.To(() => all, X => all = X, 1, 5.0f);
        DOTween.To(() => body, X => body = X, 0, 5.0f);
        DOTween.To(() => head, X => head = X, 0.5f, 5.0f);
        DOTween.To(() => eye, X => eye = X, 0.3f, 5.0f);
    }

    private void LockFalse() 
    {
        DOTween.To(() => all, X => all = X, 0, 5.0f);
        DOTween.To(() => body, X => body = X, 0, 5.0f);
        DOTween.To(() => head, X => head = X, 0, 5.0f);
        DOTween.To(() => eye, X => eye = X, 0, 5.0f);
    }
}

使用这个方法,朝向目标的IK动画切换与恢复有非常缓和的效果,但是这个方法有个致命的缺点:因为设置的IK基本是影响了全局IK属性,导致其他部位的IK也会受此影响,导致IK失效

为此,我推荐使用人型骨骼动画Mask并仅选择头部动画,并将其他IK属性置于其他层,这样就不会影响到头部的IK动画了。
在这里插入图片描述

2、添加一个空物体作为玩家的子级,跟随玩家移动,并一直置于玩家前方,当玩家视线内没有目标时,玩家便会看向此空物体,出现目标既转向目标

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;

public class IKLookAT : MonoBehaviour
{
    private Animator animator;
    private GameObject minDictObj;
    private List<GameObject> list_LookObj;

    private Vector3 TempLookPos;
    private Transform DefaultLookPos;

    public List<GameObject> List_LookObj { get { return list_LookObj; } set { list_LookObj = value; } }

    void Awake()
    {
        list_LookObj = new List<GameObject>();
        animator = gameObject.GetComponent<Animator>();
        DefaultLookPos = transform.Find("DefaultLookAtObj").GetComponent<Transform>();
    }

    private void OnAnimatorIK(int layerIndex)
    {
        if (list_LookObj.Count > 0)
        {
            float mindict = 100;
            for (int i = 0; i < list_LookObj.Count; i++)
            {
                float dict = Vector3.Distance(transform.position, list_LookObj[i].transform.position);
                if (dict < mindict) { mindict = dict; minDictObj = list_LookObj[i]; }
            }
        }
        else { minDictObj = null; }

        if (minDictObj != null)
        {
            TempLookPos =  Vector3.Lerp(TempLookPos, minDictObj.transform.position, 1.5f * Time.deltaTime);
            animator.SetLookAtPosition(TempLookPos); 
        }
        else 
        {
            TempLookPos = Vector3.Lerp(TempLookPos, DefaultLookPos.position, 5.0f * Time.deltaTime);
            animator.SetLookAtPosition(TempLookPos);
        }
        animator.SetLookAtWeight(1.0f, 0.1f, 0.5f, 0.4f, 0.8f);
    }
}

使用这个方法,动画的切换将变得不再平滑,但优点在于不会影响到其他IK属性。

主要还是本人编程水准太低的原因,如果优化一下朝向的算法,或许此方法更加适合。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!