Unity(四) 基于关键帧的动画与骨骼动画

news2024/11/18 5:39:41

Unity中有两种类型的动画:基于关键帧的动画和骨骼动画

基于关键帧的动画是最常见的动画形式,也称为帧动画。它将每一帧的动画存储为一个离散的关键帧,然后通过计算每一帧之间的差异来创建动画。这种类型的动画适用于不需要太多交互或程序控制的简单动画。在Unity中,使用Animator来创建基于关键帧的动画。

骨骼动画是通过修改骨骼层次结构中的骨头来实现的。这种类型的动画适用于需要更高程度的交互和程序控制的复杂动画,例如游戏中的角色动画。在Unity中,使用Animation和Animator来创建骨骼动画。

一、基于关键帧的动画

动画中的特定姿势由其关键帧定义。在传统的手绘动画中,关键帧是引导动画师绘制的姿势,是定义动画序列的重要(或关键)图纸。

Unity中有几种方式来制作基于关键帧的动画:

1. 使用Animation

Animation一般用于快速创建简单的动画场景,通过设置若干个关键帧,即可完成。

方法如下 (有点类似于Flash动画制作):

a. 选中目标动画物体,在Inspector面板中点击Add Component按钮,选择Animation组件

b. 在Project面板中创建Animation Clip

c. 在Animation面板中将动画剪辑拖拽到物体的Animation组件上

d. 点击Record按钮开始录制,可调整物体的属性,如位置、旋转、缩放等

e. 点击Stop按钮结束录制

2. 使用Animator Controller

Animator Controller一般用于更复杂的动画制作,它可以创建并维护状态机,并串联起多个Animation,达成复杂动画的制作,这个也可以用于后面骨骼动画的制作

动画状态机(Animation State Machine),需要考虑以下几个方面:

  • 动画状态:定义动画剪辑和动画状态之间的映射关系。
  • 过渡条件:定义两个动画状态之间的转换条件。
  • 过渡时间:定义两个动画状态之间的过渡时间。
  • 动画层:定义动画状态所在的层级,以及层级之间的权重关系。
  • 动画事件:定义动画状态播放过程中触发的事件

如图所示,可在Controller上设定动画的状态,entry入口,exit退出等状态,并根据需要配置各个状态的物体的参数,最后可设置他们进入各个状态的条件

3. 使用DoTween插件

DoTween 是由 Demigiant 开发的,被广泛应用于 Unity 游戏开发中。它是一个流行的动画插件,被许多开发者用于创建流畅、高效的动画效果,提升游戏体验。

这里推荐使用DoTweenPro,可以使用可视化的方式,很方便的完成动画制作,而不需要编写代码。它有两种形式可创建动画,DoTweenAnimation和DoTweenPath。

个人理解DoTweenAnimation一般用于对单一物体的状态改变动画制作,而DoTweenPath用于路径动画的快速制作,例如摄像机飞行等。

Animation的配置一览,可快速通过可视化的方式配置各种效果,例如旋转、放大等,并配置延迟模式,时长,缓动,循环,各种事件等

Path配置一览,可以配置固定的物理行动路线以及各个节点的状态。配置与Animation大同小异。

缓动配置一览,这个也很重要

二、骨骼动画

1. 概念

骨骼动画的制作方式与关键帧动画是一样的,但需要注意的是其底层逻辑完全不同,制作前我们最好是了解一下骨骼动画的制作过程。

模型动画中,骨骼动画一般与线性动画对比,线性动画较为简单,一般是通过顶点位置加入关键帧让动画看起来更加平滑,骨骼动画中,模型具有互相连接的骨骼组成骨架结构,通过改变骨骼的朝向和位置来为模型生成动画,从而更加拟真。

一般骨骼动画制作过程为: 蒙皮->关键帧->关键帧之间插值/姿态调整 

蒙皮的意思其实就是将网格(mesh)和顶点(vertex)绑定到骨骼上

几个概念:

皮肤(Skin):Mesh

骨骼(Skeleton):骨骼上所在的mesh和vertex随着骨骼一起运动

关节(Joints):骨骼连接处,骨骼可以围绕关节运动

一言以蔽之,将骨骼与标准姿势绑定完成后,动画师通过操纵骨骼来制作其关键帧,然后通过在关键帧之间进行插入值形成整个动画。

2. 制作

那么对于普通程序员,在拿到动画师的成果后应该怎么做呢?

