unity fbx动画按配置切割帧片段

news2024/9/27 15:19:32

主要参考该文章:人无两度s 《unity自动切割动画》 感谢作者分享

执行代码需要将模型与配置文件(.txt)放到同一目录下,批量选中模型后右键,代码中读取了选中的第一个模型同目录下可能存在的“动画帧分段.txt”,按其中的配置对选中的模型进行动画片段切分。

配置文件每行的格式:【帧数】,空格,【动画名】,空格,【模型1、模型2】
示例:
在这里插入图片描述
代码:

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
using System.Collections.Generic;
using System.IO;
using System;
using System.Linq;

//txt文件,命名为"动画帧分段",与模型位于同一目录下,每行格式为[帧数(例如”100-200“),空格,动画名(xxxx),模型名(xx、xx)]
public class AniAutoCut : Editor
{
    public static Dictionary<string, List<AniCutConfig>> configs;
    public static string filePath;

    [MenuItem("Assets/一键切割动画")]
    public static void StartCutAni()
    {
        UnityEngine.Object[] objs = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets);
        configs = new();
        foreach (var obj in objs) 
        {
            Debug.Log(obj.name);
            configs[obj.name] = new List<AniCutConfig>();
        }
        ReadConfig(AssetDatabase.GetAssetPath(objs[0]));
        BatchCut();
    }

    private static void BatchCut()
    {
        foreach (KeyValuePair<string, List<AniCutConfig>> kvp in configs)
        {
            CutAni(filePath + kvp.Key + ".fbx", kvp.Value);
        }
    }

    private static void ReadConfig(string path)
    {
        //获取配置路径
        string[] tempPath = path.Split('/');
        filePath = "";
        foreach (string i in tempPath)
        {
            if (!i.Contains(".fbx"))
            {
                filePath += (i + "/");
            }
        }

        //数据处理
        if (File.Exists(filePath + "动画帧分段.txt"))
        {
            string file = File.ReadAllText(filePath + "动画帧分段.txt");

            if (file.Contains("\n"))
            {
                //拆分成行
                string[] arr = file.Split('\n');

                for (int i = 0; i < arr.Length; i++)
                {
                    if (!string.IsNullOrEmpty(arr[i]))
                    {
                        string[] item = arr[i].Split(" ", StringSplitOptions.RemoveEmptyEntries);

                        //标准格式行"XX XX XX"
                        if (item.Length == 3)
                        {
                            AniCutConfig acc = new();
                            acc.startFrame = int.Parse(item[0].Trim().Split("-")[0]);
                            acc.endFrame = int.Parse(item[0].Trim().Split("-")[1]);
                            acc.aniName = item[1].Trim();

                            string[] ModelNames = item[2].Split("、", StringSplitOptions.RemoveEmptyEntries);
                            
                            foreach (string ModelName in ModelNames)
                            {
                                if (configs.Keys.Contains(ModelName.Trim()))
                                {
                                    configs[ModelName.Trim()].Add(acc);
                                    //Debug.Log(ModelName + "   " + acc.startFrame + "  " + acc.endFrame + "  " + acc.aniName);
                                }
                            }
                        }
                    }
                }
            }
        }
        else
        {
            Debug.LogError("在动画路径下未找到\"动画帧分段.txt\"文件!");
        }
    }

    //传入模型路径与帧数列表
    private static void CutAni(string path, List<AniCutConfig> list)
    {
        ModelImporter model = AssetImporter.GetAtPath(path) as ModelImporter;
        List<ModelImporterClipAnimation> clipAnimations = new List<ModelImporterClipAnimation>();
        for (int i = 0; i < list.Count; i++)
        {
            ModelImporterClipAnimation clip = new ModelImporterClipAnimation();

            clip.name = list[i].aniName;
            clip.firstFrame = list[i].startFrame;
            clip.lastFrame = list[i].endFrame;
            //clip.loopPose = false;
            clipAnimations.Add(clip);
        }
        model.clipAnimations = clipAnimations.ToArray();
        model.SaveAndReimport();
        AssetDatabase.ImportAsset(path);
        AssetDatabase.Refresh();
    }
}

