Unity中实现动画数据导出导入

这篇具有很好参考价值的文章主要介绍了Unity中实现动画数据导出导入。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

数据导出:

数据导入

解析数据播放动画


根据曲线插值每帧计算数据,模拟Unity中动画播放系统,实现不通过动画控制器播放动画的功能,解决帧同步中动画结果无法预测问题,其实有可能涉及到对动画插值算法的模拟。

数据导出:

首先我们要大概梳理一下Unity中动画控制器播放动画时的原理和动画控制器中动画数据的存储方式。

在Unity中有三个组件,一个是Animator动画控制器,一个是Animation,还有一个是AnimationClip,按照官方文档的介绍是

Use the Animator component to assign animation to a GameObject in your Scene. The Animator component requires a reference to an Animator Controller which defines which animation clips to use, and controls when and how to blend and transition between them.

大概翻译过来就是:

使用 Animator 组件可以将动画分配给场景中的游戏对象。Animator 组件需要引用 Animator Controller(动画控制器),后者定义要使用哪些Animation(动画片段),并控制何时以及如何在Animation(动画片段)之间进行混合和过渡。

 关于Animator参考:Unity - Manual: Animator component

 Animator Controller 参考:Unity - Manual: Animator Controller

Animation Clip参考:Unity - Manual: Animation tab 

首先我们如果要读取一个动画控制器中动画片段的数据,我们先要了解一个Unity提供给我们的API------AnimationUnitily,这个API给我们提供了修改和编辑动画片段的一些函数,同样我们也可以通过将这些数据存储下来,实现我们数据导出功能,不过这个API只能在编辑器模式下使用哦,需要放到Editor文件下中,这个API具体的我们可以参考官方文档AnimationUnility。

既然我们要保存这些数据,那我们首先要了解Unity中动画数据的存储方式,我们首先通过AnimationUtility.GetCurveBindings方式得到float曲线绑定数据,这个方法会返回EditorCurveBinding[]这样一个数组,这个里面会存储一个Animation Clip(动画片段)中所有的float曲线绑定(直白讲大概就是这样的,一个动画片段有很多动画曲线数据,这些曲线数据对应这个对象中不同的类型的值,比如坐标,旋转等等),

EditorCurveBinding这个类会定义如何将一个曲线附加到它所控制的对象,其中:

path:存储对象转换的路径,或者说是相对于父物体的路径,

PropertyName:当前这个动画属性的名称

之后我们通过AnimationUtility.GetEditorCurve这个方法获取AnimationCurve对象,这个对象存储的是一个关键帧的集合,随着时间的推移去计算值,AnimationCurve这个类中会定义所有这个曲线中每一个关键节点的信息(这个节点信息就是我们在Unity中看到的这些节点,见下图)

也就是keys这个字段,这个字段是一个Keyframe[]这样的数组,每一个Keyframe对应一个节点的值,这个节点就是可以对应到动画曲线中的单个关键帧信息,存储这起始时间,值,曲线正切值(或者说函数在该点的导数),在我们这个例子中,我们根据时间去获取对应属性在相同时间下值,在数据导入时对这些数据进行解析,之后通过其他方法去更改这些对应属性的值来实现不通过动画控制器播放动画的功能,其中最关键的动画曲线采样算法是参考Unity动画实现原理探究这篇文章来实现的。

具体代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml.Serialization;
using UnityEditor;
using UnityEngine;

public class AnimationDataOutput : MonoBehaviour
{
    [Header("文件名")]
    public string OutPutName = "";
    [Header("动画片段")]
    public AnimationClip currentClip;
    string filePath = "";
    [ContextMenu("OutputData")]
    void OutputData()
    {
        if (string.IsNullOrEmpty(OutPutName))
        {
            Debug.LogError("文件名不能为空");
            return;
        }
        string folderPath = Path.Combine(Application.persistentDataPath, "AnimationData");
        if (!Directory.Exists(folderPath))
        {
            Directory.CreateDirectory(folderPath);
        }
        filePath = folderPath + "/" + OutPutName + ".bytes";
        float frameStep = 1 / currentClip.frameRate;
        //总帧数=总时间/每帧时间
        float frameCount = currentClip.averageDuration / frameStep;   //持续时间
        AnimationData animationData = new AnimationData();
        animationData.m_bIsLoop = currentClip.isLooping;
        animationData.m_fDurationTime = currentClip.averageDuration;
        animationData.m_fFrameRate = currentClip.frameRate;
        animationData.m_sAnimationName = currentClip.name;
        foreach (var item in AnimationUtility.GetCurveBindings(currentClip))
        {
            KeyData keyData = new KeyData();
            keyData.m_fDurationTime = currentClip.averageDuration;
            keyData.m_sPath = item.path;
            keyData.m_sPropertyName = item.propertyName;
            AnimationCurve animationCurve = AnimationUtility.GetEditorCurve(currentClip, item);
            for (int i = 0; i < animationCurve.keys.Length; i++)
            {
                KeyFrameInfo keyFrameInfo = new KeyFrameInfo();
                keyFrameInfo.time = animationCurve.keys[i].time;
                keyFrameInfo.value = animationCurve.keys[i].value;
                keyFrameInfo.inTangent = animationCurve.keys[i].inTangent;
                keyFrameInfo.outTangent = animationCurve.keys[i].outTangent;
                keyFrameInfo.inWeight = animationCurve.keys[i].inWeight;
                keyFrameInfo.outWeight = animationCurve.keys[i].outWeight;
                keyFrameInfo.weightedMode = animationCurve.keys[i].weightedMode;
                keyData.m_szKeyData.Add(keyFrameInfo);
            }
            animationData.m_szFrameData.Add(keyData);
        }
        SaveObjectFile(filePath, animationData);
    }

