Collision Detection added to Flagrush tutorial - Lesson 5 - causes jitters?

Hi all,



I tried adding collision detection to flagrush tutorial lesson 5 - physics space, added staticPhysicsNode terrainNode for terrain and dynamicPhysicsNode for the box and when I load it causes jitters?? All I have done is added simple physics and for some reason it does not work. I am including the code, it compiles and runs - can someone tell me what is wrong here?



Lesson5.java




package lesson5;

import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ImageIcon;
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.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.scene.Node;
import com.jme.scene.Skybox;
import com.jme.scene.shape.Box;
import com.jme.scene.state.CullState;
import com.jme.scene.state.LightState;
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.jmex.terrain.TerrainBlock;
import com.jmex.terrain.util.MidPointHeightMap;
import com.jmex.terrain.util.ProceduralTextureGenerator;
import com.jmex.physics.PhysicsSpace;
import com.jmex.physics.DynamicPhysicsNode;
import com.jmex.physics.StaticPhysicsNode;

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

    private TerrainBlock tb;
    private ForceFieldFence fence;
    private Skybox skybox;
    private Node player;
    protected InputHandler input;
    protected Timer timer;
    private Camera cam;
    private ChaseCamera chaser;
    private Node scene;
    private int width, height, depth, freq;
    private boolean fullscreen;
 
    private PhysicsSpace physicsSpace;  
    private DynamicPhysicsNode playerNode;
    private StaticPhysicsNode terrainNode;
   
    public static void main(String[] args) {
        Lesson5 app = new Lesson5();
        app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG, Lesson5.class
                .getClassLoader().getResource(
                        "jmetest/data/images/FlagRush.png"));
        app.start();
    }


    protected void update(float interpolation)
    {
        // update the time to get the framerate
        timer.update();
        interpolation = timer.getTimePerFrame();
       
        getPhysicsSpace().update(interpolation);
        //update the keyboard input (move the player around)
        input.update(interpolation);
        //update the chase camera to handle the player moving around.
        chaser.update(interpolation);

        fence.update(interpolation);
       
        //we want to keep the skybox around our eyes, so move it with
        //the camera
        skybox.setLocalTranslation(cam.getLocation());
       
        // if escape was pressed, we exit
        if (KeyBindingManager.getKeyBindingManager().isValidCommand("exit")) {
            finished = true;
        }

        if(cam.getLocation().y < (tb.getHeight(cam.getLocation())+2)) {
            cam.getLocation().y = tb.getHeight(cam.getLocation()) + 2;
            cam.update();
        }

        float characterMinHeight = tb.getHeight(playerNode
                .getLocalTranslation())+((BoundingBox)playerNode.getWorldBound()).yExtent;
        if (!Float.isInfinite(characterMinHeight) && !Float.isNaN(characterMinHeight)) {
            playerNode.getLocalTranslation().y = characterMinHeight;
        }

        scene.updateGeometricState(interpolation, true);
    }

    protected void render(float interpolation) {
        // Clear the screen
        display.getRenderer().clearBuffers();
        display.getRenderer().draw(scene);
    }

    protected void initSystem() {
        // store the properties information
        width = properties.getWidth();
        height = properties.getHeight();
        depth = properties.getDepth();
        freq = properties.getFreq();
        fullscreen = properties.getFullscreen();
       
        try {
            display = DisplaySystem.getDisplaySystem(properties.getRenderer());
            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.update();
        timer = Timer.getTimer();
        display.getRenderer().setCamera(cam);
        KeyBindingManager.getKeyBindingManager().set("exit",
                KeyInput.KEY_ESCAPE);
    }

    protected void initGame()
    {
        display.setTitle("Flag Rush");
       
        scene = new Node("Scene graph node");
       
       
        physicsSpace = PhysicsSpace.create();
        terrainNode = getPhysicsSpace().createStaticNode();
        playerNode = getPhysicsSpace().createDynamicNode();
       
        /** Create a ZBuffer to display pixels closest to the camera above farther ones.  */
        ZBufferState buf = display.getRenderer().createZBufferState();
        buf.setEnabled(true);
        buf.setFunction(ZBufferState.CF_LEQUAL);
        scene.setRenderState(buf);
       
        CullState cs = display.getRenderer().createCullState();
        cs.setCullMode(CullState.CS_BACK);
        scene.setRenderState(cs);
        buildTerrain();
        buildLighting();
        buildEnvironment();
        buildSkyBox();
        buildPlayer();
        buildChaseCamera();
        buildInput();

        scene.updateGeometricState(0.0f, true);
        scene.updateRenderState();

    }
   
    private void buildPlayer()
    {
        Box b = new Box("box", new Vector3f(), 0.35f,0.25f,0.5f);
        b.setModelBound(new BoundingBox());
        b.updateModelBound();
        playerNode.setLocalTranslation(new Vector3f(100,10, 100));
        scene.attachChild(playerNode);
        playerNode.attachChild(b);
        playerNode.updateWorldBound();
        playerNode.generatePhysicsGeometry();
        //playerNode.computeMass();
        //playerNode.setAffectedByGravity(false);
    }
   

    private void buildEnvironment()
    {
        fence = new ForceFieldFence("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, 1.0f));
        light.setDirection(new Vector3f(1,-1,0));
        light.setEnabled(true);

        LightState lightState = display.getRenderer().createLightState();
        lightState.setEnabled(true);
        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), false);

        tb.setModelBound(new BoundingBox());
        tb.updateModelBound();

        // generate a terrain texture with 2 textures
        ProceduralTextureGenerator pt = new ProceduralTextureGenerator(
                heightMap);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/grassb.png")), -128, 0, 128);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/dirt.jpg")), 0, 128, 255);
        pt.addTexture(new ImageIcon(TestTerrain.class.getClassLoader()
                .getResource("jmetest/data/texture/highest.jpg")), 128, 255,
                384);
        pt.createTexture(32);
       
        // assign the texture to the terrain
        TextureState ts = display.getRenderer().createTextureState();
        Texture t1 = TextureManager.loadTexture(pt.getImageIcon().getImage(),
                Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR, true);
        ts.setTexture(t1, 0);

        tb.setRenderState(ts);
        tb.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
        terrainNode.attachChild(tb);
        terrainNode.generatePhysicsGeometry();
        scene.attachChild(tb);
       
       
    }
   

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

        Texture north = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/north.jpg"),
            Texture.MM_LINEAR,
            Texture.FM_LINEAR);
        Texture south = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/south.jpg"),
            Texture.MM_LINEAR,
            Texture.FM_LINEAR);
        Texture east = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/east.jpg"),
            Texture.MM_LINEAR,
            Texture.FM_LINEAR);
        Texture west = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/west.jpg"),
            Texture.MM_LINEAR,
            Texture.FM_LINEAR);
        Texture up = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/top.jpg"),
            Texture.MM_LINEAR,
            Texture.FM_LINEAR);
        Texture down = TextureManager.loadTexture(
            TestSkybox.class.getClassLoader().getResource(
            "jmetest/data/texture/bottom.jpg"),
            Texture.MM_LINEAR,
            Texture.FM_LINEAR);

        skybox.setTexture(Skybox.NORTH, north);
        skybox.setTexture(Skybox.WEST, west);
        skybox.setTexture(Skybox.SOUTH, south);
        skybox.setTexture(Skybox.EAST, east);
        skybox.setTexture(Skybox.UP, up);
        skybox.setTexture(Skybox.DOWN, down);
        skybox.preloadTextures();
        scene.attachChild(skybox);
    }
   
 
    private void buildChaseCamera() {
        Vector3f targetOffset = new Vector3f();
        targetOffset.y = ((BoundingBox) playerNode.getWorldBound()).yExtent * 1.5f;
        HashMap<String, Object> props = new HashMap<String, Object>();
        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_TARGETOFFSET, targetOffset);
        chaser = new ChaseCamera(cam, playerNode, props);
        chaser.setMaxDistance(8);
        chaser.setMinDistance(2);
    }


    private void buildInput() {
        input = new FlagRushHandler(playerNode, properties.getRenderer());
    }


    protected void reinit() {
        display.recreateWindow(width, height, depth, freq, fullscreen);
    }
   
    protected void setPhysicsSpace(PhysicsSpace physicsSpace)
    {
        if (physicsSpace != this.physicsSpace)
        {
            if ( this.physicsSpace != null )
            {
                this.physicsSpace.delete();
                this.physicsSpace = physicsSpace;
            }
        }
    }
   
    public PhysicsSpace getPhysicsSpace()
    {
        return physicsSpace;
    }

 
    protected void quit() {
        super.quit();
        System.exit(0);
    }
   

    protected void cleanup() {

    }
}



