就技能而言,我是 Unity 的初学者,所以如果可以的话,请像在和 child 说话一样解释!
问题
我想在这里更改这些名称:
我想重命名它们有两个原因:
这样它们更容易理解
因为我使用商店中的许多不同 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 重命名安全”
您当然可以添加一些快捷方式等,这取决于您;)
这里是一个正在运行的对话框的小演示,重命名一些嵌套对象并执行一些撤消/重做
但是,在您的用例中,另一种方法是简单地让您的代码使用名称,因为它们可能会使用 tags相反。
据我所知,您的代码基于三种不同的情况,因此您可以简单地为每种情况添加一个标签,例如Head
、Arms
、Legs
并相应地分配和检查它们 (GameObject.CompareTag
),并且不要触摸名称和根本没有动画。
关于c# - 如何在不破坏动画的情况下重命名生物模型的层次结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71686007/