JMonkeyEngine, Creación de nuestro primer juego.

3
16400

JMonkeyEngine,
Creación
de nuestro primer juego.

En este tutorial intentaremos enseñaros a crear vuestro
primer juego, partiremos de un ejemplo hecho de JMonkey Engine que es el
jmetest.flagrushtut.lesson9, que trata sobre el manejo de una moto por
un escenario.

Para
realizar este tutorial primero
tendremos que instalar el JMonkeyEngine con todos sus ejemplos(estos se
encuentran en todos los paquetes que empiezan por jmestest)., puede ver
como se hace en:

https://adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=BlenderavaMonkeyEngine

Seguiremos trabajando con el
proyecto creado con anterioridad en el
tutorial «Iniciarse
en el manejo de JMonkeyEngine, Creación
de un Cloth.»
. Ha este proyecto
le tendremos que añadir un
nuevo paquete que en nuestro caso lo hemos llamado «Intento1»:

Una
vez creado, añadimos las clases del ejemplo y las
renombremos si
lo vemos oportuno, en nuestro caso lo hemos hecho ya que de este modo,
podemos tener una mejor compresion, de que es cada fichero java:

FlagRushHandler.java —————-> teclado.java
Vehicle.java ————————–> moto.java
ForceFieldFence.java —————-> escenario.java
Lesson9.java ————————-> juego.java
DriftAction.java ———————-> AccionDerrape.java
ForwardAndBackwardAction.java —> AvanzarRetroceder.java
VehicleRotateACtion.java ————> RotacionMoto.java
Lesson2.java ————————-> Lecion2.java

Una
vez hecho esto quedará algo asi

:

Una vez añadido los
ficheros java tenemos que añadir
los recursos, es decir las im&aacutegenes asi que nos dirigimos a la carpeta
del proyecto, nos vamos a la carpeta src/Intento1 y una vez ahi creamos
tres carpetas: imágenes, modelo, texturas

Dentro de cada carpeta metemos
las imagenes correspondientes:

imagenes

modelo

texturas

Empecemos analizando las clases:

moto.java


package Intento1;

import com.jme.image.Texture;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jmex.effects.particles.ParticleMesh;

public class moto extends Node {
    private static final long serialVersionUID = 1L;
    private static final float LEAN_BUFFER = 0.05f;
    private Spatial model;
    private float weight;
    private float velocity;
    private float acceleration;
    private float braking;
    private float turnSpeed;

    private float maxSpeed = 30;
    private float minSpeed = 10;


    private static final Vector3f tempVa = new Vector3f();
    private int lean;
    private float leanAngle;
    private Vector3f leanAxis = new Vector3f(0,0,1);
    private Quaternion q = new Quaternion();


    Spatial frontwheel, backwheel,gun;
    private Vector3f wheelAxis = new Vector3f(0, 1, 0);
    private float angle = 0;
    private Quaternion rotQuat = new Quaternion();   
   
    public moto(String id, Spatial model) {
        super(id);
        setModel(model);
    }
   
    public moto(String id, Spatial model, float maxSpeed, float minSpeed,
            float weight, float acceleration, float braking, float turnSpeed) {
        super(id);
        setModel(model);
        this.maxSpeed = maxSpeed;
        this.minSpeed = minSpeed;
        this.weight = weight;
        this.acceleration = acceleration;
        this.braking = braking;
        this.turnSpeed = turnSpeed;
    }

    /**
     * Este método actualiza la moto basando en el tiempo 
     * que transcurre.
     */
    public void update(float time) {
        this.localTranslation.addLocal(this.localRotation.getRotationColumn(2, tempVa)
                .multLocal(velocity * time));
        rotateWheels(time);
        processLean(time);
    }

    /**
     * rorateWheels hace que las ruedas rotan un cierto angulo
     * segun la velocidad
     * de la moto.
     */
    private void rotateWheels(float time) {
        //Rotate the tires if the vehicle is moving.
        if (vehicleIsMoving()) {
            if(velocity > FastMath.FLT_EPSILON) {
                angle = angle - ((time) * velocity * 0.5f);
                if (angle < -360) {
                    angle = 0;
                }
            } else {
                angle = angle + ((time) * velocity * 0.5f);
                if (angle > 360) {
                    angle = 0;
                }
            }
            rotQuat.fromAngleAxis(angle, wheelAxis);
            frontwheel.getLocalRotation().multLocal(rotQuat);
            backwheel.setLocalRotation(frontwheel.getLocalRotation());
        }
    }

    /**
     * Este metodo determina si la moto esta moviendose o no, esto se consigue observando
     * si la velocidad es cercana a cero, teniendo en cuenta un margen de error.
     * Devuelve verdadero si la moto se esta moviendo.
     */
    public boolean vehicleIsMoving() {
        return velocity > FastMath.FLT_EPSILON
                || velocity < -FastMath.FLT_EPSILON;
    }

