Incorrect Direction for CannonBalls

Hey,

I’m very new to jMonkey. Just finished the tutorials so decided to try and bring them all together. I’m trying to create a program where the player runs around the terrain in HelloTerrain and can fire cannonballs using the mouse.

Everything works except the direction the cannonballs fire in, which seems to be (almost) fixed. Why are the cannonballs not firing in the direction the player is facing?

Here is my code (sorry but I can’t seem to work out how to attach it in a way that isn’t copying and pasting).

package mygame;

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.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
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.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Sphere;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.geomipmap.lodcalc.DistanceLodCalculator;
import com.jme3.terrain.heightmap.HillHeightMap; // for exercise 2
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import java.util.ArrayList;
import java.util.List;

/**

  • First game - user walks around a map and fires cannonballs at bricks
    /
    public class Main extends SimpleApplication implements ActionListener
    {
    /
    declaring variables to be used for collisions, audio, the map and player controls */

    private BulletAppState bulletAppState;
    private RigidBodyControl landscape;
    private CharacterControl player;
    private AudioNode audio_gun;
    private AudioNode audio_nature;
    private Vector3f walkDirection = new Vector3f();
    private boolean left = false, right = false, up = false, down = false, sprint = false;
    private TerrainQuad terrain;
    Material mat_terrain;

    /* Prepare material for the cannon balls */

    Material stone_mat;

    /* Prepare geometries and physical nodes for the cannon balls */

    private RigidBodyControl ball_phy;
    private static final Sphere sphere;

    /* Initialize the cannon ball geometry */

    static
    {
    sphere = new Sphere(32, 32, 0.4f, true, false);
    }

    /* Temporary vectors used on each frame instead of creating them every frame */

    private Vector3f camDir = new Vector3f();
    private Vector3f camLeft = new Vector3f();

    public static void main(String[] args) {
    Main app = new Main();
    app.start();
    }

    @Override
    public void simpleInitApp()
    {
    /* Set the background colour to blue for the sky */
    viewPort.setBackgroundColor(new ColorRGBA(0.7f, 0.8f, 1f, 1f));

     /* Set up the physics */
     bulletAppState = new BulletAppState();
     stateManager.attach(bulletAppState);
     
     flyCam.setMoveSpeed(50);
    
     /* Create terrain material and load four textures into it */
     
     mat_terrain = new Material(assetManager, "Common/MatDefs/Terrain/Terrain.j3md");
     
     /*
      * Add alpha map (for red-green-blue splat textures)
      */
     
     mat_terrain.setTexture("Alpha", assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
     
     /* Add GRASS texture into the red layer (Tex1) */
     Texture grass = assetManager.loadTexture("Textures/Terrain/splat/grass.jpg");
     grass.setWrap(WrapMode.Repeat);
     mat_terrain.setTexture("Tex1", grass);
     mat_terrain.setFloat("Tex1Scale", 64f);
     
     /* Add DIRT texture into the green layer (Tex2) */
     Texture dirt = assetManager.loadTexture("Textures/Terrain/splat/dirt.jpg");
     mat_terrain.setTexture("Tex2", dirt);
     mat_terrain.setFloat("Tex2Scale", 32f);
     
     /* Add ROCK texture into the blue layer (Tex3) */
     Texture rock = assetManager.loadTexture("Textures/Terrain/splat/road.jpg");
     rock.setWrap(WrapMode.Repeat);
     mat_terrain.setTexture("Tex3", rock);
     mat_terrain.setFloat("Tex3Scale", 128f);
     
     /* Create the height map */
     
     AbstractHeightMap heightMap = null;
     Texture heightMapImage = assetManager.loadTexture("Textures/Terrain/splat/mountains512.png");
     heightMap = new ImageBasedHeightMap(heightMapImage.getImage());
     heightMap.load();
     
     /* Code to create a randomly generated map */
     
     /*
     HillHeightMap heightmap = null;
     HillHeightMap.NORMALIZE_RANGE = 100; // optional
     try {
         heightmap = new HillHeightMap(513, 1000, 10, 1000, (byte) 3); // byte 3 is a random seed
     } catch (Exception ex) {
         ex.printStackTrace();
     }
     */
     
     /*
      * We have prepared material and heightmap.
      * Now we create the actual terrain:
      * 3.1) Create a TerrainQuad and name it "my terrain".
      * 3.2) A good value for terrain tiles is 64x64 -- so we supply 64+1 = 65.
      * 3.3) We prepared a heightmap of size 512x512 -- so we supply 512+1 = 513.
      * 3.4) As LOD step scale we supply Vector3f(1, 1, 1).
      * 3.5) We supply the prepared heightmap itself.
      */
     
     int patchSize = 65;
     terrain = new TerrainQuad("my terrain", patchSize, 513, heightMap.getHeightMap());
     
     /*
      * We give the terrain its material, position & scale it and attach it
      */
     
     terrain.setMaterial(mat_terrain);
     terrain.setLocalTranslation(0, -100, 0);
     terrain.setLocalScale(2f, 1f, 2f);
     rootNode.attachChild(terrain);
     
     /*
      * The LOD (level of detail) depends on where the camera is
      */
     
     TerrainLodControl control = new TerrainLodControl(terrain, getCamera());
     terrain.addControl(control);
     
     CollisionShape terrainShape = CollisionShapeFactory.createMeshShape(terrain);
     landscape = new RigidBodyControl(terrainShape, 0);
     terrain.addControl(landscape);
     rootNode.attachChild(terrain);
     
     CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(1.5f, 6f, 1);
     player = new CharacterControl(capsuleShape, 0.05f);
     player.setJumpSpeed(20);
     player.setFallSpeed(30);
     player.setGravity(30);
     player.setPhysicsLocation(new Vector3f(0, 100, 0));
     
     bulletAppState.getPhysicsSpace().add(landscape);
     bulletAppState.getPhysicsSpace().add(player);
     
     setUpKeys();
     initAudio();
     initMaterials();
     initCrossHairs();
    

    }

    /*

    • Every time the shoot action is triggered, a new cannon ball is produced.
    • It is set up to fly from the camera position
      */

    private ActionListener actionListener = new ActionListener()
    {
    public void onAction(String name, boolean keyPressed, float tpf)
    {
    if (name.equals(“Shoot”) && !keyPressed)
    {
    makeCannonBall();
    }
    }
    };

    /* Initialize the material used for the cannon ball /
    public void initMaterials()
    {
    stone_mat = new Material(assetManager, “Common/MatDefs/Misc/Unshaded.j3md”);
    TextureKey key = new TextureKey(“Textures/Terrain/Rock/Rock.PNG”);
    key.setGenerateMips(true);
    Texture tex = assetManager.loadTexture(key);
    stone_mat.setTexture(“ColorMap”, tex);
    }
    /
    Assigns custom key definitions */

    private void setUpKeys()
    {
    inputManager.addMapping(“Left”, new KeyTrigger(KeyInput.KEY_A));
    inputManager.addMapping(“Right”, new KeyTrigger(KeyInput.KEY_D));
    inputManager.addMapping(“Up”, new KeyTrigger(KeyInput.KEY_W));
    inputManager.addMapping(“Down”, new KeyTrigger(KeyInput.KEY_S));
    inputManager.addMapping(“Jump”, new KeyTrigger(KeyInput.KEY_SPACE));
    inputManager.addMapping(“Shoot”, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
    inputManager.addMapping(“Sprint”, new KeyTrigger(KeyInput.KEY_LSHIFT));
    inputManager.addListener(this, “Left”);
    inputManager.addListener(this, “Right”);
    inputManager.addListener(this, “Up”);
    inputManager.addListener(this, “Down”);
    inputManager.addListener(this, “Jump”);
    inputManager.addListener(this, “Shoot”);
    inputManager.addListener(this, “Sprint”);
    }

    /* Initiates the audio so that a gun shot is played when the left mouse button is clicked */

    private void initAudio()
    {
    /* Gun sound */
    audio_gun = new AudioNode(assetManager, “Sound/Effects/Gun.wav”);
    audio_gun.setPositional(true);
    audio_gun.setLooping(false);
    audio_gun.setLocalTranslation(cam.getLocation());
    audio_gun.setVolume(1f);

     rootNode.attachChild(audio_gun);
    

    }

    /* Creates an individual cannon ball.

    • Its is accelerated and flies from the camera position

    • in the direction the camera is facing.
      /
      public void makeCannonBall()
      {
      /
      Create cannon ball geometry and attach to scene graph */
      Geometry ball_geo = new Geometry(“cannon ball”, sphere);
      ball_geo.setMaterial(stone_mat);
      rootNode.attachChild(ball_geo);

      /* Position the cannon ball */
      ball_geo.setLocalTranslation(cam.getLocation());

      /* Make the ball physical with a mass > 0.0f */
      ball_phy = new RigidBodyControl(3f);

      /* Add the ball to the physics space */
      ball_geo.addControl(ball_phy);
      bulletAppState.getPhysicsSpace().add(ball_phy);

      /* Accelerate the physical ball to shoot it */
      ball_phy.setLinearVelocity(cam.getDirection().mult(25));
      }

    /* A plus sign is used as crosshairs to help the player with aimig=ng */
    protected void initCrossHairs()
    {
    guiNode.detachAllChildren();
    guiFont = assetManager.loadFont(“Interface/Fonts/Default.fnt”);
    BitmapText ch = new BitmapText(guiFont, false);
    ch.setSize(guiFont.getCharSet().getRenderedSize()2);
    ch.setText("+");
    ch.setLocalTranslation(settings.getWidth()/2 - guiFont.getCharSet().getRenderedSize()/3
    2, settings.getHeight()/2 + ch.getLineHeight()/2, 0);
    guiNode.attachChild(ch);
    }

    public void onAction(String binding, boolean value, float tpf)
    {
    if (binding.equals(“Left”))
    {
    if (value)
    {
    left = true;
    }

         else
         {
             left = false;
         }
     }
     
     if (binding.equals("Right"))
     {
         if (value)
         {
             right = true;
         }
         
         else
         {
             right = false;
         }
     }
     
     if (binding.equals("Up"))
     {
         if (value)
         {
             up = true;
         }
         
         else
         {
             up = false;
         }
     }
     
     if (binding.equals("Down"))
     {
         if (value)
         {
             down = true;
         }
         
         else
         {
             down = false;
         }
     }
     
     if (binding.equals("Jump"))
     {
         player.jump();
     }
     
     if (binding.equals("Shoot") && !value)
     {
         audio_gun.playInstance();
         makeCannonBall();
     }
     
     if (binding.equals("Sprint"))
     {
         sprint = true;
     }
     
     else
     {
         sprint = false;
     }
    

    }

    @Override
    public void simpleUpdate(float tpf)
    {
    camDir.set(cam.getDirection().multLocal(0.6f));
    camLeft.set(cam.getLeft().multLocal(0.4f));
    walkDirection.set(0, 0, 0);

     if (left)
     {
         walkDirection.addLocal(camLeft);
     }
     
     if (right)
     {
         walkDirection.addLocal(camLeft.negate());
     }
     
     if (up)
     {
         walkDirection.addLocal(camDir);
     }
     
     if (down)
     {
         walkDirection.addLocal(camDir.negate());
     }
     
     if (sprint)
     {
         walkDirection.addLocal(camDir.mult(1.2f));
     }
     
     player.setWalkDirection(walkDirection);
     cam.setLocation(player.getPhysicsLocation());
     
     listener.setLocation(cam.getLocation());
     listener.setRotation(cam.getRotation());
    

    }
    }