使用 Unity 开发 Android VR 应用系列之二——使用 Animator 实现鼠标悬浮 UI 缩放动画效果

0. 概览

展示效果如下

完整的代码和示例在这里

实现上图所示的动画效果,涉及两个 Unity 知识点:

  • Animator 组件
  • 动态调整 UI 渲染顺序

1. 使用 Animator 实现缩放动画

Animator 是一套灵活度跟复杂度同样高的动画系统。
简单来讲,其运作原理是:定义若干 Animation Clip,将其在 Animator Controller 中通过 Transition 拼装成一个状态机;创建若干 Parameters,用户通过为其赋不同值控制状态转移。

套用到本例来说,就是:

  1. 创建两个 Animation Clip,一个用于放大,一个用于缩小
  2. 创建一个 Animator Controller,在其中创建两个状态,分别对应上边两个 Animation Clip
  3. 创建一个 bool 参数 hovering,射线进入时为 true,射线移开时为 false
  4. 创建两个 Transition,分别使 hovering = true 时执行放大动画,hovering = false 时执行缩小动画

最终,Animator Controller 中呈现如下的状态图:

2. 使用 Canvas 组件实现动态调整渲染顺序

默认的 UI 渲染顺序是按其在场景中出现的先后顺序定义的。带来的一个问题是:当对靠前的 UI 做放大动画时,有可能和后面的 UI 重叠,导致当前选中 UI 被遮挡。

可以通过为 UI 添加 Canvas 组件解决此问题。

注意图中的 Graphic Raycaster 组件,需要同 Canvas 成对出现,否则 UI 不能正常处理射线事件。

内嵌的 Canvas 具有上图所示的属性。勾选 Override Sorting 后,通过修改 Order in Layer 项值即可自定义当前 UI 的渲染顺序,值越大,渲染顺序越靠后。

脚本代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
// 添加 Canvas 和 GraphicRaycaster 组件
if (topWhenHover) // 仅在需要时动态添加相应组件
{
if (GetComponent<Canvas>() == null)
{
gameObject.AddComponent<Canvas>();
}
if (GetComponent<GraphicRaycaster>() == null)
{
gameObject.AddComponent<GraphicRaycaster>();
}
}
1
2
3
4
5
6
7
8
9
10
11
// hover 状态变化时
private void OnHoverChanged(bool hover)
{
if (topWhenHover)
{
var canvas = GetComponent<Canvas>();
canvas.overrideSorting = hover;
canvas.sortingOrder = sortingOrder;
}
...
}

3. 把所有功能封装到一个自定义组件中

可以实现一个高度封装的组件:用户只需要添加此组件,即可自动监听射线进出事件并执行动画。

1. 使组件能接收射线进出事件

1
2
3
4
5
6
7
8
9
10
11
12
13
// 实现对应接口
public class HoverScaler : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
public void OnPointerEnter(PointerEventData eventData)
{
OnHoverChanged(true);
}

public void OnPointerExit(PointerEventData eventData)
{
OnHoverChanged(false);
}
}

2. 添加 Animator 组件到 UI

1
2
3
4
5
6
void Start()
{
animator = gameObject.AddComponent<Animator>();
var controller = Resources.Load<RuntimeAnimatorController>("Animations/ScaleWhenHover");
animator.runtimeAnimatorController = Instantiate(controller);
}

完整的组件实现在这里

4. Issues

  • 修改 Canvas 的 sortingOrder 属性,会使其父 UI 的 mask 功能失效(demo 有演示)

评论