Restricting ChaseCamera movement (solved)

I’m using a ChaseCamera for a boat racing game (adapted from the flagrush tutorials as a base.) I really want the camera to behave in a more restricted manner - right now, when the boat bounces off of terrain (its velocity is reversed and starts to go backwards), the camera swings up and around to view the boat from the front side.



I want the camera to have a limted range of movement, in which it acts like the chase camera with the spring and dampening effect and all, but I don’t want it to be able to move along the entire movement sphere.



Here’s the camera movement I’m aiming for:





I’ve been playing with the chase camera properties for a while now, and I can’t create the movement I’m looking for.

  • props.put(ChaseCamera.PROP_STAYBEHINDTARGET, "true");  and chaser.setStayBehindTarget(true); seem to have no effect on the camera's movement
  • I don't totally understand the different spring and damping constants, but none of my tests actually restrict the camera to an angle less than 360 degree rotation
  • I read about RenControlEditor here: http://www.jmonkeyengine.com/jmeforum/index.php?topic=2420.0, but can’t find where that editor exists (if it is in jME 1.0 at all) - sounds like it would be a helpful tool to debug my camera



    Here’s my camera setup:

 private void buildChaseCamera() {
        HashMap<String, Object> props = new HashMap<String, Object>();
        props.put(ChaseCamera.PROP_INITIALSPHERECOORDS, new Vector3f(5f, 0, 30 * FastMath.DEG_TO_RAD));
        props.put(ChaseCamera.PROP_DAMPINGK, "6");
        props.put(ChaseCamera.PROP_SPRINGK, "9");
        props.put(ChaseCamera.PROP_STAYBEHINDTARGET, "true"); // this doesn't seem to work
        //props.put(ChaseCamera.PROP_MAINTAINAZIMUTH, "true"); // what does this do?
        chaser = new ChaseCamera(cam, player, props);
        chaser.setMaxDistance(40);
        chaser.setMinDistance(30);
        //chaser.setStayBehindTarget(true); //doesn't seem to work either!
        chaser.update(0);
    }



I'm updating the player, then the camera, then the skybox. After updating the chaser, I do a check to make sure the camera is above the water and manually set its y pos if not. Changing the update order doesn't seem to affect anything.

Appreciate your advice and help, thanks!

- John


I found RenControlEditor



(in jmetest.input for anyone else who needs it.)



Is it just me or does PROP_STAYBEHINDTARGET not work? I can't see any difference in the camera's behavior whether it is checked or not. The described behavior "the camera does its best to move into position behind the target" is definitely a big part of what I need - did this used to work?



Also, thanks, ren for the control editor app!

The behavior if that is on should be that (as long as the chaser is being updated every frame) the chase camera tries to move to a position "behind" the player, even if no input is currently being given.  It could be currently broken, but it is not designed to limit the degree of motion, just the resting point.  There's nothing in the ChaseCamera to restrict movement to a certain arc, but you could certainly subclass ChaseCamera and add that kind of functionality.

Thanks for the reply.



I'm thinking my problem might be (if the staybehindtarget behavior is working as described) that the camera is already "at rest" on the frame when the boat bounces off of terrain and reverses its velocity, thus there is no resetting of the resting point.



I'll look into subclassing ChaseCamera to restrict the viewing angle. Not sure how to do that off the top of my head, but I'm sure I can figure it out :slight_smile:

I've subbclassed ChaseCamera and I'm implementing a "enforce angle" method that restricts the camera's angle relative to the target by a certain limit.



It works pretty well, with occasional jitter that was minimized by also restricting the camera's y axis position in an overridden enforceMinMaxDistance method:


protected void enforceBehindAngle(Vector3f camPos, Vector3f targetPos){
       
       //1. make a vector from camPos to target Pos
       Vector3f directionVec = camPos.subtract(targetPos);
       
       float vecMagnitude = directionVec.length();
       directionVec = directionVec.normalize();
       //System.out.println("directionVec: " + directionVec);
       
       // 2. make a vector representing the direction the ship is facing
       Vector3f targetRot = this.target.getWorldRotation().getRotationColumn(0);
       targetRot = targetRot.normalize();
       
       // find the angle between the camera look and ship facing vectors
       // will be ~90 when straight behind
       Float angleBetween = targetRot.angleBetween(directionVec) * FastMath.RAD_TO_DEG;
       //System.out.println("angle between: " + angleBetween);
       
       float angleOffset = 15f; // the max angle (from 90 degrees) that the camera is allowed to move
       
       // is the angle less than 75 or more than 105?
       if(angleBetween < 90f - angleOffset){
          
          // get the angle from the target we want to set it at, in radians
          Float targetAngle = 90f - angleOffset;
          targetAngle *= FastMath.DEG_TO_RAD;
          
          // reverse the equation for a normalized direction vector
          // directionVecNormalized = (camPos - targetPos) / directionVecMagnitude
          // so
          // newcamPos = newdirectionVec * directionVecMagnitude + targetPos
          float sina = FastMath.sin(targetAngle);
          float cosa = FastMath.cos(targetAngle);
          float x2 = targetRot.x * cosa + targetRot.z * sina;
          float z2 = targetRot.z * cosa - targetRot.x * sina;
           Vector3f newRotVec = new Vector3f(x2, targetRot.y, z2);
           newRotVec.y = directionVec.y;
        
           // set the new camera position based on the new rotation vector
           Vector3f newCamPos = newRotVec.mult(vecMagnitude).add(targetPos);
          cam.setLocation(newCamPos);
          
          
          
       }
       else if (angleBetween > 90f + angleOffset){
          
          // get the angle from the target we want to set it at, in radians
          Float targetAngle = 90f + angleOffset;
          targetAngle *= FastMath.DEG_TO_RAD;
          
          // reverse the equation for a normalized direction vector
          // directionVecNormalized = (camPos - targetPos) / directionVecMagnitude
          // so
          // newcamPos = newdirectionVec * directionVecMagnitude + targetPos
          float sina = FastMath.sin(targetAngle);
          float cosa = FastMath.cos(targetAngle);
          float x2 = targetRot.x * cosa + targetRot.z * sina;
          float z2 = targetRot.z * cosa - targetRot.x * sina;
           Vector3f newRotVec = new Vector3f(x2, targetRot.y, z2);
           newRotVec.y = directionVec.y;
        
           // set the new camera position based on the new rotation vector
           Vector3f newCamPos = newRotVec.mult(vecMagnitude).add(targetPos);
          cam.setLocation(newCamPos);
       
       }     
    }

 protected void enforceMinMaxDistance(Vector3f 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);
                }
            }
        }
        // extra check to make sure cam doesn't fly over the boat
        if(camPos.y > 15f)
           camPos.y = 15f;
    }

Cool, nice work!