ForceFieldFence.java



package lesson5;

import jmetest.flagrushtut.Lesson2;

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.AlphaState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;

public class ForceFieldFence extends Node {
   private static final long serialVersionUID = 1L;

    private Texture t;

    public ForceFieldFence(String name) {
        super(name);
        buildFence();
    }
   
    public void update(float interpolation) {

        t.getTranslation().y += 0.3f * interpolation;

        if(t.getTranslation().y > 1) {
            t.getTranslation().y = 0;
        }
    }
   

    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();
       
        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));
       
        Cylinder strutGeometry = new Cylinder("strut", 10,10, 0.125f, 32);
        strutGeometry.setModelBound(new BoundingBox());
        strutGeometry.updateModelBound();
       
        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));
       
        Box forceFieldX = new Box("forceFieldX", new Vector3f(-16, -3f, -0.1f), new Vector3f(16f, 3f, 0.1f));
        forceFieldX.setModelBound(new BoundingBox());
        forceFieldX.updateModelBound();

        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));

        Node forceFieldNode = new Node("forceFieldNode");
        forceFieldNode.setRenderQueueMode(Renderer.QUEUE_TRANSPARENT);
        forceFieldNode.attachChild(forceFieldX1);
        forceFieldNode.attachChild(forceFieldX2);
        forceFieldNode.attachChild(forceFieldZ1);
        forceFieldNode.attachChild(forceFieldZ2);

        AlphaState as1 = DisplaySystem.getDisplaySystem().getRenderer().createAlphaState();
        as1.setBlendEnabled(true);
        as1.setSrcFunction(AlphaState.SB_SRC_ALPHA);
        as1.setDstFunction(AlphaState.DB_ONE);
        as1.setTestEnabled(true);
        as1.setTestFunction(AlphaState.TF_GREATER);
        as1.setEnabled(true);
       
        forceFieldNode.setRenderState(as1);

        TextureState ts = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        t = TextureManager.loadTexture(Lesson2.class.getClassLoader()
                  .getResource("jmetest/data/texture/reflector.jpg"),
                  Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
       
        t.setWrap(Texture.WM_WRAP_S_WRAP_T);
        t.setTranslation(new Vector3f());
        ts.setTexture(t);
       
        forceFieldNode.setRenderState(ts);

        Node towerNode = new Node("tower");
        towerNode.attachChild(post1);
        towerNode.attachChild(post2);
        towerNode.attachChild(post3);
        towerNode.attachChild(post4);
        towerNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);

        TextureState ts2 = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        Texture t2 = TextureManager.loadTexture(Lesson2.class.getClassLoader()
                  .getResource("jmetest/data/texture/post.jpg"),
                  Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
       
        ts2.setTexture(t2);
       
        towerNode.setRenderState(ts2);

        Node strutNode = new Node("strutNode");
        strutNode.attachChild(strut1);
        strutNode.attachChild(strut2);
        strutNode.attachChild(strut3);
        strutNode.attachChild(strut4);
        strutNode.setRenderQueueMode(Renderer.QUEUE_OPAQUE);
       
        TextureState ts3 = DisplaySystem.getDisplaySystem().getRenderer().createTextureState();
        Texture t3 = TextureManager.loadTexture(Lesson2.class.getClassLoader()
                  .getResource("jmetest/data/texture/rust.jpg"),
                  Texture.MM_LINEAR_LINEAR, Texture.FM_LINEAR);
       
        ts3.setTexture(t3);   
        strutNode.setRenderState(ts3);

        this.attachChild(forceFieldNode);
        this.attachChild(towerNode);
        this.attachChild(strutNode);
    }
}