    /**
     * processLean calcula el angulo de inclinacion de la moto basandose en un 
     * factor de inclinación. Inclinamos la moto mas que inclinar el vehiculo, ya
     * que este esta centrado en la posicion que ocupa en el terreno.
     */
    private void processLean(float time) {
        //check if we are leaning at all
        if(lean != 0) {
            if(lean == -1 && leanAngle < 0) {
                leanAngle += -lean * 4 * time;
            } else if(lean == 1 && leanAngle > 0) {
                leanAngle += -lean * 4 * time;
            } else {
                leanAngle += -lean * 2 * time;
            }
            //max lean is 1 and -1
            if(leanAngle > 1) {
                leanAngle = 1;
            } else if(leanAngle < -1) {
                leanAngle = -1;
            }
        } else { //we are not leaning, so right ourself back up.
            if(leanAngle < LEAN_BUFFER && leanAngle > -LEAN_BUFFER) {
                leanAngle = 0;
            }
            else if(leanAngle < -FastMath.FLT_EPSILON) {
                leanAngle += time * 4;
            } else if(leanAngle > FastMath.FLT_EPSILON) {
                leanAngle -= time * 4;
            } else {
                leanAngle = 0;
            }
        }

        q.fromAngleAxis(leanAngle, leanAxis);
        model.setLocalRotation(q);

        lean = 0;
    }

    /**
     * Se definen distintas propiedades que modifican parametros de la moto 
     */
    public void setWeight(float weight) {
        this.weight = weight;
    }

    public float getWeight() {
        return weight;
    }

    public float getAcceleration() {
        return acceleration;
    }

    public void setAcceleration(float acceleration) {
        this.acceleration = acceleration;
    }

    public float getBraking() {
        return braking;
    }

    public void setBraking(float braking) {
        this.braking = braking;
    }

    public Spatial getModel() {
        return model;
    }

    public void setModel(Spatial model) {
        this.detachChild(this.model);
        this.model = model;
        this.attachChild(this.model);
        //Obtiene la referencia de rueda delantera y trasera.       
        backwheel = ((Node)model).getChild("backwheel");
        frontwheel = ((Node)model).getChild("frontwheel");
    }

    public float getVelocity() {
        return velocity;
    }
    
    public void setVelocity(float velocity) {
        this.velocity = velocity;
    }
    
    public float getTurnSpeed() {
        return turnSpeed;
    }

    public void setTurnSpeed(float turnSpeed) {
        this.turnSpeed = turnSpeed;
    }

    public float getMaxSpeed() {
        return maxSpeed;
    }

    public void setMaxSpeed(float maxSpeed) {
        this.maxSpeed = maxSpeed;
    }

     public float getMinSpeed() {
        return minSpeed;
    }

   public void setMinSpeed(float minSpeed) {
        this.minSpeed = minSpeed;
    }

    /**
     * brake disminuye la velocidad cuando la moto se dirige hacia delante y pulsamos
     * la tecla para que vaya marcha atras, para la moto y luego le pondra velocidad negativa
     */
    public void brake(float time) {
        velocity -= time * braking;
        if(velocity < -minSpeed) {
            velocity = -minSpeed;
        }
    }

    /**
     * accelerate acelera la moto hasta su velocidad maxima
     */
    public void accelerate(float time) {
        velocity += time * acceleration;
        if(velocity > maxSpeed) {
            velocity = maxSpeed;
        }
    }

    /**
     * drift calcula que ocurre con la moto tanto cuando acelera como cuando frena.
     * La moto frenara segun su peso.
     */
    public void drift(float time) {
        if(velocity < -FastMath.FLT_EPSILON) {
            velocity += ((weight/5) * time);            
            if(velocity > 0) {
                velocity = 0;
            }
        } else if(velocity > FastMath.FLT_EPSILON){
            velocity -= ((weight/5) * time);
            if(velocity < 0) {
                velocity = 0;
            }
        }
    }

    public void setRotateOn(int modifier) {
        lean = modifier;
    }
}

teclado.java


package Intento1;


import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;

public class teclado extends InputHandler {

     
    private moto vehicle;    
    private AccionDerrape drift;

     public void update(float time) {
        if ( !isEnabled() ) return;

        super.update(time);        
        drift.performAction(event);
        vehicle.update(time);
     }

    public teclado(moto vehicle, String api) {
        this.vehicle = vehicle;
        setKeyBindings(api);
        setActions(vehicle);

    }

