How can I center an object on my screen that moves?

I’m trying to make the screen follow my two spaceships and I’m trying to center the middle of the two ships to my screen

The coordinates of the point between the two ships are fx and fy.

Here the code:

boolean pause;
int game = 1, dg;
float tx, ty, fx, fy;
//PImage pausei;

void setup(){
 size(900, 600, P2D);
 Players.add(new Player(100, 600/2-30/2, false, 'g', 'f', false, 1));
 Players.add(new Player(800, 600/2-30/2, true, 'm', 'n', true, 2));
 //pausei = loadImage("pause.png");
 //pausei.resize(250, 50);
}

void draw(){
 pushMatrix();
 //translate(fx, fy);
 //translate(-fx + width / 2, -fy + height / 2);
 pushMatrix();
 translate(tx, ty);
 noStroke();
 rectMode(CORNER);
 fill(255, 255, 255, 100);
 rect(0, 0, width, height);
 PDraw();
 BDraw();
 CDraw();
 if(pause == true){
   fill(0, 0, 0, 25);
   noStroke();
   rect(width/2,height/2,width,height);
   tint(65);
   //image(pausei, width/2-250/2, height/2-50/2);
 }
 if(game == 0){
   if(dg < 200){
     dg++;
   }else{
     for(int i = 0; i < Players.size(); i++){
       Player aux = Players.get(i);
       aux.reset();
     }
     Bullets.clear();
     Cubes.clear();
     dg = 0;
     game = 1;
   }
 }
 if(vibrate == true){
   vibrateF(random(-20,20),random(-20,20),3,4);
 }
 popMatrix();
 popMatrix();
}

void keyPressed() {
 if(key == 'p' && game == 1){
   pause = !pause;
 }
 if(key == 'r'){
   game = 0;
   dg = 200;
 }
 for(int i = 0; i < Players.size(); i++){
   Player aux = Players.get(i);
   aux.keyPressed();
 }
}
void keyReleased() {
 for(int i = 0; i < Players.size(); i++){
   Player aux = Players.get(i);
   aux.keyReleased();
 }
}

boolean vibrate = false, vibrate2 = false;
int delayv = 0, asd = 0;
void vibrateF(float x, float y, int a, int mt){
 if(vibrate2 == false){
   tx += x;
   ty += y;
   vibrate2 = true;
 }
 if(delayv < mt){
   delayv++;
 }else{
   tx = 0;
   ty = 0;
   vibrate2 = false;
   delayv = 0;
   asd++;
 }
 if(asd == a){
   tx = 0;
   ty = 0;
   vibrate2 = false;
   vibrate = false;
   delayv = 0;
   asd = 0;  
 }
 if(asd == 2){
   x = x * -1;
   y = y * -1;
 }
}

ArrayList<Bullet> Bullets = new ArrayList<Bullet>();

class Bullet{
 float x, y, ds, sz;
 boolean del;
 int id;
 Bullet(float _x, float _y, float _ds, int _id, float _sz){
   x = _x;
   y = _y;
   ds = _ds;
   id = _id;
   sz = _sz;
 }
 void display(){
   rectMode(CENTER);
   fill(75);
   noStroke();
   rect(x, y, sz, sz);
 }
 void move(){
    x = x + ds;
   if(x > width || x < 0 || y < 0 || y > height){
     del = true;
   }
 }
 void delete(){
   del = true;
 }
 boolean del(){
   return del;
 }
 float x(){
   return x;
 }
 float y(){
   return y;
 }
 int id(){
   return id;
 }
}

void BDraw(){
 for(int i = 0; i < Bullets.size(); i++){
   Bullet aux = Bullets.get(i);
   aux.display();
   if(pause == false){
     aux.move();
     if(aux.del() == true){
       Bullets.remove(i);
     }
   }
 }
}

ArrayList<Cube> Cubes = new ArrayList<Cube>();

