c# - 如何在不破坏动画的情况下重命名生物模型的层次结构?

标签 c# unity-game-engine

就技能而言,我是 Unity 的初学者,所以如果可以的话,请像在和 child 说话一样解释!

问题

我想在这里更改这些名称:

enter image description here

我想重命名它们有两个原因:

  1. 这样它们更容易理解

  2. 因为我使用商店中的许多不同 Assets ,每个 Assets 都有不同的层次结构和不同的名称,我想标准化名称,以便我可以使用下面的代码来确定生物 body 的哪一部分被射击这样它就适用于所有生物

     public    void            CreatureHit(string bodyPart, GunInfo usedWeapon, float intensity)                    // for guns
     {
    
         usedWeapon.PlayHit(creatureSounds);
         if (creatureInfo.healthPoints > 0)                                                                        // to prevent dead creatures from being shot
         {
             if ("Torso" == bodyPart || "LeftUpperArm" == bodyPart                                                // if the part that was hit was the arms or torso
                     || "RightUpperArm" == bodyPart || "LeftLowerArm" == bodyPart                                // if the part that was hit was the arms or torso
                     || "RightLowerArm" == bodyPart)
             {
                 creatureInfo.healthPoints -= usedWeapon.damage * intensity;                                     // deal standard dmg
    
                 if (creatureInfo.healthPoints <= 0)
                     creatureInfo.deathType = CreatureInfo.BODYSHOT;
             }
             else if ("Head" == bodyPart)                                                                        // if the part that was hit was the head
             {
                 creatureInfo.healthPoints -= usedWeapon.damage * 10 * intensity;                                // deal 10x dmg
                 audioSource.PlayOneShot(creatureSounds.hitHead, 1);
    
    
                 if (creatureInfo.healthPoints <= 0)
                     creatureInfo.deathType = CreatureInfo.HEADSHOT;
             }
             else if ("RightUpperLeg" == bodyPart || "LeftUpperLeg" == bodyPart
                 || "RightLowerLeg" == bodyPart || "LeftLowerLeg" == bodyPart)
             {
                 creatureInfo.healthPoints -= usedWeapon.damage / 2 * intensity;                                    // deal half dmg
                 if (creatureInfo.healthPoints <= 0)
                     creatureInfo.deathType = CreatureInfo.BODYSHOT;
             }
         }
     }   
    

我尝试过的

我在层次结构中重命名了它们,但随后动画停止工作。我在 Unity 论坛上发现了一个旧帖子,询问这是否可以在 2015 年实现,OP 被告知不可能。后来有一些技术回复,我感到不知所措,所以我想我应该创建自己的线程。

注意:有数十个角色,每个角色都有 10 多个动画,因此理想情况下我需要一个非常有效的解决方案。

最佳答案

不幸的是,一般来说你仍然不能。 (至少不是那么简单,见下文)。

AnimationClip 基于字符串,存储从 Animator 到相应 GameObject相对路径相应组件的类型,最后是动画序列化字段和属性的名称

如果其中任何一个发生变化,例如因为您重命名了对象或更改了层次结构,通常连接会丢失并且动画会中断。

您可以实现一个编辑器脚本方法

  • 遍历对象受影响的 Animator (GetComponentInParent)
  • 迭代所有使用的 AnimationClip
  • 迭代每个剪辑属性绑定(bind)
  • 根据您的重命名重定向属性路径

这可能看起来有点像这样

