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...
Interpolação
A interpolação é uma operação comum na programação gráfica, que é usada para misturar ou fazer transições entre dois valores. Ela também pode ser usada para suavizar movimentação, rotação e etc. É muito bom se tornar familiar com ela para expandir os seus horizontes como desenvolvedor de jogos.
A ideia básica é que você quer fazer uma transição do ponto A para o ponto B. Um valor t, representa os estados intermediários.
Por exemplo, se t or 0, então o estado vai ser igual ao ponto A. Se t for 1 , então o estado é o ponto B. Qualquer coisa entre os dois vai ser uma interpolação.
Entre dois números reais (ponto flutuante), uma interpolação pode ser descrita como:
interpolation = A * (1 - t) + B * t
E é comumente simplificado para:
interpolation = A + (B - A) * t
O nome desse tipo de interpolação, que transforma um valor em outro em uma velocidade constante, é "linear". Então, quando você ouvir falar em Interpolação Linear, saberá que estão se referindo a essa fórmula.
Tem outros tipos de interpolações, que não vão ser discutidas aqui. Uma leitura recomendada depois é a página que fala sobre a Curva de Bézier.
Interpolação vetorial
Tipos de vetores (Vector2 e Vector3) também podem ser interpolados, eles vêm com funções úteis para fazer isso como: Vector2.lerp() e Vector3.lerp().
Para uma interpolação cúbica, também têm Vector2.cubic_interpolate() e Vector3.cubic_interpolate(), que fazem uma interpolação no estilo Bézier.
Aqui está um exemplo de pseudocódigo que vai do ponto A para o ponto B usando uma interpolação:
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);
}
Isso produzirá o seguinte movimento:
Transformar interpolação
Também é possível interpolar transformações inteiras (tenha certeza de que eles tenham uma escala uniforme ou, pelo menos, a mesma escala não uniforme). Para isso, pode ser usada a função Transform3D.interpolate_with().
Aqui está um exemplo de transformar um macaco da Posição1 para a Posição2:
Usando o seguinte pseudocódigo:
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);
}
E de novo, isso vai produzir a seguinte moção:
Suavizando o movimento
A interpolação pode ser usada para seguir suavemente um valor alvo em movimento, como uma posição ou uma rotação. A cada frame, lerp() move o valor atual em direção ao valor alvo por uma porcentagem fixa da diferença restante entre os valores. O valor atual se moverá suavemente em direção ao alvo, desacelerando à medida que se aproxima. Aqui está um exemplo de um círculo seguindo o mouse usando suavização por interpolação:
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);
}
Aqui está como fica:
Isso é útil para suavizar o movimento da câmera, para aliados seguindo o jogador, e também para muitos outros padrões que comumente aparecem nos jogos.
Nota
Apesar de usar delta, a fórmula usada acima é dependente da taxa de quadros, porque o parâmetro weight (peso) da função lerp() representa uma porcentagem da diferença restante nos valores, e não uma quantidade absoluta para mudar. Em _physics_process(), isso normalmente é aceitável já que é esperado que a física mantenha uma taxa de quadros constante, e portanto delta é esperado que se mantenha constante.
Para uma versão de interpolação suave que é independente da taxa de quadros que também possa ser usada em process(), use a seguinte fórmula ao invés disso:
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);
}
Derivar essa fórmula está além do escopo dessa página. Para uma explicação, veja Improved Lerp Smoothing ou assista Lerp smoothing is broken.