    /**
     * setKeyBindings establece las teclas que permitiran manejar 
     * la moto. En este caso las flechas
     */
    private void setKeyBindings(String api) {
        
        KeyBindingManager keyboard = KeyBindingManager.getKeyBindingManager();
        keyboard.set("forward", KeyInput.KEY_UP);
        keyboard.set("backward", KeyInput.KEY_DOWN);
        keyboard.set("turnRight", KeyInput.KEY_RIGHT);
        keyboard.set("turnLeft", KeyInput.KEY_LEFT);
        
    }

    /**
     * setActions define los triggers para las teclas,
     * para que hagan la accion que queremos cuando pulsamos.
     */
    
    private void setActions(moto node) {
      
        AvanzarRetroceder forward = new AvanzarRetroceder(node, AvanzarRetroceder.FORWARD);
        addAction(forward, "forward", true);
        AvanzarRetroceder backward = new AvanzarRetroceder(node, AvanzarRetroceder.BACKWARD);
        addAction(backward, "backward", true);
        RotacionMoto rotateLeft = new RotacionMoto(node, RotacionMoto.LEFT);
        addAction(rotateLeft, "turnLeft", true);
        RotacionMoto rotateRight = new RotacionMoto(node, RotacionMoto.RIGHT);
        addAction(rotateRight, "turnRight", true);

        drift = new AccionDerrape(node);
    }
}

escenario.java

package Intento1;

import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.math.FastMath;
import com.jme.math.Quaternion;
import com.jme.math.Vector3f;
import com.jme.renderer.Renderer;
import com.jme.scene.Node;
import com.jme.scene.SharedMesh;
import com.jme.scene.shape.Box;
import com.jme.scene.shape.Cylinder;
import com.jme.scene.state.BlendState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;



public class escenario extends Node {
	private static final long serialVersionUID = 1L;
   
    private Texture t;
    
    public escenario(String name) {
        super(name);
        buildFence();
    }

    /**
     * Tiene este metodo ya que se creara una especie de campo y con este metodo
     * lo podemos animar
     */
    public void update(float interpolation) {
        /**
         * Se usara el valor de interpolation para mantener la velocidad del 
         * campo de fuerza constante.
         */
         t.getTranslation().y += 0.3f * interpolation;
        
        if(t.getTranslation().y > 1) {
            t.getTranslation().y = 0;
        }
    }