    public static void SaveObjectFile(string fileName, object data)
    {
        Stream write = null;
        try
        {
            FileInfo fileInfo = new FileInfo(fileName);
            if (fileInfo.Exists) fileInfo.Delete();
            write = fileInfo.OpenWrite();
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            byte[] value = CompressedToBytes(data);
            binaryFormatter.Serialize(write, value);
        }
        catch (System.Exception)
        {
            throw;
        }
        finally
        {
            if (write != null)
            {
                write.Close();
            }
        }
    }
    public static object GetObjectData(string fileName)
    {
        Stream Read = null;
        string strErr = "";
        try
        {
            FileInfo FI = new FileInfo(fileName);

            if (FI.Exists)
            {
                Read = FI.OpenRead();
                BinaryFormatter BF = new BinaryFormatter();

                byte[] aa = (byte[])BF.Deserialize(Read);

                return DecompressToObject(aa);
            }
            else
            {
                return null;
            }
        }
        catch (Exception ex)
        {
            strErr = ex.ToString();
        }
        finally
        {
            if (Read != null)
            {
                Read.Close();
            }
        }
        return null;
    }
    public static byte[] CompressedToBytes(object obj)
    {
        MemoryStream ms = new MemoryStream();
        DeflateStream zip = new DeflateStream(ms, CompressionMode.Compress, true);
        try
        {
            BinaryFormatter serializer = new BinaryFormatter();
            serializer.Serialize(zip, obj);
            zip.Close();
            byte[] ary = ms.ToArray();
            ms.Close();
            return ary;
        }
        catch (Exception)
        {
            //Log.write(e.Message);
            zip.Close();
            ms.Close();
            return null;
        }
    }
    public static object DecompressToObject(byte[] ary)
    {
        MemoryStream ms = new MemoryStream(ary);
        DeflateStream UnZip = new DeflateStream(ms, CompressionMode.Decompress);

        try
        {
            BinaryFormatter serializer = new BinaryFormatter();
            object obj = serializer.Deserialize(UnZip);
            UnZip.Close();
            ms.Close();
            return obj;
        }
        catch (Exception)
        {
            //Log.write(e.Message);
            UnZip.Close();
            ms.Close();
            return null;
        }
    }
}

数据导入

数据导入就简单了,对我们压缩好的数据进行解析。

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;

public class AnimationDataInput : MonoBehaviour
{
    public static AnimationDataInput instance;
    public static List<KeyData> LoadAnimationData(string filePath)
    {
        filePath = Path.Combine(Application.persistentDataPath, filePath);
        object data=GetObjectData(filePath);
        return (List<KeyData>)data;
    }
    public static void SaveObjectFile(string fileName, object data)
    {
        Stream write = null;
        try
        {
            FileInfo fileInfo = new FileInfo(fileName);
            if (fileInfo.Exists) fileInfo.Delete();
            write = fileInfo.OpenWrite();
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            byte[] value = CompressedToBytes(data);
            binaryFormatter.Serialize(write, value);
        }
        catch (System.Exception)
        {
            throw;
        }
        finally
        {
            if (write != null)
            {
                write.Close();
            }
        }
    }
    public static object GetObjectData(string fileName)
    {
        Stream Read = null;
        string strErr = "";
        try
        {
            FileInfo FI = new FileInfo(fileName);

            if (FI.Exists)
            {
                Read = FI.OpenRead();
                BinaryFormatter BF = new BinaryFormatter();

                byte[] aa = (byte[])BF.Deserialize(Read);

                return DecompressToObject(aa);
            }
            else
            {
                return null;
            }
        }
        catch (Exception ex)
        {
            strErr = ex.ToString();
        }
        finally
        {
            if (Read != null)
            {
                Read.Close();
            }
        }
        return null;
    }
    public static byte[] CompressedToBytes(object obj)
    {
        MemoryStream ms = new MemoryStream();
        DeflateStream zip = new DeflateStream(ms, CompressionMode.Compress, true);
        try
        {
            BinaryFormatter serializer = new BinaryFormatter();
            serializer.Serialize(zip, obj);
            zip.Close();
            byte[] ary = ms.ToArray();
            ms.Close();
            return ary;
        }
        catch (Exception)
        {
            //Log.write(e.Message);
            zip.Close();
            ms.Close();
            return null;
        }
    }
    public static object DecompressToObject(byte[] ary)
    {
        MemoryStream ms = new MemoryStream(ary);
        DeflateStream UnZip = new DeflateStream(ms, CompressionMode.Decompress);