public class AniCutConfig
{
    public int startFrame;
    public int endFrame;
    public string aniName;
}

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

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

相关文章

[HNCTF 2022 Week1]——Web方向 详细Writeup

Week1 [HNCTF 2022 Week1]2048 f12查看源代码 可以看出游戏的分数是score 修改score的值 得到flag [HNCTF 2022 Week1]Interesting_include 得到源码 <?php //WEB手要懂得搜索 //flag in ./flag.phpif(isset($_GET[filter])){$file $_GET[filter];if(!preg_match(&qu…

axios封装/基础配置

步骤&#xff1a;装包 -> 封装axios实例 ->调用实例发送请求 1. 装包 npm install axios 2. 封装 axios基础配置 // axios实例封装 import axios from axios// 创建axios实例 const axiosInstance axios.create({baseURL:http://xxx.net, //基地址timeout:5000 //…

el-table 实现表、表格行、表格列合并

最近写vue开发项目的时候&#xff0c;很多地方用到了Element组件中的Table 表格。经过一周的边学边做&#xff0c;我总结了以下三种有关表格的合并方法。 一、合并表头 话不多说&#xff0c;先看效果图 代码如下&#xff1a; 表格结构如上&#xff0c;其中:header-cell-style对…

Java进行多线程编程?(lambda表达式~)

本文标题&#xff1a;Java进行多线程编程&#xff1f;那么&#xff0c;Java为啥不学学如何进程多进程编程呢&#xff1f;&#xff1f;原因在于&#xff1a;Java圈子中不提倡多进程编程~~ 接下来&#xff0c;我们来写一个最为基础/入门的HelloWord程序来感受如何进行多线程~~ J…

数据资产管理:数据目录怎么搞?

经过了站在业务视角的自上而下的数据梳理&#xff0c;以及站在IT视角的自下而上的数据盘点&#xff0c;一套“热腾腾”的数据资产清单终于新鲜出炉了。 通过数据资产盘点&#xff0c;企业终于知道他们拥有哪些数据、如何使用数据、是否安全以及数据在哪里。 然而&#xff0c;据…

数据库误修改后的数据恢复

一不小心将数据库数据修改了&#xff0c;而且回滚无效&#xff0c;于是去尝试各种方法恢复数据 查询到修改时间点之前的数据 恢复数据 恢复数据库被修改数据的流程及代码&#xff0c;这里被修改的表是AUTH_USER,实际应用填写对应表名。 -- 通过时间恢复删除且已提交的数据-- 1…

Jenkins安装配置及插件安装使用

个人理解持续集成&#xff1a;为解决程序代码提交质量低,提交内容导致原有系统的BUG&#xff0c;按时或按需自动编译版本&#xff0c;进行自动化测试。 百度对持续集成的定义&#xff1a;持续集成是一种软件开发实践&#xff0c;即团队开发成员经常集成他们的工作&#xff0c;…

配额 安排

一 常用Tcode 基于Tcode的顺序排列 供应商主数据物料主数据货源清单配额安排采购信息记录采购订单框架协议采购询价/报价采购申请订单收货发票校验物料需求计划BP-供应商主数据MM01 - 物料主数据新增ME01 - 维护MEQ1 - 维护ME11 - 创建ME21N - 创建框架协议-合同&#xff1a…

Qt6_贪吃蛇Greedy Snake

贪吃蛇Greedy Snake 1分析 首先这是一个贪吃蛇界面&#xff0c;由一个长方形边框和一只贪吃蛇组成 默认开局时&#xff0c;贪吃蛇身体只有3个小方块&#xff0c;使用画笔画出 1.1如何移动 对于蛇的移动&#xff0c;有2种方法 在一定时间范围内(定时器)&#xff0c;未对游戏…

谷歌 reCAPTCHA 人机验证

一、问题 smogon 论坛注册不成功。输入账号、密码、邮箱后显示 You did not complete the CAPTCHA verification properly. Please try again. 即未通过 CAPTCHA 验证。 二、原因 使用的 CAPTCHA 验证是谷歌的 reCAPTCHA 人机验证&#xff0c;国内无法访问谷歌服务。 三、解决…

分页查询实现

目录 1.实体类 2.Mapper层 2.1.xxxMapper接口 2.2.xxxMapper.xml文件 3.Service层 3.1.xxxService接口 3.2.xxxServiceImpl层 4.xxxController层 5.调用接口 6.总结 1.实体类 与数据库交互和与前端交互的实体类 这个Model是与数据库交互的实体类&#xff0c;其中的…

python调用GPT实现:智能用例生成工具

工具作用&#xff1a; 根据输入的功能点&#xff0c;生成通用测试点 实现步骤 工具实现主要分2个步骤&#xff1a; 1.https请求调用Gpt,将返回响应结果保存为.md文件 2.用python实现 将 .md文件转换成.xmind文件 3.写个简单的前端页面&#xff0c;调用上述步骤接口 详细代…

7个数据科学Python库将为您节省大量时间

7个数据科学Python库将为您节省大量时间 在进行数据科学时&#xff0c;您可能会花费大量时间编写代码并等待计算机运行某些操作。我挑选了一些可以在这两种情况下节省您时间的Python库。即使您只将其中一个库纳入您的工具库&#xff0c;您仍然可以在下次项目工作时节省宝贵的时…

揭示OLED透明屏数据:探索未来显示技术的潜力

OLED透明屏作为一项颇具吸引力的显示技术&#xff0c;以其独特的特点和卓越的画质在市场上引起了广泛关注。 在这篇文章中&#xff0c;尼伽将和大家一起深入探索OLED透明屏的数据&#xff0c;通过具体的市场趋势分析、技术指标解析、应用领域探讨和未来前景展望&#xff0c;为…

【C++基础】实现日期类

​&#x1f47b;内容专栏&#xff1a; C/C编程 &#x1f428;本文概括&#xff1a; C实现日期类。 &#x1f43c;本文作者&#xff1a; 阿四啊 &#x1f438;发布时间&#xff1a;2023.9.7 对于类的成员函数的声明和定义&#xff0c;我们在类和对象上讲到过&#xff0c;需要进行…

mac 查看端口占用

sudo lsof -i tcp:port # 示例 sudo lsof -i tcp:8080 杀死进程 sudo kill -9 PID # 示例 sudo kill -9 8080

“搞事情”?OpenAl将于11月召开其首届开发者大会

摘要&#xff1a;OpenAI也要召开它的第一届开发者大会了。这次活动&#xff0c;或许标志着OpenAI向其下一阶段的商业开发迈出了关键一步。 昨天&#xff0c;OpenAI宣布将于11月6日举办其首次开发者大会。在这场名为“OpenAI DevDay”的活动中&#xff0c;OpenAI的技术人员将进行…

欧科云链与HashKey Exchange达成合作,助力香港虚拟资产合规化

继8月10日 欧科云链 与 华为云 达成合作之后&#xff0c; 今天&#xff0c;欧科云链 又与 Hashkey Exchange 共同宣布正式达成合作&#xff01; 这次与Hashkey达成合作&#xff0c;双方又将在Web3行业中谱写怎样的故事&#xff1f; 9月6日&#xff0c;欧科云链控股有限公司&…

2023 年高教社杯全国大学生数学建模竞赛题目 C 题 蔬菜类商品的自动定价与补货决策

C 题 蔬菜类商品的自动定价与补货决策 在生鲜商超中&#xff0c;一般蔬菜类商品的保鲜期都比较短&#xff0c;且品相随销售时间的增加而变差&#xff0c; 大部分品种如当日未售出&#xff0c;隔日就无法再售。因此&#xff0c;商超通常会根据各商品的历史销售和需求情况每天进…