ChaseCamera Question

I'm new to JME and have been tinkering with the ChaseCamera.  From my lack of knowledge, I think I am just missing something.



In the following code block (from ChaseCamera), I would assume that, if I meet the conditions to get to the "set y to be opposite target facing dir" block, the camera will always be at the back of my target Spatial.




   protected void updateIdealAzimuth(float time, Vector3f camPos) {
        // update camera's ideal azimuth
        if (maintainAzimuth && !forceAzimuthUpdate) {
            ; // no need to compute azimuth
        } else {
            float offX, offZ;
            if (stayBehindTarget && !looking) {

                // set y to be opposite target facing dir.
                Vector3f rot = compVect;
                target.getLocalRotation().getRotationColumn(2, rot);
                rot.negateLocal();
                offX = rot.x;
                offZ = rot.z;
                if (worldUpVec.z == 1) {
                    offZ = rot.y;
                }           

            } else {
                forceAzimuthUpdate = false;
                offX = (camPos.x - targetPos.x);
                offZ = (camPos.z - targetPos.z);
                if (worldUpVec.z == 1) {
                    offZ = (camPos.y - targetPos.y);
                }
            }
            idealSphereCoords.y = FastMath.atan2(offZ, offX);
        }
    }




Am I not seeing this properly?  If I am wrong, how would i get the camera to stay to the back of my object?

(This is driving me nuts)

Note: I'm using Java 6 with todays CVS

are you setting this option?:

props.put(ChaseCamera.PROP_STAYBEHINDTARGET, "true");

Ok, i found the my issue.  The camera update is only happening while the object is being moved.



If I pull all the code from the ChaseCamera update proc and put into a new one like the following code.  I can get the camera to move behind the object while not moving if I execute the new proc in the main update of my GameState.  I will just need to flag when the user is moving the mouse.  Currently its a bit messy and does not move well when the mouselook is being used.



Making the ChaseCamera:


  public void buildChaseCamera()
  {
    HashMap props = new HashMap();
    props.put(ChaseCamera.PROP_MAXDISTANCE, "10");
    props.put(ChaseCamera.PROP_MINDISTANCE, "2");
    props.put(ChaseCamera.PROP_DAMPINGK, "12");
    props.put(ChaseCamera.PROP_SPRINGK, "36");
    props.put(ThirdPersonMouseLook.PROP_LOCKASCENT, "false");
    props.put(ThirdPersonMouseLook.PROP_ENABLED, "true");
    props.put(ThirdPersonMouseLook.PROP_MOUSEBUTTON_FOR_LOOKING, "1");

    chaser = new ChaseCamera(cam, player, props);
    chaser.setStayBehindTarget(true);
    chaser.setLooking(false);
    chaser.setMaintainAzimuth(false);
    chaser.setForceAzimuthUpdate(true);
    chaser.setEnableSpring(true);
  } 



New Proc: (a little sloppy but i plan to clean up after some sleep)

 public void doMoveCamTest(float time)
{
    //init flags
    boolean stayBehindTarget = true;
    boolean looking = false;
    boolean maintainAzimuth = false;
    boolean forceAzimuthUpdate = true;
    boolean enableSpring = true;

    //init vars
    float dampingK = 12f;
    float springK = 36f;
    float maxDistance = 10f;
    float minDistance = 2f;

    //init 3d vars
    Vector3f camPos = cam.getLocation();
    Vector3f idealPosition = new Vector3f();
    Vector3f targetPos = new Vector3f();
    Vector3f targetOffset = new Vector3f();

    Vector3f compVect = new Vector3f();
    Vector3f DEFAULT_WORLDUPVECTOR = new Vector3f(Vector3f.UNIT_Y);
    Vector3f worldUpVec = new Vector3f(DEFAULT_WORLDUPVECTOR);
    Vector3f velocity = new Vector3f();

    Vector3f idealSphereCoords = null;

    //setup mouse values
    ThirdPersonMouseLook mouseLook = chaser.getMouseLook();

    RelativeMouse mouse = new RelativeMouse("Mouse Input");   //need to find a way to remove this
    mouse.registerWithInputHandler(chaser);                   //need to find a way to remove this

    //flag if the user is looking around
    looking = mouseLook.isEnabled();
   
     //  updateTargetPosition(Vector3f camPos)
    targetPos.set(player.getWorldTranslation());
    if (!Vector3f.isValidVector(camPos)) {
        camPos.set(targetPos);
    }

    if (!Vector3f.isValidVector(camPos) || !Vector3f.isValidVector(targetPos))
        return;

    targetPos.addLocal(targetOffset);
  
    // back to update proc
    if (!Vector3f.isValidVector(camPos) || !Vector3f.isValidVector(targetPos))
      System.out.println("not a valid vector.");
  
  
    // updateIdealAzimuth(time, camPos)
    if (idealSphereCoords == null)
      idealSphereCoords = new Vector3f((mouseLook.getMaxRollOut()-mouseLook.getMinRollOut()) / 2f, 0, mouseLook.getMaxAscent() * .5f);

    // update camera's ideal azimuth
    if (maintainAzimuth && !forceAzimuthUpdate) {
        ; // no need to compute azimuth
    } else {
      float offX, offZ;
      if (stayBehindTarget && !looking) {
          // set y to be opposite target facing dir.
          Vector3f rot = compVect;
          player.getLocalRotation().getRotationColumn(2, rot);
          rot.negateLocal();
          offX = rot.x;
          offZ = rot.z;
          if (worldUpVec.z == 1) {
              offZ = rot.y;
          }           
      } else {
          forceAzimuthUpdate = false;
          offX = (camPos.x - targetPos.x);
          offZ = (camPos.z - targetPos.z);
          if (worldUpVec.z == 1) {
              offZ = (camPos.y - targetPos.y);
          }
      }
      idealSphereCoords.y = FastMath.atan2(offZ, offX);
    }

    //convertIdealSphereToCartesian()
    if (worldUpVec.y == 1) {
        // determine ideal position in cartesian space
        FastMath.sphericalToCartesian(idealSphereCoords, idealPosition).addLocal(targetPos);
    } else if (worldUpVec.z == 1){
        // determine ideal position in cartesian space
        FastMath.sphericalToCartesianZ(idealSphereCoords, idealPosition).addLocal(targetPos);
    }

    //updateCameraPosition(float time, Vector3f camPos)
    if (!enableSpring) {
        // ignore springs and just set to targeted "ideal" position.
        camPos.set(idealPosition);
    } else {
        // Determine displacement from current to ideal position
        // Use the spring constants to accelerate towards the ideal position
        Vector3f displace = compVect;
        camPos.subtract(idealPosition, displace);
        displace.multLocal(-springK).subtractLocal(velocity.x * dampingK,
                velocity.y * dampingK, velocity.z * dampingK);

        velocity.addLocal(displace.multLocal(time));
        if (!Vector3f.isValidVector(velocity)) velocity.zero();
        camPos.addLocal(velocity.x * time, velocity.y * time, velocity.z
                        * time);
    }

    //enforceMinMaxDistance(camPos)
    if (maxDistance > 0 || minDistance > 0) {
      float dist = camPos.distance(targetPos);
      if (dist > maxDistance || dist < minDistance) {
          // Move camera position along direction vector until distance is satisfied.
          Vector3f dir = targetPos.subtract(camPos, compVect);
          dir.normalizeLocal();
          if (dist > maxDistance) {
              dir.multLocal(maxDistance-dist);
              camPos.subtractLocal(dir);
          } else if (dist < minDistance) {
              dir.multLocal(dist-minDistance);
              camPos.addLocal(dir);
          }
      }
    }

    // Look at our target
    cam.lookAt(targetPos, worldUpVec);

    if (maintainAzimuth)
        cam.update();
 }
 



MyGameState.update()

 
public void update(float tpf) {
    if (pause)
        return;

    // Update the InputHandler
    input.update(tpf);

    //update the chase camera to handle the player moving around.
    if (chaser != null)
      chaser.update(tpf);
   
    //We don't want the chase camera to go below the world, so always keep
    //it 2 units above the level.
    if(cam.getLocation().y < (terrain.getHeight(cam.getLocation())+2))
    {
        cam.getLocation().y = terrain.getHeight(cam.getLocation()) + 2;
        cam.update();
    }
   
    //make sure the player is not below the world
    float characterMinHeight = terrain.getHeight(player.getLocalTranslation())+((BoundingBox)player.getWorldBound()).yExtent;
    if (!Float.isInfinite(characterMinHeight) && !Float.isNaN(characterMinHeight))
    {
      player.getLocalTranslation().y = characterMinHeight;
    }
    player.getLocalTranslation().y += .25;

    //make the camera moves behind the player
    doMoveCamTest(tpf);
   
    // Update the geometric state of the rootNode
    rootNode.updateGeometricState(tpf, true);
}



Ok, time to crash.  Coffee and Soda is depleted. 

The camera will move to be behind the player (even if the player is not moving) if you set stay behind to true…  Not sure what you are trying to do differently?

After some sleep and a few hours of Naruto, I found where I was going wrong in my original path of thinking (I think).



If I use the following code the camera does not move behind the target object unless the object is being moved.



public void buildChaseCamera()
  {
    HashMap props = new HashMap();
    props.put(ChaseCamera.PROP_MAXDISTANCE, "10");
    props.put(ChaseCamera.PROP_MINDISTANCE, "2");
    props.put(ChaseCamera.PROP_DAMPINGK, "12");
    props.put(ChaseCamera.PROP_SPRINGK, "36");
    props.put(ThirdPersonMouseLook.PROP_LOCKASCENT, "false");
    props.put(ThirdPersonMouseLook.PROP_ENABLED, "true");

    chaser = new ChaseCamera(cam, player, props);
    chaser.setStayBehindTarget(true);
    chaser.setLooking(false);
    chaser.setMaintainAzimuth(false);
    chaser.setForceAzimuthUpdate(true);
    chaser.setEnableSpring(true);
  } 



Adding the following line of code to the props the camera does move behind the non-moving target object, or I could have disabled the mouse look.  I fixed the problem indirectly above while I was trying to get around my problem.


    props.put(ThirdPersonMouseLook.PROP_MOUSEBUTTON_FOR_LOOKING, "1");



However, using the mousebutton to move the camera around causes some realy jerky movement.  (Something I need to look at)

Yes I took the long way around, but at least now I have a better understanding of how this part works.

Another question which I think is related to this topic:



ChaseCamera stays behind the target node, that is fine. But I would like to configure the speed with which it "swings" in position behind the target, because currently if I rotate/move around the target it takes quite a while til the camera is in its final position. Is there a way to alter this speed?

Nobody?  :smiley: What I was trying to say was, that the chase camera takes too long to reach its final position behind the target. Is there a way to speed this up or to even configure the speed exactly?

Camera speed is a matter of playing with the spring constants.  You might try playing with the editor to see what settings might suit you best.

Did you manage to fix the problem with the jerky movement when looking with the mouse button while moving?

I'm trying to get my ChaseCamera to work correct and I'm running into the same problems.



Mark