以个人行走举例,一般来说需要的几种骨骼动画类型为,静止(可加入一些闲置时候的动画)、行走,后退,左转,右转,跑步等。

a. 拿到各个状态的fbx模型文件

b. 创建Animator Controller,将fbx文件拖动为到state上

c. 创建变量和状态连线,并且设置条件,例如闲置->行走,行走->向右转弯等,这里配置要尽可能的全面。

d. 添加动画脚本,通过按键或鼠标控制角色,并改变变量。需要配置第一人称摄像头,以及控制角色的脚本

PlayerController

using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine;

public class Player : MonoBehaviour
{
    private float moveSpeed;//摄像机的移动速度
    private Animator Anim;
    private GameObject fpvCamera; // 第一人称摄像头
    void Start()
    {
        moveSpeed = -4f;
        Anim = GetComponent<Animator>();
        fpvCamera = UnityUtils.FindDeActiveObjectFirstOrDefault("CameraPlayer");
    }

    Vector3 rot = new Vector3(0, 0, 0);

    void Update()
    {
        //鼠键控制移动
        WASD();
        if (Input.anyKey)
        {
            this.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeRotation;
        }
        else
        {
            this.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeAll;
        }
    }

    /// <summary>
    /// 鼠键控制player移动
    /// </summary>
    void WASD()
    {
        if (Input.GetMouseButton(1))
        {
            if (Input.GetAxis("Mouse X") != 0)
            {
                if (Input.GetAxis("Mouse X") < 0.1f && Input.GetAxis("Mouse X") > -0.1f)
                {
                    return;
                }
                this.fpvCamera.transform.Rotate(new Vector3(0, Input.GetAxis("Mouse X") * Time.fixedDeltaTime * 200, 0));
                //clearArrow(false);
            }
            if (Input.GetAxis("Mouse Y") != 0)
            {
                if (Input.GetAxis("Mouse Y") < 0.1f && Input.GetAxis("Mouse Y") > -0.1f)
                {
                    return;
                }
                this.fpvCamera.transform.Rotate(new Vector3(Input.GetAxis("Mouse Y") * Time.fixedDeltaTime * -200, 0, 0));//摄像机的旋转速度
            }
        }

        if (Input.GetKey(KeyCode.W))
        {
            // this.fpvCamera.transform.Translate(-Vector3.forward * Time.deltaTime * moveSpeed);//移动
        }
        if (Input.GetKeyDown(KeyCode.W))
        {
            Anim.SetBool("Walk", true);
        }
        if (Input.GetKeyUp(KeyCode.W))
        {
            Anim.SetBool("Walk", false);
        }
        if (Input.GetKey(KeyCode.S))
        {
            //gameObject.transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed);//向后。
            // this.fpvCamera.transform.Translate(Vector3.forward * Time.deltaTime * moveSpeed);//向后。
        }
        if (Input.GetKeyDown(KeyCode.S))
        {
            Anim.SetBool("Back", true);
        }
        if (Input.GetKeyUp(KeyCode.S))
        {
            Anim.SetBool("Back", false);
        }
        if (Input.GetKey(KeyCode.A))
        {
            transform.RotateAround(transform.position, Vector3.up, 45f * Time.deltaTime * moveSpeed);//旋转。
            this.fpvCamera.transform.RotateAround(transform.position, Vector3.up, 30f * Time.deltaTime * moveSpeed);
        }
        //if (Input.GetKeyDown(KeyCode.A))
        //{
        //    Anim.SetBool("Left", true);
        //}
        //if (Input.GetKeyUp(KeyCode.A))
        //{
        //    Anim.SetBool("Left", false);
        //}
        if (Input.GetKey(KeyCode.D))
        {
            transform.RotateAround(transform.position, Vector3.up, -45f * Time.deltaTime * moveSpeed);//旋转。
            fpvCamera.transform.RotateAround(transform.position, Vector3.up, -30f * Time.deltaTime * moveSpeed);
        }
        //if (Input.GetKeyDown(KeyCode.D))
        //{
        //    Anim.SetBool("Right", true);
        //}
        //if (Input.GetKeyUp(KeyCode.D))
        //{
        //    Anim.SetBool("Right", false);
        //}
        if (Input.GetKeyUp(KeyCode.R))
        {
            Anim.SetBool("Run", false);
        }
        if (Input.GetKeyDown(KeyCode.R))
        {
            Anim.SetBool("Run", true);
        }
        if (Input.GetKey(KeyCode.R))
        {
            gameObject.transform.Translate(Vector3.forward * Time.deltaTime * 8);//移动
        }
        if (Input.GetKeyUp(KeyCode.F))
        {
            Anim.SetBool("Magic", false);
        }
        if (Input.GetKeyDown(KeyCode.F))
        {
            Anim.SetBool("Magic", true);
        }
        if (Input.GetKeyDown(KeyCode.Space))
        {
            transform.position += new Vector3(0, 1, 0);//跳过障碍物
        }
    }
}

