Crea un Disparador Espacial Con PlayCanvas: Parte 1
() translation by (you can also view the original English article)
PlayCanvas hace realmente fácil crear contenido interactivo 3D para la web hecho funcionar por WebGL. Todo es JavaScript, así que se ejecuta nativamente en el navegador sin ningún plugin. Es un motor muy joven que ha existido desde el 2014, pero ha estado ganando rápidamente tracción con nombres como Disney, King y Miniclip usándolo para desarrolla juegos.
Es una magnífica herramienta por dos principales razones. Primero, es un motor de juegos lleno de características, así que maneja todo desde gráficos y colisión hasta audio e incluso integración con gampads/RV. (Así que no tendremos que buscar librerías externas o preocuparnos de problemas de compatibilidad del navegador para la mayoría de las cosas.) La segunda, y la que pienso lo hace destacar, es el editor basado en el navegador.



Si estás acostumbrado a trabajar con el motor Unity, el editor de PlayCanvas debería verse familiar (incluso usa un sistema similar basado en componentes para encadenar la funcionalidad). A diferencia de Unity, PlayCanvas no es multiplataforma y únicamente puede publicar para la web. Sin embargo, si la web es todo lo que te importa, entonces éste termina siendo un gran extra, ya que el foco del motor en la web lo hace realmente rápido y ligero comparado con la competencia.
Una nota final: Mientras el mismo motor es gratuito y de código abierto, el editor online y las herramientas son únicamente gratuitas para proyectos públicos. Ciertamente vale la pena pagar por él si estás desarrollando trabajo comercial con él, pero siempre puedes usarlo como un puro framework de código también gratuitamente.
El Resultado Final
Ésto es lo que crearemos:



Puedes probar una demostración real.
El proyecto mismo es público, así que puedes echarle un vistazo y/o copiarlo en su página de proyecto.
No necesitas tener ninguna experiencia con juegos 3D para continuar, pero asumiré que tienes alguna familiaridad con las bases de JavaScript.
Creando Nuestro Propio Proyecto Desde Cero
El resultado final es una demo relativamente sencilla donde solo vuelas y empujas asteroides, pero cubre la suficiente funcionalidad básica que será util en la creación de cualquier clase de juego 3D. La Parte 1 cubrirá la configuración básica, trabajar con modelos, el sistema de físicas, y controles de la cámara. La Parte 2 cubrirá el sistema de balas, generar asteroides y trabajar con texto.
1. Configuración del Proyecto
Dirígete a playcanvas.com y crea una cuenta.
Una vez que inicies sesión, haz click en projects tab (pestaña proyectos) en el panel de control y presiona new button el botón naranja grande para crear un nuevo proyecto. Ésto haría aparecer la ventana "new proyect" (nuevo proyecto). Selecciona "blank project" (proyecto en blanco) y dále un nombre:



Una vez que termines, pulsa create button (botón crear) en la parte inferior derecha. Ésto te enviará a la página de visión general del proyecto. Aquí, puedes accesar a tus configuraciones y agregar colaboradores. Por ahora, solo entraremos al proyecto, así que haz click en editor button (el botón editor) que es grande y naranja.
Cuando ingresas a tu primer proyecto, PlayCanvas te mostrará muchas sugerencias sobre su editor. Puedes descartar esas por ahora. Las cosas principales a notar son:
- El panel izquierdo (jerarquía) es una lista de todos los objetos de tu mundo. Aquí también es donde puedes añadir, duplicar o eliminar entidades de tu escena.
- El panel derecho (inspector) es donde editas las propiedades del objeto seleccionado. Después de seleccionar un objeto (al hacer click en él), podrás establecer su posición y orientación para adjuntar scripts y componentes.
- El panel inferior (assets) contiene todos tus recursos. Aquí es donde puedes subir texturas o modelos 3D así como crear scripts.
- La escena central es donde puedes editar y construir tu mundo del juego.
2. Creando un Objeto
Para crear un nuevo objeto en tu escena, haz click en el pequeño botón con el signo de más en la parte superior del panel jerarquía:



