Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Інтерполяція
Інтерполяція — це поширена операція в графічному програмуванні, яка використовується для змішування або переходу між двома значеннями. Інтерполяція також може бути використана для плавного руху, обертання тощо. Добре ознайомитися з нею, щоб розширити свій кругозір як розробника ігор.
Основна ідея полягає в тому, що ви хочете перейти від А до В. Значення t, представляє стани між ними.
Наприклад, якщо t дорівнює 0, тоді стан дорівнює A. Якщо t дорівнює 1, то стан дорівнює B. Все, що знаходиться між ними, є інтерполяцією.
Між двома дійсними числами (з плаваючою комою) інтерполяцію можна описати так:
interpolation = A * (1 - t) + B * t
І часто спрощується до:
interpolation = A + (B - A) * t
Назва цього типу інтерполяції, яка перетворює одне значення на інше з постійною швидкістю, — «лінійна». Тож, коли ви чуєте про лінійну інтерполяцію, ви знаєте, що вони мають на увазі цю формулу.
Є й інші види інтерполяції, які тут не будуть розглядатися. Тому, після цієї статті, рекомендується прочитати сторінку Bezier.
Векторна інтерполяція
Типи векторів (Vector2 і Vector3) також можна інтерполювати, вони мають зручні функції для цього Vector2.lerp() і Vector3.lerp().
Для кубічної інтерполяції існують також Vector2.cubic_interpolate() та Vector3.cubic_interpolate(), які виконують інтерполяцію в стилі Bezier.
Ось приклад псевдокоду для переходу від точки А до точки Б за допомогою інтерполяції:
var t = 0.0
func _physics_process(delta):
t += delta * 0.4
$Sprite2D.position = $A.position.lerp($B.position, t)
private float _t = 0.0f;
public override void _PhysicsProcess(double delta)
{
_t += (float)delta * 0.4f;
Marker2D a = GetNode<Marker2D>("A");
Marker2D b = GetNode<Marker2D>("B");
Sprite2D sprite = GetNode<Sprite2D>("Sprite2D");
sprite.Position = a.Position.Lerp(b.Position, _t);
}
Він буде виробляти наступний рух:
Інтерполяція перетворень
Також можна інтерполювати цілі перетворення (переконайтеся, що вони мають або єдиний масштаб, або, принаймні, однаковий нерівномірний масштаб). Для цього можна використовувати функцію Transform3D.interpolate_with().
Ось приклад перетворення мавпочки з Позиції1 в Позицію2:
За допомогою наступного псевдокоду:
var t = 0.0
func _physics_process(delta):
t += delta
$Monkey.transform = $Position1.transform.interpolate_with($Position2.transform, t)
private float _t = 0.0f;
public override void _PhysicsProcess(double delta)
{
_t += (float)delta;
Marker3D p1 = GetNode<Marker3D>("Position1");
Marker3D p2 = GetNode<Marker3D>("Position2");
CSGMesh3D monkey = GetNode<CSGMesh3D>("Monkey");
monkey.Transform = p1.Transform.InterpolateWith(p2.Transform, _t);
}
Він буде виробляти наступний рух:
Плавний рух
Інтерполяцію можна використовувати для плавного відстеження цільового значення, яке рухається, наприклад позиції або повороту. Кожен кадр lerp() переміщує поточне значення до цільового значення на фіксований відсоток різниці між значеннями, що залишилася. Поточне значення буде плавно рухатися до цілі, сповільнюючи швидкість у міру наближення. Ось приклад кола, яке слідує за мишею з використанням інтерполяційного згладжування:
const FOLLOW_SPEED = 4.0
func _physics_process(delta):
var mouse_pos = get_local_mouse_position()
$Sprite2D.position = $Sprite2D.position.lerp(mouse_pos, delta * FOLLOW_SPEED)
private const float FollowSpeed = 4.0f;
public override void _PhysicsProcess(double delta)
{
Vector2 mousePos = GetLocalMousePosition();
Sprite2D sprite = GetNode<Sprite2D>("Sprite2D");
sprite.Position = sprite.Position.Lerp(mousePos, (float)delta * FollowSpeed);
}
Ось як це виглядає:
Це корисно для згладжування руху камери, для союзників, які стежать за гравцем (переконавшись, що вони залишаються в межах певного діапазону), і для багатьох інших типових ігрових шаблонів.
Примітка
Незважаючи на використання delta, використана вище формула залежить від частоти кадрів, оскільки параметр weight lerp() представляє відсоток різниці значень, що залишилася, а не абсолютну суму, яку потрібно змінити. У _physics_process() це зазвичай добре, оскільки очікується, що фізика підтримуватиме постійну частоту кадрів, і тому дельта має залишатися постійною.
Для незалежної від частоти кадрів версії інтерполяційного згладжування, яку також можна використовувати в process(), використовуйте натомість таку формулу:
const FOLLOW_SPEED = 4.0
func _process(delta):
var mouse_pos = get_local_mouse_position()
var weight = 1 - exp(-FOLLOW_SPEED * delta)
$Sprite2D.position = $Sprite2D.position.lerp(mouse_pos, weight)
private const float FollowSpeed = 4.0f;
public override void _Process(double delta)
{
Vector2 mousePos = GetLocalMousePosition();
Sprite2D sprite = GetNode<Sprite2D>("Sprite2D");
float weight = 1f - Mathf.Exp(-FollowSpeed * (float)delta);
sprite.Position = sprite.Position.Lerp(mousePos, weight);
}
Отримання цієї формули виходить за рамки цієї сторінки. Щоб отримати пояснення, перегляньте Покращене згладжування Lerp або подивіться Згладжування Lerp порушено.