cubic_interpolate_in_time
最后同步日期:2026-04-15 | Godot 官方原文 — cubic_interpolate_in_time
cubic_interpolate_in_time
定义
cubic_interpolate_in_time() 是一个带自定义时间参数的三次插值函数。它和 cubic_interpolate() 的作用完全一样——在两个值之间画出一条平滑的曲线——但它多了一个关键能力:你可以告诉引擎每段路程实际花了多长时间。
打个比方:假设你在跑步训练中记录了几个关键点的时间和位置——从起点 A 到中点 B 花了 2 分钟,但从 B 到终点 C 只花了 30 秒。普通的 cubic_interpolate() 假设每段路程花的时间一样多,画出来的曲线可能会不够准确。而 cubic_interpolate_in_time() 允许你把真实的时间告诉引擎,这样它就能画出更贴近实际情况的平滑曲线。
简单总结:如果你的关键点之间的间距是均匀的,用 cubic_interpolate() 就够了;如果间距不均匀(有些点离得远、有些点离得近),就需要用这个函数来告诉引擎真实的时间分布。
函数签名
// Mathf.CubicInterpolateInTime 返回 float
public static float CubicInterpolateInTime(
float from, // 起始值
float to, // 目标值
float pre, // 起始值之前的关键点
float post, // 目标值之后的关键点
float weight, // 插值权重(0 到 to_t 之间)
float to_t, // 从 from 到 to 的持续时间
float pre_t, // 从 pre 到 from 的持续时间
float post_t // 从 to 到 post 的持续时间
)func cubic_interpolate_in_time(
from: float, # 起始值
to: float, # 目标值
pre: float, # 起始值之前的关键点
post: float, # 目标值之后的关键点
weight: float, # 插值权重(0 到 to_t 之间)
to_t: float, # 从 from 到 to 的持续时间
pre_t: float, # 从 pre 到 from 的持续时间
post_t: float # 从 to 到 post 的持续时间
) -> float参数说明
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
| from | float | 是 | 起始值,插值的起点。当 weight 为 0 时,返回此值 |
| to | float | 是 | 目标值,插值的终点。当 weight 等于 to_t 时,返回此值 |
| pre | float | 是 | 起始值之前的关键点。影响曲线在起始处的弯曲方向 |
| post | float | 是 | 目标值之后的关键点。影响曲线在结束处的弯曲方向 |
| weight | float | 是 | 插值权重,范围 0 到 to_t。0 表示完全在 from 处,to_t 表示完全在 to 处 |
| to_t | float | 是 | 从 from 到 to 这段路程的持续时间。决定了 weight 的最大值。如果设为 1.0,行为就接近 cubic_interpolate() |
| pre_t | float | 是 | 从 pre 到 from 这段路程的持续时间。影响曲线在起始处的"张力"——时间越短,曲线越急 |
| post_t | float | 是 | 从 to 到 post 这段路程的持续时间。影响曲线在结束处的"张力"——时间越短,曲线越急 |
关于三个时间参数的理解:想象你在一段路上开车,经过了 4 个路标:
pre->from->to->post。pre_t是从pre到from开了多少分钟,to_t是从from到to开了多少分钟,post_t是从to到post开了多少分钟。这三个时间可以不一样——某段路程你开得快(时间短),某段开得慢(时间长)。函数根据这些时间信息来调整曲线的弯曲程度。
返回值
类型: float
返回在 from 和 to 之间按 weight 权重进行三次插值后的结果,插值曲线考虑了各段路程的实际时间分布。
不同 weight 值的行为:
| weight | 返回值 | 说明 |
|---|---|---|
0.0 | from | 完全处于起始位置 |
to_t / 2 | 中间的曲线值 | 处于曲线中段(不一定是中点) |
to_t | to | 完全处于目标位置 |
代码示例
基本用法:对比均匀时间 vs 不均匀时间
using Godot;
public partial class CubicInTimeExample : Node
{
public override void _Ready()
{
float pre = 0.0f;
float from = 2.0f;
float to = 5.0f;
float post = 3.0f;
// 场景 1:均匀时间(每段都是 1.0 秒)
float result1 = Mathf.CubicInterpolateInTime(
from, to, pre, post, 0.5f,
to_t: 1.0f, pre_t: 1.0f, post_t: 1.0f
);
// 场景 2:不均匀时间(pre 到 from 花了 0.1 秒,from 到 to 花了 2 秒)
float result2 = Mathf.CubicInterpolateInTime(
from, to, pre, post, 1.0f,
to_t: 2.0f, pre_t: 0.1f, post_t: 1.0f
);
// 场景 3:和普通 cubic_interpolate 对比(等价情况)
float result3 = Mathf.CubicInterpolate(from, to, pre, post, 0.5f);
float result4 = Mathf.CubicInterpolateInTime(
from, to, pre, post, 0.5f,
to_t: 1.0f, pre_t: 1.0f, post_t: 1.0f
);
GD.Print($"均匀时间结果: {result1:F3}");
GD.Print($"不均匀时间结果: {result2:F3}");
GD.Print($"普通插值: {result3:F3}, 带时间参数(等价): {result4:F3}");
}
}func _ready():
var pre := 0.0
var from := 2.0
var to := 5.0
var post := 3.0
# 场景 1:均匀时间(每段都是 1.0 秒)
var result1 := cubic_interpolate_in_time(
from, to, pre, post, 0.5,
1.0, # to_t
1.0, # pre_t
1.0 # post_t
)
# 场景 2:不均匀时间(pre 到 from 花了 0.1 秒,from 到 to 花了 2 秒)
var result2 := cubic_interpolate_in_time(
from, to, pre, post, 1.0,
2.0, # to_t
0.1, # pre_t
1.0 # post_t
)
# 场景 3:和普通 cubic_interpolate 对比(等价情况)
var result3 := cubic_interpolate(from, to, pre, post, 0.5)
var result4 := cubic_interpolate_in_time(
from, to, pre, post, 0.5,
1.0, 1.0, 1.0
)
print("均匀时间结果: %.3f" % result1)
print("不均匀时间结果: %.3f" % result2)
print("普通插值: %.3f, 带时间参数(等价): %.3f" % [result3, result4])实际场景:变速路径动画
using Godot;
public partial class VariableSpeedPath : Node3D
{
// 路径上的四个高度关键点
[Export] public float ExPreY = 0.0f;
[Export] public float ExFromY = 3.0f;
[Export] public float ExToY = 6.0f;
[Export] public float ExPostY = 4.0f;
// 各段时间(模拟不均匀的运动速度)
[Export] public float ExPreTime = 1.0f; // pre -> from 花了 1 秒
[Export] public float ExToTime = 2.0f; // from -> to 花了 2 秒(慢)
[Export] public float ExPostTime = 0.5f; // to -> post 花了 0.5 秒(快)
private float _elapsed = 0.0f;
public override void _Process(double delta)
{
_elapsed += (float)delta;
// weight 范围是 0 到 ExToTime
if (_elapsed > ExToTime)
_elapsed = 0.0f;
float y = Mathf.CubicInterpolateInTime(
ExFromY, ExToY, ExPreY, ExPostY,
_elapsed, // weight:当前经过的时间
ExToTime, // to_t:from -> to 的总时间
ExPreTime, // pre_t:pre -> from 的时间
ExPostTime // post_t:to -> post 的时间
);
Position = new Vector3(Position.X, y, Position.Z);
}
}extends Node3D
# 路径上的四个高度关键点
@export var ex_pre_y: float = 0.0
@export var ex_from_y: float = 3.0
@export var ex_to_y: float = 6.0
@export var ex_post_y: float = 4.0
# 各段时间(模拟不均匀的运动速度)
@export var ex_pre_time: float = 1.0 # pre -> from 花了 1 秒
@export var ex_to_time: float = 2.0 # from -> to 花了 2 秒(慢)
@export var ex_post_time: float = 0.5 # to -> post 花了 0.5 秒(快)
var _elapsed: float = 0.0
func _process(delta: float):
_elapsed += delta
# weight 范围是 0 到 ex_to_time
if _elapsed > ex_to_time:
_elapsed = 0.0
var y := cubic_interpolate_in_time(
ex_from_y, ex_to_y, ex_pre_y, ex_post_y,
_elapsed, # weight:当前经过的时间
ex_to_time, # to_t:from -> to 的总时间
ex_pre_time, # pre_t:pre -> from 的时间
ex_post_time # post_t:to -> post 的时间
)
position = Vector3(position.x, y, position.z)实际场景:多关键帧变速动画
using Godot;
public partial class TimedKeyframeAnimator : Node
{
// 关键帧值
private readonly float[] _values = { 0.0f, 3.0f, 1.0f, 4.0f, 2.0f };
// 每段的时间长度(可以不一样)
private readonly float[] _durations = { 2.0f, 0.5f, 1.5f, 1.0f };
private float _elapsed = 0.0f;
// 总动画时长
private float TotalDuration()
{
float total = 0.0f;
foreach (float d in _durations)
total += d;
return total;
}
public override void _Process(double delta)
{
float totalDuration = TotalDuration();
_elapsed += (float)delta;
if (_elapsed > totalDuration)
_elapsed -= totalDuration;
// 找到当前处于哪一段
float accumulated = 0.0f;
int segmentIndex = 0;
for (int i = 0; i < _durations.Length; i++)
{
if (_elapsed < accumulated + _durations[i])
{
segmentIndex = i;
break;
}
accumulated += _durations[i];
}
// 当前段内经过的时间
float segmentElapsed = _elapsed - accumulated;
float toT = _durations[segmentIndex];
// 获取四个关键点和对应的时间
float pre = _values[Mathf.Max(segmentIndex - 1, 0)];
float from = _values[segmentIndex];
float to = _values[segmentIndex + 1];
float post = _values[Mathf.Min(segmentIndex + 2, _values.Length - 1)];
float preT = segmentIndex > 0 ? _durations[segmentIndex - 1] : toT;
float postT = segmentIndex + 1 < _durations.Length ? _durations[segmentIndex + 1] : toT;
float value = Mathf.CubicInterpolateInTime(
from, to, pre, post,
segmentElapsed, toT, preT, postT
);
GD.Print($"当前值: {value:F2}");
}
}extends Node
# 关键帧值
var _values: Array[float] = [0.0, 3.0, 1.0, 4.0, 2.0]
# 每段的时间长度(可以不一样)
var _durations: Array[float] = [2.0, 0.5, 1.5, 1.0]
var _elapsed: float = 0.0
func _process(delta: float):
var total_duration := _durations.reduce(func(a, b): return a + b, 0.0)
_elapsed += delta
if _elapsed > total_duration:
_elapsed -= total_duration
# 找到当前处于哪一段
var accumulated := 0.0
var segment_index := 0
for i in _durations.size():
if _elapsed < accumulated + _durations[i]:
segment_index = i
break
accumulated += _durations[i]
# 当前段内经过的时间
var segment_elapsed := _elapsed - accumulated
var to_t := _durations[segment_index]
# 获取四个关键点和对应的时间
var pre := _values[maxi(segment_index - 1, 0)]
var from := _values[segment_index]
var to := _values[segment_index + 1]
var post := _values[mini(segment_index + 2, _values.size() - 1)]
var pre_t := to_t if segment_index <= 0 else _durations[segment_index - 1]
var post_t := to_t if segment_index + 1 >= _durations.size() else _durations[segment_index + 1]
var value := cubic_interpolate_in_time(
from, to, pre, post,
segment_elapsed, to_t, pre_t, post_t
)
print("当前值: %.2f" % value)注意事项
weight 的范围是 0 到
to_t:注意,这里的weight不是一个 0 到 1 的比例值,而是一个实际的"时间"值。当你把weight设为 0 时,返回from;当你把weight设为to_t时,返回to。这一点和cubic_interpolate()不同——后者要求weight在 0 到 1 之间。与
cubic_interpolate()的关系:当to_t、pre_t、post_t都设为 1.0 时,cubic_interpolate_in_time()的行为近似于cubic_interpolate()(weight范围变为 0 到 1)。但两者内部实现并不完全等价,结果可能有微小差异。时间参数不能为 0:
to_t、pre_t、post_t都应该是正数。如果设为 0 或负数,会导致除零错误或不可预测的行为。时间参数影响曲线的"松紧":
pre_t越小(pre 到 from 花的时间越短),曲线在起始处就越"紧",变化越急促;post_t越小,曲线在结束处就越"紧"。如果你想要更平缓的过渡,就把对应的时间设大一些。对于角度值请使用专用函数:如果你的值是角度(如旋转),应该使用
cubic_interpolate_angle_in_time()而不是本函数。角度专用版本会自动处理 -PI 到 PI 之间的环绕问题。适用于动画关键帧:这个函数最常见的用途是在动画系统中,当关键帧之间的时间间隔不相等时,用它来生成精确的中间帧。大多数情况下,游戏开发者不需要手动调用这个函数——Godot 的 AnimationPlayer 和 Tween 已经内置了这些功能。