Nota: Podrías accidentalmente crear un nuevo objeto dentro de uno ya existente. Ésto es útil para construir objetos compuestos de múltiples partes o que están ligados de alguna manera. Puedes mover objetos alrededor del panel jerarquía para controlar la anidación. Arrástralo al objeto Root para colocarlo en la parte superior del panel jerarquía.
Como un ejemplo, voy a crear un nuevo cuadro y voy a colorearlo de rojo. Para darle un color personalizado, tenemos que crear un nuevo material. Puedes hacer ésto en el panel assets, al hacer click derecho en cualquier parte dentro del panel, o haciendo click en el pequeño ícono del signo de más.



Una vez creado, selecciona tu material y dále un nombre descriptivo como "RedMaterial" (puedes ver el campo nombre en el panel inspector).
Ahora desplázate hacia abajo en la sección diffuse y cambia el color:



Una vez eso está hecho, regresa y selecciona el nuevo cuadro que creaste (al hacer click en él en la escena o en el panel jerarquía). Luego establece su material con el material personalizado que acabamos de crear:



¡Y el cuadro ahora debería ser rojo! Nota que el material que creaste puede ser unido a tantos objetos como quieras.
3. Agregando Físicas
Para habilitar físicas en un objeto, tenemos que añadir dos componentes: Rigid Body (Cuerpo Rígido) y Collision (Colisión).
Agrega el Rigid Body al hacer click en "Add Component" (Añadir Componente) en el panel inspector en tu objeto:



Asegúrate de que su tipo esté establecido en dynamic (dinámico):



Y luego agrega un componente de colisión de la misma manera.
Ahora lanza tu juego al hacer click en el pequeño botón de play en la parte superior derecha de tu escena. ¡Deberías ver tu cuadro cayendo a través del piso! Para arreglar eso, tendrás que agregar un cuerpo rígido y colisión al plano también, asegurándote que el tipo de su cuerpo rígido sea estático (para que no caiga también).
Desafío: Solo por diversión, trata de agregar una esfera e inclina el plano ligeramente (en el eje X o en el eje Z) para verla rodar.
Una Nota sobre el Sistema de Componentes
Vale la pena hablar brevemente sobre el sistema de componentes ya que es una parte fundamental de la arquitectura de PlayCanvas. Conceptualmente, la idea es separar la funcionalidad de los objetos. La mayor ventaja de ello es la capacidad de conformar comportamiento complejo de componentes modulares más pequeños.
Por ejemplo, si observas la cámara en tu escena, notarás que no es un objeto especial. Es solo una entidad genérica con un componente cámara adjunto. Podrías adjuntar un componente cámara a cualquier cosa para convertirlo en una cámara, o adjuntar un cuerpo rígido y colisión a la cámara para convertirla en un objeto sólido (¡inténtalo!).
Si eres curioso, puedes leer más sobre las ventajas y desventajas de sistemas de componentes en la página de Wikipedia.
4. Añadiendo un Modelo
Ahora que estás cómodo con las bases, podemos comenzar a conformar nuestro juego espacial. Necesitamos al menos una nave y un asteroide para trabajar. Hay dos manera de añadir modelos:
Toma un Modelo De la Librería de PlayCanvas
PlayCanvas tiene una tienda (similar a la Tienda de Recursos de Unity en algunas maneras) donde puedes encontrar y descargar recursos directamente a tu proyecto. Para accesar a ella, solo haz click en library en el panel recursos.
La tienda es muy nueva, así que es escasa, pero es un buen lugar para encontrar marcadores de posición o recursos con los que experimentar.
Yo usé el recurso hovership de la tienda como mi nave del jugador.
Sube Tu Propio Modelo
PlayCanvas sí soporta subir archivos FBX, OBJ, 3DS y COLLADA (DAE), pero prefiere FBX. Puedes fácilmente convertir cualquier modelo 3D en FBX al abrirlo con Blender y exportarlo en tu formato deseado.
Puedes encontrar el modelo asteroide que usé en Blendswap.com. Nota que podrías querer optimizar tus modelos 3D antes de usarlos en un juego. Por ejemplo, ese modelo de asteroide ¡contiene más de 200,000 triángulos! Eso podría estar bien para un objeto especial en el juego, pero una vez que agregué más de cien asteroides en la escena, las cosas realmente se ralentizaron mucho. El modificador Decimate de Blender es una manera fácil de optimizar tus modelos. Utilicé ese para recortar el modelo asteroide a unos 7,000 triángulos sin perder demasiado detalle.
Una vez que los modelos están en tu proyecto (podría necesitar actualizar si no los ves de inmediato en tu panel assets), puedes agregarlos a tu escena. La forma más fácil de hacer eso es solo arrastrar el modelo a la escena:



