AnimationClip同步工具

news/2024/11/15 9:50:30/文章来源:https://www.cnblogs.com/sailJs/p/18201798

用途:列出动画的第1帧与预制体GameObject当前值不同的,需要同步的可以手动同步

效果图

 

public struct ValueNotSameItem
{public EditorCurveBinding curveBinding; //关联参数public AnimationCurve animCurve; //动画曲线public float kfValue; //动画曲线上第1帧的值public float objValue; //预制体中GameObject上的值
}

 

public class AnimClipSyncToolWnd : EditorWindow
{[MenuItem("MyTools/AnimClipSyncToolWnd", false, 1)]static void ShowWindow(){var win = GetWindow(typeof(AnimClipSyncToolWnd), false, "AnimClipSyncToolWnd");win.minSize = new Vector2(500, 300);}private Vector2 m_ScrollPos;private GameObject m_PrefabAsset; //预制体资源private Animator m_Animator; //预制体根节点上的Animator组件private AnimationClip[] m_AnimClips; //Animator状态机下的所有动画文件private string[] m_AnimClipOptions;private int m_AnimClipOptionIndex;private List<string> m_NodePathList = new List<string>(); //值不同的节点路径private Dictionary<string, List<ValueNotSameItem>> m_ValueNotSameItemsDict = new Dictionary<string, List<ValueNotSameItem>>();private bool m_PrefabRefreshFlag = false;private void OnEnable(){m_PrefabRefreshFlag = true;Undo.undoRedoPerformed += On_UndoRedoPerformed;}private void OnDisable(){Undo.undoRedoPerformed -= On_UndoRedoPerformed;}private void On_UndoRedoPerformed(){var grpName = Undo.GetCurrentGroupName();Debug.Log($"undoRedo: {grpName}");m_PrefabRefreshFlag = true;}private void OnGUI(){m_ScrollPos = EditorGUILayout.BeginScrollView(m_ScrollPos);{OnGUI_ScrollView();}EditorGUILayout.EndScrollView();}private void OnGUI_ScrollView(){var prefabAsset = (GameObject)EditorGUILayout.ObjectField("Prefab", m_PrefabAsset, typeof(GameObject), false);bool isPrefabChange = m_PrefabAsset != prefabAsset;if (null != m_PrefabAsset){if (GUILayout.Button($"Refresh"))isPrefabChange = true;}if (m_PrefabRefreshFlag || isPrefabChange){m_PrefabRefreshFlag = false;m_PrefabAsset = prefabAsset;m_Animator = null;m_AnimClips = null;m_AnimClipOptions = null;m_AnimClipOptionIndex = 0;m_NodePathList.Clear();m_ValueNotSameItemsDict.Clear();if (null != m_PrefabAsset){m_Animator = m_PrefabAsset.GetComponent<Animator>();if (null != m_Animator)RefreshAnimClips(m_Animator);}}if (null == m_PrefabAsset) return;if (null == m_Animator){EditorGUILayout.LabelField("No Animator !!!");return;}foreach (var path in m_NodePathList){var list = m_ValueNotSameItemsDict[path];EditorGUILayout.LabelField(path);EditorGUI.indentLevel = 1;for (int j = 0; j < list.Count; ++j){var item = list[j];EditorGUILayout.BeginHorizontal();EditorGUILayout.LabelField($"{item.curveBinding.propertyName}: obj: {item.objValue}, kf: {item.kfValue}");if (GUILayout.Button("set as obj", GUILayout.Width(70))){float delta = item.objValue - item.kfValue;item.kfValue = item.objValue;list[j] = item;//所有帧+delta值var keyFrames = item.animCurve.keys;for (int i = 0; i < keyFrames.Length; ++i){var kf = keyFrames[i];kf.value = TrimFloat(kf.value + delta);item.animCurve.MoveKey(i, kf);}//保存var clip = m_AnimClips[m_AnimClipOptionIndex];Undo.RecordObject(clip, "all keyFrames+delta");AnimationUtility.SetEditorCurve(clip, item.curveBinding, item.animCurve);EditorUtility.SetDirty(clip);AssetDatabase.SaveAssets();Repaint();}if (GUILayout.Button("set as kf", GUILayout.Width(70))){item.objValue = item.kfValue;list[j] = item;OnClick_SetAsKf(item);}EditorGUILayout.EndHorizontal();}EditorGUI.indentLevel = 0;EditorGUILayout.Space(5); //条目间隔
        }}//刷新Animator状态机的所有AnimClipprivate void RefreshAnimClips(Animator animat){m_AnimClips = animat.runtimeAnimatorController.animationClips;if (null != m_AnimClips && m_AnimClips.Length > 0){m_AnimClipOptions = new string[m_AnimClips.Length];for (int i = 0; i < m_AnimClips.Length; ++i){var clip = m_AnimClips[i];m_AnimClipOptions[i] = clip.name;}var curClip = m_AnimClips[m_AnimClipOptionIndex];RefreshAnimCurves(curClip);}}//刷新AnimClip的所有动画曲线private void RefreshAnimCurves(AnimationClip clip){EditorCurveBinding[] bindings = AnimationUtility.GetCurveBindings(clip); //一个property关联一个动画曲线var prefabTf = m_PrefabAsset.transform;foreach (var b in bindings){var curve = AnimationUtility.GetEditorCurve(clip, b);var kframes = curve.keys;if (kframes.Length <= 0) continue;var tf = prefabTf.Find(b.path);if (null == tf) continue;var kf0 = kframes[0];CheckFirstKeyFrame(b, curve, tf, kf0);}}private void OnClick_SetAsKf(ValueNotSameItem item){var tf = m_PrefabAsset.transform.Find(item.curveBinding.path);Undo.RecordObject(tf, "Set As kf");var rtf = tf as RectTransform;switch (item.curveBinding.propertyName){case "m_LocalPosition.x":{var pos = tf.localPosition;pos.x = item.kfValue;tf.localPosition = pos;break;}case "m_LocalPosition.y":{var pos = tf.localPosition;pos.y = item.kfValue;tf.localPosition = pos;break;}case "m_LocalPosition.z":{var pos = tf.localPosition;pos.z = item.kfValue;tf.localPosition = pos;break;}case "m_LocalScale.x":{var s = tf.localScale;s.x = item.kfValue;tf.localScale = s;break;}case "m_LocalScale.y":{var s = tf.localScale;s.y = item.kfValue;tf.localScale = s;break;}case "localEulerAnglesRaw.x":{var euler = tf.localEulerAngles;euler.x = item.kfValue;tf.localEulerAngles = euler;break;}case "localEulerAnglesRaw.y":{var euler = tf.localEulerAngles;euler.y = item.kfValue;tf.localEulerAngles = euler;break;}case "localEulerAnglesRaw.z":{var euler = tf.localEulerAngles;euler.z = item.kfValue;tf.localEulerAngles = euler;break;}case "m_AnchoredPosition.x":{var anPos = rtf.anchoredPosition;anPos.x = item.kfValue;rtf.anchoredPosition = anPos;break;}case "m_AnchoredPosition.y":{var anPos = rtf.anchoredPosition;anPos.y = item.kfValue;rtf.anchoredPosition = anPos;break;}case "m_SizeDelta.x":{var size = rtf.sizeDelta;size.x = item.kfValue;rtf.sizeDelta = size;break;}case "m_SizeDelta.y":{var size = rtf.sizeDelta;size.y = item.kfValue;rtf.sizeDelta = size;break;}}EditorUtility.SetDirty(m_PrefabAsset);//PrefabUtility.SavePrefabAsset(prefabAsset);
        AssetDatabase.SaveAssets();Repaint();}//检查第1帧的值, 与预制体中GameObject当前的值是否相同/// <param name="animCurve">property关联的动画曲线</param>private void CheckFirstKeyFrame(EditorCurveBinding b, AnimationCurve animCurve, Transform tf, Keyframe kf0){float objValue = 0;switch (b.propertyName){case "m_LocalPosition.x":{objValue = tf.localPosition.x;break;}case "m_LocalPosition.y":{objValue = tf.localPosition.y;break;}case "m_LocalPosition.z":{objValue = tf.localPosition.z;break;}case "m_LocalScale.x":{objValue = tf.localScale.x;break;}case "m_LocalScale.y":{objValue = tf.localScale.y;break;}case "localEulerAnglesRaw.x":{objValue = tf.localEulerAngles.x;break;}case "localEulerAnglesRaw.y":{objValue = tf.localEulerAngles.y;break;}case "localEulerAnglesRaw.z":{objValue = tf.localEulerAngles.z;break;}case "m_AnchoredPosition.x":{var rtf = (RectTransform)tf;objValue = rtf.anchoredPosition.x;break;}case "m_AnchoredPosition.y":{var rtf = (RectTransform)tf;objValue = rtf.anchoredPosition.y;break;}case "m_SizeDelta.x":{var rtf = (RectTransform)tf;objValue = rtf.sizeDelta.x;break;}case "m_SizeDelta.y":{var rtf = (RectTransform)tf;objValue = rtf.sizeDelta.y;break;}default:return;}float delta = objValue - kf0.value;if (Mathf.Abs(delta) >= 0.001f){if (!m_ValueNotSameItemsDict.TryGetValue(b.path, out var list)){list = new List<ValueNotSameItem>();m_ValueNotSameItemsDict.Add(b.path, list);m_NodePathList.Add(b.path);}var cframe = new ValueNotSameItem();cframe.animCurve = animCurve;cframe.curveBinding = b;cframe.objValue = objValue;cframe.kfValue = kf0.value;list.Add(cframe);}}public static float TrimFloat(float f){int i = (int)(f * 1000);float result = i / 1000.0f;return result;}}

 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙HarmonyOS实战-Stage模型(开发卡片事件)

