Bolera

CLASE PRINCIPAL

/*
 * Aplicación que simula una bolera. Si se pulsa la tecla espacio o el botón izquierdo del ratón, se lanza una bola
 * que impacta contra 10 bolos(clindros) ocasionando su caída según el ángulo
 */

package bolera;

// Importo las clases necesarias para trabajar
import com.jme3.app.SimpleApplication;
import com.jme3.asset.TextureKey;
import com.jme3.audio.AudioNode;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Cylinder;
import com.jme3.scene.shape.Sphere;
import com.jme3.shadow.PssmShadowRenderer;
import com.jme3.texture.Texture;

/**
 *
 * @author Gabriel López Marcos
 */
public class Bolera extends SimpleApplication {

    public static void main(String[] args) {
        // Inicio de la aplicación
        Bolera app = new Bolera();
        app.start();
    }
   
    // Variables del juego
    private BulletAppState bulletAppState;
    private Material material_bola;
    private Material material_suelo;
    private Material material_pared;
    private Material material_bordes_pista;
    private Material material_bordes_carril;
    private Material material_bolo;
    private AudioNode audio_impacto;
    private AudioNode audio_general;
    private AudioNode audio_bola;
    private Geometry bola_geo;
    private RigidBodyControl bola_fis;
    private int disparo=0;
    private String gravedad;
    BitmapText hudText;
   
    @Override
    public void simpleInitApp() {

        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);
        // Se establece la gravedad. He utilizado la gravedad terrestre como gravedad base.
        // Espero que sea correcto ya que tampoco estoy muy seguro con las conversiones.
        bulletAppState.getPhysicsSpace().setGravity(new Vector3f(0,-9.83f,0));
        gravedad = "Sí"; // Esta variable almacena en texto si la gravedad está activa o no lo está.
        bulletAppState.getPhysicsSpace().addCollisionListener(collisionListener); // Listener de colisiones
       
        crearMateriales(); // Se crean los materiales
        crearPista(); // Se crea la pista
        crearBordes(); // Se crean los bordes delimitadores de la pista
        crearBordesPista(); // Se crean los carriles exteriores. Hago esto para evitar que la bola y los bolos caigan al vacío.
        crearPared(); // Se crea la pared.
        crearBola(); // Se crea la bola.
        musicaAmbiente(); // Se añade el sonido ambiente.
       
        // Se crean los 10 bolos. Le paso su posición X y su posición Z. La posición Y ya que conozco
        // ya que ésta es a ras del suelo.
        crearBolos(0.20f,-7f);
        crearBolos(0.60f,-7f);
        crearBolos(-0.20f,-7f);
        crearBolos(-0.60f,-7f);
        crearBolos(0,-6f);
        crearBolos(0.40f,-6f);
        crearBolos(-0.40f,-6f);
        crearBolos(0.20f,-5f);
        crearBolos(-0.20f,-5f);
        crearBolos(0,-4f);
       

       
        // Se crea una luz ambiental que proyecta cierto brillo en los objetos. No produce sombras.
        AmbientLight luzAmbiente = new AmbientLight();
        luzAmbiente.setColor(ColorRGBA.White.mult(0.6f)); // La luz será blanca. Establezco su potencia.
        rootNode.addLight(luzAmbiente);

        // Se crea una luz posicional que emite un foco general desde la derecha.
        // sobre todos los objetos.
        DirectionalLight luzDireccional = new DirectionalLight();
        luzDireccional.setColor(ColorRGBA.White.mult(0.6f));
        luzDireccional.setDirection(new Vector3f(-10,20,14).normalizeLocal()); // Establezco desde donde procede el foco de luz.
        rootNode.addLight(luzDireccional);

       
        // Se crea un renderizado de sombras que se encarga de proyectar sombras.
        PssmShadowRenderer pssmRenderer = new PssmShadowRenderer(assetManager, 2048, 3); // Le indico el tamaño de mi escena.
        pssmRenderer.setDirection(new Vector3f(-10,-20,-14).normalizeLocal()); // Le indico desde donde procede la luz.
        pssmRenderer.setShadowIntensity(0.5f);
        pssmRenderer.setEdgesThickness(1);
        viewPort.addProcessor(pssmRenderer);
       
       
        // Colocamos la cámara en una posición adecuada para ver la superficie
        // del suelo y mirando hacia ella.
        cam.setLocation(new Vector3f(0, 4f, 10f));
        cam.lookAt(new Vector3f(0, 2, 0), Vector3f.UNIT_Y);

        // Ponemos un color de fondo azul oscuro
        viewPort.setBackgroundColor(new ColorRGBA(0f, 0f, 0.2f, 0));
       
       // Se escribe sobre la pantalla por encima del hud.
       // Aquí se aportará información sobre el estado de la gravedad. ON o OFF
       hudText = new BitmapText(guiFont, false);  
       hudText.setSize(guiFont.getCharSet().getRenderedSize());
       hudText.setColor(ColorRGBA.Red);                            
       hudText.setText("Gravedad : ON");
       hudText.setLocalTranslation(0,(hudText.getLineHeight()+170),0);
       guiNode.attachChild(hudText);
       
       // Se crea un mapping de controles para interactuar con el juego.
      // La tecla (espacio) o el botón izquierdo del ratón, provocará el lanzamiento de la bola.
      inputManager.addMapping("Lanzar",new KeyTrigger(KeyInput.KEY_SPACE),new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
      // La tecla G activa o desactiva la gravedad que se aplica
      inputManager.addMapping("Gravedad",new KeyTrigger(KeyInput.KEY_G));
      
      // Añado los controles al listener de acciones.
      inputManager.addListener(actionListener, new String[]{"Lanzar"});
      inputManager.addListener(actionListener, new String[]{"Gravedad"});

    }

    // Este método implementa las acciones que se realizarán en respuesta a las pulsaciones del usuario.
    private ActionListener actionListener = new ActionListener() {
    public void onAction(String name, boolean KeyReleased, float tpf) {

      // Si el nombre de la acción es 'Lanzar' y aún no se ha lanzado la bola se ejecuta la acción.
      // La variable 'disparo' almacena si ya se ha disparado o no. 0 no, 1, sí.
      // He incluído esta variable para controlar que no se pueda imprimir fuerza sobre la bola una vez lanzada.
      // También controlo si la tecla ha sido liberada para no seguir aumentando la velocidad del disparo.
      if (name.equals("Lanzar") && disparo==0 && KeyReleased==true) {
            audioBola(); // Añado el sonido que se produce al lanzar la bola.
            bola_fis.setLinearVelocity(cam.getDirection().mult(17)); // Se lanza la bola a 17 m/seg
            disparo=1; // Indico a la variable que ya se lanzó la bola
            //crearBola();
          }
     
       // Si el nombre de la acción es 'Gravedad' se activará o desactivará la gravedad que se aplica según su estado.
      // Controla que la tecla ha sido liberada.
       if (name.equals("Gravedad") && KeyReleased==true) {

            // Se cambia el estado de la gravedad según del estado que esté.
            if ("Sí".equals(gravedad)) { // Si la gravedad está activa ...
                gravedad = "No"; // La cambio a no activa.
                hudText.setText("Gravedad : OFF"); // Establezco el texto del hud.
                // Establezco gravedad 0. Los objeto que no se mueven permanecen como estén, sin embargo, a los que se
                // le aplique alguna fuerza flotarán cerca del suelo.
                bulletAppState.getPhysicsSpace().setGravity(Vector3f.ZERO);
               
            } else { // Si la gravedad está inactiva ...
               
                gravedad = "Sí"; // La cambio a actica
                hudText.setText("Gravedad : ON"); // Establezco el texto del hud.
                // Establezco la gravedad terrestre. Los objetos que flotasen serán atraídos nuevamente al suelo.
                bulletAppState.getPhysicsSpace().setGravity(new Vector3f(0,-9.83f,0));
            }

          }
      
    }
  };

    // Método que crea los materiales
    private void crearMateriales() {
       
        // MATERIAL DEL SUELO
        // Indico el tipo de material que se añadirá.
        material_suelo = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        // Indico la textura a utilizar.
        TextureKey key = new TextureKey("Textures/madera_suelo.jpg");
        key.setGenerateMips(true);
        Texture textura = assetManager.loadTexture(key); // Cargo la textura.
        textura.setWrap(Texture.WrapMode.Repeat); // Indico que se repita.
        material_suelo.setTexture("ColorMap", textura); // Le indico al material la textura que debe usar.
       
        // MATERIAL DE LA BOLA
        // Indico el tipo de material que se añadirá.
        material_bola = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        // Indico la textura a utilizar.
        key = new TextureKey("Textures/metal_bola.jpg");
        key.setGenerateMips(true);
        textura = assetManager.loadTexture(key); // Cargo la textura.
        textura.setWrap(Texture.WrapMode.Repeat); // Indico que se repita.
        material_bola.setTexture("ColorMap", textura); // Le indico al material la textura que debe usar.
       
        // MATERIAL DE LOS BORDES DELIMITADORES DE LA PISTA
        // Indico el tipo de material que se añadirá.
        material_bordes_pista = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        // Indico la textura a utilizar.
        key = new TextureKey("Textures/metal_bordes.jpg"); // Cargo la textura.
        key.setGenerateMips(true);
        textura = assetManager.loadTexture(key); // Cargo la textura.
        textura.setWrap(Texture.WrapMode.Repeat); // Indico que se repita.
        material_bordes_pista.setTexture("ColorMap", textura); // Le indico al material la textura que debe usar.
       
        // MATERIAL DE LOS CARRILES EXTERNOS DE LA PISTA.
        // Indico el tipo de material que se añadirá.
        material_bordes_carril = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        // Indico la textura a utilizar.
        key = new TextureKey("Textures/metal_borde_carril.jpg");
        key.setGenerateMips(true);
        textura = assetManager.loadTexture(key); // Cargo la textura.
        textura.setWrap(Texture.WrapMode.Repeat); // Indico que se repita.
        material_bordes_carril.setTexture("ColorMap", textura); // Le indico al material la textura que debe usar.
       
        // MATERIAL DE LA PARED DE LA PISTA
        // Indico el tipo de material que se añadirá.
        material_pared = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        // Indico la textura a utilizar.
        key = new TextureKey("Textures/pared_gris.jpg");
        key.setGenerateMips(true);
        textura = assetManager.loadTexture(key); // Cargo la textura.
        textura.setWrap(Texture.WrapMode.MirroredRepeat); // Indico que se repita.
        material_pared.setTexture("ColorMap", textura); // Le indico al material la textura que debe usar.
       
        // MATERIAL DE LOS BOLOS
        // Indico el tipo de material que se añadirá.
        material_bolo = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        // Indico la textura a utilizar.
        key = new TextureKey("Textures/material_bolo.jpg");
        key.setGenerateMips(true);
        textura = assetManager.loadTexture(key); // Cargo la textura.
        textura.setWrap(Texture.WrapMode.Repeat); // Indico que se repita.
        material_bolo.setTexture("ColorMap", textura); // Le indico al material la textura que debe usar.
       
    }
   
    // Método que crea la pista central de la bolera.
    public void crearPista() {

        // Se crea una forma de caja con 3 metros de ancho, 10 centímetros de grosor y 11 metros de largo.
        Box pista = new Box(Vector3f.ZERO, 3, 0.1f, 11);
        // Se ajusta la textura. (¡¡REALMENTE NO SÉ COMO MANEJAR CORRECTAMENTE ESTE AJUSTE!!)
        pista.scaleTextureCoordinates(new Vector2f(6, 3));
        // Se crea un spatial de tipo geometría para asociarlo al suelo.
        Geometry pista_geo = new Geometry("Pista", pista);
        // Asigno su material.
        pista_geo.setMaterial(material_suelo);
        // Coloco la pista en la posición central
        pista_geo.setLocalTranslation(0, 0, 0);
        // Se añade su ajuste de sombras. Establezco que únicamente reciba ya que no hay objetos debajo.
        pista_geo.setShadowMode(RenderQueue.ShadowMode.Receive);
        // Lo añado a la escena
        rootNode.attachChild(pista_geo);

        // Crearemos un objeto de control físico para asociarlo al suelo
        // IMPORTANTE: tiene masa 0 para convertirlo en un objeto estático
        RigidBodyControl suelo_fis = new RigidBodyControl(0.0f);
        // asociamos el objeto de control a la geometría del suelo de la pista.
        pista_geo.addControl(suelo_fis);
        // y añadimos el objeto de control al motor de físicas
        bulletAppState.getPhysicsSpace().add(suelo_fis);
       
    }
   