private static void RenameObject(GameObject gameObject, Animator animator, string newName)
{
    if (!gameObject)
    {
        throw new ArgumentException("No object provided", nameof(gameObject));
    }

    if (string.IsNullOrWhiteSpace(newName))
    {
        throw new ArgumentException("Object name may not be empty!", nameof(newName));
    }

    if (!animator)
    {
        throw new ArgumentException($"Selected object {gameObject} is not a child of an {nameof(Animator)}!", nameof(gameObject));
    }

    if (gameObject.transform == animator.transform)
    {
        return;
    }

    // get the relative path from the animator root to this object's parent
    var path = AnimationUtility.CalculateTransformPath(gameObject.transform.parent, animator.transform);

    if (gameObject.transform.parent != animator.transform)
    {
        path += "/";
    }

    // then append the old and new names
    var oldPath = path + gameObject.name;
    var newPath = path + newName;

    // get the runtime Animation controller
    var controller = animator.runtimeAnimatorController;

    // get all clips used by this controller
    var clips = controller.animationClips;

    var changeableObjects = new List<Object>(clips.Length + 1) { gameObject };
    changeableObjects.AddRange(clips);

    Undo.RecordObjects(changeableObjects.ToArray(), "Change animated object name");

    // Go through all clips
    foreach (var clip in clips)
    {
        var floatBindingInfo = new List<AnimationFloatBindingInfo>();

        // Get and store all FLOAT keyframe bindings
        foreach (var binding in AnimationUtility.GetCurveBindings(clip))
        {
            var curve = AnimationUtility.GetEditorCurve(clip, binding);
            var curveInfo = new AnimationFloatBindingInfo(binding, curve);

            ReplaceBindingPath(curveInfo, oldPath, newPath);

            floatBindingInfo.Add(curveInfo);
        }

        var objectBindingInfos = new List<AnimationObjectBindingInfo>();

        // also do the same for all reference keyframe bindings 
        foreach (var binding in AnimationUtility.GetObjectReferenceCurveBindings(clip))
        {
            var curve = AnimationUtility.GetObjectReferenceCurve(clip, binding);
            var curveInfo = new AnimationObjectBindingInfo(binding, curve);

            ReplaceBindingPath(curveInfo, oldPath, newPath);

            objectBindingInfos.Add(curveInfo);
        }

        // a little check to avoid unnecessary work -> are there any affected property curves at all?
        if (floatBindingInfo.Count + objectBindingInfos.Count > 0)
        {
            // Now erase all curves 
            clip.ClearCurves();

            // and assign back the stored ones
            AnimationUtility.SetEditorCurves(clip, floatBindingInfo.Select(info => info.Binding).ToArray(), floatBindingInfo.Select(info => info.Curve).ToArray());
            AnimationUtility.SetObjectReferenceCurves(clip, objectBindingInfos.Select(info => info.Binding).ToArray(), objectBindingInfos.Select(info => info.Curve).ToArray());

            EditorUtility.SetDirty(clip);
        }
    }

    // finally rename the object
    gameObject.name = newName;

    EditorUtility.SetDirty(gameObject);
}

由于这个用例非常常见,我花了一些时间为此实现一个EditorWindow。它仍然有点原始,但可以工作并支持撤消重做;)你可以找到它 here

-> 在层次结构中选择对象 -> 右键单击​​ -> “为 Animator 重命名安全”

您当然可以添加一些快捷方式等,这取决于您;)

这里是一个正在运行的对话框的小演示,重命名一些嵌套对象并执行一些撤消/重做

enter image description here


但是,在您的用例中,另一种方法是简单地让您的代码使用名称,因为它们可能会使用 tags相反。

据我所知,您的代码基于三种不同的情况,因此您可以简单地为每种情况添加一个标签,例如HeadArmsLegs 并相应地分配和检查它们 (GameObject.CompareTag),并且不要触摸名称和根本没有动画。

关于c# - 如何在不破坏动画的情况下重命名生物模型的层次结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71686007/

相关文章:

c# - 使用 Lambda 表达式的高级搜索

android - 我的 Android 版 Unity 游戏无法在 Bluestacks 中运行

c# - Unity - 不同的手机尺寸

android - 缺少 Unity Google Android 项目选项

c# - 是否可以使用 Acrobat.dll 在 c# 应用程序中打开 PDF?

c# - 将 TcpClient ReceiveTimeout 与同步程序一起使用

c# - 本地化 ASP.NET MVC 2 中的错误消息(默认验证属性)

具有特殊字符的 C# 变量名

ssl - Unity WebGL 中的 Photon 安全 WebSocket 连接

unity-game-engine - 一个 Unity 实例,两个 Unity 窗口?