FirstPersonCameraControl

Hello,

I’ve juste make a control for my game and want to share it.
When you add it to a spatial, your camera follow it at first person and you can control the camera rotation.

[java]
import com.jme3.input.InputManager;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;

/**

  • This control when attached to a spatial, follow him at the first person

  • @author Kyu

  • @version 0.1
    */
    public class FirstPersonCameraControl extends AbstractControl implements ActionListener, AnalogListener
    {
    protected Camera camera;
    protected InputManager inputManager;

    protected float rotationSpeed = 1.0f;
    protected float rotation;
    protected float vRotation;

    protected float minVerticalRotation = FastMath.DEG_TO_RAD * -90;
    protected float maxVerticalRotation = FastMath.DEG_TO_RAD * 90;

    protected boolean isRotating;
    protected boolean isVRotating;
    protected boolean dragToRotate;
    protected boolean hideCursorOnRotate = true;
    protected boolean canRotate = true;

    protected boolean isMoving;
    protected Vector3f location;
    protected Vector3f lastLocation = new Vector3f();
    protected Vector3f locationCorrection = new Vector3f(Vector3f.ZERO);

    protected boolean invertYaxis;
    protected boolean invertXaxis;

    /**

    • Mapping
      */
      protected final String FirstCamDown = “FirstCamDown”;
      protected final String FirstCamUp = “FirstCamUp”;
      protected final String FirstCamZoomIn = “FirstCamZoomIn”;
      protected final String FirstCamZoomOut = “FirstCamZoomOut”;
      protected final String FirstCamMoveLeft = “FirstCamMoveLeft”;
      protected final String FirstCamMoveRight = “FirstCamMoveRight”;
      protected final String FirstCamToggleRotate = “FirstCamToggleRotate”;

    /**

    • Temporary variables
      */
      private Quaternion quadTemp = new Quaternion();

    /**

    • Temporary variable used for location computing
      */
      private Vector3f vecTemp = new Vector3f();

    /**

    • Constructor to follow a spatial by his translation
    • @param cam the camera used to follow spatial
      */
      public FirstPersonCameraControl(Camera cam)
      {
      this.camera = cam;
      }

    /**

    • Constructor to follow a spatial and have freedom on view

    • @param cam the camera where view should be controlled by inputs

    • @param inputManager the InputManager to attach mapping
      */
      public FirstPersonCameraControl(Camera cam, InputManager inputManager)
      {
      this.camera = cam;

      registerWithInput(inputManager);
      }

    /**

    • Register mapping

    • @param inputManager the InputManager where attach mapping
      */
      public final void registerWithInput(InputManager inputManager)
      {
      this.inputManager = inputManager;

      String[] inputs = {
      FirstCamToggleRotate,
      FirstCamDown,
      FirstCamUp,
      FirstCamMoveLeft,
      FirstCamMoveRight,
      };

      inputManager.addMapping(FirstCamDown, new MouseAxisTrigger(MouseInput.AXIS_Y, ((!invertYaxis) ? false : true)));
      inputManager.addMapping(FirstCamUp, new MouseAxisTrigger(MouseInput.AXIS_Y, ((!invertYaxis) ? true : false)));

      inputManager.addMapping(FirstCamMoveLeft, new MouseAxisTrigger(MouseInput.AXIS_X, ((!invertXaxis) ? false : true)));
      inputManager.addMapping(FirstCamMoveRight, new MouseAxisTrigger(MouseInput.AXIS_X, ((!invertXaxis) ? true : false)));

      inputManager.addMapping(FirstCamToggleRotate, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
      inputManager.addMapping(FirstCamToggleRotate, new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));

      inputManager.addListener(this, inputs);
      }

    @Override
    public void onAction(String name, boolean isPressed, float tpf)
    {
    if (dragToRotate && enabled)
    {
    if (name.equals(FirstCamToggleRotate) && enabled)
    {
    canRotate = isPressed;

            if (hideCursorOnRotate)
            {
                inputManager.setCursorVisible(!isPressed);
            }
        }
    }
    

    }

    @Override
    public void onAnalog(String name, float value, float tpf)
    {
    if(!enabled || !canRotate)
    return;

    switch (name)
    {
        case FirstCamMoveLeft:
            rotateCamera(-value);
            
            break;
            
        case FirstCamMoveRight:
            rotateCamera(value);
            
            break;
            
        case FirstCamUp:
            vRotateCamera(value);
            
            break;
            
        case FirstCamDown:
            vRotateCamera(-value);
            
            break;
    }
    

    }

    /**

    • Rotate the camera on the horizontal axis (used internally)
    • @param value value to rotate in radian
      */
      protected void rotateCamera(float value)
      {
      isRotating = true;
      rotation += value * rotationSpeed;
      }

    /**

    • Rotate the camera on the vertical axis (used internally)

    • @param value value to rotate in radian
      */
      protected void vRotateCamera(float value)
      {
      isVRotating = true;
      float lastGoodRot = vRotation;

      vRotation += value * rotationSpeed;

      if (vRotation -minVerticalRotation)
      vRotation = lastGoodRot;
      }

    /**

    • Update the control (used by jme thread)

    • @param tpf time since last frame
      */
      @Override
      protected void controlUpdate(float tpf)
      {
      if(!enabled)
      return;

      if(location != null)
      lastLocation.set(location);

      location = spatial.getLocalTranslation();

      isMoving = lastLocation.equals(location);

      if(isMoving)
      {
      vecTemp.zero();
      vecTemp.addLocal(location);
      vecTemp.addLocal(locationCorrection);
      camera.setLocation(vecTemp);
      }

      if(!isRotating && ! isVRotating)
      return;

      quadTemp.fromAngles(vRotation, rotation, 0);
      camera.setRotation(quadTemp);
      }

    /**

    • Update the render thread (used in jme thread)
    • @param renderManager
    • @param viewPort
      */
      @Override
      protected void controlRender(RenderManager renderManager, ViewPort viewPort)
      {

    }

    /**

    • Set the spatial where the control was attached (used internally)

    • @param spatial the spatial where control was attached
      */
      @Override
      public void setSpatial(Spatial spatial)
      {
      super.setSpatial(spatial);

      if(spatial == null)
      {
      enabled = false;

       return;
      

      }
      else
      enabled = true;

      boolean _showMouse = false;

      if(dragToRotate)
      {
      _showMouse = true;
      if(canRotate)
      {
      if(hideCursorOnRotate)
      _showMouse = false;
      else
      _showMouse = true;
      }

      }
      inputManager.setCursorVisible(_showMouse);
      }

    /**

    • Set the speed of camera rotation
    • @param rotationSpeed speed of the rotation; 1f is the origin value;
      */
      public void setRotationSpeed(float rotationSpeed)
      {
      this.rotationSpeed = rotationSpeed;
      }

    /**

    • Set the minimal vertical rotation of the camera
    • @param minVerticalRotation minimal vertical rotation value of the camera in degree
      */
      public void setMinVerticalRotation(float minVerticalRotation)
      {
      this.minVerticalRotation = FastMath.DEG_TO_RAD * minVerticalRotation;
      }

    /**

    • Set the maximal vertical rotation of the camera
    • @param maxVerticalRotation maximal vertical rotation value of the camera in degree
      */
      public void setMaxVerticalRotation(float maxVerticalRotation)
      {
      this.maxVerticalRotation = FastMath.DEG_TO_RAD * maxVerticalRotation;
      }

    /**

    • Need to click to rotate the camera

    • @param dragToRotate need to click or not to rotate the camera
      */
      public void setDragToRotate(boolean dragToRotate)
      {
      this.dragToRotate = dragToRotate;
      this.canRotate = !dragToRotate;

      inputManager.setCursorVisible(dragToRotate);
      }

    /**

    • Hide the cursor when drag to rotate
    • @param hideCursorOnRotate hide cursor
      */
      public void setHideCursorOnRotate(boolean hideCursorOnRotate)
      {
      this.hideCursorOnRotate = hideCursorOnRotate;
      }

    /**

    • Invert the X axis
    • @param invertXaxis invert X axis
      */
      public void setInvertXaxis(boolean invertXaxis)
      {
      this.invertXaxis = invertXaxis;
      }

    /**

    • Invert the Y axis
    • @param invertYaxis invert Y axis
      */
      public void setInvertYaxis(boolean invertYaxis)
      {
      this.invertYaxis = invertYaxis;
      }

    /**

    • Translate the origin of the camera on X axis
    • @param x location on X axis
      */
      public void setXCorrection(float x)
      {
      locationCorrection.setX(x);
      }

    /**

    • Translate the origin of the camera on Y axis
    • @param y location on Y axis
      */
      public void setYCorrection(float y)
      {
      locationCorrection.setY(y);
      }

    /**

    • Translate the origin of the camera on Z axis
    • @param z location on Z axis
      */
      public void setZCorrection(float z)
      {
      locationCorrection.setZ(z);
      }

    /**

    • Get the rotation speed of camera
    • @return camera rotation speed
      */
      public float getRotationSpeed()
      {
      return rotationSpeed;
      }

    /**

    • Get the minimal vertical rotation of the camera
    • @return minimal vertical rotation
      */
      public float getMinVerticalRotation()
      {
      return minVerticalRotation;
      }

    /**

    • Get the maximal vertical rotation of the camera
    • @return maximal vertical rotation
      */
      public float getMaxVerticalRotation()
      {
      return maxVerticalRotation;
      }

    /**

    • Get the camera horizontal rotation state
    • @return boolean
      */
      public boolean isIsRotating()
      {
      return isRotating;
      }

    /**

    • Get the camera vertical rotation state
    • @return boolean
      */
      public boolean isIsVRotating()
      {
      return isVRotating;
      }

    /**

    • Get the DragRoRotate mode state
    • @return boolean
      */
      public boolean isDragToRotate()
      {
      return dragToRotate;
      }

    /**

    • @return if hide the cursor in DragToRotate mode when dragging
      */
      public boolean isHideCursorOnRotate()
      {
      return hideCursorOnRotate;
      }

    /**

    • Get if can rotate the camera
    • @return boolean
      */
      public boolean isCanRotate()
      {
      return canRotate;
      }

    /**

    • Get if spatial is moving
    • @return boolean
      */
      public boolean isIsMoving()
      {
      return isMoving;
      }

    /**

    • Get the correction location
    • @return location
      */
      public Vector3f getLocationCorrection()
      {
      return locationCorrection;
      }

    /**

    • Get if Y axis is inverted
    • @return boolean
      */
      public boolean isInvertYaxis()
      {
      return invertYaxis;
      }

    /**

    • Get if X axis is inverted
    • @return boolean
      */
      public boolean isInvertXaxis()
      {
      return invertXaxis;
      }

}

[/java]

or pastebin : pastebin

You can give me advise, i take it in consideration to improve this.

Sorry if my english is bad and thank you for this great engine :slight_smile:

2 Likes

Cool, thanks for sharing!

2 Likes

This is going to be really useful. Thank you!

theres a bug in

[java] protected void vRotateCamera(float value)
{
isVRotating = true;
float lastGoodRot = vRotation;

    vRotation += value * rotationSpeed;

    if (vRotation  -minVerticalRotation)
        vRotation = lastGoodRot;
}[/java]

if (vRotation  -minVerticalRotation) does not make any sense ;) maybe something like

[java]if (vRotation - minVerticalRotation>??) {
vRotation = lastGoodRot;
}[/java]

I suspect the less than or greater than characters were swallowed by the forum given the spacing. There was probably supposed to be an < in there.

Well. If you check the pastebin link, you will see that much more has been eaten… From memory, the whole maxVerticalRotation has been swallowed…

Oh ok I see, thanks. The code tool of the forum should probably be replaced by something else…