本文将讨论面板属性Inspector的动画,也就是BeginFadeGroup与EndFadeGroup,嵌套使用问题的形成和解决。以及如何应用Tween Ease的各种效果到Inspector动画控制上。
FadeGroup动画嵌套
当一对[BeginFadeGroup, EndFadeGroup]嵌套在,另一对[BeginFadeGroup, EndFadeGroup]之中的时候,会产生动画匹配错误,也就是BeginFadeGroup对应了错误的EndFadeGroup。如下图所示:
可以看到,OuterData的动画直接匹配到了,Data1的动画上了。也就是,OuterData折叠到Data1的位置,动画就结束了。代码如下:
public class FadeGroupTest : MonoBehaviour
{
// 外层对象包含三个内层对象
public OuterData outerData;
[System.Serializable]
public class OuterData
{
public InnerData data1;
public InnerData data2;
public InnerData data3;
}
[System.Serializable]
public class InnerData
{
public int a;
public int b;
public int c;
}
}
[CustomEditor(typeof(FadeGroupTest))]
public class FadeGroupTestEditor : Editor
{
private SerializedProperty outer;
private AnimBool outerAnim;
private AnimBool innerAnim;
private SerializedProperty inner1;
private SerializedProperty inner2;
private SerializedProperty inner3;
private void OnEnable()
{
this.outer = this.serializedObject.FindProperty("outerData");
// 外层动画
this.outerAnim = new AnimBool(true);
this.outerAnim.valueChanged.AddListener(this.Repaint);
// 内层动画
this.innerAnim = new AnimBool(true);
this.innerAnim.valueChanged.AddListener(this.Repaint);
this.inner1 = this.outer.FindPropertyRelative("data1");
this.inner2 = this.outer.FindPropertyRelative("data2");
this.inner3 = this.outer.FindPropertyRelative("data3");
}
private void OnDisable()
{
this.outerAnim.valueChanged.RemoveListener(this.Repaint);
this.innerAnim.valueChanged.RemoveListener(this.Repaint);
}
public override void OnInspectorGUI()
{
this.serializedObject.Update();
this.outerAnim.target = EditorGUILayout.PropertyField(this.outer);
// 外层动画
if (EditorGUILayout.BeginFadeGroup(this.outerAnim.faded))
{
EditorGUI.indentLevel++;
this.innerAnim.target = EditorGUILayout.PropertyField(this.inner1);
// 内层动画
if (EditorGUILayout.BeginFadeGroup(this.innerAnim.faded))
{
EditorGUILayout.BeginVertical(GUI.skin.box);
EditorGUILayout.PropertyField(this.inner1.FindPropertyRelative("a"));
EditorGUILayout.PropertyField(this.inner1.FindPropertyRelative("b"));
EditorGUILayout.PropertyField(this.inner1.FindPropertyRelative("c"));
EditorGUILayout.EndVertical();
}
// 内层动画结束
// 外层动画开始,会匹配到这个结束,产生错误
EditorGUILayout.EndFadeGroup();
EditorGUILayout.PropertyField(this.inner2, true);
EditorGUILayout.PropertyField(this.inner3, true);
EditorGUI.indentLevel--;
}
// 外层动画结束
EditorGUILayout.EndFadeGroup();
this.serializedObject.ApplyModifiedProperties();
}
}
解决嵌套错误的办法有两种
第一种,使用动画条件判断
// 当外层动画不在运行,或是内层动画运行时,这时候才关闭EndFadeGroup,否则跳过关闭函数。
// 这样就可以避免BeginFadeGroup匹配错误了
if (this.outerAnim.isAnimating == false || this.innerAnim.isAnimating)
{
EditorGUILayout.EndFadeGroup();
}
第二种,使用垂直布局
// 垂直布局整体套在动画外面,就可以防止动画匹配的错乱问题
EditorGUILayout.BeginVertical(GUI.skin.box);
this.innerAnim.target = EditorGUILayout.PropertyField(this.inner1);
if (EditorGUILayout.BeginFadeGroup(this.innerAnim.faded))
{
EditorGUILayout.PropertyField(this.inner1.FindPropertyRelative("a"));
EditorGUILayout.PropertyField(this.inner1.FindPropertyRelative("b"));
EditorGUILayout.PropertyField(this.inner1.FindPropertyRelative("c"));
}
EditorGUILayout.EndFadeGroup();
// 垂直布局结束
EditorGUILayout.EndVertical();
使用Tween Ease来驱动FadeGroup动画
AnimBool本身是一个Tween的实现,但只有Mathf.Lerp一种缓动算法,而BeginFadeGroup(float)需要的只是一个当前动画位置的值,我们可以自己实现各种Tween Ease缓动算法,来实现各种效果。比如下面这个BounceOut:
实现思路有两种:
- 手动实现编辑器的Tween和Ease,并使用它来生成BeginFadeGroup的value数值。
- 手动扩展AnimBool的实现,使其具有切换TweenEase算法的功能。
「动画是体验」