Attach a gun in first person

I know that this topic has been dicussed a lot, but I’ve been looking for hours now to solve my problem but I can’t find the solution. I simply want to attach a gun and a flashlight to my first person game. This is the code I’m currently using:

[java]package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.SpotLight;
import com.jme3.math.FastMath;
import com.jme3.math.Transform;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;

/**

  • test

  • @author normenhansen
    */
    public class Main extends SimpleApplication implements ActionListener {

    private BulletAppState bulletAppState;
    private RigidBodyControl terrainControl;
    private Node player;
    private BetterCharacterControl playerControl;

    private Vector3f walkDirection = new Vector3f(0.0f, 0.0f, 0.0f);
    private boolean left,right,front,back;
    private boolean zoomEnabled = true;
    private SpotLight flashlight;
    private Spatial flashlightModel;

    private boolean flashlight_on = true;

    private Spatial gun;

    private Node modelNode;

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

    @Override
    public void simpleInitApp() {
    bulletAppState = new BulletAppState();
    stateManager.attach(bulletAppState);

     initTerrain();
     initHUD();
     initPlayer();
     initGunAndFlashlight();
     initLight();
     initKeys();
     initWall();
    

    }

    private void initTerrain() {
    Spatial terrain = assetManager.loadModel(“Scenes/Terrain.j3o”);
    terrainControl = new RigidBodyControl(0.0f);
    terrain.addControl(terrainControl);
    bulletAppState.getPhysicsSpace().add(terrainControl);
    rootNode.attachChild(terrain);
    }

    private void initHUD() {
    setDisplayStatView(false);
    setDisplayFps(false);
    BitmapText ch = new BitmapText(guiFont, false);
    ch.setSize(guiFont.getCharSet().getRenderedSize() * 3);
    ch.setText("+"); // fake crosshairs :slight_smile:
    ch.setLocalTranslation( // center
    settings.getWidth() / 2 - guiFont.getCharSet().getRenderedSize() / 3 * 2,
    settings.getHeight() / 2 + ch.getLineHeight() / 2, 0);
    guiNode.attachChild(ch);
    }

    private void initPlayer() {
    player = new Node(“player”);
    playerControl = new BetterCharacterControl(0.5f, 1.8f, 80f);
    playerControl.setApplyPhysicsLocal(true);
    playerControl.setSpatial(player);
    player.addControl(playerControl);
    bulletAppState.getPhysicsSpace().add(playerControl);
    playerControl.warp(new Vector3f(58f, 6f, 0.0f));
    rootNode.attachChild(player);
    playerControl.setGravity(new Vector3f(0f, -50, 0f));
    }

    private void initGunAndFlashlight() {

     modelNode = new Node();
     
     gun = assetManager.loadModel("Models/m16.j3o");
     gun.rotate(0f, 270 * FastMath.DEG_TO_RAD, 0f);
     gun.scale(0.2f);
     gun.setLocalTranslation(new Vector3f(0.6f, 0.8f, -1.3f)); //gun: 0.5f, 1.3f, -1.6f; m16:0.6f, 0.8f, -1.3f; shotgun: 0.4f, 1.2f, -1f; sniper:0.8f, 0.4f, -2.3f
     modelNode.attachChild(gun);
     
     flashlightModel = assetManager.loadModel("Models/flashlight.j3o");
     flashlightModel.scale(0.1f);
     flashlightModel.setLocalTranslation(new Vector3f(-0.5f, 1.3f, -1.3f));
     modelNode.attachChild(flashlightModel);
     
     player.attachChild(modelNode);
    

    }

    private void initLight() {
    flashlight = new SpotLight();
    flashlight.setSpotRange(45);
    flashlight.setSpotOuterAngle(30 * FastMath.DEG_TO_RAD);
    flashlight.setSpotInnerAngle(20 * FastMath.DEG_TO_RAD);
    flashlight.setDirection(cam.getDirection());
    flashlight.setPosition(cam.getLocation());
    rootNode.addLight(flashlight);
    }

    private void initWall() {

    }

    public void initKeys() {
    inputManager.addMapping(“Left”, new KeyTrigger(KeyInput.KEY_A));
    inputManager.addMapping(“Right”, new KeyTrigger(KeyInput.KEY_D));
    inputManager.addMapping(“Front”, new KeyTrigger(KeyInput.KEY_W));
    inputManager.addMapping(“Back”, new KeyTrigger(KeyInput.KEY_S));
    inputManager.addMapping(“Jump”, new KeyTrigger(KeyInput.KEY_SPACE));
    inputManager.addMapping(“Flashlight”, new KeyTrigger(KeyInput.KEY_F));

     inputManager.addListener(this, "Left");
     inputManager.addListener(this, "Right");
     inputManager.addListener(this, "Front");
     inputManager.addListener(this, "Back");
     inputManager.addListener(this, "Jump");
     inputManager.addListener(this, "Flashlight");
    

    }

    @Override
    public void simpleUpdate(float tpf) {
    if(zoomEnabled) {
    inputManager.deleteMapping(“FLYCAM_ZoomIn”);
    inputManager.deleteMapping(“FLYCAM_ZoomOut”);
    zoomEnabled = false;
    }

     Vector3f camDir = cam.getDirection().mult(10f);
     Vector3f camLeft = cam.getLeft().mult(5f);
     
     walkDirection.set(0.0f, 0.0f, 0.0f);
     
     if(left) {
         walkDirection.addLocal(camLeft);
     }
     if(right) {
         walkDirection.addLocal(camLeft.negate());
     }
     if(front) {
         walkDirection.addLocal(camDir);
     }
     if(back) {
         camDir = cam.getDirection().mult(2.5f);
         walkDirection.addLocal(camDir.negate());
     }
     
     walkDirection.setY(0.0f);
     
     playerControl.setWalkDirection(walkDirection);
     
     cam.setLocation(player.getLocalTranslation().add(0,1.8f,0));
     player.setLocalRotation(cam.getRotation());
     //modelNode.setLocalRotation(cam.getRotation());
     flashlight.setDirection(cam.getDirection()); 
     flashlight.setPosition(cam.getLocation());
    

    }

    @Override
    public void simpleRender(RenderManager rm) {
    //TODO: add render code
    }

    public void onAction(String name, boolean isPressed, float tpf) {
    if(name.equals(“Left”)) {
    left = isPressed;
    } else if(name.equals(“Right”)) {
    right = isPressed;
    } else if(name.equals(“Front”)) {
    front = isPressed;
    } else if(name.equals(“Back”)) {
    back = isPressed;
    } else if(name.equals(“Jump”)) {
    playerControl.jump();
    } else if(name.equals(“Flashlight”)) {
    if(isPressed) return;
    if(flashlight_on) {
    flashlight_on = false;
    rootNode.removeLight(flashlight);
    } else {
    flashlight_on = true;
    rootNode.addLight(flashlight);
    }
    }
    }
    }
    [/java]