    /**
     * buildFence crea la geometria de la valla
     */
    private void buildFence() {
        
        Cylinder postGeometry = new Cylinder("post", 10, 10, 1, 10);
        Quaternion q = new Quaternion();
        q.fromAngleAxis(FastMath.PI/2, new Vector3f(1,0,0));
        postGeometry.setLocalRotation(q);
        postGeometry.setModelBound(new BoundingBox());
        postGeometry.updateModelBound();
        /**
         * creamos los 4 postes, no es buena idea que compartamos la estructura
         * asi que usaremos los valores locales
         */
        SharedMesh post1 = new SharedMesh("post1", postGeometry);
        post1.setLocalTranslation(new Vector3f(0,0.5f,0));
        SharedMesh post2 = new SharedMesh("post2", postGeometry);
        post2.setLocalTranslation(new Vector3f(32,0.5f,0));
        SharedMesh post3 = new SharedMesh("post3", postGeometry);
        post3.setLocalTranslation(new Vector3f(0,0.5f,32));
        SharedMesh post4 = new SharedMesh("post4", postGeometry);
        post4.setLocalTranslation(new Vector3f(32,0.5f,32));
        
        /**
         * este sera el cilindro maestro qe mantendra el campo de fuerza 
         * en su sitio
         */
        
        Cylinder strutGeometry = new Cylinder("strut", 10,10, 0.125f, 32);
        strutGeometry.setModelBound(new BoundingBox());
        strutGeometry.updateModelBound();

        /**
         * Otra vez tenemos que compartir esta estrucutra asi que la rotaremos
         * para hacer las conexiones
         */
        SharedMesh strut1 = new SharedMesh("strut1", strutGeometry);
        Quaternion rotate90 = new Quaternion();
        rotate90.fromAngleAxis(FastMath.PI/2, new Vector3f(0,1,0));
        strut1.setLocalRotation(rotate90);
        strut1.setLocalTranslation(new Vector3f(16,3f,0));
        SharedMesh strut2 = new SharedMesh("strut2", strutGeometry);
        strut2.setLocalTranslation(new Vector3f(0,3f,16));
        SharedMesh strut3 = new SharedMesh("strut3", strutGeometry);
        strut3.setLocalTranslation(new Vector3f(32,3f,16));
        SharedMesh strut4 = new SharedMesh("strut4", strutGeometry);
        strut4.setLocalRotation(rotate90);
        strut4.setLocalTranslation(new Vector3f(16,3f,32));

        /**
         * Crea el campo de fuerza
         * La primera caja controla el eje x, la segunda el eje z.
         * No se rota la caja para ense�ar que se pueden crear de varias formas.
         */
        Box forceFieldX = new Box("forceFieldX", new Vector3f(-16, -3f, -0.1f), new Vector3f(16f, 3f, 0.1f));
        forceFieldX.setModelBound(new BoundingBox());
        forceFieldX.updateModelBound();
        /**
         * Tambien compartiremos estas cajas
         */
        SharedMesh forceFieldX1 = new SharedMesh("forceFieldX1",forceFieldX);
        forceFieldX1.setLocalTranslation(new Vector3f(16,0,0));
        SharedMesh forceFieldX2 = new SharedMesh("forceFieldX2",forceFieldX);
        forceFieldX2.setLocalTranslation(new Vector3f(16,0,32));

        
        Box forceFieldZ = new Box("forceFieldZ", new Vector3f(-0.1f, -3f, -16), new Vector3f(0.1f, 3f, 16));
        forceFieldZ.setModelBound(new BoundingBox());
        forceFieldZ.updateModelBound();
        SharedMesh forceFieldZ1 = new SharedMesh("forceFieldZ1",forceFieldZ);
        forceFieldZ1.setLocalTranslation(new Vector3f(0,0,16));
        SharedMesh forceFieldZ2 = new SharedMesh("forceFieldZ2",forceFieldZ);
        forceFieldZ2.setLocalTranslation(new Vector3f(32,0,16));

        /**
         * se a�ade todos los campos de fuerza a un solo nodo y se le hace parte
         * de la zona transparente.
         */
        
        Node forceFieldNode = new Node("forceFieldNode");
        forceFieldNode.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
        forceFieldNode.attachChild(forceFieldX1);
        forceFieldNode.attachChild(forceFieldX2);
        forceFieldNode.attachChild(forceFieldZ1);
        forceFieldNode.attachChild(forceFieldZ2);
        
        /**
         * se a�ade los valores alfa para el nodo transparente
         */
        
        BlendState as1 = DisplaySystem.getDisplaySystem().getRenderer().createBlendState();
        as1.setBlendEnabled(true);
        as1.setSourceFunction(BlendState.SourceFunction.SourceAlpha);
        as1.setDestinationFunction(BlendState.DestinationFunction.One);
        as1.setTestEnabled(true);
        as1.setTestFunction(BlendState.TestFunction.GreaterThan);
        as1.setEnabled(true);

        forceFieldNode.setRenderState(as1);
        
        /**
         * cargamos la textura para el campo de fuerza
         */
        
        TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        t = TextureManager.loadTexture(Leccion2.class.getClassLoader()
                  .getResource("Intento1/texturas/reflector.jpg"),
                  Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear);

        t.setWrap(Texture.WrapMode.Repeat);
        t.setTranslation(new Vector3f());
        ts.setTexture(t);

        forceFieldNode.setRenderState(ts);

        /**
         * ponemos todas las postes en el nodo de una torre
         */
        
        Node towerNode = new Node("tower");
        towerNode.attachChild(post1);
        towerNode.attachChild(post2);
        towerNode.attachChild(post3);
        towerNode.attachChild(post4);

        /**
         * se a�ade la torre a la zona opaca ya que no queremos que se vea a 
         * traves de ella
         */
        
        towerNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);

        /**
         * cargamos la textura para las torres
         */
        
        TextureState ts2 = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        Texture t2 = TextureManager.loadTexture(Leccion2.class.getClassLoader()
                  .getResource("Intento1/texturas/post.jpg"),
                  Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear);

        ts2.setTexture(t2);

        towerNode.setRenderState(ts2);
        
        /**
         * ponemos todas las estructuras en un solo nodo
         */
        
        Node strutNode = new Node("strutNode");
        strutNode.attachChild(strut1);
        strutNode.attachChild(strut2);
        strutNode.attachChild(strut3);
        strutNode.attachChild(strut4);
        /**
         * Esto tambien se encuentra en la zona opaca
         */
        strutNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);

        /**
         * cargamos la textura de las estructuras
         */
        TextureState ts3 = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        Texture t3 = TextureManager.loadTexture(Leccion2.class.getClassLoader()
                  .getResource("Intento1/texturas/rust.jpg"),
                  Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear);

        ts3.setTexture(t3);

        strutNode.setRenderState(ts3);

        /**
         * a�adimos todas las piezas en el nodo principal de la valla
         */
        
        this.attachChild(forceFieldNode);
        this.attachChild(towerNode);
        this.attachChild(strutNode);
    }
}

RotacionMoto.java


package Intento1;

import com.jme.input.action.InputActionEvent;
import com.jme.input.action.KeyInputAction;
import com.jme.math.FastMath;
import com.jme.math.Matrix3f;
import com.jme.math.Vector3f;


public class RotacionMoto extends KeyInputAction {
    public static final int RIGHT = 0;
    public static final int LEFT = 1;
    
    /**
     * Variables temporales para la rotacion
     */
    
    private static final Matrix3f incr = new Matrix3f();
    private static final Matrix3f tempMa = new Matrix3f();
    private static final Matrix3f tempMb = new Matrix3f();

    
    private Vector3f upAxis = new Vector3f(0,1,0);
    
    private moto vehicle;
    private int direction;
    private int modifier = 1;

    
    public RotacionMoto(moto vehicle, int direction) {
        this.vehicle = vehicle;
        this.direction = direction;
    }

    /**
     * giraremos por su velocidad de giro. Si vamos marcha atras girará al reves
     */
    
    public void performAction(InputActionEvent evt) {
        if(vehicle.getVelocity() > -FastMath.FLT_EPSILON && vehicle.getVelocity() < FastMath.FLT_EPSILON) {
            return;
        }
        
        if(direction == LEFT) {
            modifier = 1;
        } else if(direction == RIGHT) {
            modifier = -1;
        }
        
        if(vehicle.getVelocity() < 0) {
            incr.fromAngleNormalAxis(-modifier * vehicle.getTurnSpeed() * evt.getTime(), upAxis);
        } else {
            incr.fromAngleNormalAxis(modifier * vehicle.getTurnSpeed() * evt.getTime(), upAxis);
        }
        vehicle.getLocalRotation().fromRotationMatrix(
                incr.mult(vehicle.getLocalRotation().toRotationMatrix(tempMa),
                        tempMb));
        vehicle.getLocalRotation().normalize();
        vehicle.setRotateOn(modifier);
    }
}

AvanzarRetroceder.java


package Intento1;

import com.jme.input.action.InputActionEvent;
import com.jme.input.action.KeyInputAction;


public class AvanzarRetroceder extends KeyInputAction {
    public static final int FORWARD = 0;
    public static final int BACKWARD = 1;
    
    private moto node;
    private int direction;

    
    public AvanzarRetroceder(moto node, int direction) {
        this.node = node;
        this.direction = direction;
    }

    /**
     * la acción llama a los metodos de accelerate o brake que son los que ajustan su velocidad
     */
    
    public void performAction(InputActionEvent evt) {
        if(direction == FORWARD) {
            node.accelerate(evt.getTime());
        } else if(direction == BACKWARD){
            node.brake(evt.getTime());
        }
    }
}

AccionDerrape.java


package Intento1;

import com.jme.input.action.InputActionEvent;
import com.jme.input.action.KeyInputAction;


public class AccionDerrape extends KeyInputAction {
  
    private moto vehicle;

    public AccionDerrape(moto vehicle) {
        this.vehicle = vehicle;
    }

    /**
     * la accion llama al metodo drift de la moto
     */
    public void performAction(InputActionEvent evt) {
        vehicle.drift(evt.getTime());
    }
}

juego.java

package Intento1;


import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.ImageIcon;

import jmetest.renderer.ShadowTweaker;
import jmetest.renderer.TestSkybox;
import jmetest.terrain.TestTerrain;

import com.jme.app.BaseGame;
import com.jme.bounding.BoundingBox;
import com.jme.image.Texture;
import com.jme.input.ChaseCamera;
import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.thirdperson.ThirdPersonMouseLook;
import com.jme.intersection.BoundingCollisionResults;
import com.jme.intersection.CollisionResults;
import com.jme.light.DirectionalLight;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.pass.BasicPassManager;
import com.jme.renderer.pass.RenderPass;
import com.jme.renderer.pass.ShadowedRenderPass;
import com.jme.scene.Node;
import com.jme.scene.Skybox;
import com.jme.scene.Spatial;
import com.jme.scene.Text;
import com.jme.scene.state.CullState;
import com.jme.scene.state.LightState;
import com.jme.scene.state.MaterialState;
import com.jme.scene.state.TextureState;
import com.jme.scene.state.ZBufferState;
import com.jme.system.DisplaySystem;
import com.jme.system.JmeException;
import com.jme.util.TextureManager;
import com.jme.util.Timer;
import com.jme.util.export.binary.BinaryImporter;
import com.jmex.terrain.TerrainBlock;
import com.jmex.terrain.util.MidPointHeightMap;
import com.jmex.terrain.util.ProceduralTextureGenerator;


public class juego extends BaseGame {
    private static final Logger logger = Logger.getLogger(juego.class
            .getName());

    // El terreno donde andara nuestra moto.
    private TerrainBlock tb;
    // La valla que nos mantendra en el escenario.
    private escenario fence;
    //La caja que contiene el cielo (la actualizaremos en cada frame).
    private Skybox skybox;
    //el jugador que moveremos.
    private moto player;
    //private ChaseCamera chaser;
    protected InputHandler input;
    //Variable de tiempo que se usa para las velocidades
    protected Timer timer;
    //La camara
    private Camera cam;
    //La camara que sigue a nuestro jugador
    private ChaseCamera chaser;
    //El nodo principal del juego
    protected Node scene;
    /**
     * los atributos par ala ventana
     */

    private int width, height, depth, freq;
    private boolean fullscreen;

    //almacena el vector normal del terreno
    private Vector3f normal = new Vector3f();

    //Altura sobre el nivel el suelo
    private float agl;

    private static ShadowedRenderPass shadowPass = new ShadowedRenderPass();
    private BasicPassManager passManager;
   
    public static void main(String[] args) {
        juego app = new juego();
        app.setConfigShowMode(ConfigShowMode.AlwaysShow, juego.class
                .getClassLoader().getResource(
                        "Intento1/imagenes/Autentia.jpg"));
        new ShadowTweaker(shadowPass).setVisible(true);
        app.start();
    }

    /**
     * durante la actualizacion de la pantalla miraremos si se ha pulsado el boton
     * escape.
     */

    protected void update(float interpolation) {
        // actualizacion del framerate
        timer.update();
        interpolation = timer.getTimePerFrame();
        //actualizacion de las entradas de teclado para que el jugador se mueva
        input.update(interpolation);
        //actualizacion de la camara
        chaser.update(interpolation);
        //actualizacion de la valla del campo de fuerza
        fence.update(interpolation);

        /**
         * queremos que el cielose mantenga a la vista asi que lo moveremos
         * con la camara
         */

        skybox.setLocalTranslation(cam.getLocation());
        skybox.updateGeometricState(0, true);

        //cuando pulsemos la tecla escape salimos
        if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit")) {
            finished = true;
        }

        /**
         * no queremos que la camara de seguimiento pueda ir por debajo del mundo,
         * asi que lo mantenemos siempre 2 unidades por encima
         */

        if(cam.getLocation().y < (tb.getHeight(cam.getLocation())+2)) {
            cam.getLocation().y = tb.getHeight(cam.getLocation()) + 2;
            cam.update();
        }
        
        /**
         *Nos aseguramos de que el jugador no choca por abandonar el nivel(esto nos sera mas util cuando
         * a�adamos colisiones)
         */

        float characterMinHeight = tb.getHeight(player
                .getLocalTranslation())+agl;
        if (!Float.isInfinite(characterMinHeight) && !Float.isNaN(characterMinHeight)) {
            player.getLocalTranslation().y = characterMinHeight;
        }

        /**
         * cogemos el vector normal del terreno en la posicion actual de la moto
         * y se la aplicamos al vector de subida del jugador
         */

        tb.getSurfaceNormal(player.getLocalTranslation(), normal);
        if(normal != null) {
            player.rotateUpTo(normal);
        }
        
        /**
         * como estamos cambiado la escena, necesitamos actualizar
         * los graficos
         */
        
