Crea un Space Shooter con PlayCanvas Parte 2

Esta es la segunda parte de nuestra búsqueda para crear un tirador espacial en 3D. En la primera parte, vimos cómo configurar un juego básico de PlayCanvas, con física y colisión, nuestros propios modelos y una cámara..

Para referencia, aquí hay una demostración en vivo de nuestro resultado final de nuevo..

En esta parte, nos centraremos en la creación dinámica de entidades con scripts (para generar balas y asteroides), así como en la forma de agregar elementos como un contador de FPS y texto del juego. Si ya has seguido la parte anterior y estás contento con lo que tienes, puedes comenzar a construir a partir de eso y omitir la siguiente sección de configuración mínima. De lo contrario, si necesita reiniciar desde cero:

Configuración mínima

  1. Iniciar un nuevo proyecto.
  2. Elimina todos los objetos en la escena excepto Cámara, Caja y Luz.
  3. Pon tanto la luz como la cámara. dentroel objeto caja en el panel de jerarquía.
  4. Coloque la cámara en posición (0,1.5,2) y rotación (-20,0,0).
  5. Asegúrate de que el objeto de luz esté colocado en una posición que se vea bien (lo coloco en la parte superior de la caja).
  6. Adjunta un cuerpo rígido Componente a la caja. Establece su tipo en dinámica. Y establecer su mojadura a 0.95 (tanto lineal como angular).
  7. Adjunta un colisión componente a la caja.
  8. Selecciona el gravedad a 0 (de los ajustes de escena).
  9. Coloque una esfera en (0,0,0) solo para marcar esta posición en el espacio.
  10. Crea y adjunta este script a la caja y llámalo Fly.js:
var Fly = pc.createScript ('fly'); Fly.attributes.add ('speed', type: 'number', predeterminado: 50); // inicializar el código llamado una vez por entidad Fly.prototype.initialize = function () ; // código de actualización llamado todos los cuadros Fly.prototype.update = function (dt) // Presione Z para empujar si (this.app.keyboard.isPressed (pc.KEY_Z)) // Muévase en la dirección en que está orientado var force = this.entity.forward.clone (). scale (this.speed); this.entity.rigidbody.applyForce (fuerza);  // Gire hacia arriba / abajo / izquierda / derecha si (this.app.keyboard.isPressed (pc.KEY_UP)) var force_up = this.entity.right.clone (). Scale (1); this.entity.rigidbody.applyTorque (force_up);  if (this.app.keyboard.isPressed (pc.KEY_DOWN)) var force_down = this.entity.right.clone (). scale (-1); this.entity.rigidbody.applyTorque (force_down);  if (this.app.keyboard.isPressed (pc.KEY_RIGHT)) // Gire a la derecha var force_right = this.entity.up.clone (). scale (-1); this.entity.rigidbody.applyTorque (force_right);  if (this.app.keyboard.isPressed (pc.KEY_LEFT)) var force_left = this.entity.up.clone (). scale (1); this.entity.rigidbody.applyTorque (force_left); ; // método swap llamado para la recarga en caliente del script // hereda el estado de su script aquí Fly.prototype.swap = function (old) ; // para obtener más información sobre la anatomía del script, lea: // http://developer.playcanvas.com/en/user-manual/scripting/

Prueba de que todo funcionó. Debes poder volar con Z para empujar y las flechas para rotar!

8. Asteroides reproductores

La creación dinámica de objetos es crucial para casi cualquier tipo de juego. En la demostración que creé, estoy generando dos tipos de asteroides. El primer tipo simplemente flota alrededor y actúa como obstáculos pasivos. Reaparecen cuando se alejan demasiado para crear un campo de asteroides consistentemente denso alrededor del jugador. El segundo tipo aparece desde más lejos y se mueve hacia el jugador (para crear una sensación de peligro incluso si el jugador no se está moviendo). 

Necesitamos tres cosas para engendrar nuestros asteroides:

  1. Un AsteroidModel Entidad desde la cual clonar todos los demás asteroides..
  2. Un AsteroidSpawner guión adjunto a la raíz Objeto que actuará como nuestra fábrica / clonador..
  3. Un Asteroide Guión para definir el comportamiento de cada asteroide.. 

Creando un modelo de asteroide 

Cree una nueva entidad a partir de un modelo de su elección. Esto podría ser algo fuera de la tienda PlayCanvas, o algo de BlendSwap, o simplemente una forma básica. (Si está utilizando sus propios modelos, es una buena práctica abrirlo en Blender primero para verificar el número de caras utilizadas y optimizarlo si es necesario).

Dale una forma de colisión adecuada y un componente de cuerpo rígido (asegúrate de que sea dinámico). Una vez que estés feliz con eso, desmarca la Habilitado caja:

Cuando desactivas un objeto como este, es equivalente a eliminarlo del mundo en lo que respecta al jugador. Esto es útil para eliminar objetos temporalmente, o en nuestro caso, para mantener un objeto con todas sus propiedades pero no para que aparezca en el juego..

Creando el Script Asteroid Spawner 

Cree un nuevo script llamado AsteroidSpawner.js y adjúntelo a la Raíz Objeto en la jerarquía. (Tenga en cuenta que la raíz es solo un objeto normal que puede tener cualquier componente adjunto, al igual que la cámara).

Ahora abre el script que acabas de crear. 

La forma general de clonar una entidad y agregarla al mundo a través de un script se ve así:

// Crea el clon var newEntity = oldEntity.clone (); // Agregarlo al objeto raíz this.app.root.addChild (newEntity); // Dale un nuevo nombre, de lo contrario también obtiene el nombre oldEntity newEntity.name = "ClonedEntity"; // Habilitarlo, asumiendo que oldEntity está configurado como inhabilitado newEntity.enabled = true;

Así es como clonarías un objeto si ya tuvieras un objeto "oldEntity". Esto deja una pregunta sin respuesta: ¿Cómo accedemos al AsteroidModel que creamos?? 

Hay dos maneras de hacer esto. La forma más flexible es crear un atributo de secuencia de comandos que contenga la entidad a clonar, para que pueda intercambiar modelos fácilmente sin tocar la secuencia de comandos. (Así es exactamente como hicimos el guión lookAt de la cámara en el paso 7.)

La otra forma es usar la función findByName. Puede llamar a este método en cualquier entidad para encontrar cualquiera de sus hijos. Así que podemos llamarlo en el objeto raíz:

var oldEntity = this.app.root.findByName ("AsteroidModel");

Y así se completará nuestro código desde arriba. El script completo de AsteroidSpawner ahora se ve así:

var AsteroidSpawner = pc.createScript ('asteroidSpawner'); // inicializar el código llamado una vez por entidad AsteroidSpawner.prototype.initialize = function () var OldEntity = this.app.root.findByName ("AsteroidModel"); // Crea el clon var newEntity = oldEntity.clone (); // Agregarlo al objeto raíz this.app.root.addChild (newEntity); // Dale un nuevo nombre, de lo contrario también obtiene el nombre oldEntity newEntity.name = "ClonedEntity"; // Habilitarlo, asumiendo que oldEntity está configurado como inhabilitado newEntity.enabled = true; // Establecer su posición newEntity.rigidbody.teleport (new pc.Vec3 (0,0,1)); ; // código de actualización llamado cada fotograma AsteroidSpawner.prototype.update = function (dt) ; 

Pruebe que esto funcionó lanzando y observando si existe su modelo de asteroide.

Nota: utilicé newEntity.rigidbody.teleport en lugar de newEntity.setPosition. Si una entidad tiene un cuerpo rígido, entonces el cuerpo rígido anulará la posición y la rotación de la entidad, así que recuerde establecer estas propiedades en el cuerpo rígido y no en la entidad en sí..

Antes de continuar, intente que genere diez o más asteroides alrededor del jugador, ya sea de forma aleatoria o sistemática (¿tal vez incluso en un círculo?). Ayudaría poner todo su código de generación en una función para que se vea algo como esto:

AsteroidSpawner.prototype.initialize = function () this.spawn (0,0,0); este engendro (1,0,0); este engendro (1,1,0); // etc ...; AsteroidSpawner.prototype.spawn = function (x, y, z) // Generando código aquí ...

Creación de la secuencia de comandos de asteroides

Ya deberías estar cómodo agregando nuevos scripts. Cree un nuevo script (llamado Asteroid.js) y adjúntelo a AsteroidModel. Dado que todos nuestros asteroides generados son clones, todos tendrán el mismo script adjunto.. 

Si estamos creando muchos asteroides, sería una buena idea asegurarse de que se destruyan cuando ya no los necesitemos o cuando estén lo suficientemente lejos. Aquí hay una forma de hacer esto:

Asteroid.prototype.update = function (dt) // Obtener el reproductor var player = this.app.root.findByName ("Ship"); // Reemplace "Ship" con el nombre que tenga su jugador // Clone la posición del asteroide var distance = this.entity.getPosition (). Clone (); // Resta la posición del jugador de la posición distance.sub de este asteroide (player.getPosition ()); // Obtenga la longitud de este vector if (distance.length ()> 10) // Algún umbral arbitrario this.entity.destroy (); ;

Punta de depuración: Si desea imprimir algo, siempre puede usar la consola del navegador como si fuera una aplicación JavaScript normal. Así que podrías hacer algo como console.log (distance.toString ()); para imprimir el vector de distancia, y se mostrará en la consola.

Antes de continuar, compruebe que el asteroide desaparece cuando se aleja de él..

9. Balas de desove

Las balas de desove serán más o menos la misma idea que los asteroides de desove, con un nuevo concepto: Queremos detectar cuando la bala golpea algo y eliminarlo.. Para crear nuestro sistema de bala, necesitamos:

  1. Un modelo de bala a clonar.. 
  2. Un script de Shoot.js para generar balas cuando presionas X. 
  3. Un script Bullet.js para definir el comportamiento de cada viñeta.. 

Creación de un modelo de bala

Puedes usar cualquier forma para tu bala. Usé una cápsula solo para tener una idea de en qué dirección estaba la bala. Al igual que antes, cree su entidad, redúzcala y déle un cuerpo rígido dinámico y una caja de colisión adecuada. Dale el nombre "Bullet" para que sea fácil de encontrar.

Una vez que haya terminado, asegúrese de desactivarlo (con la casilla de verificación Habilitado).

Creación de una secuencia de comandos de disparo

Crea un nuevo script y adjúntalo a tu nave de jugador. Esta vez vamos a utilizar un atributo para obtener una referencia a nuestra entidad de viñetas:

Shoot.attributes.add ('bullet', type: 'entity');

Vuelva al editor y presione "analizar" para que aparezca el nuevo atributo, y seleccione la entidad de viñeta que creó.. 

Ahora en la función de actualización, queremos:

  1. Clónalo y añádelo al mundo..
  2. Aplica una fuerza en la dirección hacia la que se enfrenta el jugador.. 
  3. Colócalo delante del jugador..

Ya has sido introducido a todos estos conceptos. Has visto cómo clonar asteroides, cómo aplicar una fuerza en una dirección para hacer que la nave se mueva y cómo colocar las cosas. Dejaré la implementación de esta parte como un reto. (Pero si te quedas atascado, siempre puedes ver cómo implementé mi propio script Shoot.js en mi proyecto).

Aquí hay algunos consejos que podrían ahorrarle un poco de dolor de cabeza:

  1. Utilizar teclado.estuvo presionado en lugar de teclado.isprimido. Al detectar cuándo se presiona la tecla X para disparar un disparo, la primera es una forma conveniente de hacer que se dispare solo cuando se presiona en lugar de disparar mientras se mantenga presionado el botón.
  2. Utilizar rotarLocal En lugar de establecer una rotación absoluta. Asegurarse de que la bala siempre aparezca paralela a la nave, fue un dolor calcular correctamente los ángulos. Una forma mucho más fácil es simplemente establecer la rotación de la bala en la rotación de la nave, y luego girar la bala en su espacio local en 90 grados sobre el eje X.

Creación del script de comportamiento de bala

En este punto, tus balas deberían estar desovando, golpeando los asteroides y simplemente rebotando en un espacio vacío. La cantidad de balas puede volverse abrumadora rápidamente, y saber cómo detectar colisiones es útil para todo tipo de cosas. (Por ejemplo, puede haber notado que puede crear objetos que solo tienen un componente de colisión pero no un cuerpo rígido. Estos actuarían como disparadores pero no reaccionarían físicamente). 

Cree un nuevo script llamado Bullet y adjúntelo a su modelo de Bullet que se clone. PlayCanvas tiene tres tipos de eventos de contacto. Vamos a escuchar colisión, que se dispara cuando los objetos se separan (de lo contrario, la bala se destruiría antes de que el asteroide tenga la oportunidad de reaccionar).  

Para escuchar un evento de contacto, escriba esto en su función de inicio:

this.entity.collision.on ('collisionend', this.onCollisionEnd, this);

Y luego crea el oyente en sí mismo:

Bullet.prototype.onCollisionEnd = function (result) // Destruye la bala si golpea un asteroide if (result.name == "Asteroid") this.entity.destroy (); ;

Aquí es donde el nombre que le dio a sus asteroides cuando los generó se vuelve relevante. Queremos que la bala solo se destruya cuando colisiona con un asteroide. resultado es la entidad con la que terminó chocando.

Alternativamente, puedes eliminar ese cheque y simplemente destruirlo en caso de colisión con cualquier cosa..

Es cierto que no hay otros objetos en el mundo con los que colisionar, pero al principio tuve algunos problemas con el jugador que activó la colisión de la bala por un fotograma y desapareció antes de que pudiera lanzarse. Si tiene necesidades de colisión más complicadas, PlayCanvas admite grupos y máscaras de colisión, pero no está muy bien documentado al momento de escribir.

10. Agregar un medidor de FPS

Básicamente hemos terminado con el juego en este punto. Por supuesto, hay muchas cosas pequeñas que he añadido a la demostración final, pero no hay nada que no puedas hacer con lo que has aprendido hasta ahora.. 

