关于 tween-animation
面向 Godot 4.3+ 开发者的 Tween 动画技能,提供 GDScript 和 C# 示例,涵盖属性动画、序列编排、缓动与生命周期管理。
主要功能
- 01属性、方法与回调动画
- 02串行和并行动画编排
- 03过渡与缓动设置
- 04循环、信号和生命周期管理
- 05常见动画配方与故障排查
适用场景
- 01制作 UI 淡入淡出和面板滑动
- 02编排物体移动、旋转与缩放
- 03制作按钮弹跳或屏幕震动
- 04排查动画冲突与暂停行为
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jame581/ godotprompter --skill tween-animation安装后在 Claude Code 中输入/tween-animation确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/common-recipes.mdlifecycle.mdlooping-and-signals.mdproperty-tweener-modifiers.md详细介绍
Godot 4.3+ 中的 Tween
所有示例均适用于 Godot 4.3+,且不使用任何已弃用的 API。先展示 GDScript,再展示 C#。
相关技能: animation-system 用于 AnimationPlayer/AnimationTree(基于关键帧),godot-ui 用于 UI 过渡,shader-basics 用于着色器参数补间,camera-system 用于摄像机抖动和过渡,math-essentials 用于缓动曲线和插值数学,particles-vfx 用于代码驱动的 VFX 时序和编排。
1. 核心概念
Tween 与 AnimationPlayer
| 特性 | Tween(代码驱动) | AnimationPlayer(数据驱动) |
|---|---|---|
| 设置 | 仅需代码 — 无需编辑器 | 使用带关键帧的动画面板 |
| 适用场景 | 程序化运动、UI 过渡、VFX | 复杂的多轨道、由美术控制的动画片段 |
| 可复用性 | 每次使用时重新创建 — 轻量 | 保存为资源,可在多个场景中复用 |
| 混合 | 否 — 每次只能为一个属性创建一个 Tween | 是 — AnimationTree 支持混合 |
| 方法调用 | 可在任意位置使用 tween_callback() | 在关键帧时间点调用方法轨道 |
经验法则: 对于一次性的程序化动画(淡入、弹跳、滑动),使用 Tween。对于循环播放、经美术调整的动画(行走循环、攻击序列),使用 AnimationPlayer。
创建 Tween
可以从任何 Node 创建 Tween,它会自动绑定到该节点。释放节点时,Tween 会自动停止。
# Creates a tween bound to this node
var tween := create_tween()
tween.tween_property(self, "position", Vector2(400, 300), 1.0)
var tween = CreateTween();
tween.TweenProperty(this, "position", new Vector2(400, 300), 1.0f);
重要: 每次调用
create_tween()都会创建一个新的 Tween。同一属性上的先前 Tween 不会自动终止,它们会相互竞争。如果不希望发生冲突,请在同一属性上创建新的 Tween 前先终止旧的 Tween。
2. Tweener 类型
tween_property() — 为任意属性添加动画
var tween := create_tween()
# Animate position over 0.5 seconds
tween.tween_property($Sprite2D, "position", Vector2(200, 100), 0.5)
# Animate modulate alpha (fade out)
tween.tween_property($Sprite2D, "modulate:a", 0.0, 0.3)
var tween = CreateTween();
tween.TweenProperty(GetNode("Sprite2D"), "position", new Vector2(200, 100), 0.5);
tween.TweenProperty(GetNode("Sprite2D"), "modulate:a", 0.0f, 0.3);
访问子属性: 使用 : 定位单个分量 — "position:x"、"modulate:a"、"scale:y"。
tween_callback() — 在序列中的某个时间点调用方法
var tween := create_tween()
tween.tween_property(self, "position", Vector2.ZERO, 0.5)
tween.tween_callback(func(): print("Arrived!"))
tween.tween_callback(queue_free)
var tween = CreateTween();
tween.TweenProperty(this, "position", Vector2.Zero, 0.5f);
tween.TweenCallback(Callable.From(() => GD.Print("Arrived!")));
tween.TweenCallback(Callable.From(QueueFree));
tween_interval() — 在步骤之间等待/延迟
var tween := create_tween()
tween.tween_property(self, "modulate:a", 0.0, 0.3) # fade out
tween.tween_interval(1.0) # wait 1 second
tween.tween_property(self, "modulate:a", 1.0, 0.3) # fade back in
var tween = CreateTween();
tween.TweenProperty(this, "modulate:a", 0.0f, 0.3);
tween.TweenInterval(1.0f);
tween.TweenProperty(this, "modulate:a", 1.0f, 0.3);
tween_method() — 使用插值后的值为自定义方法添加动画
# Animate a method that receives interpolated float values
func _ready() -> void:
var tween := create_tween()
tween.tween_method(_set_health_bar, 100.0, 0.0, 2.0)
func _set_health_bar(value: float) -> void:
$HealthBar.value = value
$HealthLabel.text = "%d%%" % int(value)
public override void _Ready()
{
var tween = CreateTween();
tween.TweenMethod(Callable.From<float>(SetHealthBar), 100.0f, 0.0f, 2.0f);
}
private void SetHealthBar(float value)
{
GetNode<ProgressBar>("HealthBar").Value = value;
GetNode<Label>("HealthLabel").Text = $"{(int)value}%";
}
3. 编排 — 链式与并行
顺序执行(默认)
默认情况下,tweener 会按顺序运行 — 每个 tweener 都会等待前一个完成。
var tween := create_tween()
tween.tween_property(self, "position:x", 300.0, 0.5) # Step 1
tween.tween_property(self, "position:y", 200.0, 0.5) # Step 2 (after Step 1)
tween.tween_property(self, "rotation", PI, 0.3) # Step 3 (after Step 2)
并行 — 同时运行 tweener
选项 1:set_parallel(true) — 同时运行所有 tweener
var tween := create_tween().set_parallel(true)
tween.tween_property(self, "position", Vector2(300, 200), 0.5)
tween.tween_property(self, "rotation", PI, 0.5)
tween.tween_property(self, "modulate:a", 0.5, 0.5)
var tween = CreateTween().SetParallel(true);
tween.TweenProperty(this, "position", new Vector2(300, 200), 0.5f);
tween.TweenProperty(this, "rotation", Mathf.Pi, 0.5f);
tween.TweenProperty(this, "modulate:a", 0.5f, 0.5f);
选项 2:chain() — 在 Tween 运行期间切回顺序执行
var tween := create_tween().set_parallel(true)
# These two run at the same time
tween.tween_property(self, "position", Vector2(300, 200), 0.5)
tween.tween_property(self, "scale", Vector2(2, 2), 0.5)
# Switch back to sequential for the next step
tween.chain().tween_property(self, "modulate:a", 0.0, 0.3)
tween.tween_callback(queue_free)
var tween = CreateTween().SetParallel(true);
tween.TweenProperty(this, "position", new Vector2(300, 200), 0.5f);
tween.TweenProperty(this, "scale", new Vector2(2, 2), 0.5f);
tween.Chain().TweenProperty(this, "modulate:a", 0.0f, 0.3f);
tween.TweenCallback(Callable.From(QueueFree));
选项 3:parallel() — 让下一个 tweener 与前一个并行运行
var tween := create_tween()
tween.tween_property(self, "position", Vector2(300, 200), 0.5)
# This runs at the same time as position, not after
tween.parallel().tween_property(self, "rotation", PI, 0.5)
# This runs after both finish (back to sequential)
tween.tween_callback(func(): print("done"))
4. 缓动与过渡
设置缓动
var tween := create_tween()
# Set defaults for the entire tween
tween.set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT)
tween.tween_property(self, "position", Vector2(400, 300), 0.6)
var tween = CreateTween();
tween.SetTrans(Tween.TransitionType.Cubic).SetEase(Tween.EaseType.Out);
tween.TweenProperty(this, "position", new Vector2(400, 300), 0.6f);
单个 Tweener 覆盖设置
var tween := create_tween()
# This tweener uses bounce, overriding the tween default
tween.tween_property(self, "position:y", 0.0, 0.5) \
.set_trans(Tween.TRANS_BOUNCE).set_ease(Tween.EASE_OUT)
var tween = CreateTween();
tween.TweenProperty(this, "position:y", 0.0f, 0.5f)
.SetTrans(Tween.TransitionType.Bounce)
.SetEase(Tween.EaseType.Out);
过渡类型
| 过渡 | 特征 | 常见用途 |
|---|---|---|
TRANS_LINEAR | 匀速 | 进度条、计时器 |
TRANS_SINE | 平缓加速 | 细腻的 UI 动画 |
TRANS_QUAD | 中等曲线 | 通用移动 |
TRANS_CUBIC | 平滑曲线 | 大多数 UI 过渡 |
TRANS_QUART | 强烈曲线 | 戏剧性的入场效果 |
TRANS_QUINT | 非常强烈的曲线 | 强调效果 |
TRANS_EXPO | 指数曲线——急促地开始或停止 | 吸附到位置 |
TRANS_CIRC | 圆形曲线——平滑减速 | 自然运动 |
TRANS_BACK | 略微越过目标 | 有弹性的 UI 按钮 |
TRANS_ELASTIC | 弹簧振荡 | 活泼的卡通效果 |
TRANS_BOUNCE | 结束时弹跳 | 物体下落、着陆 |
缓动类型
| 缓动 | 行为 |
|---|---|
EASE_IN | 开始慢,结束快 |
EASE_OUT | 开始快,结束慢(最自然) |
EASE_IN_OUT | 开始和结束都慢 |
EASE_OUT_IN | 开始和结束都快(很少使用) |
最常见的组合:
TRANS_CUBIC+EASE_OUT,适用于感觉自然的 UI 过渡。TRANS_BACK+EASE_OUT,适用于活泼的弹入效果。
5. PropertyTweener 修饰符
每次 tween_property() 调用都会返回一个 PropertyTweener,你可以为其链式调用修饰符:.from(value) 用于指定自定义起始值,.from_current() 用于捕获当前值,.as_relative() 用于相加(而非替换),.set_delay(seconds) 用于延迟此 tweener 的开始时间。
每个修饰符的完整代码示例,请参阅 references/property-tweener-modifiers.md。
6. 循环和信号
tween.set_loops(N) 会重复 N 次(0 = 无限次)。信号:finished(整个 tween 已完成)、step_finished(idx)(一个步骤已完成)、loop_finished(loop_count)(一个完整周期已完成;最后一次循环后不会发出该信号,而是触发 finished)。
有关循环次数语义和信号连接,请参阅 references/looping-and-signals.md。
7. Tween 生命周期
Tween 由其所属的 SceneTree 管理。开始新的 tween 前,使用 tween.kill() 终止正在运行的 tween,以避免叠加。使用 set_pause_mode、set_speed_scale 和(Godot 4.4+)set_ignore_time_scale 进行运行时控制。
有关终止/替换模式、暂停模式、速度缩放和忽略时间缩放,请参阅 references/lifecycle.md。
has_tweeners()(Godot 4.7+)
Tween.has_tweeners()(const)会在 tween 有效且已向其中添加任何 Tweener 时返回 true——当动态追加 tweener,且 tween 最终可能为空时,这一方法很有用。在空 tween 开始前将其终止,可以避免错误。
var tween := create_tween()
_add_intro_steps(tween) # may append zero tweeners
if not tween.has_tweeners():
tween.kill()
var tween = CreateTween();
AddIntroSteps(tween); // may append zero tweeners
if (!tween.HasTweeners())
tween.Kill();
8. 常见示例
大多数项目都需要的模式:淡入/淡出、UI 面板滑入/滑出、按钮按下弹跳、伤害数字弹出、脉冲/呼吸效果、屏幕抖动、着色器参数动画。
每种示例的可直接使用代码,请参阅 references/common-recipes.md。
9. 常见问题
| 症状 | 原因 | 修复方法 |
|---|---|---|
| Tween 跳变或抖动 | 多个 Tween 同时操作同一属性 | 创建新 Tween 前先终止旧 Tween |
| Tween 没有任何效果 | Tween 完成前节点已被释放 | 检查 is_valid(),或用 tween_callback 替代 await |
| Tween 在暂停期间运行或停止 | 默认的 TWEEN_PAUSE_BOUND 会遵循绑定节点的 process_mode | 使用 TWEEN_PAUSE_STOP 始终暂停,或使用 TWEEN_PAUSE_PROCESS 保持运行 |
from() 的值被忽略 | 只有在 tweener 开始时才会应用 from() 的值 | 如果需要提前显示该值,请先将属性设置为起始值 |
| Tween 循环不流畅 | 结束值与起始值不匹配,无法实现无缝循环 | 旋转时使用 as_relative(),或让起始值和结束值相同 |
| 相对 Tween 在多次循环后发生偏移 | as_relative() 会在每次循环时累积 | 循环时使用绝对值;单次移动时使用相对值 |
| 回调触发时机不正确 | 回调被添加到了并行区段 | 使用 chain() 在回调前切回顺序执行 |
| 未找到 Tween 属性路径 | 路径拼写错误或格式不正确 | 使用 "property:component",例如 "position:x"、"modulate:a" |
| Tween 比预期更快或更慢 | Engine.time_scale 会影响 Tween | 慢动作期间用于 UI 的 Tween,可使用 set_ignore_time_scale()(4.4+) |
10. 实现检查清单
- 在同一属性上创建新 Tween 前,先终止旧 Tween
- 需要稍后终止 Tween 时,将其引用存储在变量中
- 设置缓动(自然运动使用
TRANS_CUBIC+EASE_OUT,而非默认的TRANS_LINEAR) - 多个属性需要同时动画时,使用
set_parallel(true) - 并行区段结束后,使用
chain()切回顺序执行 - 起始值很重要(而不仅仅是结束值)时,使用
from()或from_current() - 使用
as_relative()实现增量移动,而不是计算绝对目标值 - 循环 Tween 使用
set_loops(),而非手动重新创建 - 暂停期间必须运行的 Tween 使用
set_pause_mode(TWEEN_PAUSE_PROCESS) - 对于即发即弃序列(伤害数字、粒子效果),在末尾使用
tween_callback(queue_free)