Cómo construir un juego de realidad virtual multijugador en tiempo real (Parte 1)
Publicado: 2022-03-10En esta serie de tutoriales, crearemos un juego de realidad virtual multijugador basado en la web en el que los jugadores deberán colaborar para resolver un rompecabezas. Usaremos A-Frame para el modelado de realidad virtual, MirrorVR para la sincronización en tiempo real entre dispositivos y A-Frame Low Poly para la estética low-poly. Al final de este tutorial, tendrá una demostración en línea completamente funcional que cualquiera puede jugar.
Cada pareja de jugadores recibe un anillo de orbes. El objetivo es "encender" todos los orbes, donde un orbe está "encendido" si está elevado y brillante. Un orbe está "apagado" si está más bajo y tenue. Sin embargo, ciertos orbes "dominantes" afectan a sus vecinos: si cambia de estado, sus vecinos también cambian de estado. Solo el jugador 2 puede controlar los orbes dominantes, mientras que solo el jugador 1 puede controlar los orbes no dominantes. Esto obliga a ambos jugadores a colaborar para resolver el rompecabezas. En esta primera parte del tutorial, construiremos el entorno y agregaremos los elementos de diseño para nuestro juego de realidad virtual.
Los siete pasos de este tutorial se agrupan en tres secciones:
- Configuración de la escena (Pasos 1 y 2)
- Creación de los orbes (pasos 3 a 5)
- Cómo hacer que los orbes sean interactivos (pasos 6 y 7)
Esta primera parte concluirá con un orbe en el que se puede hacer clic que se enciende y se apaga (como se muestra a continuación). Utilizará A-Frame VR y varias extensiones de A-Frame.
Preparando la escena
1. Vamos con una escena básica
Para comenzar, echemos un vistazo a cómo podemos configurar una escena simple con un suelo:
Las primeras tres instrucciones a continuación están extraídas de mi artículo anterior. Comenzará configurando un sitio web con una sola página HTML estática. Esto le permite codificar desde su escritorio e implementarlo automáticamente en la web. El sitio web implementado se puede cargar en su teléfono móvil y colocar dentro de un auricular VR. Alternativamente, el sitio web implementado se puede cargar con un auricular VR independiente.
Comience navegando a glitch.com. Luego, haz lo siguiente:
- Haga clic en "Nuevo proyecto" en la parte superior derecha,
- Haga clic en "hola-página web" en el menú desplegable,
- A continuación, haga clic en index.html en la barra lateral izquierda. Nos referiremos a esto como su "editor".
Ahora debería ver la siguiente pantalla Glitch con un archivo HTML predeterminado.
Al igual que con el tutorial vinculado anterior, comience eliminando todo el código existente en el archivo index.html actual. Luego, escriba lo siguiente para un proyecto webVR básico, usando A-Frame VR. Esto crea una escena vacía utilizando la iluminación y la cámara predeterminadas de 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 la cámara a la altura de pie. Según las recomendaciones de A-Frame VR (problema de Github), envuelva la cámara con una nueva entidad y mueva la entidad principal en lugar de la cámara directamente. Entre sus etiquetas a-scene
a en las líneas 8 y 9, agregue lo siguiente.
<!-- Camera! --> <a-entity position="0 3 0"> <a-camera wasd-controls look-controls></a-camera> </a-entity>
Luego, agregue un cuadro grande para indicar el suelo, usando a-box
. Coloque esto directamente debajo de su cámara de la instrucción anterior.
<!-- Action! --> <a-box shadow width="75" height="0.1" depth="75" position="0 -1 0" color="#222"></a-box>
Su archivo index.html ahora debería coincidir exactamente con lo siguiente. Puede encontrar el código fuente completo aquí, en 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>
Esto concluye la instalación. A continuación, personalizaremos la iluminación para una atmósfera más misteriosa.
2. Añadir ambiente
En este paso, configuraremos la niebla y la iluminación personalizada.
Agregue una niebla, que oscurecerá los objetos lejanos para nosotros. Modifique la etiqueta a-scene
a en la línea 8. Aquí, agregaremos una niebla oscura que oscurecerá rápidamente los bordes del suelo, dando el efecto de un horizonte distante.
<a-scene fog="type: linear; color: #111; near:10; far:15"></a-scene>
El #111
gris oscuro se desvanece linealmente desde una distancia de 10 a una distancia de 15. Todos los objetos a más de 15 unidades de distancia están completamente oscurecidos, y todos los objetos a menos de 10 unidades de distancia son completamente visibles. Cualquier objeto en el medio está parcialmente oscurecido.
Agregue una luz ambiental para iluminar los objetos del juego y una luz unidireccional para acentuar las superficies reflectantes que agregará más adelante. Colóquelo directamente después de la etiqueta a-scene
que modificó en las instrucciones anteriores.
<!-- 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>
Directamente debajo de las luces de la instrucción anterior, agregue un cielo oscuro. Observe que el #111
gris oscuro coincide con el de la niebla distante.
<a-sky color="#111"></a-sky>
Esto concluye las modificaciones básicas del estado de ánimo y, en términos más generales, la configuración de la escena. Verifique que su código coincida exactamente con el código fuente del Paso 2 en Github. A continuación, agregaremos un orbe de baja poli y comenzaremos a personalizar la estética del orbe.
Creando los orbes
3. Crea un orbe Low-Poly
En este paso, crearemos un orbe giratorio y reflectante como se muestra a continuación. El orbe está compuesto por dos esferas estilizadas de bajo contenido de poliéster con algunos trucos para sugerir material reflectante.
Comience importando la biblioteca low-poly en su etiqueta head
. Inserte lo siguiente entre las líneas 4 y 5.
<script src="https://cdn.jsdelivr.net/gh/alvinwan/[email protected]/dist/aframe-low-poly.min.js"></script>
Cree un contenedor de carrusel, envoltura y orbe. El carousel
contendrá múltiples orbes, el wrapper
nos permitirá rotar todos los orbes alrededor de un eje central sin rotar cada orbe individualmente, y el container
, como sugiere el nombre, contendrá todos los componentes del 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 del contenedor del orbe, agregue el orbe mismo: una esfera es ligeramente translúcida y desplazada, y la otra es completamente sólida. Los dos combinados imitan superficies reflectantes.
<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 la esfera indefinidamente agregando la siguiente etiqueta a-animation
inmediatamente después de la lp-sphere
dentro de la entidad .orb
en la última instrucción.
<a-animation attribute="rotation" repeat="indefinite" from="0 0 0" to="0 360 0" dur="5000"></a-animation>
Su código fuente para los envoltorios del orbe y el propio orbe deben coincidir exactamente con lo siguiente.
<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 que su código fuente coincida con el código fuente completo para el paso 3 en Github. Su vista previa ahora debería coincidir con lo siguiente.
A continuación, agregaremos más iluminación al orbe para obtener un tono dorado.
4. Ilumina el orbe
En este paso añadiremos dos luces, una de color y otra blanca. Esto produce el siguiente efecto.
Comience agregando la luz blanca para iluminar el objeto desde abajo. Usaremos un punto de luz. Directamente antes #orb0
pero dentro de #container-orb0
, agregue la siguiente luz de punto de compensación.
<a-entity position="-2 -1 0"> <a-light distance="8" type="point" color="#FFF" intensity="0.8"></a-light> </a-entity>
En su vista previa, verá lo siguiente.
Por defecto, las luces no decaen con la distancia. Al agregar distance="8"
, nos aseguramos de que la luz decaiga por completo con una distancia de 8 unidades, para evitar que la luz puntual ilumine toda la escena. A continuación, agregue la luz dorada. Agregue lo siguiente directamente encima de la última luz.
<a-light class="light-orb" distance="8" type="point" color="#f90" intensity="1"></a-light>
Verifique que su código coincida exactamente con el código fuente del paso 4. Su vista previa ahora coincidirá con lo siguiente.
A continuación, realizará la modificación estética final del orbe y agregará anillos giratorios.
5. Agregar anillos
En este paso, producirá el orbe final, como se muestra a continuación.
Agregue un anillo en #container-orb0
directamente 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>
Tenga en cuenta que el anillo en sí no contiene color, ya que el color será imbuido por la luz del punto en el paso anterior. Además, el material="side:double"
es importante ya que, sin él, la parte trasera del anillo no se renderizaría; esto significa que el anillo desaparecería durante la mitad de su rotación.
Sin embargo, la vista previa con solo el código anterior no se verá diferente. Esto se debe a que el anillo está actualmente perpendicular a la pantalla. Por lo tanto, solo se ve el "lado" del anillo (que tiene un grosor 0). Coloque la siguiente animación entre las etiquetas a-ring
A en la instrucción anterior.
<a-animation attribute="rotation" easing="linear" repeat="indefinite" from="0 0 0" to="0 360 0" dur="8000"></a-animation>
Su vista previa ahora debería coincidir con lo siguiente:
Crea un número variable de anillos con diferentes ejes de rotación, velocidades y tamaños. Puede utilizar los siguientes anillos de ejemplo. Cualquier anillo nuevo debe colocarse debajo del ú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>
Su vista previa ahora coincidirá con lo siguiente.
Verifique que su código coincida con el código fuente del paso 5 en Github. Esto concluye la decoración del orbe. Con el orbe terminado, a continuación agregaremos interactividad al orbe. En el siguiente paso, agregaremos específicamente un cursor visible con una animación de clic cuando apunte a objetos en los que se puede hacer clic.
Hacer que los orbes sean interactivos
6. Agregar un cursor
En este paso, agregaremos un cursor blanco que puede activar objetos en los que se puede hacer clic. El cursor se muestra a continuación.
En su etiqueta a-camera
, agregue la siguiente entidad. El atributo fuse
le permite a esta entidad la capacidad de desencadenar eventos de clic. El atributo raycaster
determina con qué frecuencia y en qué medida comprobar los objetos en los que se puede hacer clic. El atributo de objects
acepta un selector para determinar en qué entidades se puede hacer clic. En este caso, se puede hacer clic en todos los objetos de la clase clickable
.
<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>
A continuación, agregue la animación del cursor y un anillo adicional para la estética. Coloque lo siguiente dentro del objeto de cursor de entidad de arriba. Esto agrega animación al objeto del cursor para que los clics sean visibles.
<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>
A continuación, agregue la clase en la que se clickable
a #orb0
para que coincida con lo siguiente.
<a-entity class="orb clickable" data->
Verifique que su código coincida con el código fuente del Paso 6 en Github. En su vista previa, arrastre el cursor fuera de ellos hacia el orbe para ver la animación de clic en acción. Esto se muestra a continuación.
Tenga en cuenta que el atributo en el que se puede hacer clic se agregó al propio orbe y no al contenedor del orbe. Esto es para evitar que los anillos se conviertan en objetos en los que se pueda hacer clic. De esta forma, el usuario debe hacer clic en las esferas que forman el propio orbe.
En nuestro paso final de esta parte, agregará una animación para controlar los estados de encendido y apagado del orbe.
7. Agregar estados de orbe
En este paso, animarás el orbe dentro y fuera del estado al hacer clic. Esto se muestra a continuación.
Para empezar, encogerás y bajarás el orbe al suelo. Agregue etiquetas a-animation
al #container-orb0
justo después de #orb0
. Ambas animaciones se activan con un clic y comparten la misma función ease-elastic
suave para un ligero rebote.
<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 aún más el estado apagado, eliminaremos la luz del punto dorado cuando el orbe esté apagado. Sin embargo, las luces del orbe se colocan fuera del objeto del orbe. Por lo tanto, el evento de clic no pasa a las luces cuando se hace clic en el orbe. Para sortear este problema, usaremos Javascript ligero para pasar el evento de clic a la luz. Coloque la siguiente etiqueta de animación en #light-orb0
. La luz se activa mediante un evento de switch
personalizado.
<a-animation class="animation-intensity" begin="switch" attribute="intensity" from="0" to="1" direction="alternate"></a-animation>
A continuación, agregue el siguiente detector de eventos de clic al #container-orb0
. Esto transmitirá los clics a las luces del orbe.
<a-entity ...>
Verifique que su código coincida con el código fuente del Paso 7 en Github. Finalmente, abra su vista previa y mueva el cursor dentro y fuera del orbe para alternar entre los estados de encendido y apagado. Esto se muestra a continuación.
Esto concluye la interactividad del orbe. El jugador ahora puede encender y apagar los orbes a voluntad, con estados de encendido y apagado que se explican por sí mismos.
Conclusión
En este tutorial, construyó un orbe simple con estados de encendido y apagado, que se puede alternar con un clic de cursor compatible con auriculares VR. Con varias técnicas de iluminación y animaciones diferentes, pudo distinguir entre los dos estados. Esto concluye los elementos de diseño de realidad virtual para los orbes. En la siguiente parte del tutorial, llenaremos los orbes dinámicamente, agregaremos mecánicas de juego y configuraremos un protocolo de comunicación entre un par de jugadores.