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.

Matemática vetorial

Introdução

Este tutorial é uma introdução curta e prática à Álgebra Linear e como ela se aplica ao desenvolvimento de jogos. A Álgebra Linear é o estudo de vetores e seus usos. Vetores possuem diversas aplicações em desenvolvimento 2D e 3D, e Godot as usa extensivamente. Desenvolver um bom entendimento de matemática vetorial é essencial para se tornar um bom desenvolvedor de jogos.

Nota

Este tutorial não é um livro formal sobre álgebra linear. Veremos apenas como os vetores são aplicados no desenvolvimento de jogos. Para uma visão mais ampla sobre matemática, veja https://www.khanacademy.org/math/linear-algebra

Sistema de Coordenadas (2D)

Em espaço 2D, coordenadas são definidas usando um eixo horizontal (x) e um eixo vertical (y). Uma posição particular em espaço 2D é escrito como um par de valores como (4, 3).

../../_images/vector_axis1.png

Nota

Se você é novo em gráficos de computador, pode parecer estranho que o eixo y positivo aponte para baixo ao invés de para cima, como você provavelmente aprendeu em aulas de matemática. Contudo, isso é comum em muitas aplicações gráficas de computador.

Qualquer posição no plano 2D pode ser identificada por um par de números dessa maneira. No entanto, também podemos pensar na posição (4, 3) como um deslocamento do ponto (0, 0), ou origem. Desenhe uma seta apontando da origem para o ponto:

../../_images/vector_xy1.png

Isto é um vetor. Um vetor representa muitas informações úis. Além de nos dizer que o ponto está em (4, 3), também podemos pensar nele como um ângulo θ (teta) e um comprimento (ou magnitude) m. Neste caso, a seta é um vetor de posição — ela denota uma posição no espaço, em relação à origem.

Um ponto muito importante a ser considerado sobre os vetores é que eles representam apenas a direção e a magnitude relativas. Não existe o conceito de posição de um vetor. Os dois vetores a seguir são idênticos:

../../_images/vector_xy2.png

Ambos os vetores representam um ponto 4 unidades à direita e 3 unidades abaixo de algum ponto inicial. Não importa onde no plano você desenhe o vetor, ele sempre representa uma direção e magnitude relativas.

Operações vetoriais

Você pode usar qualquer um dos métodos (coordenadas x e y ou ângulo e magnitude) para se referir a um vetor, mas, por conveniência, os programadores normalmente usam a notação de coordenadas. Por exemplo, no Godot, a origem é o canto superior esquerdo da tela, então para colocar um nó 2D chamado Node2D 400 pixels para a direita e 300 pixels para baixo, use o seguinte código:

$Node2D.position = Vector2(400, 300)

O Godot suporta tanto o Vector2 quanto o Vector3 para uso em 2D e 3D, respectivamente. As mesmas regras matemáticas discutidas neste artigo se aplicam a ambos os tipos, e onde quer que façamos link para métodos de Vector2 na referência da classe, você também pode verificar seus equivalentes em Vector3.

Acesso de membros

Os componentes individuais do vetor podem ser acessados diretamente pelo nome.

# Create a vector with coordinates (2, 5).
var a = Vector2(2, 5)
# Create a vector and assign x and y manually.
var b = Vector2()
b.x = 3
b.y = 1

Adicionando vetores

Ao somar ou subtrair dois vetores, os componentes correspondentes são somados:

var c = a + b  # (2, 5) + (3, 1) = (5, 6)

Também podemos ver isso visualmente adicionando o segundo vetor ao final do primeiro:

../../_images/vector_add1.png

Observe que somar a + b dá o mesmo resultado que b + a.

Multiplicação escalar

Nota

Os vetores representam tanto a direção quanto a magnitude. Um valor que representa apenas a magnitude é chamado de escalar. Os escalares usam o tipo float no Godot.

Um vetor pode ser multiplicado por um escalar:

