VRCTween
VRCTween 允许您使用强大的 DOTween 库在 VRChat 世界中创建平滑动画。您可以用几行代码动画化位置、旋转、缩放等。
概述
VRCTween 是一个内置的补间动画系统,随时间平滑插值(或"补 间")值。无需逐帧手动更新位置或旋转,VRCTween 为您处理数学计算和时序。
常见用例:
- 动画化 UI 元素,如按钮、面板和菜单。
- 创建平滑的对象移动。
- 为世界增添精致感。
VRCTween 使用 DOTween,一个流行的 Unity 补间库,并与 UdonSharp 和 Udon Graph 配合使用。
基本用法
创建补间
使用目标类型上的扩展方法或 VRCTween 上的静态方法创建补间。每个方法返回一个可用于控制和配置补间的 VRCTweenHandle:
using VRC.SDK3.Components;
public class MyScript : UdonSharpBehaviour
{
public GameObject cube;
void Start()
{
// Move the cube up over 2 seconds.
VRCTweenHandle tweenHandle = cube.TweenPosition(new Vector3(0, 5, 0), 2f, VRCTweenEase.OutQuad);
}
}
接收回调
要在补间完成时收到通知,在句柄上链式调用 .OnComplete():
public class MyScript : UdonSharpBehaviour
{
void Start()
{
gameObject.TweenScale(Vector3.one * 2f, 1f, VRCTweenEase.OutBounce)
.OnComplete(this, nameof(OnScaleComplete));
}
public void OnScaleComplete()
{
Debug.Log("Scale animation complete!");
}
}
补间类型
补间创建方法可作为目标类型上的扩展方法(例如 gameObject.TweenPosition(...))或 VRCTween 上的静态方法使用。所有方法都返回一个 VRCTweenHandle。
- Transform:TweenPosition、TweenLocalPosition、TweenRotation、TweenLocalRotation、TweenScale
- Path:TweenPath、TweenLocalPath
- UI:TweenColor、TweenFade(Graphic)、TweenFade(CanvasGroup)、TweenValue(Slider)、TweenAnchorPos、TweenSizeDelta
- Sprite:TweenColor、TweenFade(SpriteRenderer)
- Renderer:TweenColor、TweenFloat(Renderer)
- Light:TweenIntensity、TweenColor(Light)
- Audio:TweenVolume、TweenPitch(AudioSource)
有关详细代码示例,请参见内置补间类型。
虚拟补间
虚拟补间通过每帧写入 UdonBehaviour 上的变量来动画化任意值(float、int、Color、Vector3)。当没有内置补间类型满足您的需求时使用它们。详情和示例请参见虚拟补间。
控制和配置补间
使用 VRCTweenHandle 上的实例方法控制和配置补间。配置方法返回句柄以支持链式调用:
cube.TweenPosition(new Vector3(0, 5, 0), 2f, VRCTweenEase.OutQuad)
.From()
.SetDelay(0.5f)
.SetLoops(2, VRCTweenLoopType.Yoyo)
.OnComplete(this, nameof(OnDone));
有关完整的 API 参考和缓动类型表,请参见设置和控制。
示例
此示例演示了句柄存储、回调和清理:
using UdonSharp;
using UnityEngine;
using VRC.SDK3.Components;
public class TweenExample : UdonSharpBehaviour
{
public GameObject button;
private VRCTweenHandle scaleTween;
public override void Interact()
{
// Scale up and down when clicked.
scaleTween = button.TweenScale(Vector3.one * 1.2f, 0.15f, VRCTweenEase.OutQuad)
.OnComplete(this, nameof(OnTweenComplete));
}
public void OnTweenComplete()
{
// Scale back down.
button.TweenScale(Vector3.one, 0.15f, VRCTweenEase.OutQuad);
}
void OnDestroy()
{
// Clean up tweens when object is destroyed.
button.KillAllTweens();
}
}
最佳实践
存储补间句柄
如果需要稍后控制补间,请保存句柄:
private VRCTweenHandle myTween;
void Start()
{
myTween = gameObject.TweenPosition(Vector3.up, 2f, VRCTweenEase.OutQuad);
}
public void StopTween()
{
myTween.Kill();
}
清理补间
补间在完成时会自动清理。但是,仍在运行的补间(例如无限循环或长时补间)应在对象销毁时停止:
void OnDestroy()
{
gameObject.KillAllTweens();
}
链式补间与回调
通过使用回调和自定义事件名称创建序列:
void Start()
{
cube.TweenPosition(Vector3.up * 5, 1f, VRCTweenEase.OutQuad)
.OnComplete(this, nameof(OnFirstTweenComplete));
}
public void OnFirstTweenComplete()
{
cube.TweenRotation(new Vector3(0, 180, 0), 1f, VRCTweenEase.InOutQuad)
.OnComplete(this, nameof(OnSecondTweenComplete));
}
public void OnSecondTweenComplete()
{
Debug.Log("Sequence complete!");
}
使用 DelayedCall 创建可取消的计时器
VRCTween.DelayedCall 是 SendCustomEventDelayedSeconds 的可取消替代方案:
private VRCTweenHandle timerHandle;
void Start()
{
// Schedule a delayed callback.
timerHandle = VRCTween.DelayedCall(this, nameof(OnTimerFinished), 3.0f);
}
public void CancelTimer()
{
// Cancel the timer at any time.
timerHandle.Kill();
}
public void OnTimerFinished()
{
Debug.Log("3 seconds have passed!");
}
Udon Graph
VRCTween 可以在 Udon Graph 中使用。在节点搜索中查找 VRCTween 和 VRCTweenHandle 节点:
- 搜索"VRCTween TweenPosition"(或任何创建方法)来创建补间。
- 连接你的 GameObject 和参数。
- 节点会输出一个
VRCTweenHandle。 - 使用
VRCTweenHandle节点(Kill、Pause、SetDelay、OnComplete 等)来控制和配置它。
对于回调,使用 VRCTweenHandle OnComplete 节点:
- 连接补间句柄和你的 UdonBehaviour(通常是"this")。
- 提供一个自定义事件名称字符串。
- 在你的图表中实现相应的自定义事件。
限制
- 补间对每个玩家是本地的,不会自动跨网络同步。
- 对于需要网络同步的动画,考虑结合使用 Udon 的网络功能和补间。
- 非常短的补间持续时间(少于 0.01 秒)可能动画不平滑。
- 补间句柄在每个场景实例中是唯一的。
输入验证
VRCTween 会拒绝会破坏 DOTween 状态或 Unity Transform 的值。拒绝不会引发异常,以避免导致包含的 UdonBehavior 崩溃,因此一个"什么都没做"的补间通常意味着以下输入之一无效。
创建方法在以下情况下返回无效句柄(并跳过创建补间):
target为null。duration为负数、NaN 或 Infinity。零是允许的,用于创建可复用的补间。- 位置、缩放或路径航点包含 NaN、Infinity,或绝对值超过约 520,000 单位的组件。旋转补间仅需要有限值;允许较大的欧拉角。
- 路径的
resolution会被静默限 制在 1–50 范围内;超出该范围的值仍会创建补间,只是分辨率会被限制。
配置和控制方法在给定无效参数时静默无操作:
SetDuration忽略负数、NaN 或 Infinity 值。零是允许的。SetDelay忽略负数、NaN 或 Infinity 值。Goto忽略 NaN 或 Infinity,否则限制在补间的持续时间内。ChangeEndValue忽略在创建时未通过相同有限/幅度检查的 float/vector。
如果需要在补间是否实际启动时进行分支判断,请在创建后检查 handle.IsValid。
性能提示
一般提示
- 使用
KillAllTweens()一次性清理对象上的所有补间。 - 避免同时创建数百个补间。将它们分散到多个帧中以分摊负载(见下方示例)。
- 对于大多数世界(5 到 50 个通过交互触发的补间),创建新补间完全没问题。DOTween 内部池化补间对象,因此开销很小。
错开大量补间
如果你需要为大量对象(如一网格的瓦片)制作动画,请避免在同一帧中创建所有补间。相反,将它们随时间分散,每个之间使用小延迟:
[SerializeField] private GameObject[] tiles;
[SerializeField] private float delayBetween = 0.05f;
private int _nextIndex;
public void AnimateAll()
{
_nextIndex = 0;
_AnimateNext();
}
public void _AnimateNext()
{
if (_nextIndex >= tiles.Length) return;
tiles[_nextIndex].TweenScale(Vector3.one, 0.3f, VRCTweenEase.OutBack);
_nextIndex++;
SendCustomEventDelayedSeconds(nameof(_AnimateNext), delayBetween);
}
这样每次调用创建一个补间,将工作分散到多个帧中。视觉效果是错开的级联动画,而非所有内容同时变化——这反而可能看起来更有趣!
在热路径中复用补间
如果你有频繁重定向的补间,例如跟随移动目标的 UI 元素,或响应每帧玩家输入的对象,使用 ChangeEndValue、SetDuration 和 SetEase 复用句柄可以避免每帧分配。在 300 帧内更新 500 个补间的内部基准测试中,复用模式分配的内存比销毁重建模式少 46 倍,运行速度快 10 倍。
这种优化在补间以高频率创建和销毁时很重要。对于一次性动画(门、按钮、缩放弹跳),简单的销毁重建代码更易读且工作良好。
VRCTweenHandle _moveHandle;
void Start()
{
// Create once with infinite loops so it stays alive after completing.
_moveHandle = gameObject.TweenPosition(Vector3.zero, 1f, VRCTweenEase.OutQuad)
.SetLoops(-1, VRCTweenLoopType.Restart)
.Pause();
}
public void MoveTo(Vector3 target, float duration)
{
// Reconfigure and restart without allocating.
_moveHandle.ChangeEndValue(target, true)
.SetDuration(duration)
.SetEase(VRCTweenEase.OutCubic);
_moveHandle.Restart();
}
更多详情请参见设置与控制中的补间复用。
资源
- DOTween 文档 - 了解底层库。
- 缓动可视化工具 - 查看不同缓动类型的效果。