        scene.updateGeometricState(interpolation, true);

        
    }

   
    protected void render(float interpolation) {
        // limpiamos la pantalla
        display.getRenderer().clearBuffers();
       
        passManager.renderPasses(display.getRenderer());
    }

   
    protected void initSystem() {
        
        width = settings.getWidth();
        height = settings.getHeight();
        depth = settings.getDepth();
        freq = settings.getFrequency();
        fullscreen = settings.isFullscreen();

        try {
            display = DisplaySystem.getDisplaySystem(settings.getRenderer());
            display.setMinStencilBits(8);
            display.createWindow(width, height, depth, freq, fullscreen);

            cam = display.getRenderer().createCamera(width, height);
        } catch (JmeException e) {
            logger.log(Level.SEVERE, "Could not create displaySystem", e);
            System.exit(1);
        }

        display.getRenderer().setBackgroundColor(ColorRGBA.black.clone());

        cam.setFrustumPerspective(45.0f, (float) width / (float) height, 1,
                5000);
        cam.setLocation(new Vector3f(200,1000,200));

        cam.update();

       timer = Timer.getTimer();

        display.getRenderer().setCamera(cam);

        KeyBindingManager.getKeyBindingManager().set("exit",
                KeyInput.KEY_ESCAPE);

    }

  
    protected void initGame() {
        display.setTitle("Moto Autentia");

        scene = new Node("Scene graph node");

        
       
	  ZBufferState buf = display.getRenderer().createZBufferState();
        buf.setEnabled(true);
        buf.setFunction(ZBufferState.TestFunction.LessThanOrEqualTo);
        scene.setRenderState(buf);

        /**
         * vamos hacer una peque�a optimizacion. No necesitamos renderizar, la cara
         * de atras de los triangulos, entonces no lo haremos, esto nos dara un empuje
         * sin mucho esfuerzo
         */
        
        CullState cs = display.getRenderer().createCullState();
        cs.setCullFace(CullState.Face.Back);
        scene.setRenderState(cs);


        //a�adimos el terreno a la escena
        buildTerrain();
        
        //a�adimos luz
        buildLighting();
        //a�adimos e� campo de fuerza
        buildEnvironment();
        //a�adimos el cielo
        buildSkyBox();
        //construimos al jugador
        buildPlayer();
        //construimos la camara de seguimiento
        buildChaseCamera();
        //construimos el Input del jugador
        buildInput();
        
        
        buildPassManager();

        // update the scene graph for rendering
        scene.updateGeometricState(0.0f, true);
        scene.updateRenderState();
        
    }

    private void buildPassManager() {
        passManager = new BasicPassManager();

        // Add skybox first to make sure it is in the background
        RenderPass rPass = new RenderPass();
        rPass.add(skybox);
        passManager.add(rPass);

        shadowPass.add(scene);
        shadowPass.addOccluder(player);
        shadowPass.setRenderShadows(true);
        shadowPass.setLightingMethod(ShadowedRenderPass.LightingMethod.Modulative);
        passManager.add(shadowPass);
    }

   
    


    private void buildPlayer() {
        Spatial model = null;
        try {
            URL bikeFile = juego.class.getClassLoader().getResource("Intento1/modelo/bike.jme");
            BinaryImporter importer = new BinaryImporter();
            model = (Spatial)importer.load(bikeFile.openStream());
            model.setModelBound(new BoundingBox());
            model.updateModelBound();
            //scale it to be MUCH smaller than it is originally
            model.setLocalScale(.0025f);
        } catch (IOException e) {
            logger
                    .throwing(this.getClass().toString(), "buildPlayer()",
                            e);
        }

       
        player = new moto("Player Node", model);
        player.setAcceleration(15);
        player.setBraking(15);
        player.setTurnSpeed(2.5f);
        player.setWeight(25);
        player.setMaxSpeed(25);
        player.setMinSpeed(15);

        player.setLocalTranslation(new Vector3f(100,0, 100));
        scene.attachChild(player);
        scene.updateGeometricState(0, true);
        agl = ((BoundingBox)player.getWorldBound()).yExtent;
        player.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
    }


      
    

    private void buildEnvironment() {
        
        fence = new escenario("fence");
        fence.setLocalScale(5);
        fence.setLocalTranslation(new Vector3f(25, tb.getHeight(25,25)+10, 25));
        scene.attachChild(fence);
    }

    private void buildLighting() {
        
        DirectionalLight light = new DirectionalLight();
        light.setDiffuse(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
        light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, .5f));
        light.setDirection(new Vector3f(1,-1,0));
        light.setShadowCaster(true);
        light.setEnabled(true);

        LightState lightState = display.getRenderer().createLightState();
        lightState.setEnabled(true);
        lightState.setGlobalAmbient(new ColorRGBA(.2f, .2f, .2f, 1f));
        lightState.attach(light);
        scene.setRenderState(lightState);
    }

    private void buildTerrain() {


        MidPointHeightMap heightMap = new MidPointHeightMap(64, 1f);
        Vector3f terrainScale = new Vector3f(4, 0.0575f, 4);
         tb = new TerrainBlock("Terrain", heightMap.getSize(), terrainScale,
                heightMap.getHeightMap(), new Vector3f(0, 0, 0));

        tb.setModelBound(new BoundingBox());
        tb.updateModelBound();
        ProceduralTextureGenerator pt = new ProceduralTextureGenerator(
                heightMap);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("Intento1/texturas/grassb.png")), -128, 0, 128);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("Intento1/texturas/dirt.jpg")), 0, 128, 255);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("Intento1/texturas/highest.jpg")), 128, 255,
                384);
        pt.createTexture(32);

        TextureState ts = display.getRenderer().createTextureState();
        Texture t1 = TextureManager.loadTexture(pt.getImageIcon().getImage(),
                Texture.MinificationFilter.Trilinear, Texture.MagnificationFilter.Bilinear, true);
        ts.setTexture(t1, 0);
        
        Texture t2 = TextureManager.loadTexture(
                TestTerrain.class.getClassLoader().getResource(
                "Intento1/texturas/Detail.jpg"),
                Texture.MinificationFilter.Trilinear,
                Texture.MagnificationFilter.Bilinear);

        ts.setTexture(t2, 1);
        t2.setWrap(Texture.WrapMode.Repeat);

        t1.setApply(Texture.ApplyMode.Combine);
        t1.setCombineFuncRGB(Texture.CombinerFunctionRGB.Modulate);
        t1.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture);
        t1.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);
        t1.setCombineSrc1RGB(Texture.CombinerSource.PrimaryColor);
        t1.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);

        t2.setApply(Texture.ApplyMode.Combine);
        t2.setCombineFuncRGB(Texture.CombinerFunctionRGB.AddSigned);
        t2.setCombineSrc0RGB(Texture.CombinerSource.CurrentTexture);
        t2.setCombineOp0RGB(Texture.CombinerOperandRGB.SourceColor);
        t2.setCombineSrc1RGB(Texture.CombinerSource.Previous);
        t2.setCombineOp1RGB(Texture.CombinerOperandRGB.SourceColor);

        tb.setRenderState(ts);
        tb.setDetailTexture(1, 16);
        tb.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
        scene.attachChild(tb);


    }

    
    private void buildSkyBox() {
        skybox = new Skybox("skybox", 10, 10, 10);

        Texture north = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "Intento1/texturas/north.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture south = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "Intento1/texturas/south.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture east = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "Intento1/texturas/east.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture west = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "Intento1/texturas/west.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture up = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "Intento1/texturas/top.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);
        Texture down = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "Intento1/texturas/bottom.jpg"),
            Texture.MinificationFilter.BilinearNearestMipMap,
            Texture.MagnificationFilter.Bilinear);

        skybox.setTexture(Skybox.Face.North, north);
        skybox.setTexture(Skybox.Face.West, west);
        skybox.setTexture(Skybox.Face.South, south);
        skybox.setTexture(Skybox.Face.East, east);
        skybox.setTexture(Skybox.Face.Up, up);
        skybox.setTexture(Skybox.Face.Down, down);
        skybox.preloadTextures();
        skybox.updateRenderState();
    }

    private void buildChaseCamera() {
        HashMap props = new HashMap();
        props.put(ThirdPersonMouseLook.PROP_MAXROLLOUT, "6");
        props.put(ThirdPersonMouseLook.PROP_MINROLLOUT, "3");
        props.put(ThirdPersonMouseLook.PROP_MAXASCENT, ""+45 * FastMath.DEG_TO_RAD);
        props.put(ChaseCamera.PROP_INITIALSPHERECOORDS, new Vector3f(5, 0, 30 * FastMath.DEG_TO_RAD));
        props.put(ChaseCamera.PROP_DAMPINGK, "4");
        props.put(ChaseCamera.PROP_SPRINGK, "9");
        chaser = new ChaseCamera(cam, player, props);
        chaser.setMaxDistance(8);
        chaser.setMinDistance(2);
    }

   
    private void buildInput() {
        input = new teclado(player, settings.getRenderer());
    }



    /**
     * se llamara si cambia la resolucion
     */
    protected void reinit() {
        display.recreateWindow(width, height, depth, freq, fullscreen);
    }

    /**
     * cerrams la ventana y salimos de la aplicacion
     */
    protected void quit() {
        super.quit();
        System.exit(0);
    }

    /**
     * limpiamos las texturas
     */
    protected void cleanup() {

    }
}