        try
        {
            BinaryFormatter serializer = new BinaryFormatter();
            object obj = serializer.Deserialize(UnZip);
            UnZip.Close();
            ms.Close();
            return obj;
        }
        catch (Exception)
        {
            //Log.write(e.Message);
            UnZip.Close();
            ms.Close();
            return null;
        }
    }
}
[Serializable]
public class AnimationData
{
    public float m_fDurationTime { get; set; }
    public string m_sAnimationName { get; set; }
    public float m_fFrameRate { get; set; }
    public float m_fFrameStep
    {
        get
        {
            return 1 / m_fFrameRate;
        }
    }
    public bool m_bIsLoop { get; set; }
    public List<KeyData> m_szFrameData { get; set; } = new List<KeyData>();
}
[SerializeField]
public class KeyData
{
    public float m_fDurationTime { get; set; }
    public string m_sPath { get; set; }
    public string m_sPropertyName { get; set; }
    public List<KeyFrameInfo> m_szKeyData { get; set; } = new List<KeyFrameInfo>();
}

[Serializable]
public class KeyFrameInfo
{
    //
    // 摘要:
    //     The time of the keyframe.
    public float time { get; set; }
    //
    // 摘要:
    //     The value of the curve at keyframe.
    public float value { get; set; }
    //
    // 摘要:
    //     Sets the incoming tangent for this key. The incoming tangent affects the slope
    //     of the curve from the previous key to this key.
    public float inTangent { get; set; }
    //
    // 摘要:
    //     Sets the outgoing tangent for this key. The outgoing tangent affects the slope
    //     of the curve from this key to the next key.
    public float outTangent { get; set; }
    //
    // 摘要:
    //     Sets the incoming weight for this key. The incoming weight affects the slope
    //     of the curve from the previous key to this key.
    public float inWeight { get; set; }
    //
    // 摘要:
    //     Sets the outgoing weight for this key. The outgoing weight affects the slope
    //     of the curve from this key to the next key.
    public float outWeight { get; set; }
    //
    // 摘要:
    //     Weighted mode for the keyframe.
    public WeightedMode weightedMode { get; set; }
}

解析数据播放动画

之前我们存储的属性名这会派上用场了,通过对比来决定当前值所代表的含义。


    /// <summary>
    /// 设置数据
    /// </summary>
    /// <param name="trans"></param>
    /// <param name="value"></param>
    /// <param name="propertyName"></param>
    void SetData(Transform trans, FP value, string propertyName)
    {
        //YhLog.YhTempLog("set value start TransName=" + trans.name + ",value=" + value + ",propertyName=" + propertyName + ",pos=" + trans.position + ",localpos=" + trans.localPosition) ;
        if (propertyName == "m_LocalPosition.x")
        {
            TSVector data = trans.localPosition.ToTSVector();
            data.x = value;
            trans.localPosition = data.ToVector();
        }
        else if (propertyName == "m_LocalPosition.y")
        {
            TSVector data = trans.localPosition.ToTSVector();
            data.y = value;
            trans.localPosition = data.ToVector();
        }
        else if (propertyName == "m_LocalPosition.z")
        {
            TSVector data = trans.localPosition.ToTSVector();
            data.z = value;
            trans.localPosition = data.ToVector();
        }
        else if (propertyName == "m_LocalRotation.x")
        {
            Quaternion data = trans.localRotation;
            data.x = value.AsFloat();
            trans.localRotation = data;
        }
        else if (propertyName == "m_LocalRotation.y")
        {
            Quaternion data = trans.localRotation;
            data.y = value.AsFloat();
            trans.localRotation = data;
        }
        else if (propertyName == "m_LocalRotation.z")
        {
            Quaternion data = trans.localRotation;
            data.z = value.AsFloat();
            trans.localRotation = data;
        }
        else if (propertyName == "m_LocalRotation.w")
        {
            Quaternion data = trans.localRotation;
            data.w = value.AsFloat();
            trans.localRotation = data;
        }
        else if (propertyName == "m_LocalScale.x")
        {
            Vector3 data = trans.localScale;
            data.x = value.AsFloat();
            trans.localScale = data;
        }
        else if (propertyName == "m_LocalScale.y")
        {
            Vector3 data = trans.localScale;
            data.y = value.AsFloat();
            trans.localScale = data;
        }
        else if (propertyName == "m_LocalScale.z")
        {
            Vector3 data = trans.localScale;
            data.z = value.AsFloat();
            trans.localScale = data;
        }
    }

