ease
2026/4/14大约 6 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — ease
ease
定义
ease() 是一个缓动函数,用来让动画从"匀速直线运动"变成"有加速、减速效果的自然运动"。
打个比方:电梯从 1 楼到 10 楼,如果匀速运行,启动和停止都会很突兀,乘客会觉得猛地一晃。真实的电梯会在启动时慢慢加速、快到楼层时提前减速,让人觉得平稳舒适。ease() 函数就是帮你实现这种"慢慢开始、慢慢结束"的效果。
在游戏开发中,ease() 常用于 UI 动画(菜单弹出、按钮缩放)、摄像机移动、物体抛出与回收、血条变化等各种需要"自然过渡"的场景。如果你觉得游戏里的某个动画看起来"太机械",加一个 ease() 往往就能让它变得舒服很多。
参数 curve 怎么理解? curve 的值决定缓动的"风格":
- 正值:产生"减速进入"效果(先快后慢),值越大减速越明显
- 负值:产生"加速进入"效果(先慢后快),绝对值越大加速越明显
- 0:相当于没有缓动,和线性插值(
lerp)一样
函数签名
C#
public static float Ease(float s, float curve)GDScript
func ease(s: float, curve: float) -> float参数说明
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
s | float | 是 | 插值进度,取值范围 0.0 到 1.0。0 表示动画起点,1 表示动画终点。超出范围时行为不保证 |
curve | float | 是 | 缓动曲线的弯曲程度。0 = 线性无缓动,正值 = 减速(ease-out),负值 = 加速(ease-in) |
返回值
float —— 经过缓动处理后的进度值,范围通常在 0.0 到 1.0 之间。
不同 curve 值的效果:
curve = 0:线性,返回值等于s(无缓动)curve > 0(如 1、2、5):先快后慢(减速进入,类似"刹车"效果)curve < 0(如 -1、-2、-5):先慢后快(加速进入,类似"弹射"效果)curve = 1:相当于smoothstep()的效果curve = 2:常见的 ease-out 曲线
代码示例
基础用法:不同缓动曲线的效果
C#
using Godot;
public partial class EaseExample : Node
{
public override void _Ready()
{
float s = 0.5f; // 动画进度到一半
// 无缓动(线性),结果就是 s 本身
float linear = Mathf.Ease(s, 0f);
GD.Print($"curve=0 (线性): {linear}");
// 运行结果: curve=0 (线性): 0.5
// 先快后慢(减速)
float easeOut1 = Mathf.Ease(s, 1f);
GD.Print($"curve=1 (减速): {easeOut1}");
// 运行结果: curve=1 (减速): 0.75
float easeOut2 = Mathf.Ease(s, 2f);
GD.Print($"curve=2 (强减速): {easeOut2}");
// 运行结果: curve=2 (强减速): 0.875
// 先慢后快(加速)
float easeIn1 = Mathf.Ease(s, -1f);
GD.Print($"curve=-1 (加速): {easeIn1}");
// 运行结果: curve=-1 (加速): 0.25
float easeIn2 = Mathf.Ease(s, -2f);
GD.Print($"curve=-2 (强加速): {easeIn2}");
// 运行结果: curve=-2 (强加速): 0.125
}
}GDScript
func _ready():
var s = 0.5 # 动画进度到一半
# 无缓动(线性),结果就是 s 本身
var linear = ease(s, 0.0)
print("curve=0 (线性): %f" % linear)
# 运行结果: curve=0 (线性): 0.500000
# 先快后慢(减速)
var ease_out1 = ease(s, 1.0)
print("curve=1 (减速): %f" % ease_out1)
# 运行结果: curve=1 (减速): 0.750000
var ease_out2 = ease(s, 2.0)
print("curve=2 (强减速): %f" % ease_out2)
# 运行结果: curve=2 (强减速): 0.875000
# 先慢后快(加速)
var ease_in1 = ease(s, -1.0)
print("curve=-1 (加速): %f" % ease_in1)
# 运行结果: curve=-1 (加速): 0.250000
var ease_in2 = ease(s, -2.0)
print("curve=-2 (强加速): %f" % ease_in2)
# 运行结果: curve=-2 (强加速): 0.125000实际场景:菜单弹出动画
C#
using Godot;
public partial class PopupMenu : Control
{
// 导出属性:动画持续时间(秒)
[Export] public float ExDuration = 0.5f;
// 导出属性:缓动曲线强度
[Export] public float ExCurveStrength = 2.0f;
// 内部变量:动画计时器
private float _elapsedTime = 0f;
// 内部变量:是否正在播放动画
private bool _isAnimating = false;
public void ShowPopup()
{
_elapsedTime = 0f;
_isAnimating = true;
Visible = true;
}
public override void _Process(double delta)
{
if (!_isAnimating) return;
_elapsedTime += (float)delta;
// 计算线性进度(0 到 1)
float t = Mathf.Clamp(_elapsedTime / ExDuration, 0f, 1f);
// 用 ease 做减速效果,让菜单弹出时"快速到位、缓缓停下"
float easedT = Mathf.Ease(t, ExCurveStrength);
// 应用缩放:从 0.5 倍放大到 1.0 倍
Scale = new Vector2(
Mathf.Lerp(0.5f, 1.0f, easedT),
Mathf.Lerp(0.5f, 1.0f, easedT)
);
if (t >= 1.0f)
{
_isAnimating = false;
GD.Print("弹出动画完成");
}
}
}GDScript
extends Control
# 导出属性:动画持续时间(秒)
@export var ex_duration: float = 0.5
# 导出属性:缓动曲线强度
@export var ex_curve_strength: float = 2.0
# 内部变量:动画计时器
var _elapsed_time: float = 0.0
# 内部变量:是否正在播放动画
var _is_animating: bool = false
func show_popup() -> void:
_elapsed_time = 0.0
_is_animating = true
visible = true
func _process(delta):
if not _is_animating:
return
_elapsed_time += delta
# 计算线性进度(0 到 1)
var t = clampf(_elapsed_time / ex_duration, 0.0, 1.0)
# 用 ease 做减速效果,让菜单弹出时"快速到位、缓缓停下"
var eased_t = ease(t, ex_curve_strength)
# 应用缩放:从 0.5 倍放大到 1.0 倍
scale = Vector2(
lerp(0.5, 1.0, eased_t),
lerp(0.5, 1.0, eased_t)
)
if t >= 1.0:
_is_animating = false
print("弹出动画完成")进阶用法:配合 Tween 实现自定义缓动
C#
using Godot;
public partial class CustomTweenDemo : Node2D
{
private Sprite2D _sprite;
private float _elapsedTime = 0f;
// 导出属性:总动画时长
[Export] public float ExAnimLength = 2.0f;
public override void _Ready()
{
_sprite = GetNode<Sprite2D>("Sprite2D");
}
public override void _Process(double delta)
{
_elapsedTime += (float)delta;
// 让时间在 0~1 之间循环
float linearT = (_elapsedTime % ExAnimLength) / ExAnimLength;
// 先加速后减速:用 pingpong 配合 ease 实现"呼吸"效果
float pingpongT = Mathf.PingPong(linearT * 2f, 1.0f);
float easedT = Mathf.Ease(pingpongT, 3.0f);
// 控制 Y 轴位置:上下浮动
float yPos = Mathf.Lerp(100f, 300f, easedT);
_sprite.Position = new Vector2(_sprite.Position.X, yPos);
// 控制透明度:上下时淡入淡出
float alpha = Mathf.Lerp(0.3f, 1.0f, easedT);
_sprite.Modulate = new Color(1f, 1f, 1f, alpha);
}
}GDScript
extends Node2D
@onready var _sprite = $Sprite2D
# 导出属性:总动画时长
@export var ex_anim_length: float = 2.0
var _elapsed_time: float = 0.0
func _process(delta):
_elapsed_time += delta
# 让时间在 0~1 之间循环
var linear_t = fmod(_elapsed_time, ex_anim_length) / ex_anim_length
# 先加速后减速:用 pingpong 配合 ease 实现"呼吸"效果
var pingpong_t = pingpong(linear_t * 2.0, 1.0)
var eased_t = ease(pingpong_t, 3.0)
# 控制 Y 轴位置:上下浮动
var y_pos = lerp(100.0, 300.0, eased_t)
_sprite.position.y = y_pos
# 控制透明度:上下时淡入淡出
var alpha = lerp(0.3, 1.0, eased_t)
_sprite.modulate = Color(1.0, 1.0, 1.0, alpha)注意事项
s的取值范围:ease()设计上s应该在 0.0 到 1.0 之间。超出这个范围时,返回值可能不在 0~1 之间,具体行为取决于curve的值。curve = 0等价于线性插值:当curve为 0 时,ease(s, 0)就等于s,没有任何缓动效果。- 与 Tween 的关系:Godot 内置的
Tween(也叫SceneTreeTween)已经内置了多种缓动曲线(如EaseType.InOut、EaseType.Out等),通常不需要手动调用ease()。但当你需要在_Process()中手动控制动画进度时,ease()就非常有用。 - 与
smoothstep()的关系:ease(s, 1.0)的效果类似于smoothstep(),都是一种平滑的 S 形曲线。 - 正负值方向容易搞混:记住 正值 = 先快后慢(像刹车),负值 = 先慢后快(像弹弓)。绝对值越大,效果越明显。