FPCamera:从默认摄像机转为第一人称摄像机。

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

public class FPS : MonoBehaviour
{
    private GameObject fpvCamera; // 第一人称摄像头
    // Start is called before the first frame update
    void Start()
    {
        fpvCamera = UnityUtils.FindDeActiveObjectFirstOrDefault("CameraPlayer");
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void FPSStart()
    {
        Camera2.enableMainView = !Camera2.enableMainView;
        if (Camera2.enableMainView)
        {
            fpvCamera.SetActive(false);
        }
        else
        {
            fpvCamera.SetActive(true);
        }
    }
}

代码很简单,做动画最重要需要尝试

3. CPU Instancing & GPU Instancing

扩展一点内容,以上我们制作的都是CPU Instancing的方式,针对大型游戏等场景,Unity提供了GPU Instancing的方式来提升动画性能(在Web上不可用),其作用如下:

有两种 GPU 加速模式:

1. 缓存每一帧顶点坐标,顶点着色器根据帧数和顶点编号获取顶点数据进行渲染,此为方案 A;

2. 缓存每一帧骨骼变换矩阵,顶点着色器计算蒙皮,此为方案 B;

处理大规模角色,同时不要求动画效果很精细,内存够用的情况,可以采用方案A。

处理逻辑压力大、CPU消耗多并且渲染压力小的情况,可以选择方案B。

插件:GPU Animation Baker Pro

个人也没有相关的制作经验,仅做了了解。如需深入,可以参考文章:

Unity - Manual: GPU instancing

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1039895.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【network】丢包网络情况记录

目录 2023-09-25 09:20 2023-09-25 11:23 接口流量 2023-09-25 09:20 2023-09-25 11:23 接口流量

使用自功率谱、互功率谱估计滤波器幅频特性

这段时间终于对工程中的随机信号的一般处理方式有点头绪了&#xff0c;功率谱密度估计是十分重要的方式之一&#xff0c;仍需继续深入细化相关内容。 示例&#xff1a;使用自功率谱、互功率谱估计滤波器幅频特性&#xff0c;自己实现 & Matlab自带函数实现。 clc;clear;cl…

BUUCTF [BJDCTF2020]EasySearch 1

“.swp” 后缀通常用于表示 Vim&#xff08;一种文本编辑器&#xff09;的交换文件。Vim 是一个强大的文本编辑器&#xff0c;它在编辑文件时会创建交换文件以确保文件内容的安全性。 审阅 访问 index.php.swp 得到源码 <?phpob_start();function get_hash(){$chars ABC…

【PDF】pdf 学习之路

PDF 文件格式解析 https://www.cnblogs.com/theyangfan/p/17074647.html 权威的文档&#xff1a; 推荐第一个连接&#xff1a; PDF Explained &#xff08;译作《PDF 解析》&#xff09; | PDF-Explained《PDF 解析》https://zxyle.github.io/PDF-Explained/ https://zxyle…

缓冲区溢出漏洞预防

什么是缓冲区溢出 组成所有应用程序的程序由缓冲区组成&#xff0c;缓冲区是在内存中分配的临时空间&#xff0c;用于保存数据&#xff0c;直到它们移动到程序的其他部分&#xff0c;缓冲区可以包含的数据字节数最初将在代码开发期间指定&#xff0c;由于没有任何类型的边界检…

UNet网络训练

UNet网络训练 训练资源 构建好UNet网络模型后&#xff0c;需要进行训练。但是训练需要特别多的原始图像和标签图像&#xff0c;对于一般而言这一步特别繁琐&#xff0c;不过在网上有一些免费的数据集可以让我们省略这一步&#xff0c;直接进行训练测试。 VOC&#xff08;Visu…

Centos环境使用Docker安装Kafka

1 Kafka简介 1、kafka是什么&#xff1f; Kafka是一种高吞吐量的分布式发布订阅消息系统&#xff0c;它可以处理消费者规模的网站中的所有动作流数据&#xff0c;具有高性能、持久化、多副本备份、横向扩展能力。 2、kafka的工作原理[去耦合] Kafka采用的是订阅-发布的模式&am…

Android应用线上闪退问题解决

解决Android应用线上闪退问题需要仔细的监控、调试和分析。以下是一些解决Android线上闪退问题的工具和方法&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 工具&#xff1a; 1.Google Play 控制台&…

anaconda navigator启动时一直卡在 loading applications 页面

anaconda navigator启动时一直卡在 loading applications 页面 方法1 在安装目录找到D:\anaconda\Lib\site-packages\anaconda_navigator\api 然后打开conda_api.py&#xff0c; 在1358行找到data yaml.load(f)&#xff0c;将其改为data yaml.safeload(f) 猜测为保证代码…

精准对接促合作:飞讯受邀参加市工信局举办的企业供需对接会

2023年9月21日&#xff0c;由惠州市工业和信息化局主办的惠州市工业软件企业与制造业企业供需对接会成功举办&#xff0c;对接会旨在促进本地工业软件企业与制造业企业的紧密合作&#xff0c;推动数字化转型的深入发展。此次会议在市工业和信息化局16楼会议室举行&#xff0c;会…

【校招VIP】产品行测之逻辑计算题

考点介绍&#xff1a; 数理逻辑包括对于统计学有基础的了解&#xff0c;有基础的数据敏感性&#xff0c;拥有从数据层层深挖定位到问题的能力。知道先验概率&#xff0c;置信度&#xff0c;归因方法等基础的统计学概念。作为产品经理都应该去理解这些逻辑&#xff0c;并且思考如…

DirectX12学习笔记-创建窗口

创建窗口就是纯的Win API&#xff0c;我设想的窗口是这样的&#xff1a; 我们调用WinMain启动窗口&#xff0c;然后在WinMain初始化和启动消息循环。 消息会传入OnEvent, WndProc是窗口过程函数&#xff08;每个窗口都有一个WndProc函数&#xff0c;用于接收和处理窗口相关的…

yolov8训练自己的数据集(标注到训练)

yolov8可以用作目标检测&#xff0c;分割&#xff0c;姿态&#xff0c;跟踪。这里举例目标检测从标注到训练的过程。 官网连接 先把代码下载下来&#xff0c;这个不用说了。 然后准备数据集&#xff0c;创建一个文件夹dataset&#xff08;自己命名&#xff09;&#xff0c;下面…

m1芯片-centos安装mysql

在m1芯片中&#xff0c;虚拟机centos7使用mysql官方的yum源安装mysql没问题&#xff0c;但是在启动mysql的时候会报错&#xff0c;从日志上看是硬件问题&#xff0c;报错信息为 Most likely, you have hit a bug, but this error can also be caused by malfunctioning hardwar…

OpenCV项目开发实战--主成分分析(PCA)的特征脸应用(附C++/Python实现源码)

什么是主成分分析? 这是理解这篇文章的先决条件。 图 1:使用蓝线和绿线显示 2D 数据的主要组成部分(红点)。 快速回顾一下,我们了解到第一个主成分是数据中最大方差的方向。第二主成分是空间中与第一主成分垂直(正交)的最大方差方向,依此类推。第一和第二主成分红点(2…

【周赛364-数组】美丽塔 I-力扣 2865

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

SpringBoot统一返回处理和全局异常处理

统一接口返回 前后端分离项目&#xff0c;通常后端会返回给前端统一的数据格式&#xff0c;一般包括code,msg,data信息。 创建返回统一实体类 package com.example.exceptionspring.domain;import lombok.Data;Data public class Result {private Integer code;private Strin…

基于微信小程序的校园二手交易平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言学生的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W…

别问怎么下载,金蝶云星空SaaS BI系统不用下载

国产自研的奥威软件-金蝶云星空SaaS BI&#xff0c;不下载不安装&#xff0c;从浏览器上一键注册登录即可使用&#xff1a;一键点击下载金蝶云星空方案&#xff0c;执行后&#xff0c;BI系统将基于金蝶云星空内的数据与方案自带的BI报表&#xff0c;智能计算分析指标&#xff0…

python模拟斐波那契数列输出

用户输入指定的数列范围&#xff0c;正确输出结果。 源代码&#xff1a; def fiebo(n): a 1 b 1 for i in range(n): if i 0: print(a, end" ") elif i 1: print(b, end" ") else: …