    // Método que crea los bordes de la pista
    public void crearBordes() {
       
        // Se crean dos formas de caja con 1 metro de ancho, 10 centímetros de grosor y 11 metros de largo.
        // Coíncide completamente con la pista.
        Box borde1 = new Box(Vector3f.ZERO, 1, 0.1f, 11f);
        Box borde2 = new Box(Vector3f.ZERO, 1, 0.1f, 11f);

        // Se ajusta la textura. (¡¡REALMENTE NO SÉ COMO MANEJAR CORRECTAMENTE ESTE AJUSTE.!!)
        borde1.scaleTextureCoordinates(new Vector2f(6, 3));
        borde2.scaleTextureCoordinates(new Vector2f(6, 3));

        // Se crean dos spatials de tipo geometría para asociarlo a los bordes.
        Geometry borde1_geo = new Geometry("Bordes", borde1);
        Geometry borde2_geo = new Geometry("Bordes", borde2);
        // Se asigna el material a los dos bordes
        borde1_geo.setMaterial(material_bordes_pista);
        borde2_geo.setMaterial(material_bordes_pista);

        // Los bordes se colocan en los extremos de la pista. Estás situados más bajos que la misma ya que
        // quiero que la bola se quede en estos bordes si se sale.
        borde1_geo.setLocalTranslation(-2.8f, -0.1f, 0);
        borde2_geo.setLocalTranslation(2.8f, -0.1f, 0);
        // Establezco las sombras de los bordes. Únicamente reciben sombra.
        borde1_geo.setShadowMode(RenderQueue.ShadowMode.Receive);
        borde2_geo.setShadowMode(RenderQueue.ShadowMode.Receive);
        // Los añado a la escena.
        rootNode.attachChild(borde1_geo);
        rootNode.attachChild(borde2_geo);

        // Crearemos un objeto de control físico para asociarlo a la pared
        // IMPORTANTE: tiene masa 0 para convertirlo en un objeto estático
        RigidBodyControl borde1_fis = new RigidBodyControl(0.0f);
        RigidBodyControl borde2_fis = new RigidBodyControl(0.0f);
        // asociamos el objeto de control a la geometría de los bordes.
        borde1_geo.addControl(borde1_fis);
        borde2_geo.addControl(borde2_fis);
        // y añadimos los objetos de control al motor de físicas
        bulletAppState.getPhysicsSpace().add(borde1_fis);
        bulletAppState.getPhysicsSpace().add(borde2_fis);
    }
   
    // Método que crea los carriles exteriores de la pista.
    public void crearBordesPista() {

        // Se crean dos formas de caja con 20 centímetros de ancho, 10 centímetros de grosor y 11 metros de largo.
        // Coíncide completamente con la pista.
        Box borde1 = new Box(Vector3f.ZERO, 0.2f, 0.1f, 11);
        Box borde2 = new Box(Vector3f.ZERO, 0.2f, 0.1f, 11);

        // Se ajusta la textura. (¡¡REALMENTE NO SÉ COMO MANEJAR CORRECTAMENTE ESTE AJUSTE.!!)
        borde1.scaleTextureCoordinates(new Vector2f(6, 3));
        borde2.scaleTextureCoordinates(new Vector2f(6, 3));

        // Se crean dos spatial de tipo geometría para asociarlo a los carriles.
        Geometry borde1_geo = new Geometry("Carriles", borde1);
        Geometry borde2_geo = new Geometry("Carriles", borde2);
        // Se asigna el material correspondiente.
        borde1_geo.setMaterial(material_bordes_carril);
        borde2_geo.setMaterial(material_bordes_carril);
        // Se colocan en los extremos de la pista.
        // Estos carriles impiden que la bola o los boles caigan por los laterales.
        borde1_geo.setLocalTranslation(-3.9f, 0.1f, 0);
        borde2_geo.setLocalTranslation(3.9f, 0.1f, 0);
        // Se añaden sus sombras. Reciben y proyectan sombras.
        borde1_geo.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        borde2_geo.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        // Se añaden a la escena.
        rootNode.attachChild(borde1_geo);
        rootNode.attachChild(borde2_geo);

        // Crearemos un objeto de control físico para asociarlo a la pared
        // IMPORTANTE: tiene masa 0 para convertirlo en un objeto estático
        RigidBodyControl borde1_fis = new RigidBodyControl(0.0f);
        RigidBodyControl borde2_fis = new RigidBodyControl(0.0f);
        // asociamos el objeto de control a la geometría de los carriles.
        borde1_geo.addControl(borde1_fis);
        borde2_geo.addControl(borde2_fis);
        // y añadimos el objeto de control al motor de físicas
        bulletAppState.getPhysicsSpace().add(borde1_fis);
        bulletAppState.getPhysicsSpace().add(borde2_fis);
    }
   
    // Método que crea la pared frontal de la pista.
    public void crearPared() {
       
        // Se crea una forma de caja con 20 metros de ancho, 50 centímetros de grosor y 10 metros de largo.
        // Coíncide completamente con la pista.
        Box pared = new Box(Vector3f.ZERO, 10f, 0.5f, 10f);
        // Se ajusta la textura. (¡¡REALMENTE NO SÉ COMO MANEJAR CORRECTAMENTE ESTE AJUSTE.!!)
        pared.scaleTextureCoordinates(new Vector2f(6, 3));

        // Se crea un spatial de tipo geometría para asociarlo a la pared
        Geometry pared_geo = new Geometry("Floor", pared);
        // asignamos el material
        pared_geo.setMaterial(material_pared);
        // lo desplazamos detrás del suelo e inclinado 90 grados en el eje X
        // para ponerla en vertical
        pared_geo.setLocalTranslation(0, -0.1f, -11);
        pared_geo.rotate((float) Math.PI / 2.0f, 0f, 0);
        // Se añaden sus sombras. La pared recibe y proyecta sombra.
        pared_geo.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        // y, finalmente, lo incluimos en el grafo de escena
        rootNode.attachChild(pared_geo);

        // Crearemos un objeto de control físico para asociarlo a la pared
        // IMPORTANTE: tiene masa 0 para convertirlo en un objeto estático
        RigidBodyControl pared_fis = new RigidBodyControl(0.0f);
        // asociamos el objeto de control a la geometría de la pared
        pared_geo.addControl(pared_fis);
        // y añadimos el objeto de control al motor de físicas
        bulletAppState.getPhysicsSpace().add(pared_fis);
    }
   
    // Método que crea los bolos. En el constructor se le pasa la posición en X y la posición en Z
    // La posición en Y siempre es 0.
    public void crearBolos(float posicionX, float posicionZ) {

        // Se crea un objeto cilíndrico con 11 centímetros de radio y 40 centímetros de altura.
        Cylinder bolo = new Cylinder(15, 15, 0.11f,0.40f,true);
      
        // Se coloca en posición vertical.
        Quaternion q = new Quaternion();
        q.fromAngleAxis(FastMath.PI/2,new Vector3f(1,0,0));
        // Se ajusta la textura. (¡¡REALMENTE NO SÉ COMO MANEJAR CORRECTAMENTE ESTE AJUSTE.!!)
        bolo.scaleTextureCoordinates(new Vector2f(6, 3));
      

        // Se crea un spatial de tipo geometría para asociarlo al bolo.
        Geometry bolo_geo = new Geometry("Bolo", bolo);
        // Se asigna el material.
        bolo_geo.setMaterial(material_bolo);
        // Se colocan sonbre la pista. En el constructor se indica las posiciones que ocuparán.
        bolo_geo.setLocalTranslation(posicionX, 0.25f, posicionZ);
        bolo_geo.setLocalRotation(q);
        // Se añaden sus sombras. El bolo recibe y proyecta sombra.
        bolo_geo.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        // Se añade a la escena.
        rootNode.attachChild(bolo_geo);

        // Crearemos un objeto de control físico para asociarlo al suelo
        // Cada bolo tiene un peso de 1 kilo y medio.
        RigidBodyControl bolo_fis = new RigidBodyControl(1.50f);
        // asociamos el objeto de control a la geometría del bolo.
        bolo_geo.addControl(bolo_fis);
        // y añadimos el objeto de control al motor de físicas
        bulletAppState.getPhysicsSpace().add(bolo_fis);
        
    }
   
    // Método que crea una bola.
    public void crearBola() {
       
        // Se crea un esfera con 21 centímetros de diámetro.
        Sphere esfera = new Sphere(32, 32, 0.216f);
       
        // Se crea un spatial de tipo geometría para asociarlo a la bola.
        bola_geo = new Geometry("Bola", esfera);
        // Se asigna su material.
        bola_geo.setMaterial(material_bola);
        // Se añaden sus sombras. La sombra recibe y proyecta sombra.
        bola_geo.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
        // Se añade a la escena.
        rootNode.attachChild(bola_geo);
        // Se coloca sobre la pista.
        bola_geo.setLocalTranslation(0, 0.1f, 10f);

        // La bola pesa 3 kilos.
        bola_fis = new RigidBodyControl(3f);
        // Asociar la geometría de la bola al control físico
        bola_geo.addControl(bola_fis);
        // Añadirla al motor de física
        bulletAppState.getPhysicsSpace().add(bola_fis);
       
    }
   
    // Método que implemeta la música que acompañará la aplicación.
  private void musicaAmbiente() {

    // Se crea el nodo de tipo audio. He cargado la canción 'Saturday Night Fever' de los Bee Gees.
    // Se irá reproduciendo a la vez que se carga en buffer.
    audio_general = new AudioNode(assetManager, "Sounds/night_fever.wav", false);
    audio_general.setLooping(true);  // Se reproducirá en bucle.
    audio_general.setPositional(true);
    // Se añade a la escena.
    rootNode.attachChild(audio_general);
    audio_general.play(); // Reproducción.
  }
 
  // Método que reproduce un audio cuando la bola impacta con cada bolo.
  private void impacto() {

    // Se crea el nodo de tipo audio. Cuando se produzca una colisión bola-bolo, se reproducirá un sonido.
    audio_impacto = new AudioNode(assetManager, "Sounds/rompe.wav", false);
    audio_impacto.setLooping(false);  // Sólo sonará una vez.
    audio_impacto.setPositional(true);  
    // Se añade a la escena.
    rootNode.attachChild(audio_impacto);
    audio_impacto.play(); // Reproducción.
  }
 
  // Método que reproduce un sonido al lanzar la bola.
  private void audioBola() {

    // Se crea el nodo de tipo audio. Un sonido se reproducirá cuando se dispare la bola.
    audio_bola = new AudioNode(assetManager, "Sounds/Bang.wav", false);
    audio_bola.setLooping(false);  // Sólo sonará una vez.
    audio_bola.setPositional(true);  
    // Se añade a la escena.
    rootNode.attachChild(audio_bola);
    audio_bola.play(); // Reproducción.
   
  }
 
  // Método que detecta las colisiones que se produzcan
  private PhysicsCollisionListener collisionListener = new PhysicsCollisionListener() {


        @Override
        public void collision(PhysicsCollisionEvent colision) {

            // Si la bola colisiones con un bolo ......
            if ("Bola".equals(colision.getNodeA().getName()) && "Bolo".equals(colision.getNodeB().getName())) {
                impacto(); // Se reproduce el audio del impacto.
            }
           
        }

    };
 
    @Override
    public void simpleUpdate(float tpf) {

       // Aquí controlo los cambios direccionales de los audios.
       // La fuente de éstos es la posición de la cámara.
       listener.setLocation(cam.getLocation());
       listener.setRotation(cam.getRotation());
   
   
       
    }

    @Override
    public void simpleRender(RenderManager rm) {
      
       
    }

 


 
 
}