The weapons get displayed correctly, but do not rotate with the camera (I thought the line: player.setLocalRotation(cam.getRotation()); would solve this, but it doesn’t…). Secondly the weapons get stuck if I move to a hill. I read of something like a second viewport but I’ve got no idea what this actually is :slight_smile:

A strange thing I forgot to say: When I add modelNode.setLocalRotation(cam.getRotation()); in the update method, neither the gun nor the flashlight is displayed…

Okay, I solved the problem now myself using a second viewport:

1.Delete everything of the modelNode and the weapon spatial int the code.
2. Add a new File with this source code:
[java]package mygame;

import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.light.DirectionalLight;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Matrix3f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.Spatial.CullHint;

public class ViewPortState extends AbstractAppState {

private Camera cam;
private ViewPort view;
private Node root;

public ViewPortState() {
    root = new Node("root");
}

public Node getRoot() {
    return root;
}

public Camera getCamera() {
    return cam;
}

@Override
public void initialize(AppStateManager stateManager, Application app) {
    super.initialize(stateManager, app);
    root = new Node("Viewport Root");
    root.setCullHint(CullHint.Never);

    cam = app.getCamera().clone();
    cam.setViewPort(0.0f, 1.0f, 0.0f, 1f);

    initScifirifle(app);
    initFlashlight(app);

    view = app.getRenderManager().createMainView("GunViewPort", cam);
    view.setEnabled(true);
    view.setClearFlags(false, true, false);
    view.attachScene(root);

    //Light
    DirectionalLight sun = new DirectionalLight();
    sun.setDirection((new Vector3f(-0.5f, -0.5f, -0.5f)).normalizeLocal());
    sun.setColor(ColorRGBA.White.mult(0.6f));
    root.addLight(sun);


    root.updateLogicalState(1);
    root.updateGeometricState();
}

private void initColt(Application app) {
    Spatial gun = app.getAssetManager().loadModel("Models/colt.j3o");
    gun.scale(0.7f);
    gun.setLocalTranslation(4f, -3f, 0f);
    gun.rotate(-10 * FastMath.DEG_TO_RAD, 190 * FastMath.DEG_TO_RAD, 0);
    root.attachChild(gun);
}

private void initFlashlight(Application app) {
    Spatial flashlight = app.getAssetManager().loadModel("Models/flashlight.j3o");
    flashlight.setLocalTranslation(-5.5f, -5.8f, 0f);
    flashlight.rotate(20 * FastMath.DEG_TO_RAD, -20 * FastMath.DEG_TO_RAD, 0);
    root.attachChild(flashlight);
}

@Override
public void render(RenderManager rm) {
    root.updateGeometricState();
}

@Override
public void update(float tpf) {
    root.updateLogicalState(tpf);
}

} [/java]

(Of course you’ll have to modify the things with the models.)

  1. Add these lines in your first file:
    [java]
    stateManager.attach(new ViewPortState());
    cam.setViewPort(0.0f, 1.0f, 0.0f, 1.0f);
    [/java]

Now it should be working!