Vetores e Trigonometria

De Aulas
Revisão de 14h11min de 16 de abril de 2025 por Admin (discussão | contribs)
(dif) ← Edição anterior | Revisão atual (dif) | Versão posterior → (dif)

Links Relacionados: Matemática e Física para Jogos, Jogos Digitais

Descrição

Na Computação Gráfica — especialmente nos jogos digitais que utilizam esse recurso para criar apresentações visuais e permitir a interação com o ambiente — é comum o uso de grandezas vetoriais, como a direção e força do vento, a gravidade ou a direção para onde um personagem está olhando.

Essas informações são representadas por vetores, que indicam tanto a direção quanto a intensidade (ou módulo) de uma ação. Um vetor possui características como sentido, direção e magnitude, sendo geralmente representado graficamente por uma seta.

À esquerda temos uma seta diagonal apontando para em cima à direita. No meio uma seta vertical menor apontando para baixo e à direita uma seta horizontal maior apontando para a esquerda.

Características dos Vetores

Para representar e caracterizar nossos vetores, precisamos de informações como o tamanho, a direção e o sentido. E graficamente é uma seta que tem o ponto de origem, que é o ponto (0, 0) dela, e o ponto destino que é a ponta de uma seta, que representa as informações do vetor.

Gráfico bidimensional com os eixos x e y e um vetor saindo do ponto (0, 0) até (4, 3), dando o deslocamento de x e y.
  • Tamanho: é a intensidade. É também chamado de magnitude;
  • Direção: indica a inclinação do vetor no espaço;
  • Sentido: mostra para onde o vetor está apontando;
Observação: vetores não possuem como propriedade a posição.

Vetores representam deslocamentos com sinal em cada eixo.

  • Em 2 dimensões têm-se um valor para x e y.
  • Em 3 dimensões, têm-se x, y e z.
  • Em 4 dimensões, têm-se x, y, z e w.

Graficamente, representamos vetores como uma flecha. A posição da flecha não importa, desde que sua direção, tamanho e sentido sejam mantidos.

Representação na Programação

No C++, python ou no Java, esses valores virariam variáveis float. Abaixo temos a representação, em linguagem de programação, da estrutura de dados de um ponto. No caso, um ponto no plano, espaço bidimensional, representado por duas variáveis de ponto flutuante x e y. A origem é sempre zero, então não é necessário representar.

class Vector2D:
    def __init__(self, x: float, y: float):
        self.x: float = x
        self.y: float = y

Tamanho

Para encontrarmos o tamanho, utilizamos a fórmula da Distância Euclidiana apresentada na última aula. Transcrevendo-a em código de programação, temos:

    def size(self) -> float:
        return math.sqrt((self.x * self.x) + (self.y * self.y))

Soma de Vetores

Também podemos utilizar operações de soma de vetores. Por exemplo:

Imagem de duas operações de vetores, obtendo o resultado da soma de um vetor a e um vetor b.
    def add(self, other: "Vector2D") -> None:
        self.x += other.x
        self.y += other.y

    @staticmethod
    def sum(a: "Vector2D", b: "Vector2D") -> "Vector2D":
        return Vector2D(a.x + b.x, a.y + b.y)

Um exemplo prático pode se ver na figura a seguir. Se quisermos representar a trajetória da bola, podemos somar o vetor gravidade ao vetor da trajetória. É possível também adicionar vetores como vento, por exemplo.

Na imagem, vemos um jogador de futebol chutando uma bola. Podemos perceber por meio da evolução dos vetores a balística acontecendo. Ao chutar, tempo 0, o vetor aponta diagonalmente para em cima a direita. Após 1 segundo, o vetor do chute é somado ao vetor gravidade, que é negativo verticalmente, apontando para baixo. Isso altera no sentido horário o ângulo do vetor do chute. A cada novo segundo, novamente o vetor gravidade é adicionado. Nesse sentido, a bola do jogador inicialmente está indo para frente e subindo, até que chega ao seu máximo para então descer em direção ao chão, mas ainda com uma velocidade horizontal à direita.

Vetores também podem ser multiplicados por valores. Para tal, o código abaixo efetua essa operação:

    def multiply(self, other: "Vector2D") -> None:
        self.x *= other.x
        self.y *= other.y

    @staticmethod
    def multiply_vectors(a: "Vector2D", b: "Vector2D") -> "Vector2D":
        return Vector2D(a.x * b.x, a.y * b.y)

Normalização

ara normalizar um vetor, ou seja, ajustar seu tamanho para que ele tenha comprimento 1, utilizamos a operação de normalização. Isso mantém a direção do vetor, mas reduz sua magnitude para 1. Esse vetor unitário pode, então, ser multiplicado por um valor escalar caso desejemos ajustar seu tamanho.

Para normalizar, basta dividir as componentes x e y do vetor pelo seu comprimento. Veja o exemplo a seguir:

    def normalize(self) -> "Vector2D":
        s: float = self.size()
        return Vector2D(self.x / s, self.y / s)

Vetores e Ângulos

Se quisermos que o nosso atacante chute uma bola baseado no ângulo e na força, podemos criar um vetor com essas informações.

Se for dado um chute de ângulo 50 graus e a uma força 10, poderíamos chamar a função a baixo. Lembrando que o ângulo é em radianos (1 grau = PI/180 radianos).

    @staticmethod
    def by_size_and_angle(size: float, angle: float) -> "Vector2D":
        radians = math.radians(angle)  # Converte graus para radianos
        return Vector2D(math.cos(radians) * size, math.sin(radians) * size)

Da mesma forma, podemos retornar qual o ângulo (em radianos) de um vetor por meio do arco tangente de x e y, conforme o código abaixo:

    def angle(self) -> float:
        return math.atan2(self.y, self.x) # Retorna o ângulo em radianos

Para facilitar, podemos fazer funções de conversão de radianos para graus e graus para radianos:

# Converte graus para radianos
def grad2radians(grad: float) -> float:
    return (grad * math.pi) / 180

# Converte radianos para graus
def radians2grad(radians: float) -> float:
    return (radians * 180) / math.pi

Também podemos rotacionar um vetor, utilizando a operação de rotação (veremos melhor sobre a rotação nas próximas aulas). Contudo, nesse caso estamos trabalhando apenas com vetores, em que a posição não importa. Então estamos sempre tendo como base o eixo (0, 0) do vetor, não necessitando das operações de translação.

   def rotate(self, angle: float):
        # Calculando o seno e o cosseno uma única vez
        s = math.sin(angle)
        c = math.cos(angle)

        # Fórmula de rotação
        new_x = (self.x * c) - (self.y * s)
        new_y = (self.x * s) + (self.y * c)

        # Atualizando as coordenadas do vetor
        self.x = new_x
        self.y = new_y

        return self  # Retorna o próprio objeto para encadeamento de métodos
                     # (ex: v.rotate(...).rotate(...))

Ângulo entre dois vetores

Para encontrar o ângulo entre dois vetores, é utilizado o produto escalar, onde A ⋅ B é o produto escalar entre os pontos A e B, sendo que o produto escalar de A e B é igual à Ax multiplicado por Bx mais Ay multiplicado por By:

ou, temos que o produto escalar dos vetores A e B é igual à magnitude de A (ou comprimento) multiplicado pela magnitude de B (ou comprimento) multiplicado pelo ângulo entre os vetores A e B.

Assim, podemos dizer que o ângulo entre A e B é igual ao produto escalar de A e B dividido pelo Comprimento de A multiplicado pelo comprimento de B.:

Onde |A| e |B| são respectivamente os tamanhos de A e B e α o ângulo que queremos calcular. Agora, note como o cálculo seria simplificado se A e B fossem vetores de tamanho 1:

ou seja, com os comprimentos de A e B sendo 1, ficamos simplesmente como sendo que o ângulo de A e B é o produto escalar de A e B:

Evitar o cálculo do tamanho reduz muito processamento, por isso é bom usar vetores normalizados para representar direções. O método para o cálculo do produto escalar fica assim:

    def dot(self, other: "Vector2D") -> float:
        """
        Calcula o produto escalar entre este vetor e outro vetor.
        Fórmula: A · B = Ax * Bx + Ay * By
        """
        return (self.x * other.x) + (self.y * other.y)

E o do ângulo entre os dois vetores unitários, assim:


    def angle_between(self, other: "Vector2D") -> float:
        dp = self.dot(other)
        size_product = self.size() * other.size()
        # Evita divisão por zero caso um dos vetores tenha tamanho zero
        if size_product == 0:
            return 0.0  # ou talvez levantar exceção

        # Garantir que o valor do produto escalar esteja entre -1 e 1
        cos_angle = dp / size_product
        cos_angle = max(-1.0, min(1.0, cos_angle))  # clamp

        # Calcular o ângulo em radianos
        ang = math.acos(cos_angle)

        # Produto vetorial 2D para decidir o sinal
        cross = self.x * other.y - self.y * other.x

        # Retornar o ângulo positivo ou negativo dependendo do sinal do produto escalar
        return ang if cross >= 0 else -ang

Exercícios

1. Faça um programa com as seguintes características:

  • Clique na tela para definir o primeiro ponto (Ponto A)
  • Clique na tela para definir o segundo ponto (Ponto B)
    • Desenhe uma linha entre o Ponto A e o Ponto B
    • Informe na tela, em elementos tipo texto:
      • a distância entre os pontos
      • o ângulo do Ponto A para o Ponto B
  • A cada novo clique, o Ponto B se tornará o Ponto A e o Ponto B será o novo local onde foi dado o clique. Redesenhe a linha entre A e B e refaça os cálculos e apresente-os na tela.


3. Faça um programa com as seguintes características:

  • Desenhe do lado esquerdo da tela e abaixo um pequeno canhão.
  • A movimentação do mouse para cima e para baixo altera o ângulo de tiro.
  • A movimentação do mouse para a esquerda e direita altera a força do tiro.
  • O clique do mouse efetua um disparo (represente o disparo com um ponto grande ou com uma imagem)
  • Quando a bala do canhão cair, calcule a distância entre o canhão e a bala.
  • Considere a gravidade como 9.8 m/s.
  • Mostre na tela as informações de:
    • Ângulo do tiro
    • Força do tiro
    • Distância percorrida pela bala do canhão.


4. No programa anterior, acrescente vento. Para isso:

  • Crie um vetor de direção e velocidade do vento. Como estamos em um espaço bidimensional, considere apenas contra o canhão ou a favor do tiro do canhão (direita para esquerda ou esquerda para direita).
  • Altere a velocidade do vento por meio das setas direcionais ( <- e -> ).
  • Acrescente a informação da direção do vento e força.