var c = a * 2  # (2, 5) * 2 = (4, 10)
var d = b / 3  # (3, 6) / 3 = (1, 2)
var e = d * -2 # (1, 2) * -2 = (-2, -4)
../../_images/vector_mult1.png

Nota

Multiplicar um vetor por um escalar positivo não altera sua direção, apenas sua magnitude. Multiplicar por um escalar negativo resulta em um vetor na direção oposta. É assim que você escala um vetor.

Aplicações práticas

Vejamos dois usos comuns para a soma e subtração de vetores.

Movimento

Um vetor pode representar qualquer quantidade com magnitude e direção. Exemplos típicos são: posição, velocidade, aceleração e força. Nesta imagem, a espaçonave no passo 1 tem um vetor de posição de (1, 3) e um vetor de velocidade de (2, 1). O vetor de velocidade representa o quão longe a nave se move a cada passo. Podemos encontrar a posição para o passo 2 somando a velocidade à posição atual.

../../_images/vector_movement1.png

Dica

A velocidade mede a mudança na posição por unidade de tempo. A nova posição é encontrada somando a velocidade multiplicada pelo tempo decorrido (aqui assumido como uma unidade, por exemplo, 1 s) à posição anterior.

Em um cenário típico de jogo 2D, você teria uma velocidade em pixels por segundo e a multiplicaria pelo parâmetro delta (tempo decorrido desde o quadro anterior) a partir dos callbacks _process() ou _physics_process().

Apontando para um alvo

Neste cenário, você tem um tanque que deseja apontar sua torre para um robô. Subtrair a posição do tanque da posição do robô resulta no vetor que aponta do tanque para o robô.

../../_images/vector_subtract2.webp

Dica

Para encontrar um vetor que aponta de A para B, use B - A.

Vetores unitários

Um vetor com magnitude de 1 é chamado de vetor unitário. Eles também são chamados às vezes de vetores de direção ou normais. Os vetores unitários são úteis quando você precisa acompanhar uma direção.

Normalização

Normalizar um vetor significa reduzir seu comprimento para 1 preservando sua direção. Isso é feito dividindo cada um de seus componentes por sua magnitude. Como esta é uma operação muito comum, o Godot fornece um método dedicado normalized() para isso:

a = a.normalized()

Aviso

Como a normalização envolve a divisão pelo comprimento do vetor, você não pode normalizar um vetor de comprimento 0. Tentar fazer isso normalmente resultaria em um erro. No GDScript, no entanto, tentar chamar o método normalized() em um vetor de comprimento 0 deixa o valor inalterado e evita o erro para você.

Reflexão

Um uso comum de vetores unitários é indicar normais. Vetores normais são vetores unitários alinhados perpendicularmente a uma superfície, definindo sua direção. Eles são comumente usados para iluminação, colisões e outras operações que envolvem superfícies.

Por exemplo, imagine que temos uma bola em movimento que queremos rebater em uma parede ou outro objeto:

../../_images/vector_reflect1.png

A normal da superfície tem um valor de (0, -1) porque esta é uma superfície horizontal. Quando a bola colide, pegamos seu movimento restante (a quantidade que sobra quando ela atinge a superfície) e o refletimos usando a normal. No Godot, existe um método bounce() para lidar com isso. Aqui está um exemplo de código do diagrama acima usando um CharacterBody2D:

var collision: KinematicCollision2D = move_and_collide(velocity * delta)
if collision:
    var reflect = collision.get_remainder().bounce(collision.get_normal())
    velocity = velocity.bounce(collision.get_normal())
    move_and_collide(reflect)

Produto escalar

O produto escalar (dot product) é um dos conceitos mais importantes na matemática de vetores, mas frequentemente é mal compreendido. O produto escalar é uma operação em dois vetores que retorna um escalar. Ao contrário de um vetor, que contém magnitude e direção, um valor escalar possui apenas magnitude.

