5 minute read

Nos adentramos en la octava semana del blog. Los retos indicados son:

Malla de colisión

Maestro - Esclavo

Enriquecer motor de físicas

Malla de colision

Respecto a las mallas de colisión he decir que el motor de fisicas ammo contiene a ammo-shape que se encarga de crearlas, por tanto, se ha de añadir a través de ese sistema. Tras la visualización de diversas páginas web he llegado a la conclusión de que no se puede exportar la malla de colisión creada en Blender, sino que se ha de ajustar manualmente.

  1. Entidades dinamicas

La malla de colisión ha de ser una geometria simple que nos permita encapsular nuestro modelo 3D de la forma mas sencilla posible.

  1. Entidades estaticas

La malla de colisión pueden ser varios geometrias simples, entidades hijas, que nos permitan encapsular nuestro modelo 3D acercandonos lo máximo posible a su forma.

Siguiendo la siguiente documentación podemos encontrar otro modo de determinar la malla de colisión, pero me parece muy complejo.

Documentación

Documentación

Maestro - Esclavo

Buscamos que en el presente proyecto unicamente uno de los usuarios tenga la capacidad de activar las fisicas, ya que hemos observado como de esta manera cada jugador se identifica de forma predeterminada como maestro o esclavo y se obtiene un buen resultado.

  1. A continuacion voy a intentar que el primer usuario que inicialice la escena sea aquel que tiene el poder de las fisicas y que el otro usuario sea un mero espectador. Parece ser que es necesario un document.addEventListener() para poder compartir entre usuarios valores, ya que desde funciones init no se obtienen resultados.

  2. Creación de un window.addEventListener(‘load’) que funcione de manera inmediata al iniciar el usuario la escena. De nuevo, no parece ser una opción adecuada, ya que no se obtienen los resultados buscados.

  3. Uso de document.addEventListerner(‘keydown’) y dos variables, uno de ellas común y otra individual. A271.html

Si la variable común al pulsar el usuario la tecla ‘m’ del teclado es igual a false transformaremos su valor y la de la variable individual a true. Por tanto, si usamos la tecla espacio para modificar el color de la esfera solo se logrará si la variable individual es igual a true.

El resultado obtenido es adecuado, pero el usuario ha de tener en cuenta que para iniciar el juego uno de ellos ha de proclamarse maestro.

<script>
        var text = document.querySelector('#text');
        var sphere = document.querySelector('#sphere');
        var individual = "false";
        //Evento carga de la escena completada
        document.addEventListener('keydown', (event) => {
          var name = event.key;
          var code = event.code;
          /*
            Variable 'comun' determina el comportamiento maestro-esclavo
            Aquel navegador que pulse la tecla 'm' en primer lugar sera el que se comporte como maestro
          */
          if (name == "m") {
            if (this.text.getAttribute('text').value == "false") {
              individual = "true";
              this.text.setAttribute('text',{
                      value: 'true'
              });
              NAF.utils.takeOwnership(this.text);

              console.log(individual);
            }else{
              individual == "false";      
            }
          }

          if (name == " ") {
            if (individual == "true") {
              this.sphere.setAttribute('material', {
                  color: "black"
              });
              NAF.utils.takeOwnership(this.sphere);
            }
          }
        });
</script>

Enriquecer el motor de físicas

Rebote pasados ‘x’ segundos

Una de las ideas dadas por José María Cañas para enriquecer el motor de físicas fue añadir un rebote del objeto, pero este se realizaria pasados unos segundos. Podemos encontrar el codigo completo en A272.html.

Opción 1: Podemos encontrar el codigo completo en A272.html.

<script>
  AFRAME.registerComponent('rebote', {
    init: function() {
          var el = this.el;
          el.addEventListener("collidestart", function () {
            sleep(2000);
            const impulse = new Ammo.btVector3(0, 10, 0);
            const pos = new Ammo.btVector3(0, 0, 0);
            el.body.applyImpulse(impulse, pos);
            Ammo.destroy(impulse);
            Ammo.destroy(pos);
        
          });
    }
  });
  function sleep(milliseconds) {
    var start = new Date().getTime();
    for (var i = 0; i < 1e7; i++) {
      if ((new Date().getTime() - start) > milliseconds) {
      break;
      }
    }
  }
</script>

Nota: Podría ser que el problema de sincronización lo este ocasionando la funcion sleep(), ya que lo que hace es parar la escena.

Opción 2: Podemos encontrar el codigo completo en A291.html. El problema es que unicamente acepta 4 colisiones, ya que en la cuarta iteracción deja de funcionar.

<script>
  AFRAME.registerComponent('push', {
    init: function() {
          var el = this.el;
          el.addEventListener("collidestart", function () {
            console.log("I am the first log");


            setTimeout(function(){
                //Esfera
                var sphere = document.querySelector('#sphere');
                this.sphere.removeAttribute('ammo-body');
                this.sphere.removeAttribute('ammo-shape');
                this.sphere.setAttribute('ammo-body', {
                        type: 'dynamic',
                        emitCollisionEvents: true,
                        gravity: "0 9.8 0",
                });

                this.sphere.setAttribute('ammo-shape',{
                        type: 'sphere'
                });

                NAF.utils.takeOwnership(this.sphere);
                setTimeout(function(){
                    //Esfera
                    var sphere = document.querySelector('#sphere');
                    this.sphere.removeAttribute('ammo-body');
                    this.sphere.removeAttribute('ammo-shape');
                    this.sphere.setAttribute('ammo-body', {
                            type: 'dynamic',
                            emitCollisionEvents: true,
                            gravity: "0 -9.8 0",
                    });

                    this.sphere.setAttribute('ammo-shape',{
                            type: 'sphere'
                    });

                    NAF.utils.takeOwnership(this.sphere);
                },2000);
            },2000);
          });
    }
  });

 </script>

Opción 3:

Podemos encontrar el codigo completo en A293.html.

<script>
  AFRAME.registerComponent('rebote', {
    init: function() {
          var el = this.el;
          el.addEventListener("collideend", function () {
            sleep(2000);
            const impulse = new Ammo.btVector3(0, 10, 0);
            const pos = new Ammo.btVector3(0, 0, 0);
            el.body.applyImpulse(impulse, pos);
            Ammo.destroy(impulse);
            Ammo.destroy(pos);
        
          });
    }
  });
  function sleep(milliseconds) {
    var start = new Date().getTime();
    for (var i = 0; i < 1e7; i++) {
      if ((new Date().getTime() - start) > milliseconds) {
      break;
      }
    }
  }
</script>

Nota: Podría ser un problema la funcion sleep(), pero no encuentro otro modo para que la esfera rebote pasados unos segundos.

Resultado:

Mejora de la precisión de las físicas

He observado que la ampliación del motor de físicas funciona correctamente cuando no estoy ejecutando otro programas, pero cuando intento grabar la escena se dan fallos de posicionamiento que he intentado solucionar con el siguiente código. ¿Se observa alguna mejora? No, no se observa ninguna mejora.

      if (!NAF.schemas.hasTemplate("#sphere-template")) {
        NAF.schemas.add({
          template: '#sphere-template',
          components: [
          {
            //Mejora precisión fisicas
            component: "position",
            requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.001)
          },
          {
            component: "rotation",
            requiresNetworkUpdate: NAF.utils.vectorRequiresUpdate(0.5)
          },
            {
              component: 'material',
              property: 'color'

            }
            // Modificación de otro componente
            // { component: 'geometry', property: 'primitive' }
          ]
        });
      }

Rebote isofacto

Con el siguiente código otorgo impulso a los objetos cuando se dan colisiones:

<script>
AFRAME.registerComponent('push', {
  init: function() {
        /* el -> Objeto */
        var el = this.el;
        console.log(el + " <-- EL")
        el.addEventListener("collidestart", function () {
          const impulse = new Ammo.btVector3(x, y, z);
          const pos = new Ammo.btVector3(0, 0, 0);
          el.body.applyImpulse(impulse, pos);
          Ammo.destroy(impulse);
          Ammo.destroy(pos);
    });
  }
});
</script>

La variable const impulse = new Ammo.btVector3(x, y, z); nos permite otorgar el dicho impulso. El principal problema es que este impulso no siempre es igual, es decir, en el mundo real un mismo objeto no cae y rebota siempre de la misma forma. Por otro lado, estos impulsos dependerán del objeto con el que choquen y desde el punto que lo hagan.

  1. Cada entidad estatica tiene un impulso propio. Problema: Independientemente desde donde proceda el choque siempre se dará el mismo impulso.

  2. Guardar la posición cada ‘x’ segundos y compararla con la posición en el momento de la colisión. En función de estas posiciones se dará un impulso u otro.