Here is my solution:
This works like this: camera gets its position from the ball/player with a delay (easing/ damping)
LookAt = ball / Player Position
// 3D sketch: shows a ball that moves in the x/z plane (height = y = 500) and is followed by a camera.
// The goal of the sketch is to demonstrate the usage of a following camera.
// cubes
float angleCubes=45; // angle for the cubes in the scene
CameraClass cam;
PVector ballPos = new PVector (300, 500, 300);
PVector ballVel = new PVector (
random(1, 2),
0,
random(-2, -1));
// -----------------------------------------------------
void setup() {
size (1400, 800, P3D);
cam = new CameraClass ();
cam.camPos = ballPos.copy();
cam.camPos.x += 113;
cam.camPos.y -= 63;
cam.camPos.z += 113;
} // func
void draw() {
// clear canvas
background(111);
// avoid clipping : https : // forum.processing.org/two/discussion/4128/quick-q-how-close-is-too-close-why-when-do-3d-objects-disappear
perspective(PI/3.0, (float) width/height, 1, 1000000);
// apply lights
lights();
// camera follows ball
if (! (keyPressed&&key==' ')) {
cam.lookAtPVectorFollow(ballPos);
}
// apply the values of the class to the real camera
cam.set();
// move ball and draw it
if (! (keyPressed&&key=='m')) {
ballPos.add(ballVel);
}
fill(0, 255, 0); // green
noStroke();
mySphere(ballPos.x, ballPos.y, ballPos.z, 10);
// contain ball in a invisible box
contain(ballPos, ballVel);
// make the scene with the boxes
scene();
// text upper left corner
fill(0, 255, 0);
cam.HUD_text("The green ball is contained in an "
+"red field."
+"\nHold space bar to stop camera."
+"\nHold 'm' to stop the Movement of the ball.");
//
} // func
// ---------------------------------------------------
void scene () {
// Drawing of the red field
fill(255, 2, 2);//RED
noStroke();
mySphere(500, 500, 500, 20);
mySphere(100, 500, 100, 20);
mySphere(100, 500, 500, 20);
mySphere(500, 500, 100, 20);
//
// rect
stroke(255, 2, 2);//RED
line(500, 500, 500, 500, 500, 100);//up
line(500, 500, 100, 100, 500, 100); //left
line(100, 500, 100, 100, 500, 500 ); // down
line(100, 500, 500, 500, 500, 500 ); // down
stroke(0);
float z ;
// one wall of boxes
for (int x = 10; x < 600; x+= 100)
{
fill(x/3, 2, 2);
for (int y = 10; y < 600; y+= 100)
{
z = -600;
myBox(x, y, z, 24);
// println ( "Box: " + x + ", "+y+ " "+z);
}
fill(0, 0, 254);
z=-800;
myBox(x, 10, z, 24);
}
//
// a few additional boxes
fill(0, 0, 254);
z=-400;
myBox(220, 10, z, 24);
myBox(600, 10, z, 24);
z=-400;
myBox(220, 510, z, 24);
myBox(600, 510, z, 24);
z=399;
myBox(220, 510, z, 24);
myBox(600, 510, z, 24);
z=900;
myBox(220, 510, z, 24);
myBox(600, 510, z, 24);
angleCubes++;
//
}
void myBox(float x, float y, float z,
float size1) {
// one nice wrapper for build in box-command
pushMatrix();
translate(x, y, z);
rotateY(radians(angleCubes));
rotateX(radians(45));
box(size1);
popMatrix();
}
void mySphere(float x, float y, float z,
float size1) {
// one nice wrapper for build in sphere-command
pushMatrix();
translate(x, y, z);
sphere(size1);
popMatrix();
}
void contain(PVector ballPos, PVector ballVel) {
// contain ball
if (ballPos.x>500)
ballVel.x=abs(ballVel.x)*-1;
if (ballPos.z>500)
ballVel.z=abs(ballVel.z)*-1;
if (ballPos.x<100)
ballVel.x=abs(ballVel.x);
if (ballPos.z<100)
ballVel.z=abs(ballVel.z);
}
//
// ===============================================
class CameraClass {
// capsules the normal camera() command and its vectors
PVector camPos; // its vectors
PVector camLookAt;
PVector camUp;
PVector camPosInitial; // its vectors - the default (unchanged)
PVector camLookAtInitial;
PVector camUpInitial;
// for follow
PVector camWhereItShouldBe = new PVector(0, 0, 0);
PVector camAdd = new PVector(0, -60, 0);
float easing = .019; // .07; // how fast it changes
float camCurrentAngle=0;// -90; // for cam rotation around itself (around Y-axis)
float camRadius; // same situation
// constructor without parameters
CameraClass() {
// constr
// set vectors
camPos = new PVector(width/2.0, height/2.0, 990);
camLookAt = new PVector(width/2.0, height/2.0, -600);
camUp = new PVector( 0, 1, 0 );
// save the initial values
camPosInitial = camPos.get();
camLookAtInitial = camLookAt.get();
camUpInitial = camUp.get();
} // constr
// ----------------------------------------
void set() {
// apply vectors to actual camera
camera (camPos.x, camPos.y, camPos.z,
camLookAt.x, camLookAt.y, camLookAt.z,
camUp.x, camUp.y, camUp.z);
}
void setLookAt (float x1, float y1, float z1) {
camLookAt = new PVector(x1, y1, z1);
}
void lookAtPVectorFollowOnlyLookAt(PVector followMe) {
// follows a player (e.g.)
// change only look at
camLookAt = followMe.get();
}
void lookAtPVectorFollow (PVector followMe) {
// follows a ball / player (e.g.)
// complete following (lookAt and camPos)
// manage Look At
camLookAt = followMe.get();
// manage position of the camera
float easing = 0.0198;
// x
camPos.x += (followMe.x-camPos.x) * easing;
// y
// camPos.y += (followMe.y-camPos.y) * easing -20;
camPos.y = followMe.y - 50; // other principle for y by the way
// z
camPos.z += (followMe.z-camPos.z) * easing;
}
void printData() {
println ( "Cam at " + camPos
+ " looking at " + camLookAt
+ " (angle = "
+camCurrentAngle
+").");
}
void HUD_text (String a1) {
// HUD text upper left corner
// this must be called at the very end of draw()
// this is a 2D HUD
camera();
hint(DISABLE_DEPTH_TEST);
noLights();
// ------------------
textSize(16);
text (a1, 20, 20);
// ------------------
// reset all parameters to defaults
textAlign(LEFT, BASELINE);
rectMode(CORNER);
textSize(32);
hint(ENABLE_DEPTH_TEST); // no HUD anymore
lights();
} // method
//
} // class
// ======================================