记录一个碰到的问题:

在序列化数据的过程中发现,所有序列化对象中的字段都必须要为可序列化字段,如List<T>类型的T必须也为序列化对象。

先写这么多吧,具体原理有时间再好好梳理一下。

~.~

参考:Unity动画实现原理探究文章来源地址https://www.toymoban.com/news/detail-412515.html

到了这里,关于Unity中实现动画数据导出导入的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java 实现数据导入导出

    官方文档(完整功能展示):地址 依赖 导出 1.随便一个实体类(默认不管加不加@ExcelProperty的注解的所有字段都会参与读写,若某字段不想参与,可在字段上添加@ExcelIgnore。若只想加@ExcelProperty才参与,则类上加@ExcelIgnoreUnannotated)更多注解使用 2.编一个方法用于返回要导出

    2024年02月13日
    浏览(40)
  • Unity3D中实现动画的方式:

    Unity3D中实现动画的方式有很多种,以下是其中两种常用的方式: 1. 使用Animation组件 使用Animation组件可以制作简单的关键帧动画。步骤如下: 将需要动画的物体选中,然后在Inspector面板中点击Add Component按钮,选择Animation组件。 在Project面板中创建动画剪辑(Animation Clip)。

    2024年02月08日
    浏览(60)
  • poi技术实现数据的导入与导出

    前言: POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。 Microsoft Office:是由微软公司开发的一套 办公软件套装 。常用组件有Word、Excel、PowerPoint等。 POI也用于操作Excel(电子表格) 一个excel文件就是一个工作簿Workbook 一个工

    2024年02月05日
    浏览(31)
  • 基于Luckysheet实现的协同编辑在线表格支持在线导入数据库,前端导出,前端导入,后端导出

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 提示:这里可以添加本文要记录的大概内容: 这两年,在线表格协作工具越来越火,但开源界一直没有相关的实现,被垄断在几个大厂手上,随着Luckysheet 的横空出世,开源界终于也有一个漂亮能打的在

    2024年02月11日
    浏览(43)
  • 从UE5导出Metahuman面部控制器动画并导入maya教程

    从UE5导出Metahuman面部控制器动画并导入maya教程 B站视频

    2024年02月12日
    浏览(26)
  • FBX动画导入Unity

    1.1 第一章 Unity导入FBX模型动画但不能动 随着AR/VR等技术发展,摸一手Unity动画总是不亏的 示例:电脑自带的3d查看器点开能自动播放的fbx文件(仅作演示) 3d查看器打开fbx文件的动画示例视频 点击导入的文件→ Materials →Location中选 Use External Materials(Legacy) → Apply 点击导入的

    2024年02月05日
    浏览(36)
  • Unity Animation -- 导入动画

            尽管Unity中的动画工具已经比较强大了,但对于非常复杂的或很长的动画,通常是由外部DCC创建。特别是对于角色动画来说,需要在动画过程中进行复杂的控制。因此我们通常需要将外部制作的动画导入到Unity中。         对于复杂的动画,制作过程会非常耗时。动

    2024年02月12日
    浏览(44)
  • poi实现excel文件导入导出(基本数据导出、含格式导出、含批注导出、含图片图表导出)——springboot

    本文主要是介绍springboot + poi实现基本的excel文件导入导出,包含数据导出导入时数据的其他需求校验,导出含有批注信息、导出含有图片信息、导出含有图表信息等的介绍等等,主要是一个demo尽可能简单明了的来介绍相关功能即可。有什么问题可以在留言哦!并在文章末尾附

    2024年02月08日
    浏览(69)
  • Apache POI实现Excel导入读取数据和写入数据并导出

    Apache POI POI介绍 Apache POI是用Java编写的免费开源的跨平台的Java API,Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能,其中使用最多的就是使用POI操作Excel文件。 maven坐标: POI结构: 入门案例 ExcelTest .java文件 从Excel文件读取数据

    2024年02月12日
    浏览(33)
  • Unity导入FBX动画文件

    给刚入门的同学稍微记一下fbx格式动画导入unity的步骤: 首先直接把fbx文件拖入unity内,如图操作 将avatar骨骼创建之后,我们接下来就根据自己对动画的一些需求设置一下动画的参数,比如截取一下动画的开始帧和结束帧来达到剪切的目的,或者loop Time将动画设置为循环等等

    2024年02月11日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包