class Cube{
 float x, y, rx, ry, sz;
 boolean del;
 int d;
 Cube(float _x, float _y, float _sz){
   x = _x;
   y = _y;
   rx = random(-4, 4);
   ry = random(-4, 4);
   sz = _sz;
 }
 void display(){
   rectMode(CENTER);
   fill(75);
   noStroke();
   rect(x, y, sz, sz);
 }
 void move(){
   if(d > 0){
     y += ry;
     x += rx;
     if(rx > 0.01){
       rx -= 0.01;
     }
     if(rx < -0.01){
       rx += 0.01;
     }
     if(ry > 0.01){
       ry -= 0.01;
     }
     if(ry < -0.01){
         ry += 0.01;
     }
   }else{
     d++;
   }
 }
 void delete(){
   del = true;
 }
 boolean del(){
   return del;
 }
 float x(){
   return x;
 }
 float y(){
   return y;
 }
}

void CDraw(){
 for(int i = 0; i < Cubes.size(); i++){
   Cube aux = Cubes.get(i);
   aux.display();
   if(pause == false){
     aux.move();
     if(aux.del() == true){
       Cubes.remove(i);
     }
   }
 }
}

ArrayList<Player> Players = new ArrayList<Player>();

class Player{
 float x, y, sxl, sxr, syu, syd, sp = 0.25, sz = 12, x_, y_;
 int bdelay;
 boolean up, down, left, right, arrow;
 boolean up2, down2, left2, right2;
 boolean bs = true, facingc = false, cs = true, explo = false, s = true, fc_;
 char cshoot, cfac;
 int id;
 Player(float _x, float _y, boolean _facingc, char _cshoot, char _cfac, boolean _arrow, int _id){
   x = _x;
   y = _y;
   x_ = _x;
   y_ = _y;
   facingc = _facingc;
   fc_ = _facingc;
   cshoot = _cshoot;
   cfac = _cfac;
   arrow = _arrow;
   id = _id;
 }
 void display(){
   rectMode(CENTER);
   fill(75);
   stroke(75);
   if(explo == false){
   rect(x, y, sz, sz);
   rect(x+sz, y, sz, sz);
   if(facingc == false){
     rect(x+sz, y+sz, sz, sz);
     rect(x+sz+sz, y+sz, sz, sz);
   }else{
     rect(x, y+sz, sz, sz);
     rect(x-sz, y+sz, sz, sz);
   }
   rect(x, y+sz+sz, sz, sz);
   rect(x+sz, y+sz+sz, sz, sz);
   }
 }
 void move(){
   if(bs == false){
     if(bdelay < 25){
       bdelay++;
     }else{
       bs = true;
       bdelay = 0;
     }
   }
   if(down == true){
     down2 = true;
     if(syd < 8){
       syd += sp;
     }
     y += syd;
   }else if(down2 == true){
     if(syd > 0){
       syd -= sp;
     }else{
       down2 = false;
       syd = 1;
     }
     y += syd;
   }
   if(up == true){
     up2 = true;
     if(syu < 8){
       syu += sp;
     }
     y = y - syu;
   }else if(up2 == true){
     if(syu > 0){
       syu -= sp;
     }else{
       up2 = false;
       syu = 1;
     }
     y -= syu;
   }
   if(left == true){
   left2 = true;
     if(sxl < 8){
       sxl += sp;
     }
     x = x - sxl;
   }else if(left2 == true){
     if(sxl > 0){
       sxl -= sp;
     }else{
       left2 = false;
       sxl = 1;
     }
     x -= sxl;
   }
   if(right == true){
     right2 = true;
     if(sxr < 8){
       sxr += sp;
     }
     x = x + sxr;
   }else if(right2 == true){
     if(sxr > 0){
       sxr -= sp;
     }else{
       right2 = false;
       sxr = 1;
     }
     x += sxr;
   }
   for(int i = 0; i < Bullets.size(); i++){
     Bullet aux = Bullets.get(i);
     if(dist(aux.x(), aux.y(), x+sz, y+sz+sz/2) < 15 && id != aux.id() || dist(aux.x(), aux.y(), x+sz, y+sz) < 15 && id != aux.id()){
       if(s == true){
         explode();
         aux.delete();
         s = false;
       }
     }
   }
   for(int i = 0; i < Cubes.size(); i++){
     Cube aux = Cubes.get(i);
     if(dist(aux.x(), aux.y(), x+sz, y+sz+sz/2) < 15 || dist(aux.x(), aux.y(), x+sz, y+sz) < 15){
       if(s == true){
         explode();
         aux.delete();
         s = false;
       }
     }
   }
 }
 void keyPressed(){
   if(s == true){
   if(key=='s' & down == false && arrow == false || key==CODED && keyCode==DOWN && arrow == true){
       down = true;
   }
   if(key=='w' && up == false && arrow == false || key==CODED && keyCode==UP && arrow == true){
     up = true;
   }
   if(key=='a' && left == false && arrow == false || key==CODED && keyCode==LEFT && arrow == true){
     left = true;
   }
   if(key=='d' && right == false && arrow == false || key==CODED && keyCode==RIGHT && arrow == true){
     right = true;
   }
   if(key==cshoot){
     if(bs == true && cs == true){
       if(facingc == false){
         Bullets.add(new Bullet(x+sz/2, y+sz, 15, id, sz));
       }else{
         Bullets.add(new Bullet(x+sz/2, y+sz, -15, id, sz));
       }
       cs = false;
       bs = false;
     }
   }
   if(key==cfac){
     facingc = !facingc;
   }
   }
 }
 void keyReleased(){
   if(s == true){
   if(key=='s' && arrow == false || key==CODED && keyCode==DOWN && arrow == true){
     down = false;
     up2 = false;
   }
   if(key=='w' && arrow == false || key==CODED && keyCode==UP && arrow == true){
     up = false;
     down2 = false;
   }
   if(key=='a' && arrow == false || key==CODED && keyCode==LEFT && arrow == true){
     left = false;
     right2 = false;
   }
   if(key=='d' && arrow == false || key==CODED && keyCode==RIGHT && arrow == true){
     right = false;
     left2 = false;
   }
   if(key==cshoot){
     cs = true;
   }
   }
 }
 void explode(){
   //random(-5, 5)
   vibrate = true;
   explo = true;
   Cubes.add(new Cube(x, y, sz));
   Cubes.add(new Cube(x+sz, y, sz));
   if(facingc == false){
     Cubes.add(new Cube(x+sz, y+sz, sz));
     Cubes.add(new Cube(x+sz+sz, y+sz, sz));
   }else{
     Cubes.add(new Cube(x, y+sz, sz));
     Cubes.add(new Cube(x-sz, y+sz, sz));
   }
   Cubes.add(new Cube(x, y+sz+sz, sz));
   Cubes.add(new Cube(x+sz, y+sz+sz, sz));
   s = false;
   game = 0;
 }
 void reset(){
   explo = false;
   s = true;
   cs = true;
   bs = true;
   bdelay = 0;
   sxl = 1; 
   sxr = 1;
   syu = 1; 
   syd = 1;
   x = x_;
   y = y_;
   facingc = fc_;
   up = false;
   down = false;
   left = false;
   right = false;
   up2 = false;
   down2 = false;
   left2 = false;
   right2 = false;
 }
 boolean s(){
   return s;
 }
 float x(){
   return x;
 }
 float y(){
   return y;
 }
}

