RayCasting

RayCasting se trata de uma técnica de mapeamento 3D de um plano 2D. Era utilizado muito nos primeiros jogos pseudo-3d, como por exemplo o Wolfenstein 3d. Esta técnica é bastante simples, porém exige um mínimo de conhecimento de matemática geométrica. Este post lhe dará uma explicação simples de uma implementação do RayCasting em Java.

O código deste tutorial é básico. Não está sendo considerada as técnicas de adição de textura, luminosidade, reflexo, etc... Apenas o básico para ter um início bem sutil.

Pois bem, vamos lá!



Criação do projeto, classe e métodos

1. Primeiramente, em nossa IDE (estou usando o NetBeans), criaremos um novo projeto.



2. Agora iremos criar uma classe. Para esta classe dei o nome de RayCasting. Ela deverá conter o método main.



3. A classe deverá ser filha da classe JFrame para criação de uma janela swing. Também deve implementar as interfaces: Runnable e KeyListener

Runnable: Para criação de uma Thread
KeyListener: Para criação da movimentação do personagem a partir das setas do teclado



4. Como podemos ver, ao informar as interfaces devemos adicionar os métodos abstratos a serem implementados. Os métodos são:

Runnable:
run() : Método para execução da Thread.

KeyListener:
keyTyped(KeyEvent e): Este método não será utilizado.
keyPressed(KeyEvent e): Capturar eventos do teclado (tecla pressionada).
keyReleased(KeyEvent e): Capturar eventos do teclado (tecla solta).



5. Iremos adicionar mais um método para ser sobrescrito. Este método é o "paint".



6. Criaremos um construtor para a classe, para iniciar as variáveis que serão implementadas.



Constantes

7. Criaremos as constantes de tamanho da janela. Irei usar um tamanho de 800 x 600.

Será criada também as constantes para pegar o centro da largura e altura.



8. Devemos criar agora as constantes de Ângulos. Estas constantes são de grande importância para o desenvolvimento. Para criação das mesmas precisamos escolher o ângulo que será nosso "FOV" (ângulo de visão do personagem). Normalmente o "FOV" é de 60°. A visão do personagem será o total da largura de nossa tela. Portanto:



9. Precisamos de constantes para melhor definir a movimentação do personagem.

Por exemplo: Andar para cima é representado por 1, parado por -1...



Atributos

10. Iremos precisar de alguns atributos de imagem, gráficos e etc...

Criaremos os seguintes atributos:

BufferedImage: Para criar a imagem em memória e desenhar na tela;
Graphics: Para pegar os gráficos da imagem e em cima dele efetuar os desenhos;
Color[]: Array de cores para definir as cores das paredes;
int[][]: Matriz que será nosso mapa;
int: Representar evento do personagem;



Construtor

11. Iremos atribuir os atributos declarados no construtor.

BufferedImage: Irá possuir o tamanho da tela e o tipo de cor "RGB";
Graphics irá ser os gráficos no BuferredImage;
Color[] ganhará algumas cores primárias;
int[][] será o mapa. Sendo 1,2,3,n = parede e 0 = vazio;



12. Adicionar o KeyListener em nossa classe para poder fazer a validação de eventos. Aproveitando também, setaremos um título, tamanho, etc.. para nossa janela.



13. Ao final do construtor iniciar uma Thread para rodar a aplicação. Isto irá chamar nosso método run().



Lançamento de Raios

14. Devemos possuir algumas variáveis de posição e angulo para começar a lançar nossos raios e criar nosso RayCasting. Precisamos da posição do jogador (posJogador), posição do raio (raio), incremento da posição do raio para guiar o raio em sua trajetória (etapas), angulo de visão do personagem (anguloPersonagem) e angulo do raio (anguloRaio).



15. Vamos fazer um loop infinito para rodar nossa Thread. Só termina quando encerramos o programa.



16. Setaremos o angulo do raio para cada passada do loop. O angulo do raio deve ser o angulo do personagem menos 30°. Será criado um outro loop para realizar o lançamento dos raios que irá lançar raios diante de 60°.




17. Aqui começa a mágica da coisa. Iremos fazer um loop de lançamento de raio que lançará 800 raios. (800 é nosso FOV). Lançando 800 raios irá desenhar em nossa tela de 800 x 600 as paredes encontradas no mapa.



18. Devemos atribuir nossas variáveis de posição conforme cada passada do loop. Para a posição dos raios será obviamente atribuído a posição do jogador.



19. Precisamos atribuir as variáveis que guiarão nossos raios no mapa. Elas são de extrema importância para o RayCasting. Utilizaremos as funções de (cosseno e seno) más os mesmos trabalham com o tipo "Radiano" e não "Grau". Faremos então um método para converter graus em radiano.



20. Agora atribuiremos as variáveis de etapas. A divisão por 80 irá melhorar a qualidade das paredes e servir como escala para o mapa. (tamanho de cada bloco para parede)



21. Iremos agora fazer a lógica para encontrar as paredes no mapa. Para isto utilizaremos uma variável "parede" para armazenar a parede encontrada no mapa e uma variável "distância" para ser incrementada a cada busca por uma parede no mapa. Ela irá definir o tamanho da parede na imagem em tela.



22. Dentro do loop para que servirá para encontrar uma parede no mapa, faremos a lógica do percurso do raio. Cada passada do loop será incrementado a posição X e Y do raio conforme as etapas. Ao incrementar, será verificado se esta posição é uma parede no mapa. Se não for, ele incrementará mais uma vez e incrementará a distância.



23. Utilizando a distancia como este valor inteiro não teremos uma boa qualidade para as paredes podendo até mesmo ser não notável a aparência 3D. Então, para tornar a distancia um valor correspondente, faremos uma divisão e o resultado será atribuído na variável "alturaParede".Utilizei o valor (20000) pois ele dá uma resolução ótima.



24. Iremos desenhar agora as paredes. Utilizamos o "drawLine" pois as paredes serão feitas conforme a união das linhas desenhadas. cada passo será desenhadas 3 linhas na vertical, sendo a primeiro o "céu", a segundo a "parede" e a terceira o "chão". Assim, a união das linhas formará um efeito extraordinário.





25. Não podemos esquecer de que após concluir este raio, incrementar o angulo dos raio para ele jogar o próximo raio na posição correta, e não na mesma posição deste ultimo raio lançado.



26. Após jogar todos os raios e desenhar todas as linhas, precisamos apagar o que foi desenhado. Assim, será feito uma "leitura" do mapa novamente e desenhada as linhas novamente.


27. A parte do RayCasting está pronta, más ainda precisamos desenhar este efeito na tela. Para isto, usaremos o método sobrescrito "paint". Dentro deste método, deve ser feito um "drawImage" passando nosso "BufferedImage" por parâmetro para que seja desenhado na tela.



28. Para testar, podemos instanciar em nosso método main, um objeto desta classe, e executar o programa.



Evento do Personagem (Movimentação)

29. Para realizar a movimentação de nosso personagem utilizaremos os métodos do KeyListener. Para verificar qual tecla foi apertada, vamos fazer um "switch".

Se a tecla apertada for "Seta para Cima", atribuímos para nossa variável "eventoPersongaem" o valor da constante "CIMA". Ou seja, ganhará o valor 1.



30. Não podemos esquecer de que se o usuário soltar uma seta, mudar o valor da variável para -1 ou seja, atribuir o valor da constante "PARADO".



31. Faremos agora a validação da variável de evento ou seja, verificar se a variável é "CIMA", "BAIXO", "ESQUERDA" ou "DIREITA". Se for cima, incrementar as variáveis de posição e assim por diante. Faremos isto no método run(), após ser lançado todos os raios e ser desenhado na tela.

Para atribuir a nova posição do personagem, iremos partir do angulo do personagem, realizar um calculo e setar a posição. Este calculo é feito pelas funções de (cosseno e seno) igual ao lançamento dos raios, más estas funções irão utilizar o angulo do personagem.

A divisão (/ 32) serve para lidar com a velocidade de movimentação do personagem.

Fluxo:
Setar angulo do raio > Lançar os raios > Desenhar > Mudar posição (Evento)



32.Precisamos implementar uma validação de colisão. Para isto iremos precisar de 2 variáveis para pegar a posição do personagem atual, realizar o calculo da nova posição, e se esta nova posição for uma parede, setar a posição atual como a antiga do personagem.



33. Agora faremos a parte de virada para esquerda ou direita. Para isto utilizaremos o angulo do personagem e a constante "ANGULO6" para cada virada incrementar ou decrementar o valor do angulo de 6°.

A divisão (/ 6) server para controlar a velocidade da virada do personagem.



34. Assim finalizamos o código. Basta rodar o programa e ver o efeito acontecer. É impressionante.

Podemos adicionar novas parede nos mapa controlando a cor da parede pelo seu número.

Utilizei o seguinte mapa:



Resultado:



Comentários

Postar um comentário

Postagens mais visitadas deste blog

Mode 7

Mode 7 - Rotation