Igual que antes, agrega un cuerpo rígido y un componente colisión a la nave. Un truco que podrías hacer con la colisión es agregar la malla del objeto real como su propia forma de colisión. Ésto resultaría en una malla de colisión pixel-perfect, pero sería muy eficiente. Para ésta demostración, opté por un cuadro sencillo como mi forma de colisión (y una esfera para los asteroides) y edité los half-extents para que coincidieran de manera no tan precisa con la forma del modelo.
Cómo Desplazar la Forma de Colisión
Un problema que podrías encontrar cuando ajustas las formas de colisión es la incapacidad de desplazarlas del centro. Una manera fácil de resolver ésto (además de tener que desplazar el modelo mismo en algo como Blender antes de exportarlo) es crear un objeto padre que tienga la colisión y el cuerpo rígido, y un objeto hijo que tenga el mismo modelo. Así podrías desplazar el modelo como un hijo del padre que contiene la colisión.
Así es cómo lo configuré para el proyecto de demostración, para que puedas echarle un vistazo para ver cómo se hace.
5. Cambiando los Ajustes de Gravedad & Escena
Ya que nuestro juego es establecido en el espacio, necesitamos modificar la gravedad predeterminada. Puedes hacer ésto en los ajustes de la escena. En la misma parte inferior izquierda de tu pantalla, haz click en el ícono de la rueda dentada. Ésto abrirá los ajustes en el panel inspector. Encuentra la sección de físicas y cambia el valor de gravedad:



Para asgurar que funcione, intenta lanzar de nuevo y ver si la nave está solo flotando en el espacio.
No es espacio sin un fondo estrellado, así que mientras estamos en los ajustes de la escena, agreguemos un skybox. Puedes tomar uno de la tienda o solo encontrar uno online que te guste. Una vez que lo tengas, agrégalo en la sección de renderizado:



Eso debería darle al juego un sentido más nebuloso. Ésto también sería un buen momento para limpiar tu escena y eliminar cualquier objeto de prueba que creamos antes.
6. Ejecutando Scripts en la Nave
Aquí es donde finalmente tenemos que escribir algo de código. El sistema de script de PlayCanvas es otra cosa que debería ser familiar si has utilizado Unity. Puedes crear scripts que pueden ser adjuntados a cualquier objeto, y éstos scripts pueden tener atributos que son configurados en una base por-objeto. Los atributos del script son muy útiles y logran dos cosas principales:
- Modularidad. Puedes crear un script que define cómo un enemigo se mueve con un atributo de velocidad, y se rehusa eso para diferentes clases de enemigos con diferentes velocidades.
- Colaboración. Los atributos del script puedes ser modificados directamente en el editor sin tener que tocar ningún código. Ésto permite a los diseñadores continuar y modificar los valores ellos mismo sin tener que molestar al programador o examinar el código.
Crea un Script
Ve a la pestaña assets (recursos) y crea un nuevo recurso de tipo Script. Éste será el código para el comportamiento de la nave, así que nómbralo como "Fly". Haz doble click en él para abrir el editor de script.
El manual de usuario de PlayCanvas es una referencia muy útil cuando se escriben scripts, como es la referencia de la API. La completación automática también hace realmente fácil averiguar qué métodos están disponibles. Comenzaremos por solo hacer que rote nuestra nave. Escribe ésto en la función update:
1 |
this.entity.rigidbody.applyTorque(0,1,0); |
Dentro de cualquier script, this
se refiere al mismo componente script, mientras que this.entity
se refiere al objeto del script al que está adjunto. Puedes accesar a cualquiera de los componentes adjuntos a la entidad de ésta manera. Aquí estamos accesando al cuerpo rígido y aplicando una fuerza angular a él.
Asegúrate de guardar tu script ahora.
Adjunta un Script
Antes de que tu script se involucre demasiado, adjuntémoslo a nuestra nave para ver si funciona. Para hacer eso, solo agrega un componente script a tu nave, y luego agrega tu script "fly" a ello. Nota que puedes únicamente agregar un componente script por objeto, pero puedes agregar múltiples script dentro de ese componente.
Una vez que lances, ¡deberías ver tu nave rotando!
Añade un Atributo
Como se discutió arriba, atributos script hacen nuestro código mucho más flexible. Puedes agrega uno al escribir ésto en la parte superior de tu código, justo después de la primera línea donde es creado el script:
1 |
Fly.attributes.add('speed', { type: 'number', default:10, title:'Ship Speed' }); |
En éste caso, el nombre de mi script es Fly
. La única opción requerida es type
.
Para ver el atributo en el editor, regresa a tu componente script, y haz click en el ícono con dos flechas en el script fly. Éste es el botón parse que buscará cualquier atributo y actualizará el editor. Tu componente debería ahora verse así:



Finalmente, usar el valor del atributo en tu script, solo haz this.[attribute_name]
. Así que si querías que ésta fuera la velocidad de rotación, podríamos cambiar nuestra línea de código a:
1 |
this.entity.rigidbody.applyTorque(0,this.speed,0); |
Nota: Ya que no hay atenuación angular, la nave continuará rotando más rápido mientras mas prolongada sea la fuerza aplicada. Si eliminas la fuerza, retendrá su inercia y se mantendrá rotando a la misma velocidad. Para cambiar ésto, establece la atenuación angular en el componente cuerpo rígido en algo arriba de cero.
Movimiento Con las Teclas de las Flechas
Ahora queremos ejecutar el script para que podamos orientar la nave con las teclas de las flechas. Un enfoque ingenuo podría verse así:
1 |
Fly.prototype.update = function(dt) { |
2 |
if(this.app.keyboard.isPressed(pc.KEY_RIGHT)){ |
3 |
this.entity.rigidbody.applyTorque(0,this.speed,0); |
4 |
}
|
5 |
if(this.app.keyboard.isPressed(pc.KEY_LEFT)){ |
6 |
this.entity.rigidbody.applyTorque(0,this.speed*-1,0); |
7 |
}
|
8 |
if(this.app.keyboard.isPressed(pc.KEY_UP)){ |
9 |
this.entity.rigidbody.applyTorque(this.speed*-1,0,0); |
10 |
}
|
11 |
if(this.app.keyboard.isPressed(pc.KEY_DOWN)){ |
12 |
this.entity.rigidbody.applyTorque(this.speed,0,0); |
13 |
}
|
14 |
|
15 |
};
|
¿Puedes decir cuál es el problema con éste script? Inténtalo. ¿Puedes facilmente apuntar la nave a donde quieres?
Piensa en ello antes de continuar leyendo. ¿Cómo arreglarías ésto?
El problema es que estamos aplicando una fuerza en coordenadas globales sin tomar en cuenta hacia donde está la parte frontal de la nave. Si la nave es horizontal en relación a la cámara, y la rotamos en el eje y cuando presionamos izquierda/derecha, entonces rota correctamente. Pero si la nave es vertical, una rotación en el eje y ahora es una voltereta.
El mismo problema ocurriría si estuviéramos tratando de mover la nave hacia adelante también. La dirección que es "adelante" depende de donde está la parte frontal de la nave y no puede ser absoluta.
Ahora convenientemente, cada entidad tiene tres vectores de dirección que podemos usar: up
(arriba), right
(derecha) y forward
(adelante). Para voltear izquierda/derecha, rotamos el eje up
, y arriba y abajo rotamos en el eje right
. Éstos son up
y right
relativos a la entidad. Una versión aplicando una solución se vería así:
1 |
Fly.prototype.update = function(dt) { |
2 |
var horizontalForce = this.entity.up.clone(); |
3 |
var verticalForce = this.entity.right.clone(); |
4 |
|
5 |
if(this.app.keyboard.isPressed(pc.KEY_RIGHT)){ |
6 |
this.entity.rigidbody.applyTorque(horizontalForce.scale(this.speed * -1)); |
7 |
}
|
8 |
if(this.app.keyboard.isPressed(pc.KEY_LEFT)){ |
9 |
this.entity.rigidbody.applyTorque(horizontalForce.scale(this.speed)); |
10 |
}
|
11 |
if(this.app.keyboard.isPressed(pc.KEY_UP)){ |
12 |
this.entity.rigidbody.applyTorque(verticalForce.scale(this.speed * -1)); |
13 |
}
|
14 |
if(this.app.keyboard.isPressed(pc.KEY_DOWN)){ |
15 |
this.entity.rigidbody.applyTorque(verticalForce.scale(this.speed)); |
16 |
}
|
17 |
|
18 |
};
|
Agregando movimiento hacia adelante es la misma idea:
1 |
if(this.app.keyboard.isPressed(pc.KEY_Z)){ |
2 |
this.entity.rigidbody.applyForce(this.entity.forward.clone().scale(-1)); |
3 |
}
|
Si el movimiento se siente fuera o demasiado resbaloso, pasa algún tiempo modificando las velocidades y los factores de atenuación para llevarlo a donde se sienta bien.
7. Controles de la Cámara
Es difícil mantener un seguimiento de una nave que se mueve con una cámara estática. La manera más fácil de hacer que la cámara siga un objeto es solo poniendo la cámara como un hijo de ese objeto.
Trata de arrastrar la cámara en el panel jerarquía a tu nave. Una forma conveniente de ajustar la vista de la cámara es cambiar a la vista de la cámara en la escena. Haz click en el botón en la parte inferior de la pantalla donde dice Perspective (Perspectiva). Ésto te dará un desplegable con todas las vistas de la escena que puedes seleccionar. Selecciona Camera, que debería ser la que está más abajo. Ésta es una vista especial porque lo que ves en el editor es lo que la cámara verá en el juego.
Una vez que has ajustado la vista de la cámara, asegúrate de regresar a perspectiva o a cualquier otra vista para evitar accidentalmente desordenar los ángulos de la cámara.
Consejo: Si tienes un objeto seleccionado en la jerarquía, pero no puedes encontrarlo en tu escena, presiona F. Ésto enfocará la vista en ese objeto y hará un acercamiento en él. Puedes ver más atajos de teclado al hacer click en el botón del teclado en la extrema izquierda de tu pantalla.
En éste punto, deberías tener una cámara siguiendo a tu nave (tan rígida como puede ser). (No podrás decir si estás moviéndote si la cámara está moviéndose y no hay otros objetos en el mundo, así que trata de agregar algunos.)
Scripts de Cámara
Una cámara que está solo varada en el jugador no es muy interesante. Éste post en el blog de PlayCanvas explora varios tipos de movimiento de cámara. El más sencillo que podemos implementar es el look at camera (observa la cámara).
Para hacer eso, primero mueve la cámara de nuevo hacia el objeto raíz.
A continuación, crea un nuevo script llamado lookAt.
La función actualizar de ese script debería verse como:
1 |
LookAt.prototype.update = function(dt) { |
2 |
this.entity.lookAt(this.target.getPosition()); |
3 |
};
|
Y debería tener un atributo:
1 |
LookAt.attributes.add('target', {type: 'entity'}); |
Ahora adjunta ese script al objeto cámara. Presiona el botón sparse y establece que el objetivo sea la entidad nave.
¡Intenta lanzarlo! Si todo fue bien, tu cámara estará permaneciendo en el lugar pero solo se orientará hacia la nave.
Puedes implementar los otros tipos de cámaras de la misma manera. La trailing follow camera mencionada en el post del blog idealmente se ve como la más agradable, pero he encontrado que vibra demasiado cuando los fotogramas por segundo bajan un poco, así que para la demostración final, terminé eligiendo una cámara que estaba adjunta como hijo de la nave pero realicé el script para que se moviera y rotara como lo hacía la nave.
Conclusion
No te preocupes si algo de ésto te parece un poco abrumante. PlayCanvas es un motor complejo con muchas prestaciones adicionales. Hay mucho que explorar, y mantener el manual cerca es una buena manera de encontrar tus respaldos. Otra buena manera de aprender es solo encontrando proyectos públicos y viendo cómo son hechas las cosas.
La Parte 2 comenzará con la creación de un sistema de balas, y luego agregar algunos asteroides a los cuales disparar, y lo puliremos al agregar un contador FPS y texto en el juego. Si tienes peticiones o sugerencias, o si cualquier cosa no está clara, por favor ¡permíteme saberlo en los comentarios!