void PDraw(){
 fill(0);
 ellipse(fx, fy, 10, 10);
 for(int i = 0; i < Players.size(); i++){
   Player aux = Players.get(i);
   aux.display();
   if(pause == false){
     aux.move();
   }
   for(int e = 0; e < Players.size(); e++){
     Player aux2 = Players.get(e);
     if(e != i){
     fx = aux.x()/2 + aux2.x()/2;
     fy= aux.y()/2 + aux2.y()/2;
     }
   }
 }
}
1 Like

-a- i think the ( here disabled ) translate should work,
but you need to force it to the middle by using also width/2, height/2

-b- your code uses ( multiple ) push pop,
here in case you have 2 pop at the end of draw is a clear indication
that it is UNUSED and could be disabled.

-c- using boolean variables ( and set with bx = true or bx = false )
allows to use them bx inside if without the == true
( and instead bx == false can use ! bx )

1 Like

In 3D there is a camera.

In 2D there is none. Instead you move your scene under the camera using translate.

It’s called viewport.

1 Like

Understanding the relationship between translate, scale, and the idea of a 2D “camera” can sometimes be a bit tricky. It is worth reviewing the coordinate system and 2D transformations tutorials:

However, in the end, there are three key transformations:

  1. translate to the center of the screen
  2. optionally scale the view, e.g. based on player speed or inter-player distance
  3. translate again to recenter the camera on the player – or on a selected point of view, the midpoint between two players, or the center of a selected group of objects et cetera.
/**
 * Two Player Camera 2D
 * 2019-06 - Jeremy Douglass - Processing 3.4
 * Keep two balls (players) in view by midpoint/distance.
 * Press any key to switch modes.
 * 
 * Switch between modes: FLAT, TRACK, TRACK_ZOOM.
 * 0 FLAT is a normal 2D view.
 * 1 TRACK centers view on midpoint between two balls.
 * 2 TRACK_ZOOM tracks + zooms distance between balls.
 */

Ball p1;
Ball p2;
float[] bounds;
PVector view;
int mode; // 0=FLAT, 1=TRACK, 2=TRACK_ZOOM

void setup() {
  size(200, 200);
  fill(0);

  // create a bounding box with two bouncing balls
  bounds = new float[]{1, height*.2, width-1, height*.6};
  p1 = new Ball(random(width), height/2, 2, 10, bounds);
  p2 = new Ball(random(width), height/2, 2, 10, bounds);
}

void draw() {
  background(128);

  pushMatrix();
  noFill();
  // update ball locations
  p1.move();
  p2.move();

  // view x,y is a world-space location halfway between two balls
  view = PVector.lerp(p1.pos, p2.pos, 0.5);
  // view.z is the recommended zoom based on inter-ball distance
  view.z = (width-20)/(20+p1.pos.dist(p2.pos));
  
  switch(mode){
    case 0: // FLAT
    break;
    case 1: // TRACK
    translate(width/2, height/2);  // center screen on 0,0
    translate(-view.x, -view.y);   // recenter screen on view
    break;
    case 2: // TRACK_ZOOM
    translate(width/2, height/2);  // center screen on 0,0
    scale(view.z);                 // scale screen to ball distance
    translate(-view.x, -view.y);   // recenter screen on view
    break;
  }

  // draw
  rect(bounds[0], bounds[1], bounds[2], bounds[3]);
  p1.render();
  p2.render();
  ellipse(view.x, view.y, 3, 3);
  popMatrix();

  // status
  String msg = "mode:" + mode + "  xy:" + (int)view.x + "," + (int)view.y + "  zoom:" + nf(view.z, 0, 1) + "\nPress any key";
  text(msg, 5, 15);
}

// simple bouncing ball with a bounding box
class Ball {
  float[] box;
  PVector pos;
  PVector speed;
  float rad;
  Ball(float x, float y, float speed, float rad, float[] box) {
    this.pos = new PVector(x, y);
    this.rad = rad;
    this.box = box;
    this.speed = PVector.random2D().setMag(speed);
  }
  void move() {
    if (pos.x > box[0]+box[2] || pos.x < box[0]) speed.x *= -1;
    if (pos.y > box[1]+box[3] || pos.y < box[1]) speed.y *= -1;
    pos.add(speed);
  }
  void render() {
    ellipse(pos.x, pos.y, 10, 10);
  }
}

// press any key to switch between modes 0, 1, 2
void keyReleased() {
  mode = (mode+1)%3;
}

0674 1375

These operations are order dependent if you are scaling: you must center your coordinate system first, then scale from the center, then translate to your view. If you try to scale first or last then bad things happen: either scaling happens from the top left, or the view scale does not match the drawing scale and they are misaligned.

2 Likes

that is a nice piece of code.