Why is my camera movement jumpy?

Take a look at this GIF:

You will have to wait for the entire GIF to run smoothly to see the problem, but then you will see that the plane appears to “jump” backwards and forwards.

What is really happening here is that the camera seems to be somewhat lagging behind the plane, or moving to a slightly incorrect location, causing the plane too appear to jump. I can verify that the camera is the issue by crashing the plane into the ground and watching it smoothly bounce around.

I am currently updating the camera position every frame:

@Override
public void simpleRender(RenderManager rem) {
   updateCamera();
   Display.sync(24); //The issue occurs most at lower FPS, hence why I simulated "lag" here.
}
public void updateCamera(){
    // Camera positioning code
    if (crashed) return; //Don't move the camera if the plane crashed
    float[] angles = new float[3];
angles = plane.getLocalRotation().toAngles(angles);
    double z = Math.toDegrees(angles[2]);
    
    int widt = Display.getWidth();
    int heit = Display.getHeight();
    float verti = -(getInputManager().getCursorPosition().y-15 - heit/2)*1080/heit; 
    float horiz = -(getInputManager().getCursorPosition().x+16 - widt/2)*1920/widt;
    float rot = (float) -z / 50;
    //End of rotation
    //Camera rotation
    Vector3f plDir = player.getPhysicsRotation().getRotationColumn(2);
    Vector3f dirP = plDir;
    Vector3f camMove = cam.getLeft();
    float horMove = horiz / 600;
    float verMove = verti / 400;
    verMove/=1.5;
    if (verMove > 0){
        verMove/=2;
    }
    
    
    Vector3f camUp = cam.getUp();
    Vector3f move = new Vector3f(0, 0, 0).add(new Vector3f((camMove.x * rot) * 15, 7 + (-(camUp.y *  verMove) * 10), (camMove.z * rot) * 15)).subtract(dirP.mult(40));
    cam.setLocation(player.getPhysicsLocation().add(move));
    cam.setRotation(player.getPhysicsRotation());
    //End of camera rotation
}

What am I doing wrong with this update code, and how can I modify it so that my plane moves smoothly? I’ve been racking my brains for hours on this one but I just can’t find an answer.

Looks like something is wrong with FOV to me…

if (boosting){
       boost = 500;
       cam.setFrustumPerspective(90, (float) settings.getWidth() / (float) settings.getHeight(), 2f, zFar);
    }else{
        if (boost > 300){
            boost-= 100 * tpf;
        }
        cam.setFrustumPerspective(85, (float) settings.getWidth() / (float) settings.getHeight(), 2f, zFar);
    }

If you’re referring to the FOV “jump”, this was me enabling boost so that I could crash the plane faster to fit into the GIF time - Not part of the issue I was trying to show.

Are you referring specifically to the FOV, or to the Frustrum in general? I can’t see how anything FOV-Specific could cause this kind of issue.

I’ll search through my code as I also had this issue.
The Problem is: You are setting your camera in update() but the physics (which are executed at last) override that again.
Just a sec

Edit: Found my commit
Just move your updateCamera() into the simplyRender() method :slight_smile:

But it is in the simpleRender() method.

embarrasing :smiley:
call super.render(rm) before

Edit: And move it out into an AppState, that’s what I did.
That way your plane is drawn before you move the camera or something. Atleast it fixed it for me

Apologies for my noobishness, but where would I be calling that? Also, I’ve never worked with anything related to appstates, so I’m not sure how I would do that or if it would help (remember I would have to do it for every object, or else things would be at least a little jumpy).

Edit: Also, there is no super.render() method.

Maybe @Darkchaos meant super.simpleRender(rm) ?

Yeah you can’t call it here since you’re in your Applications simpleRenderer method.
Mine is completely empty :stuck_out_tongue:

Take a look at http://wiki.jmonkeyengine.org/doku.php/jme3:advanced:application_states
To name it short: Add a new Class which extends AbstractAppState and simply move the code into it’s render Method.

Then make sure to pass the camera in the constructor and init it:

MyAppState m = new MyAppState();
stateManager.attachState(m);

Then you’re basically done.

Don’t set the camera to the physics location. Set it to the spatial’s location. Physics may have moved on by the time you ask it but in simpleRender() the spatial’s location is 100% guaranteed to be where it will be displayed.

This helps to mitigate the problem - the plane is smoother. However, the issue still occurs, especially at low FPS.

public void updateCamera(){
    // Rotation code
    if (crashed) return;
    float[] angles = new float[3];
angles = plane.getLocalRotation().toAngles(angles);
    double z = Math.toDegrees(angles[2]);
    
    int widt = Display.getWidth();
    int heit = Display.getHeight();
    float verti = -(getInputManager().getCursorPosition().y-15 - heit/2)*1080/heit; 
    float horiz = -(getInputManager().getCursorPosition().x+16 - widt/2)*1920/widt;
    float rot = (float) -z / 50;
    //End of rotation
    //Camera rotation
    Vector3f plDir = plane.getLocalRotation().getRotationColumn(2);
    Vector3f dirP = plDir;
    Vector3f camMove = cam.getLeft();
    float horMove = horiz / 600;
    float verMove = verti / 400;
    verMove/=1.5;
    if (verMove > 0){
        verMove/=2;
    }
    
    
    Vector3f camUp = cam.getUp();
    Vector3f move = new Vector3f(0, 0, 0).add(new Vector3f((camMove.x * rot) * 15, 7 + (-(camUp.y *  verMove) * 10), (camMove.z * rot) * 15)).subtract(dirP.mult(30));
    cam.setLocation(plane.getLocalTranslation().add(move));
    cam.setRotation(plane.getLocalRotation());
    //End of camera rotation
}

For example, if I move the plane backwards at a great speed, the plane starts to come into the camera, even though the camera is supposed to be in a fixed position behind it.

What happens if you remove all of your position slop calculations and just set the camera to exactly behind the plane?

I reduced the code to this:

public void updateCamera(){
    // Rotation code
    if (crashed) return;
    
    Vector3f plDir = plane.getLocalRotation().getRotationColumn(2);
    Vector3f dirP = plDir;
   
    Vector3f move = (dirP.mult(-40));
    cam.setLocation(plane.getLocalTranslation().add(move));
    cam.setRotation(plane.getLocalRotation());
    //End of camera rotation
}

However the same issue still occurs.

As I said before, your solution is still much better than my original code, and definitely helps to make the issue far less noticeable. However, I would prefer to remove the issue entirely if possible.

Do it in an app state render instead of simpleRender(). simpleRender() is actually called after the stuff is rendered.

Really, I strongly advise no one to ever use simpleRender() or simpleUpdate() for anything really. Always always always always use app states.

Edit: that will be especially important when SimpleApplication is deprecated.

Hold on @pspeed … SimpleApplication deprecated?

1 Like

Someday. It’s a very ugly class, encourages bad behavior, and ultimately the “magic” protected fields harm new users more than they help them. There are designs to replace it with a proper base class refactoring the stuff that’s hard coded into Application and SimpleApplication into app states… even more than is done now.

1 Like