Unity动画️11. IK动画—手与木头的匹配_unity onanimatorik-程序员宅基地

技术标签: c#  unity  # 动画系统  游戏引擎  

 

 

IK动画与MatchTarget区别:

MatchTarget实现从一段时间区域内,一个点到另一个点匹配的过程;IK动画用于直接将手或脚与某点的匹配

控制策略:a、在木头下新建两个点(空的GameObject),将这两个点调到合适位置,让手与这两个点进行匹配,完成托举木头的效果;

b、在匹配过程中,手部的旋转角度和位置是根据GameObject来匹配的,我们可以通过调节GameObject的角度和位置完成动画的手部角度的调节。

    运行时可暂停运行,调节位置,逐帧更新动画,Transform齿轮处可Copy Component,调节完成后取消运行动画,Paste Component Values

1、勾选动画层的IK Pass

2、Player C#补充:

OnAnimatorIK(int layerIndex)方法在Update()方法外,因为勾选了IK Pass,系统会自动调用

print(layerIndex);可在控制台输出当前调用的动画层是几

Player总代码:

using UnityEngine;

public class Player : MonoBehaviour {

    private Animator anim;
    private int speedRotateID = Animator.StringToHash("SpeedRotate");
    private int speedZID = Animator.StringToHash("SpeedZ");
    private int vaultID = Animator.StringToHash("Vault");
    private Vector3 matchTarget;
    private CharacterController characterController;
    private int vaultCurveID = Animator.StringToHash("vaultCurve");
    private int sliderID = Animator.StringToHash("Slider");
    private int sliderCurveID = Animator.StringToHash("sliderCurve");
    private int isHoldLogID = Animator.StringToHash("IsHoldLog");
    public Transform LeftHand, RightHand;
    public GameObject unityLog;

    void Start () {
        anim = GetComponent<Animator>();
        characterController = GetComponent<CharacterController>();
	}
	
	void Update () {

        anim.SetFloat(speedZID, Input.GetAxis("Vertical") * 4.5f);
        anim.SetFloat(speedRotateID, Input.GetAxis("Horizontal")*126);

        ProcessVault();
        ProcessSlider();
    }