Para cualquier duda de los
metodos y las clases de JMonkey Engine se
peude consultar http://wiki.jmonkeyengine.org/doku.php/

Con esto ya tenemos una moto funcional, es decir que se mueve alrededor del escenario sin problemas, en el siguiente tutorial os diremos como añadir colisiones.

Para ver algunos detalles de este juego vean Detalles del juego de la moto

Esperemos que les haya sido
útil este tutorial, seguiremos haciendo más
tutoriales
sobre esta
tecnología analizando más ejemplos algo
más
complicados, todo el que quiera hacer una aportación
séra
bien recibida.

Para comunicarme cualquier problema o sugerencia de mejora
podeís utilizar la
zona de comentarios, de este modo todo el mundo se podrá
aprovechar de
las respuestas.

Saludos.

3 COMENTARIOS

  1. Que mas Javier, muy claro el tuto, pero tengo problemas por que no encuentro ni los modelos ni las texturas para poderlo correr. Le agradezco si puede ayudarme.

    …Yeyo…

  2. Hola Javier primero que todo quiero felicitarlo por tan excelente material, y el aporte tan importante que nos deja a los que visitamos sus paginas. Quisiera si es posible, que me orientara ya que baje todo el código, lo escribi igual a como esta en la pagina y no me funciona nada. Le agradeceria sus comentarios.

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad