FlyByCam Maximum Y rotation Problem

Hello, i was wondering if anyone as a great idea to prevent the FlyByCam Attached to my character in FirstPersonMode to go all around the Y axis, i wish i could prevent the player to turn it head all around and he can look behind him with the head is upside down, just like this picture show

flyCam.setUpVector() :slight_smile:

@BigBob said: flyCam.setUpVector() :)

lol i was kinda wondering what it was used for xD i will try it right away! haha

Edit: i got to prevent going left right but how do i prevent going up? if i get to prevent this i could freeze the value to increase the camera rotation in Y.
or i think

I believe there is a flyCam.setMaximumVerticalRotation or something similar.

You can try messing around with that.

This is a known issue with FlyByCamera: https://code.google.com/p/jmonkeyengine/issues/detail?id=626

1 Like

There is still no answer on the issue you sent me, I dont think i will waste any time on this until some one find a great way to work this out, like a already implemented function to the flybycam. Anyway thank you for the tip!

@n3cr0 said: There is still no answer on the issue you sent me

keep reading (hint: also check out the links)

well flybycam is meant to be well, a “fly” cam. not really a FPS cam. being able to go upside down could be useful for a flycam. for instance i use it for debugging/exploring my scene.

if i were to use it for an FPS game id probably start by copying from it, but theres a lot of changes it would need besides just limiting the vertical rotation to be a good fps camera…

The thing is, most often in a real game you end up controlling your character and not the camera. The camera just follows the character in some way. Otherwise you also miss physics, proper game data/visual separation, etc.

@pspeed said: The thing is, most often in a real game you end up controlling your character and not the camera. The camera just follows the character in some way. Otherwise you also miss physics, proper game data/visual separation, etc.

I have all the physic, and i control a CharacterControl, the thing is the camera as no limitation on the Y axis, and lead to make a turn arround on it self if the player try to look higher then the sky.

Anyway it would be a great thing to add a function to replace the flybycam with a basic fps cam with limitation if possible, then the people could only attach the cam to the CharacterControl(betterCharacterControl).

I dont think it would be easy, but it could be a good thing to have this in the engine it self.

You just have to understand what is going on and limit it. To create a “drag n drop” game engine would be to create an engine in which all games look the same. To solve the problem, just check the axis value you want to restrict, and deny movement if a value is reached or exceeded.

Here is a solution @wezrule posted a few months back: http://hub.jmonkeyengine.org/forum/topic/limiting-camera-updown/

1 Like
@jayfella said: Here is a solution @wezrule posted a few months back: http://hub.jmonkeyengine.org/forum/topic/limiting-camera-updown/

Thank you i will try to work it with the solution there, but i still think the engine should get a fps cam implementation to simplify fps game if possible.

Thank for all the tip everyone.

@n3cr0 said: I have all the physic, and i control a CharacterControl, the thing is the camera as no limitation on the Y axis, and lead to make a turn arround on it self if the player try to look higher then the sky.

How are you controlling the character control? In a real game, I’d expect the input to be modifying the character control directly. The camera would just be along for the ride. FlyByCam is good for demos and tests but not at all suited for real games where the requirements will vary greatly.

Maybe we are somewhere suggesting to do it the wrong way in a real game but I think that’s wrong then.

@n3cr0 said: Anyway it would be a great thing to add a function to replace the flybycam with a basic fps cam with limitation if possible, then the people could only attach the cam to the CharacterControl(betterCharacterControl).

I dont think it would be easy, but it could be a good thing to have this in the engine it self.

It’s really trivial to add. In fact, you could cut and paste the fly cam and add it in two lines of code if you wanted to continue pushing the horse around with the card. :wink:

Since I’ve never used CharacterControl then maybe I’m missing some flyByCam tie in that it forces you into. Which is a shame, because FlyByCam is ill-suited for actual game use, really. To handle even the most common use-cases would probably turn it into a bloated mess.

@pspeed the hello collision documentation is where the root of the cause lies. The flycam is used for ‘head’ rotation:

https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:beginner:hello_collision

But I don’t think that page actually misleads anybody or provides a bad example, it does the job it was set out to do, and as you said yourself, different games use different methods, its just that this particular method is being duplicated as the initial best way for end-users. I think as their game progresses, they will need the actual head to move, and thus implement the correct behaviour.

@pspeed said: How are you controlling the character control? In a real game, I'd expect the input to be modifying the character control directly. The camera would just be along for the ride. FlyByCam is good for demos and tests but not at all suited for real games where the requirements will vary greatly.

Maybe we are somewhere suggesting to do it the wrong way in a real game but I think that’s wrong then.

It’s really trivial to add. In fact, you could cut and paste the fly cam and add it in two lines of code if you wanted to continue pushing the horse around with the card. :wink:

Since I’ve never used CharacterControl then maybe I’m missing some flyByCam tie in that it forces you into. Which is a shame, because FlyByCam is ill-suited for actual game use, really. To handle even the most common use-cases would probably turn it into a bloated mess.

Then as you said, i will give a try to make a FPS cam with the code of the Fly Cam, I am not really en expert in the matter but i really want to get the game out with the basic system working fine ^^. If i get to make a usable class for the community i will try to send it to you @pspeed. (By usable i mean easy to implement in people game logic)

@n3cr0 said: Then as you said, i will give a try to make a FPS cam with the code of the Fly Cam, I am not really en expert in the matter but i really want to get the game out with the basic system working fine ^^. If i get to make a usable class for the community i will try to send it to you @pspeed. (By usable i mean easy to implement in people game logic)

Sounds cool. If nothing else then you will have something more extensible for your own game which is always a bonus.

One of the to-do list items on my plate is to deprecate and eventually retire FlyByCamera. Really, camera control should just be an app state and should probably support controlling things other than cameras. The “refactor the application base class” is ahead of that on the list, though.

1 Like

Allright, here is the new code for the cam(wasn’t really hard…)
I only removed the AWSD and added a condition so the cam wont try to move the AXIS over 0.9 and under -0.9
To replace the old flycam only call

app.getFlyByCamera().setEnabled(false);
app.getFlyByCamera().unregisterInput();

to add the new FPS cam on your CharacterControl

FPScam fps = new FPScam(app.getCamera());
fps.setEnabled(true);
fps.registerWithInput(inputManager);

and don’t forget to update the cam position!

app.getCamera().setLocation(playerPhysic.getPhysicsLocation());

@pspeed
I wish it will work for now, I don’t think the solution could be a long term solution for some people!

thank you for the feed back

[java]
package junglesurvival.newFPScam;

import com.jme3.collision.MotionAllowedListener;
import com.jme3.input.FlyByCamera;
import com.jme3.input.InputManager;
import com.jme3.input.Joystick;
import com.jme3.input.JoystickAxis;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.math.FastMath;
import com.jme3.math.Matrix3f;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;

/**
*Retouched by

  • @author Necro
    */
    public class FPScam implements AnalogListener, ActionListener {

    private static String[] mappings = new String[]{
    “FLYCAM_Left”,
    “FLYCAM_Right”,
    “FLYCAM_Up”,
    “FLYCAM_Down”,

         "FLYCAM_StrafeLeft",
         "FLYCAM_StrafeRight",
         "FLYCAM_Forward",
         "FLYCAM_Backward",
    
         "FLYCAM_ZoomIn",
         "FLYCAM_ZoomOut",
         "FLYCAM_RotateDrag",
    
         "FLYCAM_Rise",
         "FLYCAM_Lower",
         
         "FLYCAM_InvertY"
     };
    

    protected Camera cam;
    protected Vector3f initialUpVec;
    protected float rotationSpeed = 1f;
    protected float moveSpeed = 3f;
    protected float zoomSpeed = 1f;
    protected MotionAllowedListener motionAllowed = null;
    protected boolean enabled = true;
    protected boolean dragToRotate = false;
    protected boolean canRotate = false;
    protected boolean invertY = false;
    protected InputManager inputManager;

    /**

    • Creates a new FlyByCamera to control the given Camera object.
    • @param cam
      */
      public FPScam(Camera cam){
      this.cam = cam;
      initialUpVec = cam.getUp().clone();
      }

    /**

    • Sets the up vector that should be used for the camera.
    • @param upVec
      */
      public void setUpVector(Vector3f upVec) {
      initialUpVec.set(upVec);
      }

    public void setMotionAllowedListener(MotionAllowedListener listener){
    this.motionAllowed = listener;
    }

    /**

    • Sets the move speed. The speed is given in world units per second.
    • @param moveSpeed
      */
      public void setMoveSpeed(float moveSpeed){
      this.moveSpeed = moveSpeed;
      }

    /**

    • Gets the move speed. The speed is given in world units per second.
    • @return moveSpeed
      */
      public float getMoveSpeed(){
      return moveSpeed;
      }

    /**

    • Sets the rotation speed.
    • @param rotationSpeed
      */
      public void setRotationSpeed(float rotationSpeed){
      this.rotationSpeed = rotationSpeed;
      }

    /**

    • Gets the move speed. The speed is given in world units per second.
    • @return rotationSpeed
      */
      public float getRotationSpeed(){
      return rotationSpeed;
      }

    /**

    • Sets the zoom speed.
    • @param zoomSpeed
      */
      public void setZoomSpeed(float zoomSpeed) {
      this.zoomSpeed = zoomSpeed;
      }

    /**

    • Gets the zoom speed. The speed is a multiplier to increase/decrease
    • the zoom rate.
    • @return zoomSpeed
      */
      public float getZoomSpeed() {
      return zoomSpeed;
      }

    /**

    • @param enable If false, the camera will ignore input.
      */
      public void setEnabled(boolean enable){
      if (enabled && !enable){
      if (inputManager!= null && (!dragToRotate || (dragToRotate && canRotate))){
      inputManager.setCursorVisible(true);
      }
      }
      enabled = enable;
      }

    /**

    • @return If enabled
    • @see FlyByCamera#setEnabled(boolean)
      */
      public boolean isEnabled(){
      return enabled;
      }

    /**

    • @return If drag to rotate feature is enabled.
    • @see FlyByCamera#setDragToRotate(boolean)
      */
      public boolean isDragToRotate() {
      return dragToRotate;
      }

    /**

    • Set if drag to rotate mode is enabled.
    • When true, the user must hold the mouse button
    • and drag over the screen to rotate the camera, and the cursor is
    • visible until dragged. Otherwise, the cursor is invisible at all times
    • and holding the mouse button is not needed to rotate the camera.
    • This feature is disabled by default.
    • @param dragToRotate True if drag to rotate mode is enabled.
      */
      public void setDragToRotate(boolean dragToRotate) {
      this.dragToRotate = dragToRotate;
      if (inputManager != null) {
      inputManager.setCursorVisible(dragToRotate);
      }
      }

    /**

    • Registers the FlyByCamera to receive input events from the provided

    • Dispatcher.

    • @param inputManager
      */
      public void registerWithInput(InputManager inputManager){
      this.inputManager = inputManager;

      // both mouse and button - rotation of cam
      inputManager.addMapping(“FLYCAM_Left”, new MouseAxisTrigger(MouseInput.AXIS_X, true),
      new KeyTrigger(KeyInput.KEY_LEFT));

      inputManager.addMapping(“FLYCAM_Right”, new MouseAxisTrigger(MouseInput.AXIS_X, false),
      new KeyTrigger(KeyInput.KEY_RIGHT));

      inputManager.addMapping(“FLYCAM_Up”, new MouseAxisTrigger(MouseInput.AXIS_Y, false),
      new KeyTrigger(KeyInput.KEY_UP));

      inputManager.addMapping(“FLYCAM_Down”, new MouseAxisTrigger(MouseInput.AXIS_Y, true),
      new KeyTrigger(KeyInput.KEY_DOWN));

      // mouse only - zoom in/out with wheel, and rotate drag
      // inputManager.addMapping(“FLYCAM_ZoomIn”, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false));
      // inputManager.addMapping(“FLYCAM_ZoomOut”, new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true));
      // inputManager.addMapping(“FLYCAM_RotateDrag”, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
      //
      // // keyboard only WASD for movement and WZ for rise/lower height
      // inputManager.addMapping(“FLYCAM_StrafeLeft”, new KeyTrigger(KeyInput.KEY_A));
      // inputManager.addMapping(“FLYCAM_StrafeRight”, new KeyTrigger(KeyInput.KEY_D));
      // inputManager.addMapping(“FLYCAM_Forward”, new KeyTrigger(KeyInput.KEY_W));
      // inputManager.addMapping(“FLYCAM_Backward”, new KeyTrigger(KeyInput.KEY_S));
      // inputManager.addMapping(“FLYCAM_Rise”, new KeyTrigger(KeyInput.KEY_Q));
      // inputManager.addMapping(“FLYCAM_Lower”, new KeyTrigger(KeyInput.KEY_Z));

      inputManager.addListener(this, mappings);
      inputManager.setCursorVisible(dragToRotate || !isEnabled());

      Joystick[] joysticks = inputManager.getJoysticks();
      if (joysticks != null && joysticks.length > 0){
      for (Joystick j : joysticks) {
      mapJoystick(j);
      }
      }
      }

    protected void mapJoystick( Joystick joystick ) {

     // Map it differently if there are Z axis
     if( joystick.getAxis( JoystickAxis.Z_ROTATION ) != null && joystick.getAxis( JoystickAxis.Z_AXIS ) != null ) {
    
         // Make the left stick move
         joystick.getXAxis().assignAxis( "FLYCAM_StrafeRight", "FLYCAM_StrafeLeft" );
         joystick.getYAxis().assignAxis( "FLYCAM_Backward", "FLYCAM_Forward" );
         
         // And the right stick control the camera                       
         joystick.getAxis( JoystickAxis.Z_ROTATION ).assignAxis( "FLYCAM_Down", "FLYCAM_Up" );
         joystick.getAxis( JoystickAxis.Z_AXIS ).assignAxis(  "FLYCAM_Right", "FLYCAM_Left" );
    
         // And let the dpad be up and down           
         joystick.getPovYAxis().assignAxis("FLYCAM_Rise", "FLYCAM_Lower");
    
         if( joystick.getButton( "Button 8" ) != null ) { 
             // Let the stanard select button be the y invert toggle
             joystick.getButton( "Button 8" ).assignButton( "FLYCAM_InvertY" );
         }                
         
     } else {             
         joystick.getPovXAxis().assignAxis("FLYCAM_StrafeRight", "FLYCAM_StrafeLeft");
         joystick.getPovYAxis().assignAxis("FLYCAM_Forward", "FLYCAM_Backward");
         joystick.getXAxis().assignAxis("FLYCAM_Right", "FLYCAM_Left");
         joystick.getYAxis().assignAxis("FLYCAM_Down", "FLYCAM_Up");
     }                
    

    }

    /**

    • Registers the FlyByCamera to receive input events from the provided

    • Dispatcher.

    • @param inputManager
      */
      public void unregisterInput(){

      if (inputManager == null) {
      return;
      }

      for (String s : mappings) {
      if (inputManager.hasMapping(s)) {
      inputManager.deleteMapping( s );
      }
      }

      inputManager.removeListener(this);
      inputManager.setCursorVisible(!dragToRotate);

      Joystick[] joysticks = inputManager.getJoysticks();
      if (joysticks != null && joysticks.length > 0){
      Joystick joystick = joysticks[0];

       // No way to unassing axis
      

      }
      }

    protected void rotateCamera(float value, Vector3f axis){
    if (dragToRotate){
    if (canRotate){
    // value = -value;
    }else{
    return;
    }
    }

     Matrix3f mat = new Matrix3f();
     mat.fromAngleNormalAxis(rotationSpeed * value, axis);
    
     Vector3f up = cam.getUp();
     Vector3f left = cam.getLeft();
     Vector3f dir = cam.getDirection();
    
     mat.mult(up, up);
     mat.mult(left, left);
     mat.mult(dir, dir);
    
     Quaternion q = new Quaternion();
     q.fromAxes(left, up, dir);
     q.normalizeLocal();
    
     cam.setAxes(q);
    

    }

    protected void zoomCamera(float value){
    // derive fovY value
    float h = cam.getFrustumTop();
    float w = cam.getFrustumRight();
    float aspect = w / h;

     float near = cam.getFrustumNear();
    
     float fovY = FastMath.atan(h / near)
               / (FastMath.DEG_TO_RAD * .5f);
     fovY += value * 0.1f * zoomSpeed;
    
     h = FastMath.tan( fovY * FastMath.DEG_TO_RAD * .5f) * near;
     w = h * aspect;
    
     cam.setFrustumTop(h);
     cam.setFrustumBottom(-h);
     cam.setFrustumLeft(-w);
     cam.setFrustumRight(w);
    

    }

    protected void riseCamera(float value){
    Vector3f vel = new Vector3f(0, value * moveSpeed, 0);
    Vector3f pos = cam.getLocation().clone();

     if (motionAllowed != null)
         motionAllowed.checkMotionAllowed(pos, vel);
     else
         pos.addLocal(vel);
    
     cam.setLocation(pos);
    

    }

    protected void moveCamera(float value, boolean sideways){
    Vector3f vel = new Vector3f();
    Vector3f pos = cam.getLocation().clone();

     if (sideways){
         cam.getLeft(vel);
     }else{
         cam.getDirection(vel);
     }
     vel.multLocal(value * moveSpeed);
    
     if (motionAllowed != null)
         motionAllowed.checkMotionAllowed(pos, vel);
     else
         pos.addLocal(vel);
    
     cam.setLocation(pos);
    

    }

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

     if (name.equals("FLYCAM_Left")){
         rotateCamera(value, initialUpVec);
     }else if (name.equals("FLYCAM_Right")){
         rotateCamera(-value, initialUpVec);
     }else if (name.equals("FLYCAM_Up")){
         if(cam.getDirection().y < 0.9){
             rotateCamera(-value * (invertY ? -1 : 1), cam.getLeft());
         }
     }else if (name.equals("FLYCAM_Down")){
         if(cam.getDirection().y > -0.9){
             rotateCamera(value * (invertY ? -1 : 1), cam.getLeft());
         }
     }else if (name.equals("FLYCAM_Forward")){
         moveCamera(value, false);
     }else if (name.equals("FLYCAM_Backward")){
         moveCamera(-value, false);
     }else if (name.equals("FLYCAM_StrafeLeft")){
         moveCamera(value, true);
     }else if (name.equals("FLYCAM_StrafeRight")){
         moveCamera(-value, true);
     }else if (name.equals("FLYCAM_Rise")){
         riseCamera(value);
     }else if (name.equals("FLYCAM_Lower")){
         riseCamera(-value);
     }else if (name.equals("FLYCAM_ZoomIn")){
         zoomCamera(value);
     }else if (name.equals("FLYCAM_ZoomOut")){
         zoomCamera(-value);
     }
    

    }

    public void onAction(String name, boolean value, float tpf) {
    if (!enabled)
    return;

     if (name.equals("FLYCAM_RotateDrag") && dragToRotate){
         canRotate = value;
         inputManager.setCursorVisible(!value);
     } else if (name.equals("FLYCAM_InvertY")) {
         // Toggle on the up.
         if( !value ) {  
             invertY = !invertY;
         }
     }        
    

    }

}

[/java]