🚀一、开发卡片事件 HarmonyOS元服务卡片页面(Metaservice Card Page)是指在HarmonyOS系统中,用于展示元服务的页面界面。元服务是指一组提供特定功能或服务的组件,例如天气服务、音乐播放服务等。元服务卡片页面可以显示元服务的相关信息和操作选项,用户可以通过点击卡…

【论文阅读】FlexGraph: A Flexible and Efficient Distributed Framework for GNN Training

阅读思考问题: Please briefly describe how hierarchical dependency graphs are built in FlexGraph, and point out the specific stage in the NAU abstraction where this process takes place. 请简要描述在FlexGraph中如何构建分层依赖图,并指出在NAU抽象中的具体阶段…

N 年前,为了学习分库分表,我把 Cobar 源码抄了一遍

10 几年前,互联网产业蓬勃发展,相比传统 IT 企业,互联网应用每天会产生海量的数据。 如何存储和分析这些数据成为了当时技术圈的痛点,彼时,分库分表解决方案应运而生。 当时最流行的 Java 技术论坛是 javaeye ,有位淘宝的技术人员分享了一篇分库分表的文章 ,这篇文章,我…

4、Git之分支操作

4.1、分支的概述 在版本控制过程中,当需要同时推进多个任务时,可以为每个任务创建的单独分支。 虽然分支的底层实现是指针的引用,但是初学阶段可以将分支简单理解为副本,一个分支就是一个单独的副本。 使用分支,意味着从原来的主干上分离开,在分支上做的任何改动,在合并…

Spring 对 Junit4,Junit5 的支持上的运用

1. Spring 对 Junit4,Junit5 的支持上的运用 @目录1. Spring 对 Junit4,Junit5 的支持上的运用每博一文案2. Spring对Junit4 的支持3. Spring对Junit5的支持4. 总结:5. 最后:每博一文案 关于理想主义,在知乎上看到一句话:“他们并不是不懂别人口中的现实,他们只是不信,事…

Eclipase的JNnit导包报错

在使用Eclipase 创建项目时系统会自动帮我们生成一个module文件,JNnit单元测试时,记得删除自动生成的 module-info.java文件,不然会一直报错找不到 org

来玩 GitHub 啊,SSH 连接方式

Windows 11 git version 2.32.0.windows.2 GitHub 20240520 --今天找回了自己的 GitHub 账号密码,继续玩吧,再次加入 蓝星的开源软件基地。 使用邮箱注册的,找回密码也很方便。本文简要展示 按照官方文档的介绍 使用 SSH 连接 GitHub 的过程。 简述为:创建SSH密钥对 公钥注…

来玩 GitHub 啊,SSH 连接

今天找回了自己的 GitHub 账号密码,继续玩吧,再次加入 蓝星的开源软件基地。 使用邮箱注册的,找回密码也很方便。本文简要展示 按照官方文档的介绍 使用 SSH 连接 GitHub 的国产。主要文档 1、Connecting to GitHub with SSH https://docs.github.com/en/authentication/con…

ABC 354 (atcoder beginer 354) D、E、F

D检查: 1. 有可能是推导式有问题,比如-/+写错 2. x,y A、B、C、D 顺序可能搞反了 不要盲目调试,先用人眼看一下代码的情况,找一下错误很简单的找规律的题目。 很不能理解过的人,就这些。 x方向,y方向,都是4行/列,一个规律的循环。求(0,0)到(x,y)中的黑色块: 第0-3…

BUUCTF-WEB(21-25)

[HCTF 2018]admin 这道题目就是admin说明得管理员登录 那我直接创一个admin的账号但是显示已经存在了说明用户名就是admin,然后我们直接爆破,也是爆破出来密码就是123直接登录[MRCTF2020]你传你🐎呢 这个题就是文件上传,我们MIME绕过,后缀名都被ban了,我们上传.htaccess…

5.8-随堂练习1

1992年,姜万勐 看到 MPEG芯片 Epiphany – 用MPEG1 把图像和声音同时存储在一张小光盘上 VCD(Video Compact Disc)。 1993年9月 万燕第一批1000台VCD机下线。 几乎都被国内外家电公司买去解剖做样机 万燕VCD 100% 市场份额 1995年 各路仿制的VCD机大举进军市场,竞争激烈。 …

5.8-随堂练习-小组练习2

结合本小组项目写下能想到的所有 SWOT答案: 【第九组】 在充电桩电控系统项目中,可以想到以下所有的SMOT(Strengths, Weaknesses, Opportunities, Threats): Strengths (强项):功能丰富:系统具有多种功能,包括充电桩管理、用户充电、支付功能等。 易用性:界面设计简洁…