mcpField
T
tween-animation

tween-animation

github.com/jame581开发889172 次安装5 天前更新
代码仓库

关于 tween-animation

面向 Godot 4.3+ 开发者的 Tween 动画技能,提供 GDScript 和 C# 示例,涵盖属性动画、序列编排、缓动与生命周期管理。

主要功能

  1. 01属性、方法与回调动画
  2. 02串行和并行动画编排
  3. 03过渡与缓动设置
  4. 04循环、信号和生命周期管理
  5. 05常见动画配方与故障排查

适用场景

  1. 01制作 UI 淡入淡出和面板滑动
  2. 02编排物体移动、旋转与缩放
  3. 03制作按钮弹跳或屏幕震动
  4. 04排查动画冲突与暂停行为

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jame581/godotprompter --skill tween-animation

安装后在 Claude Code 中输入/tween-animation确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
common-recipes.md
lifecycle.md
looping-and-signals.md
property-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)

评论

支持 Markdown