FlagRushHandler code in the next post..

Dev

FlagRushHandler.java




package lesson5;

import com.jme.input.InputHandler;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.action.KeyNodeBackwardAction;
import com.jme.input.action.KeyNodeForwardAction;
import com.jme.input.action.KeyNodeRotateLeftAction;
import com.jme.input.action.KeyNodeRotateRightAction;
import com.jme.scene.Spatial;

/**
 * Input Handler for the Flag Rush game. This controls a supplied spatial
 * allowing us to move it forward, backward and rotate it left and right.
 * @author Mark Powell
 *
 */
public class FlagRushHandler extends InputHandler {

    /**
     * Supply the node to control and the api that will handle input creation.
     * @param node the node we wish to move
     * @param api the library that will handle creation of the input.
     */
    public FlagRushHandler(Spatial node, String api) {

        setKeyBindings(api);
        setActions(node);

    }

    /**
     * creates the keyboard object, allowing us to obtain the values of a keyboard as keys are
     * pressed. It then sets the actions to be triggered based on if certain keys are pressed (WSAD).
     * @param api
     */
    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);
    }

    /**
     * assigns action classes to triggers. These actions handle moving the node forward, backward and
     * rotating it.
     * @param node the node to control.
     */
    private void setActions(Spatial node) {
        KeyNodeForwardAction forward = new KeyNodeForwardAction(node, 30f);
        addAction(forward, "forward", true);
       
        KeyNodeBackwardAction backward = new KeyNodeBackwardAction(node, 15f);
        addAction(backward, "backward", true);
       
        KeyNodeRotateRightAction rotateRight = new KeyNodeRotateRightAction(node, 5f);
        rotateRight.setLockAxis(node.getLocalRotation().getRotationColumn(1));
        addAction(rotateRight, "turnRight", true);
       
        KeyNodeRotateLeftAction rotateLeft = new KeyNodeRotateLeftAction(node, 5f);
        rotateLeft.setLockAxis(node.getLocalRotation().getRotationColumn(1));
        addAction(rotateLeft, "turnLeft", true);
    }
}



Dev

well this is most likely the same as described here: jittery movement controller with physics node

PhysicNodes should be moved with force and torque and not with the usual Controllers / KeyInputActions.




edit:

actually, never mind that, because i somehow can’t reproduce the jittery movement anymore heh …



But still, you should only modify the translation/rotation of PhysicNodes yourself, if this PhysicNode is cleared of any forces.

Still it's worth reading the thread Core-Dump mentioned.



You probably control the dynamic entities via physics (as you added that) and direct setting of translation/rotation (as Lesson5 does that). Which is not a sound controller.

Alright… thanks a lot guys… I will give that a go and rewrite my code for movement in a similar way as done in SimpleThirdPersonGame and if it still doesnt work than do it for Lesson5 and if it still doesnt work then dont know  :? But I just keep reading that jittery movements disappear automatically for everyone else just dont know why does that not happen to me  :wink: … Also, jitters suggest that its more of an update issue, may be its not updating properly but cant seem to find a problem there to??



Thanks again,



Dev

"Fundamentals are the building block of any major software" - I just learned this the hard way - my fundamentals arent the best and was rushing through tutorials and now I paid huge chunk of time may be half or quarter of which could have taken me to learn things properly… The reason my terrain did jittered was because of update problem - well indirectly. Instead of attaching the terrainblock to the Node and then attaching that Node to the static node I previously did it the other way round and so it did not update properly. I found this out while trying different terrains and then noticing the difference. I bet it was the same with other guys and while messing around manipulating the terrains, they indirectly stumbled on the right solution.



Also, I find it much easier to use the Handler instead of forces and torques and it works fine! Just need to tweak the physicsal attributes of the box so it does not fly on collision! But no more jitters!!!



Thanks all for your help and hope this helps other guys who have been having the same problem!!!



Regards,



Dev



Edit: I still dont get this - I am not using SimplePhysicsGame so I am creating a 'scene' node(like rootNode) to which all the nodes in the world are added. So when creating dynamicPhysicsNode, I initialize it and add it to scene and then add box or other objects to it.  However this does not work for terrain. I add terrainBlock tb to the scene and then have to add the scene to the staticPhysicsNode?? Also, am currently working on adding forces which seem like a better idea although harder work then adding velocities as probably I'll need to get the force right for the game…