    private void ProcessVault()
    {
        if (anim.GetFloat(speedZID) > 3 && anim.GetCurrentAnimatorStateInfo(0).IsName("LocalMotion"))
        {
            RaycastHit hit;
            if (Physics.Raycast(transform.position + Vector3.up * 0.3f, transform.forward, out hit, 4.5f))
            {
                if (hit.collider.tag == "Obstacle")
                {
                    if (hit.distance > 3)
                    {
                        Vector3 point = hit.point;
                        point.y = hit.transform.position.y + hit.collider.bounds.size.y + 0.09f;  //普通的cube在水平面时默认中心高度y=0.5,但此模型水平中心高度为0(和底边一致)!,所以是它最低点位置+Y轴的大小
                        matchTarget = point;

                        anim.SetBool(vaultID, true);
                    }

                }
            }
            else anim.SetBool(vaultID, false);
        }

        characterController.enabled = anim.GetFloat(vaultCurveID) < 0.5f;

        if (anim.GetCurrentAnimatorStateInfo(0).IsName("Vault"))
        {
            anim.MatchTarget(matchTarget, Quaternion.identity, AvatarTarget.LeftHand, new MatchTargetWeightMask(Vector3.one, 0), 0.32f, 0.4f);
        }
    }
    private void ProcessSlider()
    {
        if (anim.GetFloat(speedZID) > 3 && anim.GetCurrentAnimatorStateInfo(0).IsName("LocalMotion"))
        {
            RaycastHit hit;
            if (Physics.Raycast(transform.position + Vector3.up * 1.5f, transform.forward, out hit, 5f))
            {
                if (hit.collider.tag == "Obstacle")
                {
                    if (hit.distance <2.8)
                    {
                        Vector3 point = hit.point;
                        point.y = 0 ; 
                        matchTarget = point+transform.forward*2;
                        anim.SetBool(sliderID, true);
                    }
                }
            }
        }
        else anim.SetBool(sliderID, false);

        if (anim.GetCurrentAnimatorStateInfo(0).IsName("Slider"))
        {
            anim.MatchTarget(matchTarget, Quaternion.identity, AvatarTarget.Root, new MatchTargetWeightMask(new Vector3(1,0,1), 0), 0.38f, 0.67f);
        }

        if (anim.GetCurrentAnimatorStateInfo(0).IsName("Slider" ))
        {
            characterController.enabled = anim.GetFloat(sliderCurveID) < 0.5f;
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Log")
        {
            Destroy(other.gameObject);
            unityLog.SetActive(true);
            anim.SetBool(isHoldLogID, true);
        }
    }

    private void OnAnimatorIK(int layerIndex)
    {
        if (layerIndex == 1)  //动画层从0开始排序
        {
            //print(layerIndex);
            int Weight = anim.GetBool(isHoldLogID) ? 1 : 0;  //三元运算符
            anim.SetIKPosition(AvatarIKGoal.LeftHand, LeftHand.position);
            anim.SetIKRotation(AvatarIKGoal.LeftHand, LeftHand.rotation);
            anim.SetIKPositionWeight(AvatarIKGoal.LeftHand, Weight);
            anim.SetIKRotationWeight(AvatarIKGoal.LeftHand, Weight);

            anim.SetIKPosition(AvatarIKGoal.RightHand, RightHand.position);
            anim.SetIKRotation(AvatarIKGoal.RightHand, RightHand.rotation);
            anim.SetIKPositionWeight(AvatarIKGoal.RightHand, Weight);
            anim.SetIKRotationWeight(AvatarIKGoal.RightHand, Weight);
        }
    }
}

动画展示:

动画系统其他各功能链接:TimeLine

大家还有什么问题,欢迎在下方留言!


 

在这里插入图片描述


如果你有 技术的问题 或 项目开发

都可以加下方联系方式

和我聊一聊你的故事

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_38239050/article/details/79859384

智能推荐

正点原子linux开发板使用ffmpeg代码播放视频_正点原子 ffmpeg-程序员宅基地

文章浏览阅读745次,点赞21次,收藏22次。正点原子linux开发板,使用ffmpeg代码播放视频。_正点原子 ffmpeg

基于单片机GSM通讯手机SIM900模块系统设计(毕设课设)_gsm无线通讯代码-程序员宅基地

文章浏览阅读1.5k次。我本设计介绍了一种基于STM32的多功能GSM通讯系统设计方案。系统以STM32单片机为核心,采用SIM900模块GSM通讯,利用μC/OS-II与μC/GUI系统进行多任务与TFT彩屏的控制,并通过STM32的串口控制GSM模块STM900实现通讯。系统通过STM32的SPI接口实现触控从而实现系统的输入、通过STM32的FSMC接口实现TFT显示从而实现系统的输出。通过STM32的串口可有效发送与接收GSM模块的信息。手机SIM900模块GSM通讯系统【资源下载】下载地址如下(870):http_gsm无线通讯代码

计算机网络自顶向下方法第七版 英文pdf_《计算机网络:自顶向下方法》读书笔记——第一章:计算机网络和因特网...-程序员宅基地

文章浏览阅读2.4k次。前言本文首发于我的个人博客:《计算机网络:自顶向下方法》笔记——第一章:计算机网络与因特网 - YouWolf's Blog​youwolf.cn由于不确定知乎对markdown的支持程度,本人精力有限,来不及修改,所以可以前往本人博客获得更好的阅读体验什么是因特网具体构成概述因特网是一个世界范围的计算机网络,即它是一个互联了遍及全世界数十亿计算设备的网络。所有设备被称为主机(host)或端系统 ..._计算机网络自顶向下第七版pdf

Spring Cache_sync=true-程序员宅基地

文章浏览阅读1.8w次,点赞30次,收藏69次。为什么使用缓存前几天我在文章《我是如何把一个15分钟的程序优化到了10秒的》中,提到了一些在代码层面优化性能的方法。其中第一个就是使用缓存。使用缓存是一个很“高性价比”的性能优化方式,尤其是对于有大量重复查询的程序来说。通常来说,在WEB后端应用程序来说,耗时比较大的往往有两个地方:一个是查数据库,一个是调用其它服务的API(因为其它服务最终也要去做查数据库等耗时操作)。重复查询也有两种。一种是我们在应用程序中代码写得不好,写的for循环,可能每次循环都用重复的参数去查询了。这种情况,比较聪明一_sync=true

一看就会的nestjs解决跨域-程序员宅基地

文章浏览阅读2.9k次,点赞7次,收藏10次。nestjs解决跨域

数据中台:建立在数据网络效应之上的赛道_横向竞争-程序员宅基地

文章浏览阅读357次。好文8145字 约15分钟阅读过去两年,数据中台的概念在中国遍地开花,这个源自于阿里巴巴数据实践的名词,借助数字化转型的东风迅速成为了企业CTO谈论的中心,迅速发展成为一个至少千亿级别的赛道。2019年,也被称为数据中台元年。为什么阿里巴巴能成为现在普遍认同的技术领先公司,持续推进数据的大规模运用是其中最重要的因素之一,这套数据方法论最终沉淀下来成为了今天我们所认知的数据中台。数据中台之所以区别于此前的大数据平台和数据湖,来源于其融合了两大属性:技术属性和业务属性。如果说过去所有的I..._横向竞争

随便推点

解决postcss、postcss-loader 和less-loader 导致的报错问题_typeerror: "postcss-pxtorem" is not a function-程序员宅基地

文章浏览阅读1.5w次,点赞4次,收藏15次。这里写自定义目录标题欢迎使用Markdown编辑器一、问题背景介绍二、报错问题分析:欢迎使用Markdown编辑器解决 Syntax Error: Error: PostCSS plugin postcss-pxtorem requires PostCSS 8.一、问题背景介绍1、在Vue的项目中使用了postcss-pxtorem这个包,同时在项目根目录中,配置了postcss.config.js,如下图所示:module.exports = { plugins: { 'autopr_typeerror: "postcss-pxtorem" is not a function

表单标签<input>的介绍_input标签-程序员宅基地

文章浏览阅读3.1w次,点赞56次,收藏398次。表单标签的介绍_input标签

解决Cannot load php7apache2_4.dll into server导致无法启动Apache-程序员宅基地

文章浏览阅读8.2k次,点赞6次,收藏9次。萌新在面对服务端开发使用PHP搭配Apache时会遇见Cannot load php7apache2_4.dll into server 例如01 最最最主要的问题Apache与PHP版本不一致cd 进入Apache的bin目录使用命令httpd -version来查看Apache的版本, 例如我的版本显示的是Win32 此时就要去PHP官网下载对应的32位版..._cannot load php7apache2_4

大数据各岗位薪资多少?一张图告诉你答案_云计算工资水平折现图-程序员宅基地

文章浏览阅读2.5k次。目前,借由大数据时代的高速发展,它的岗位需求开始迅速扩张,从而给想从事大数据行业的人带来了大量的发展机会,同时也为大家提供了大量的职业发展通道。那么,在这个高速运转的时代,面对如此众多的大数据就业岗位的时候,我们应该去选择什么样的职业发展方向并努力的去学习相应技能从而达到企业要求呢?根据我们行业内各大权威的机构预测,在2020年期间,大数据行业在大数据相关岗位需求中不断激增,这其中Java大数据工程师的缺口大约会在14万到19万人之间,对于懂得如何利用大数据做决策的分析师和经理的岗位缺口则将达到150万_云计算工资水平折现图

大数据法律监督模型优势特色及应用场景_大数据监管算法模型-程序员宅基地

文章浏览阅读1.4k次。大数据法律监督平台是基于监督数据整合管理平台、监督模型构建平台、内置模型库以及法律监督线索管理平台打造的一套服务于检察机关法律监督工作的专业化系统。_大数据监管算法模型

29-Nor Flash电路设计-程序员宅基地

文章浏览阅读1.2k次,点赞11次,收藏32次。nor flash硬件电路设计_nor flash电路设计