A fórmula para o produto escalar assume duas formas comuns:

../../_images/vector_dot1.png

e

../../_images/vector_dot2.png

A notação matemática ||A|| representa la magnitude do vetor A, e Ax significa o componente x do vetor A.

No entanto, na maioria dos casos, é mais fácil usar o método embutido dot(). Observe que a ordem dos dois vetores não importa:

var c = a.dot(b)
var d = b.dot(a)  # These are equivalent.

O produto escalar é mais útil quando usado com vetores unitários, fazendo com que a primeira fórmula se reduza a apenas cos(θ). Isso significa que podemos usar o produto escalar para nos dizer algo sobre o ângulo entre dois vetores:

../../_images/vector_dot3.png

Ao usar vetores unitários, o resultado estará sempre entre -1 (180°) e 1 (0°).

Orientação (Voltado para)

Podemos usar esse fato para detectar se um objeto está voltado para outro objeto. No diagrama abaixo, o jogador P está tentando evitar os zumbis A and B. Assumindo que o campo de visão de um zumbi seja de 180°, eles conseguem ver o jogador?

../../_images/vector_facing2.png

As setas verdes fA e fB são vetores unitários que representam a direção para onde o zumbi está voltado, e o semicírculo azul representa seu campo de visão. Para o zumbi A, encontramos o vetor de direção AP que aponta para o jogador usando P - A e o normalizamos, no entanto, o Godot possui um método auxiliar para fazer isso chamado direction_to(). Se o ângulo entre este vetor e o vetor de direção frontal for menor que 90°, então o zumbi consegue ver o jogador.

Em código, ficaria assim:

var AP = A.direction_to(P)
if AP.dot(fA) > 0:
    print("A sees P!")

Produto vetorial

Assim como o produto escalar, o produto vetorial (cross product) é uma operação em dois vetores. No entanto, o resultado do produto vetorial é um vetor com uma direção que é perpendicular a ambos. Sua magnitude depende do ângulo relativo entre eles. Se dois vetores forem paralelos, o resultado de seu produto vetorial será um vetor nulo.

../../_images/vector_cross1.png ../../_images/vector_cross2.png

O produto vetorial é calculado da seguinte forma:

var c = Vector3()
c.x = (a.y * b.z) - (a.z * b.y)
c.y = (a.z * b.x) - (a.x * b.z)
c.z = (a.x * b.y) - (a.y * b.x)

Com o Godot, você pode usar o método embutido Vector3.cross():

var c = a.cross(b)

O produto vetorial não é matematicamente definido em 2D. O método Vector2.cross() é um análogo comumente usado do produto vetorial 3D para vetores 2D.

Nota

No produto vetorial, a ordem importa. a.cross(b) não dá o mesmo resultado que b.cross(a). Os vetores resultantes apontam em direções opostas.

Calculando normais

Um uso comum de produtos vetoriais é encontrar a normal da superfície de um plano ou superfície no espaço 3D. Se tivermos o triângulo ABC, podemos usar a subtração de vetores para encontrar duas arestas AB e AC. Usando o produto vetorial, AB × AC produz um vetor perpendicular a ambas: a normal da superfície.

Aqui está uma função para calcular a normal de um triângulo:

func get_triangle_normal(a, b, c):
    # Find the surface normal given 3 vertices.
    var side1 = b - a
    var side2 = c - a
    var normal = side1.cross(side2)
    return normal

Apontando para um destino

Na seção do produto escalar acima, vimos como ele poderia ser usado para encontrar o ângulo entre dois vetores. No entanto, em 3D, essa informação não é suficiente. Também precisamos saber em torno de qual eixo rotacionar. Podemos descobrir isso calculando o produto vetorial entre a direção frontal atual e a direção do alvo. O vetor perpendicular resultante é o eixo de rotação.

Mais informações

Para mais informações sobre o uso de matemática vetorial no Godot, consulte os seguintes artigos: