#3DモデルのScaleFactor変更 → アニメーションがおかしくなる問題
Unityに3Dモデルを読み込んだ後、Unity上でサイズを1/2にしました。
ScaleFactorを 1.0 → 0.5 に変更。メッシュは問題なさそう。
ただ、よーく見るとアニメーションがおかしい…。
左のローポリモデル がScaleFactor変更モデル。右が変更なしモデル。
同じアニメーションのはずが、ローポリの方のステップがキレキレ過ぎる。
実は、アニメーションにはScaleFactorが適用されない仕様でした。
そのため、AnimationClipのPosition値が相対的に大きくなることに。ちなみに、Rotation値はスケールの影響を受けないので元の動きのまま。
#AnimationClipをEditorスクリプトで変更する
対処としては、AnimationClipをスケール変換するツールを自作しました。
ソースは記事の下にあります。
指定されたAnimationClipを読み込み、全てのキーフレームの値にScaleFactorをかけています。
今回はScaleFactorの影響を受けるPositionのみに適用しています。
最終的に、「ファイル名_scaled.anim」というAnimationClipを生成します。
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace ScalerAnimation
{
public class ScalerAnimationEditorWindow : EditorWindow
{
private AnimationClip m_animationClip;
private float m_scaleFactor = 1.0f;
[MenuItem("Tools/ScalerAnimation")]
static void Window()
{
var window = GetWindow<ScalerAnimationEditorWindow>("ScalerAnimation");
window.Show();
}
private void Update()
{
}
private void OnGUI()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("AnimationClip");
m_animationClip = (AnimationClip)EditorGUILayout.ObjectField(m_animationClip, typeof(AnimationClip), true);
m_scaleFactor = EditorGUILayout.FloatField("Scale Factor", m_scaleFactor);
if (m_animationClip != null)
{
if (GUILayout.Button("Apply"))
{
ScalerAnimation(m_animationClip);
}
}
}
private void ScalerAnimation(AnimationClip animationClip)
{
// Copy Clip
var copyAnimationClip = CopyAnimationClip(animationClip);
// Get Properties
var animationProperties = GetAnimationProperties(copyAnimationClip);
// Apply Scale Factor
ApplyScalerAnimation(copyAnimationClip, animationProperties);
}
private AnimationClip CopyAnimationClip(AnimationClip animationClip)
{
var copyAnimationClip = Instantiate(animationClip) as AnimationClip;
var path = AssetDatabase.GetAssetPath(animationClip);
var directoryName = System.IO.Path.GetDirectoryName(path);
AssetDatabase.CreateAsset(copyAnimationClip, $"{directoryName}/{animationClip.name}_scaled.anim");
return copyAnimationClip;
}
private void ApplyScalerAnimation(AnimationClip animationClip, Dictionary<string, AnimationProperty> animationProperties)
{
var bindings = AnimationUtility.GetCurveBindings(animationClip);
foreach (var binding in bindings)
{
var curve = AnimationUtility.GetEditorCurve(animationClip, binding);
var boneName = System.IO.Path.GetFileName(binding.path);
var animationProperty = animationProperties[boneName];
switch (binding.propertyName)
{
case "m_LocalPosition.x":
curve.keys = animationProperty.GetScalerLocalPositionX(m_scaleFactor);
AnimationUtility.SetEditorCurve(animationClip, binding, curve);
break;
case "m_LocalPosition.y":
curve.keys = animationProperty.GetScalerLocalPositionY(m_scaleFactor);
AnimationUtility.SetEditorCurve(animationClip, binding, curve);
break;
case "m_LocalPosition.z":
curve.keys = animationProperty.GetScalerLocalPositionZ(m_scaleFactor);
AnimationUtility.SetEditorCurve(animationClip, binding, curve);
break;
}
}
}
private Dictionary<string, AnimationProperty> GetAnimationProperties(AnimationClip animationClip)
{
var animationProperties = new Dictionary<string, AnimationProperty>();
var bindings = AnimationUtility.GetCurveBindings(animationClip);
foreach (var binding in bindings)
{
var curve = AnimationUtility.GetEditorCurve(animationClip, binding);
var boneName = System.IO.Path.GetFileName(binding.path);
if (!animationProperties.ContainsKey(boneName))
{
animationProperties.Add(boneName, new AnimationProperty());
}
switch (binding.propertyName)
{
case "m_LocalPosition.x":
animationProperties[boneName].localPositionX.AddRange(curve.keys);
break;
case "m_LocalPosition.y":
animationProperties[boneName].localPositionY.AddRange(curve.keys);
break;
case "m_LocalPosition.z":
animationProperties[boneName].localPositionZ.AddRange(curve.keys);
break;
}
}
return animationProperties;
}
}
}
###使い方
Tools > ScalerAnimationから実行できます。
###AnimationClip スケール変換ツール
githubに置いています。
#参考
こちらのミラー変換ツールを参考にしました。
https://github.com/hisakataIsm/MirrorAnimation