Como construir um jogo de realidade virtual multijogador em tempo real (parte 1)
Publicados: 2022-03-10Nesta série de tutoriais, construiremos um jogo de realidade virtual multijogador baseado na web no qual os jogadores precisarão colaborar para resolver um quebra-cabeça. Usaremos A-Frame para modelagem VR, MirrorVR para sincronização em tempo real entre dispositivos e A-Frame Low Poly para estética low-poly. No final deste tutorial, você terá uma demonstração online totalmente funcional que qualquer pessoa pode jogar.
Cada par de jogadores recebe um anel de orbes. O objetivo é “ligar” todos os orbes, onde um orbe está “ligado” se estiver elevado e brilhante. Um orbe está “desligado” se estiver mais baixo e escuro. No entanto, certos orbes “dominantes” afetam seus vizinhos: se ele mudar de estado, seus vizinhos também mudarão de estado. Apenas o jogador 2 pode controlar os orbes dominantes, enquanto apenas o jogador 1 pode controlar os orbes não dominantes. Isso força os dois jogadores a colaborar para resolver o quebra-cabeça. Nesta primeira parte do tutorial, construiremos o ambiente e adicionaremos os elementos de design para nosso jogo de RV.
As sete etapas neste tutorial são agrupadas em três seções:
- Configurando a cena (etapas 1–2)
- Criando os Orbes (Passos 3–5)
- Tornando os orbes interativos (etapas 6 a 7)
Esta primeira parte será concluída com um orbe clicável que liga e desliga (como na foto abaixo). Você usará A-Frame VR e várias extensões A-Frame.
Configurando a cena
1. Vamos com uma cena básica
Para começar, vamos dar uma olhada em como podemos configurar uma cena simples com um terreno:
As três primeiras instruções abaixo são extraídas do meu artigo anterior. Você começará configurando um site com uma única página HTML estática. Isso permite que você codifique a partir de sua área de trabalho e implante automaticamente na web. O site implantado pode ser carregado em seu telefone celular e colocado dentro de um headset de RV. Como alternativa, o site implantado pode ser carregado por um headset VR autônomo.
Comece navegando até glitch.com. Então, faça o seguinte:
- Clique em “Novo Projeto” no canto superior direito,
- Clique em “hello-webpage” no menu suspenso,
- Em seguida, clique em index.html na barra lateral esquerda. Vamos nos referir a isso como seu “editor”.
Agora você deve ver a seguinte tela Glitch com um arquivo HTML padrão.
Assim como no tutorial vinculado acima, comece excluindo todo o código existente no arquivo index.html atual. Em seguida, digite o seguinte para um projeto básico de webVR, usando A-Frame VR. Isso cria uma cena vazia usando a iluminação e a câmera padrão do A-Frame.
<!DOCTYPE html> <html> <head> <title>Lightful</title> <script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script> </head> <body> <a-scene> </a-scene> </body> </html>
Levante a câmera até a altura de pé. De acordo com as recomendações do A-Frame VR (problema do Github), envolva a câmera com uma nova entidade e mova a entidade pai em vez da câmera diretamente. Entre suas tags a-scene
nas linhas 8 e 9, adicione o seguinte.
<!-- Camera! --> <a-entity position="0 3 0"> <a-camera wasd-controls look-controls></a-camera> </a-entity>
Em seguida, adicione uma caixa grande para indicar o solo, usando a-box
. Coloque-o diretamente abaixo de sua câmera da instrução anterior.
<!-- Action! --> <a-box shadow width="75" height="0.1" depth="75" position="0 -1 0" color="#222"></a-box>
Seu arquivo index.html agora deve corresponder exatamente ao seguinte. Você pode encontrar o código-fonte completo aqui, no Github.
<html> <head> <title>Lightful</title> <script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script> </head> <body> <a-scene> <!-- Camera! --> <a-entity position="0 3 0"> <a-camera wasd-controls look-controls></a-camera> </a-entity> <!-- Action! --> <a-box shadow width="75" height="0.1" depth="75" position="0 -1 0" color="#222"></a-box> </a-scene> </body> </html>
Isso conclui a configuração. Em seguida, personalizaremos a iluminação para uma atmosfera mais misteriosa.
2. Adicione Atmosfera
Nesta etapa, vamos configurar a neblina e a iluminação personalizada.
Adicione um nevoeiro, que obscurecerá objetos distantes para nós. Modifique a tag a-scene
na linha 8. Aqui, adicionaremos uma névoa escura que rapidamente obscurece as bordas do solo, dando o efeito de um horizonte distante.
<a-scene fog="type: linear; color: #111; near:10; far:15"></a-scene>
O cinza escuro #111
aparece linearmente de uma distância de 10 a uma distância de 15. Todos os objetos a mais de 15 unidades de distância são completamente obscurecidos e todos os objetos a menos de 10 unidades são completamente visíveis. Qualquer objeto no meio é parcialmente obscurecido.
Adicione uma luz ambiente para iluminar os objetos do jogo e uma luz unidirecional para acentuar as superfícies reflexivas que você adicionará mais tarde. Coloque-o diretamente após a tag a-scene
que você modificou na instrução anterior.
<!-- Lights! --> <a-light type="directional" castshadow="true" intensity="0.5" color="#FFF" position="2 5 0"></a-light> <a-light intensity="0.1" type="ambient" position="1 1 1" color="#FFF"></a-light>
Diretamente abaixo das luzes da instrução anterior, adicione um céu escuro. Observe que o cinza escuro #111
combina com o nevoeiro distante.
<a-sky color="#111"></a-sky>
Isso conclui as modificações básicas no clima e, mais amplamente, na configuração da cena. Verifique se seu código corresponde exatamente ao código-fonte da Etapa 2 no Github. Em seguida, adicionaremos um orbe de baixo poli e começaremos a personalizar a estética do orbe.
Criando os orbes
3. Crie um Orbe Low-Poly
Nesta etapa, criaremos um orbe giratório e reflexivo, conforme ilustrado abaixo. O orbe é composto por duas esferas estilizadas de baixo poli com alguns truques para sugerir material refletivo.
Comece importando a biblioteca low-poly em sua tag head
. Insira o seguinte entre as linhas 4 e 5.
<script src="https://cdn.jsdelivr.net/gh/alvinwan/[email protected]/dist/aframe-low-poly.min.js"></script>
Crie um carrossel, um wrapper e um contêiner orb. O carousel
conterá vários orbes, o wrapper
nos permitirá girar todos os orbes em torno de um eixo central sem girar cada orbe individualmente, e o container
conterá - como o nome sugere - todos os componentes do orbe.
<a-entity> <a-entity rotation="0 90 0" class="wrapper" position="0 0 0"> <a-entity class="container" position="8 3 0" scale="1 1 1"> <!-- place orb here --> </a-entity> </a-entity> </a-entity>
Dentro do recipiente do orbe, adicione o próprio orbe: uma esfera é levemente translúcida e deslocada, e a outra é completamente sólida. As duas superfícies refletivas combinadas imitam.
<a-entity class="orb" data-> <lp-sphere seed="0" shadow max-amplitude="1 1 1" position="-0.5 0 -0.5"></lp-sphere> <lp-sphere seed="0" shadow max-amplitude="1 1 1" rotation="0 45 45" opacity="0.5" position="-0.5 0 -0.5"></lp-sphere> </a-entity>
Finalmente, gire a esfera indefinidamente adicionando a seguinte tag a-animation
imediatamente após a lp-sphere
dentro da entidade .orb
na última instrução.
<a-animation attribute="rotation" repeat="indefinite" from="0 0 0" to="0 360 0" dur="5000"></a-animation>
Seu código-fonte para os wrappers orb e o próprio orb devem corresponder exatamente ao seguinte.
<a-entity> <a-entity rotation="0 90 0" class="wrapper" position="0 0 0"> <a-entity class="container" position="8 3 0" scale="1 1 1"> <a-entity class="orb" data-> <lp-sphere seed="0" shadow max-amplitude="1 1 1" position="-0.5 0 -0.5"></lp-sphere> <lp-sphere seed="0" shadow max-amplitude="1 1 1" rotation="0 45 45" opacity="0.5" position="-0.5 0 -0.5"></lp-sphere> <a-animation attribute="rotation" repeat="indefinite" from="0 0 0" to="0 360 0" dur="5000"></a-animation> </a-entity> </a-entity> </a-entity> </a-entity>
Verifique se seu código-fonte corresponde ao código-fonte completo da etapa 3 no Github. Sua visualização agora deve corresponder ao seguinte.
Em seguida, adicionaremos mais iluminação ao orbe para obter um tom dourado.
4. Acenda o Orbe
Nesta etapa, adicionaremos duas luzes, uma colorida e outra branca. Isso produz o seguinte efeito.
Comece adicionando a luz branca para iluminar o objeto por baixo. Usaremos uma luz pontual. Diretamente antes #orb0
mas dentro de #container-orb0
, adicione a seguinte luz de ponto de deslocamento.
<a-entity position="-2 -1 0"> <a-light distance="8" type="point" color="#FFF" intensity="0.8"></a-light> </a-entity>
Em sua visualização, você verá o seguinte.
Por padrão, as luzes não diminuem com a distância. Ao adicionar distance="8"
, garantimos que a luz decaia totalmente com uma distância de 8 unidades, para evitar que a luz pontual ilumine toda a cena. Em seguida, adicione a luz dourada. Adicione o seguinte diretamente acima da última luz.
<a-light class="light-orb" distance="8" type="point" color="#f90" intensity="1"></a-light>
Verifique se seu código corresponde exatamente ao código-fonte da etapa 4. Sua visualização agora corresponderá ao seguinte.
Em seguida, você fará sua modificação estética final no orbe e adicionará anéis rotativos.
5. Adicionar anéis
Nesta etapa, você produzirá o orbe final, conforme ilustrado abaixo.
Adicione um anel em #container-orb0
diretamente antes #orb0
.
<a-ring color="#fff" material="side:double" position="0 0.5 0" radius-inner="1.9" radius-outer="2" opacity="0.25"></a-ring>
Observe que o anel em si não contém cor, pois a cor será impregnada pela luz pontual na etapa anterior. Além disso, o material="side:double"
é importante porque, sem ele, a parte de trás do anel não seria renderizada; isso significa que o anel desapareceria na metade de sua rotação.
No entanto, a visualização apenas com o código acima não será diferente. Isso ocorre porque o anel está atualmente perpendicular à tela. Assim, apenas o “lado” do anel (que tem espessura 0) é visível. Coloque a seguinte animação entre as tags a-ring
na instrução anterior.
<a-animation attribute="rotation" easing="linear" repeat="indefinite" from="0 0 0" to="0 360 0" dur="8000"></a-animation>
Sua visualização agora deve corresponder ao seguinte:
Crie um número variável de anéis com diferentes eixos de rotação, velocidades e tamanhos. Você pode usar os seguintes anéis de exemplo. Quaisquer novos anéis devem ser colocados embaixo do último a-ring
<a-ring color="#fff" material="side:double" position="0 0.5 0" radius-inner="2.4" radius-outer="2.5" opacity="0.25"> <a-animation attribute="rotation" easing="linear" repeat="indefinite" from="0 45 0" to="360 45 0" dur="8000"></a-animation> </a-ring> <a-ring color="#fff" material="side:double" position="0 0.5 0" radius-inner="1.4" radius-outer="1.5" opacity="0.25"> <a-animation attribute="rotation" easing="linear" repeat="indefinite" from="0 -60 0" to="-360 -60 0" dur="3000"></a-animation> </a-ring>
Sua visualização agora corresponderá ao seguinte.
Verifique se seu código corresponde ao código-fonte da etapa 5 no Github. Isso conclui a decoração do orbe. Com o orbe concluído, adicionaremos interatividade ao orbe. Na próxima etapa, adicionaremos especificamente um cursor visível com uma animação de clique quando apontado para objetos clicáveis.
Tornando os orbes interativos
6. Adicione um cursor
Nesta etapa, adicionaremos um cursor branco que pode acionar objetos clicáveis. O cursor é mostrado abaixo.
Em sua tag a-camera
, adicione a seguinte entidade. O atributo fuse
permite que essa entidade acione eventos de clique. O atributo raycaster
determina com que frequência e até que ponto verificar objetos clicáveis. O atributo objects
aceita um seletor para determinar quais entidades são clicáveis. Nesse caso, todos os objetos da classe clickable
são clicáveis.
<a-entity cursor="fuse: true; fuseTimeout: 250" position="0 0 -1" geometry="primitive: ring; radiusInner: 0.03; radiusOuter: 0.04" material="color: white; shader: flat; opacity: 0.5" scale="0.5 0.5 0.5" raycaster="far: 20; interval: 1000; objects: .clickable"> <!-- Place cursor animation here --> </a-entity>
Em seguida, adicione animação de cursor e um anel extra para estética. Coloque o seguinte dentro do objeto cursor de entidade acima. Isso adiciona animação ao objeto cursor para que os cliques fiquem visíveis.
<a-circle radius="0.01" color="#FFF" opacity="0.5" material="shader: flat"></a-circle> <a-animation begin="fusing" easing="ease-in" attribute="scale" fill="backwards" from="1 1 1" to="0.2 0.2 0.2" dur="250"></a-animation>
Em seguida, adicione a classe clickable
ao #orb0
para corresponder ao seguinte.
<a-entity class="orb clickable" data->
Verifique se seu código corresponde ao código-fonte da Etapa 6 no Github. Na sua visualização, arraste o cursor deles para o orbe para ver a animação do clique em ação. Isso é retratado abaixo.
Observe que o atributo clicável foi adicionado ao próprio orbe e não ao contêiner do orbe. Isso é para evitar que os anéis se tornem objetos clicáveis. Dessa forma, o usuário deve clicar nas esferas que compõem o próprio orbe.
Em nossa etapa final para esta parte, você adicionará animação para controlar os estados ligado e desligado do orbe.
7. Adicione Estados Orbes
Nesta etapa, você animará o orbe dentro e fora de um estado desligado ao clicar. Isso é retratado abaixo.
Para começar, você encolherá e abaixará o orbe até o chão. Adicione tags a-animation
ao #container-orb0
logo após #orb0
. Ambas as animações são acionadas por um clique e compartilham a mesma função ease-elastic
para um leve salto.
<a-animation class="animation-scale" easing="ease-elastic" begin="click" attribute="scale" from="0.5 0.5 0.5" to="1 1 1" direction="alternate" dur="2000"></a-animation> <a-animation class="animation-position" easing="ease-elastic" begin="click" attribute="position" from="8 0.5 0" to="8 3 0" direction="alternate" dur="2000"></a-animation>
Para enfatizar ainda mais o estado desligado, removeremos a luz do ponto dourado quando o orbe estiver desligado. No entanto, as luzes do orbe são colocadas fora do objeto orbe. Assim, o evento click não é passado para as luzes quando o orbe é clicado. Para contornar esse problema, usaremos algum Javascript leve para passar o evento click para a luz. Coloque a seguinte tag de animação em #light-orb0
. A luz é acionada por um evento de switch
personalizado.
<a-animation class="animation-intensity" begin="switch" attribute="intensity" from="0" to="1" direction="alternate"></a-animation>
Em seguida, adicione o seguinte ouvinte de evento de clique ao #container-orb0
. Isso retransmitirá os cliques para as luzes do orbe.
<a-entity ...>
Verifique se seu código corresponde ao código-fonte da Etapa 7 no Github. Por fim, abra sua visualização e mova o cursor para dentro e para fora do orbe para alternar entre os estados desligado e ligado. Isso é retratado abaixo.
Isso conclui a interatividade do orbe. O jogador agora pode ligar e desligar os orbes à vontade, com estados ligados e desligados autoexplicativos.
Conclusão
Neste tutorial, você construiu um orbe simples com estados ligado e desligado, que pode ser alternado por um clique de cursor compatível com fone de ouvido VR. Com várias técnicas de iluminação e animações diferentes, você conseguiu distinguir entre os dois estados. Isso conclui os elementos de design de realidade virtual para os orbes. Na próxima parte do tutorial, preencheremos os orbes dinamicamente, adicionaremos mecânicas de jogo e configuraremos um protocolo de comunicação entre um par de jogadores.