Quería mostrarte cómo crear un medidor de FPS (aunque PlayCanvas ya tiene un generador de perfiles; puedes desplazarte sobre el botón de reproducción y marcar la casilla del generador de perfiles) porque es un buen ejemplo de cómo agregar un elemento DOM que está fuera del motor de PlayCanvas. 

Vamos a utilizar esta biblioteca de FPSMeter. Lo primero que debe hacer es dirigirse al sitio web de la biblioteca y descargar la versión de producción minified.

Vuelva a su editor de PlayCanvas, cree un nuevo script y cópielo sobre el código fpsMeter.min.js. Adjunte este script al objeto raíz.

Ahora que la biblioteca ha sido cargada, cree un nuevo script que inicialice y use la biblioteca. Llámelo meter.js, y del ejemplo de uso en el sitio web de la biblioteca, tenemos:

var Meter = pc.createScript ('meter'); Meter.prototype.initialize = function () this.meter = new FPSMeter (document.body, graph: 1, heat: 1); ; Meter.prototype.update = function (dt) this.meter.tick (); ; 

Agregue también el script del medidor al objeto raíz e inicie. Debería ver el contador de FPS en la esquina superior izquierda de la pantalla!

11. añadiendo texto

Por último, vamos a añadir algún texto en nuestro mundo. Este es un poco complicado ya que hay varias maneras de hacerlo. Si solo desea agregar una UI estática, una forma de hacerlo es trabajar directamente con el DOM, superponiendo sus elementos de la UI sobre el elemento del lienzo de PlayCanvas. Otro método es utilizar SVGs. Esta publicación discute algunas de estas diferentes formas..

Dado que estas son todas las formas estándar de manejo de texto en la web, opté por mirar cómo crear texto que exista en el espacio del mundo del juego. Así que piense en él como un texto que iría en un letrero en el entorno o en un objeto en el juego..

La forma en que hacemos esto es creando un material Por cada trozo de texto que queremos hacer. Entonces creamos un lienzo invisible que renderizamos el texto utilizando el método de lienzo relleno del texto. Finalmente nosotros hacer el lienzo sobre el material para que aparezca en el juego.

Tenga en cuenta que este método se puede utilizar para más que solo texto. Podría dibujar dinámicamente texturas o hacer cualquier cosa que un lienzo pueda hacer..

Crear el material de texto

Crea un nuevo material y llámalo como "TextMaterial". Establece su color difuso en negro ya que nuestro texto será blanco.

Crea una entidad plana y adjunta este material a ella..

Crear el guión de texto

Puede encontrar el script completo de text.js en esta lista:

https://gist.github.com/OmarShehata/e016dc219da36726e65cedb4ab9084bd

Puedes ver cómo configura la textura para usar el lienzo como fuente, específicamente en la línea: this.texture.setSource (this.canvas);

Crea este script y adjúntalo a tu plano. Observe cómo crea dos atributos: texto y tamaño de fuente. De esta manera usted podría usar el mismo script para cualquier objeto de texto en su juego.

Inicie la simulación y debería ver el gran texto "Hola mundo" en algún lugar. Si no lo ves, asegúrate de que a) tenga una fuente de luz cerca yb) estés mirando el lado correcto. El texto no se renderizará si lo miras desde atrás. (También ayuda colocar un objeto físico cerca del plano para ubicarlo al principio).

12. Publicación

Una vez que haya reunido su impresionante prototipo, puede hacer clic en el ícono de PlayCanvas en la esquina superior izquierda de la pantalla y seleccionar "Publicar". Aquí es donde puedes publicar nuevas compilaciones para alojarlas en PlayCanvas y compartirlas con el mundo.!

Conclusión

Eso es todo para esta demo. Hay mucho más que explorar en PlayCanvas, pero esperamos que este resumen te haga sentir lo suficientemente cómodo con lo básico para comenzar a construir tus propios juegos. Es un motor realmente bueno que creo que más gente debería usar. Mucho de lo que se ha creado con él ha sido demostraciones técnicas en lugar de juegos completos, pero no hay razón para que no puedas compilar y publicar algo asombroso con él..

Una característica de la que realmente no hablé, pero podría haber sido evidente es que el editor de PlayCanvas te permite actualizar tu juego en tiempo real. Esto se aplica al diseño, ya que puede mover elementos en el editor y se actualizarán en la ventana de inicio si lo tiene abierto, así como para el código, con su recarga en caliente..

Finalmente, si bien el editor es realmente conveniente, cualquier cosa que pueda hacer con él se puede hacer con código puro. Entonces, si necesita usar PlayCanvas con un presupuesto, una buena referencia para usar es la carpeta de ejemplos en GitHub. (Un buen lugar para comenzar sería este simple ejemplo de un cubo giratorio).

¡Si algo es confuso, hágamelo saber en los comentarios! O solo si ha creado algo genial y quiere compartir, o ha descubierto una manera más fácil de hacer algo, me encantaría verlo.!