Desenvolvendo o campo de visão de um personagem em um plano 2D

Detectando objetos que entram dentro do campo de visão do personagem. Útil para servir de "gatilho" para eventos em um jogo.

Postado em 04 abril 2023

Atualizado em 04 abril 2023



Introdução

Esse projeto é um esboço de um jogo 2D visto do topo da tela. As funcionalidades que serão desenvolvidas serão o campo de visão do personagem e o detector de objetos dentro do campo de visão. Antes de começar, recomenda-se um certo conhecimento em relação à vetores geométricos e o uso de cálculos trigonométricos em desenvolvimento de jogos bidimensionais que pode ser visto nesse tutorial do esboço de jogo de tiro 2D.

O campo de visão é útil para criar inimigos com uma visão limitada em relação ao ângulo e a distância em relação ao objeto alvo. O campo de visão acompanha a direção que o personagem está apontando e se torna cada vez mais amplo conforme a distância da visão pode chegar mais longe. Os humanos tem um ângulo de visão aproximado de 150 graus, enquanto outros animais como alguns pássaros podem ter uma visão de 360 graus. Isso permite que o jogo possa ficar mais realista e estratégico, tornando-se mais divertido.

Resultado final

O projeto foi desenvolvido em javascript, com a biblioteca p5. O resultado final pode ser verificado no link abaixo:

A biblioteca P5 usa apenas duas funções: “setup” e “draw”. O “setup” é chamado apenas uma vez ao executar o programa e a função “draw” é chamada repetidamente.

Planejamento

O projeto possuirá duas classes para representar os objetos na tela:

  • Player
  • Obstacle

O player pode ser controlado pelo usuário usando as teclas de direção. O obstacle é um objeto contendo apenas coordenadas e nada mais.

campo de visão do player exemplo

A imagem acima mostra que apenas os objetos dentro do campo de visão do player ficam vermelhos. Os outros objetos amarelos não estão dentro do campo de visão do player e nem se situam a uma distância mínima na qual seria necessário para ser “enxergado”.

Campo de visão do player

O campo de visão do player será de 90 graus, portanto tendo dois ângulos de 45 graus para representar os lados esquerdo e direito. Lembrando que os ângulos serão calculados em radianos.

let FOV = Math.PI / 2; // Campo de visão de 90 graus
let HALF_FOV = FOV / 2; // Metade do campo de visão de 45 graus
let MAG = 100; // Magnitude de quão longe o player pode ver

Os valores acima serão constantes e essenciais para definir o ângulo e a distância que entre dois pontos. Três linhas serão desenhadas a partir do centro do player e representarão o campo de visão do player.

ângulos do campo de visão

Todos os cálculos de FOV (Field of view) serão baseados na linha no centro do campo de visão do player. A partir dele, calcularemos se o ângulo entre o objeto à frente está situado em uma posição entre 45 graus em relação à reta no meio do campo de visão.

Fórmula utilizada

A fórmula utilizada para calcular os objetos dentro do campo de visão será a seguinte:
cosθ=a.bab cos\theta = \frac{a.b}{|\vec{a}||\vec{b}|}

O numerador da fórmula acima será o produto escalar dos vetores. O denominador será a multiplicação dos módulos dos vetores. O cosseno do resultado da divisão do numerador com o denominador será o ângulo que precisamos.

Desenvolvimento

O desenvolvimento será divido nas seguintes etapas:

  • Calcular e exibir o campo de visão do player (drawFov)
  • Detecção de objetos de do campo de visão (isInsideFov)

Outras funcionalidades como deslocamento e a rotação do player usando as setas de direção, serão herdados do projeto anterior (esqueleto de um jogo de tiro 2D).

Calcular e exibir o campo de visão do player (drawFov)

A função “drawFov” será implementada dentro da classe do player. Essa função é responsável por exibir o campo de visão na tela, que será efetuado desenhando três linhas na tela.

let des = createVector(this.pos.x + cos(this.angle) * MAG, this.pos.y + sin(this.angle) * MAG);
line(this.pos.x, this.pos.y, des.x, des.y)

A variável “des” irá armazenar o vetor que indica a extremidade da linha central do campo de visão. O cosseno do ângulo atual será multiplicado pela magnitude declarada com a variável “MAG” e será somado com a posição de origem, que é a posição do player. A mesma lógica acontece com seno. Lembrando que o cosseno é somado com a posição horizontal e o seno é somado com a posição vertical do player.

As duas outras linhas serão calculadas da mesma forma, apenas iremos modificar manualmente o ângulo atual em 45 graus no sentido negativo (esquerda) e no sentido positivo (direita).

let halfLeft = createVector(this.pos.x + cos(this.angle - HALF_FOV) * 100, this.pos.y + sin(this.angle - HALF_FOV) * 100)
line(this.pos.x, this.pos.y, halfLeft.x, halfLeft.y)

let halfRight = createVector(this.pos.x + cos(this.angle + HALF_FOV) * 100, this.pos.y + sin(this.angle + HALF_FOV) * 100)
line(this.pos.x, this.pos.y, halfRight.x, halfRight.y)

Uma vez que o código acima foi implementado, invocaremos na função “draw” do P5.

draw() {
    player.drawFov();
}

Detecção de objetos de do campo de visão (isInsideFov)

Esse será o principal algoritmo desse projeto. Ao verificarmos que há algum objeto dentro do campo de visão do player, mudamos a cor desse objeto para tomarmos conhecimento de que o objeto entrou no nosso campo de visão com sucesso.

A função “isInsideFov” será declarada em escopo global e não pertencerá a nenhuma classe. Ela terá três parâmetros:

function isInsideFov(origin, view, target) { ... }

Todos os parâmetros serão vetores e terão as seguintes responsabilidades:

  • origin: Origem do campo de visão. Geralmente a posição do player
  • view: A extremidade do campo de visão do player. Será a extremidade da linha no centro do campo de visão do player.
  • target: Posição do objeto na tela.

O conteúdo dentro da função será o seguinte:

let targetDistance = p5.Vector.sub(target, origin); // Distância entre o objeto e o player
let viewDistance = p5.Vector.sub(view, origin); // Distância que o player é capaz de enxergar
  
let dotProduct = p5.Vector.dot(targetDistance, viewDistance); // Produto escalar das duas variáveis acima
let totalMag = targetDistance.mag() * viewDistance.mag(); // Multiplicação das magnitudes das duas variáveis acima
let result = dotProduct / totalMag;
  
let angleInRad = acos(result); // Invés do cosseno, descobre o arcocosseno do resultado final em radianos

// Checa se o ângulo está dentro dos 45 graus e tem uma magnitude menor do que a declarada
if (angleInRad < HALF_FOV && targetDistance.mag() < MAG) {
  return true;
}
  
return false;

Caso o objeto esteja dentro do campo de visão, será retornado “true”. Esse valor boolean será calculado a cada frame para cada objeto existente na tela:

for(let i = 0; i < obstacles.length; i++) {
    if (isInsideFov(player.pos, player.getAngleFov(), obstacles[i].pos)) {
	  // Muda a cor do objeto para vermelho caso esteja dentro do campo de visão
      fill(color(255,0,0))
    } else {
	  // Muda a cor do objeto para amarelo caso esteja fora do campo de visão
      fill(color(255,255,0))
    }
  
	// Exibi o obstáculo na tela
    obstacles[i].draw()
}

Se tudo funcionar conforme o previsto, teremos o resultado abaixo:
resultado final

Conclusão

O campo de visão simula a capacidade de um objeto em enxergar uma determinada área na tela. A implementação do campo de visão traz mais complexidades em jogos, tornando-os mais estratégicos e divertidos.

Cálculos de ângulos usando vetores são um conceito básico no desenvolvimento de jogos. Ao aprendermos a interpretar e aplicar fórmulas já elaboradas por programadores passados, podemos economizar tempo e melhorar a eficiência do jogo.

Postagens mais vistas

Os 5 principais componentes do computador

Os 5 principais componentes do computador são a unidade de controle, unidade aritmética e lógica, memória, dispositivo de entrada e dispositivo de saída.

Portas TCP e UDP

A porta é um número de 16 bits que é adicionado no final do endereço IP, insinuando qual aplicativo está vinculado e atuando nessa porta.

LAN

Rede local de computadores (LAN) é um conjunto de computadores ou dispositivos conectados uns aos outros de forma isolada em um pequeno local.

Retornar aos projetos