SceneTree.create_tween
2026/4/14大约 6 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — SceneTree.create_tween
SceneTree.create_tween
定义
create_tween 就像给动画师下达一条指令——你告诉他"让这个角色从左边走到右边,花 2 秒钟,先快后慢",他就会帮你一步步完成。在 Godot 里,"动画师"就是 Tween(补间动画)对象,它可以在一段时间内平滑地改变节点的属性值(位置、颜色、透明度、大小等)。
打个比方:你要把灯光从"关"调到"全亮",不是瞬间打开(像开灯关灯),而是慢慢变亮(像日出)。create_tween 就是创建这个"渐变过程"的工具——你指定起点、终点、花多长时间,它自动帮你计算中间的每一帧该怎么变。
Tween 动画是游戏开发中最常用的视觉效果工具之一:角色移动、UI 弹入弹出、血条变化、镜头震动等等,都可以用 Tween 实现。
函数签名
C#
public Tween CreateTween()GDScript
func create_tween() -> Tween参数说明
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
| 无 | — | — | 此方法不需要参数。创建的 Tween 会自动绑定到场景树 |
返回值
Tween——返回一个补间动画对象。你可以链式调用它的方法来定义动画序列。
| 常用方法 | 说明 |
|---|---|
TweenProperty() | 对某个属性做动画,如位置、颜色、透明度 |
TweenCallback() | 在动画序列中插入一个回调函数 |
TweenInterval() | 在动画序列中插入一段等待时间 |
SetEase() | 设置缓动曲线(先快后慢、先慢后快等) |
SetTrans() | 设置过渡类型(线性、弹性、回弹等) |
SetLoops() | 设置循环次数(-1 为无限循环) |
SetParallel() | 让后续动画与前一个同时播放 |
代码示例
基础用法:让节点平滑移动
C#
public override void _Ready()
{
// 创建一个 Tween,让当前节点在 2 秒内向右移动 300 像素
Tween tween = GetTree().CreateTween();
tween.TweenProperty(this, "position:x", 300.0f, 2.0);
// 再创建一个 Tween,让节点淡入(透明度从 0 变到 1)
Tween fadeTween = GetTree().CreateTween();
fadeTween.TweenProperty(this, "modulate:a", 1.0f, 1.0);
GD.Print("动画开始播放!");
// 运行结果: 动画开始播放!
}GDScript
func _ready():
# 创建一个 Tween,让当前节点在 2 秒内向右移动 300 像素
var tween = get_tree().create_tween()
tween.tween_property(self, "position:x", 300.0, 2.0)
# 再创建一个 Tween,让节点淡入(透明度从 0 变到 1)
var fade_tween = get_tree().create_tween()
fade_tween.tween_property(self, "modulate:a", 1.0, 1.0)
print("动画开始播放!")
# 运行结果: 动画开始播放!实际场景:按钮弹入动画和血条变化
C#
using Godot;
public partial class UIButton : Control
{
public override void _Ready()
{
// 按钮初始时缩小到 0,然后弹性放大到正常大小
Scale = Vector2.Zero; // 初始缩放为 0
Tween popTween = GetTree().CreateTween();
popTween.SetEase(Tween.EaseType.Out);
popTween.SetTrans(Tween.TransitionType.Back);
popTween.TweenProperty(this, "scale", Vector2.One, 0.5);
// 运行结果: 按钮从无到有弹出,带有一点回弹效果
}
public void PlayClickAnimation()
{
// 点击时缩小再恢复
Tween clickTween = GetTree().CreateTween();
clickTween.TweenProperty(this, "scale", new Vector2(0.9f, 0.9f), 0.1);
clickTween.TweenProperty(this, "scale", Vector2.One, 0.1);
// 运行结果: 按钮先缩小再弹回,产生"按下"的视觉效果
}
}
public partial class HealthBar : ProgressBar
{
private Tween _healthTween;
public void SetHealth(float targetValue)
{
// 停止上一个动画(如果还在播放)
_healthTween?.Kill();
// 平滑过渡到新的血量
_healthTween = GetTree().CreateTween();
_healthTween.SetTrans(Tween.TransitionType.Sine);
_healthTween.SetEase(Tween.EaseType.Out);
_healthTween.TweenProperty(this, "value", targetValue, 0.3);
GD.Print($"血量动画:{Value} -> {targetValue}");
// 运行结果: 血量动画:100 -> 75
}
}GDScript
# ===== UIButton.gd —— 按钮弹入动画 =====
extends Control
func _ready():
# 按钮初始时缩小到 0,然后弹性放大到正常大小
scale = Vector2.ZERO # 初始缩放为 0
var pop_tween = get_tree().create_tween()
pop_tween.set_ease(Tween.EASE_OUT)
pop_tween.set_trans(Tween.TRANS_BACK)
pop_tween.tween_property(self, "scale", Vector2.ONE, 0.5)
# 运行结果: 按钮从无到有弹出,带有一点回弹效果
func play_click_animation():
# 点击时缩小再恢复
var click_tween = get_tree().create_tween()
click_tween.tween_property(self, "scale", Vector2(0.9, 0.9), 0.1)
click_tween.tween_property(self, "scale", Vector2.ONE, 0.1)
# 运行结果: 按钮先缩小再弹回,产生"按下"的视觉效果
# ===== HealthBar.gd —— 血条平滑变化 =====
# extends ProgressBar
#
# var _health_tween: Tween
#
# func set_health(target_value: float):
# # 停止上一个动画(如果还在播放)
# if _health_tween:
# _health_tween.kill()
#
# # 平滑过渡到新的血量
# _health_tween = get_tree().create_tween()
# _health_tween.set_trans(Tween.TRANS_SINE)
# _health_tween.set_ease(Tween.EASE_OUT)
# _health_tween.tween_property(self, "value", target_value, 0.3)
#
# print("血量动画:%s -> %s" % [value, target_value])
# # 运行结果: 血量动画:100 -> 75进阶用法:复杂动画序列和链式调用
C#
using Godot;
public partial class TitleScreen : Control
{
private Label _title;
private Label _subtitle;
private Button _startButton;
public override void _Ready()
{
_title = GetNode<Label>("Title");
_subtitle = GetNode<Label>("Subtitle");
_startButton = GetNode<Button>("StartButton");
// 初始状态:都隐藏
_title.Modulate = new Color(1, 1, 1, 0);
_subtitle.Modulate = new Color(1, 1, 1, 0);
_startButton.Modulate = new Color(1, 1, 1, 0);
PlayIntroSequence();
}
private void PlayIntroSequence()
{
// 创建一个链式动画序列
Tween sequence = GetTree().CreateTween();
// 第 1 步(0~1 秒):标题淡入 + 从上方滑下
sequence.SetParallel(true); // 同时执行
sequence.TweenProperty(_title, "modulate:a", 1.0f, 1.0);
sequence.TweenProperty(_title, "position:y", 100.0f, 1.0)
.SetEase(Tween.EaseType.Out);
// 第 2 步(1~1.5 秒):等待 0.5 秒
sequence.SetParallel(false); // 恢复顺序执行
sequence.TweenInterval(0.5);
// 第 3 步(1.5~2 秒):副标题淡入
sequence.TweenProperty(_subtitle, "modulate:a", 1.0f, 0.5);
// 第 4 步(2~2.5 秒):等待 0.5 秒
sequence.TweenInterval(0.5);
// 第 5 步(2.5~3 秒):按钮弹出
sequence.SetEase(Tween.EaseType.Out);
sequence.SetTrans(Tween.TransitionType.Back);
sequence.TweenProperty(_startButton, "modulate:a", 1.0f, 0.5);
// 第 6 步:动画结束后打印日志
sequence.TweenCallback(Callable.From(() =>
{
GD.Print("开场动画播放完毕!");
}));
// 运行结果: 开场动画播放完毕!
GD.Print("开场动画开始播放...");
// 运行结果: 开场动画开始播放...
}
public void OnStartButtonPressed()
{
// 点击开始后,整个画面快速淡出
Tween fadeOut = GetTree().CreateTween();
fadeOut.SetParallel(true);
fadeOut.TweenProperty(this, "modulate:a", 0.0f, 0.3);
fadeOut.TweenProperty(this, "modulate:a", 0.0f, 0.3);
fadeOut.TweenCallback(Callable.From(() =>
{
GetTree().ChangeSceneToFile("res://scenes/game.tscn");
}));
}
}GDScript
extends Control
@onready var _title = $Title
@onready var _subtitle = $Subtitle
@onready var _start_button = $StartButton
func _ready():
# 初始状态:都隐藏
_title.modulate = Color(1, 1, 1, 0)
_subtitle.modulate = Color(1, 1, 1, 0)
_start_button.modulate = Color(1, 1, 1, 0)
play_intro_sequence()
func play_intro_sequence():
# 创建一个链式动画序列
var sequence = get_tree().create_tween()
# 第 1 步(0~1 秒):标题淡入 + 从上方滑下
sequence.set_parallel(true) # 同时执行
sequence.tween_property(_title, "modulate:a", 1.0, 1.0)
sequence.tween_property(_title, "position:y", 100.0, 1.0).set_ease(Tween.EASE_OUT)
# 第 2 步(1~1.5 秒):等待 0.5 秒
sequence.set_parallel(false) # 恢复顺序执行
sequence.tween_interval(0.5)
# 第 3 步(1.5~2 秒):副标题淡入
sequence.tween_property(_subtitle, "modulate:a", 1.0, 0.5)
# 第 4 步(2~2.5 秒):等待 0.5 秒
sequence.tween_interval(0.5)
# 第 5 步(2.5~3 秒):按钮弹出
sequence.set_ease(Tween.EASE_OUT)
sequence.set_trans(Tween.TRANS_BACK)
sequence.tween_property(_start_button, "modulate:a", 1.0, 0.5)
# 第 6 步:动画结束后打印日志
sequence.tween_callback(func():
print("开场动画播放完毕!")
)
# 运行结果: 开场动画播放完毕!
print("开场动画开始播放...")
# 运行结果: 开场动画开始播放...
func _on_start_button_pressed():
# 点击开始后,整个画面快速淡出
var fade_out = get_tree().create_tween()
fade_out.set_parallel(true)
fade_out.tween_property(self, "modulate:a", 0.0, 0.3)
fade_out.tween_callback(func():
get_tree().change_scene_to_file("res://scenes/game.tscn")
)注意事项
- Tween 会自动管理生命周期:当 Tween 的动画全部播放完毕后,它会自动被释放。你通常不需要手动管理 Tween 对象。
- 用
Kill()停止动画:如果需要在动画播放中途停止,调用tween.Kill()。这在血条更新、重复触发动画等场景中很有用——先 Kill 掉旧的再创建新的,避免动画叠加冲突。 - 链式调用的执行顺序:默认情况下,Tween 的方法是顺序执行的(一个动画结束才开始下一个)。调用
SetParallel(true)后,后续的动画会与前一个同时播放。 - 场景切换时 Tween 会失效:因为 Tween 绑定在场景树上,如果场景被卸载(比如
ChangeSceneToFile),正在播放的 Tween 也会被销毁。 - 属性路径用字符串表示:
"position:x"表示只对 X 坐标做动画,"modulate:a"表示只对透明度做动画。用冒号:分隔属性名和子属性。 - C# 差异:C# 中方法名用 PascalCase(
CreateTween、TweenProperty),GDScript 中用 snake_case(create_tween、tween_property)。
