3rd Person Shooter translate() and camera()

Hello all,

I would like to make a 3rd Person Shooter (or 3rd Person Player) where a player in 3D is followed by a camera. Move with mouse and wasd keys.

I found a code in the forum.

  • But in the code, the player and camera are fixed in the center and it is the floor that in facts move.

I want it in another way:

  • That the floor is fixed and we move the player and with it the camera (being always behind the player).

Unfortunately, the code doesn’t work. Can anyone help me debug it? I am out of my depth because the maths and rotations. Thank you so much!

This is part of a much bigger project but I made a mcve, based on the code I found.

Any help much appreciated!

Warm regards,

Chrisir

// this version with heavy new structure : not the ground moves anymore but now player plus camera move!!! 

import java.awt.*;

Robot robot;

float cameraRotateX;
float cameraRotateY;
float cameraSpeed;

float angle;

boolean wPressed, sPressed, aPressed, dPressed;
PVector pressedDir = new PVector();
float accelMag;

Player player; 

com.jogamp.newt.opengl.GLWindow myGLWindow;

float gridLevelY; 

// ----------------------------------------------------------------------------------------

void setup() {
  fullScreen(P3D);

  myGLWindow=getFrame(getSurface());

  accelMag   = 2;

  player     = new Player();

  gridLevelY = height/2; 

  cameraSpeed = TWO_PI / width;
  cameraRotateY = -PI/6;

  try {
    robot = new Robot();
  } 
  catch(Exception ex) {
    println(ex);
  }

  noCursor();
}

void draw() {
  background(125);
  lights();

  updateRotation();

  beginCamera(); // ??????????????????????????
  camera();
  translate(player.pos.x, gridLevelY-player.bsize/2, player.pos.y); // ??? qqqq 
  rotateX(cameraRotateY);
  rotateY(cameraRotateX);
  endCamera();


  translate(width/2, height/10, 0);

  // pushMatrix(); 

  player.jumpManagement(); 
  player.display(); 
  player.move();

  decoration(); 
  drawGrid();

  //  popMatrix(); 

  angle += mouseChangeX()*0.003;
  robot.mouseMove(getSketchCenterX(), getSketchCenterY());
}//func

// -----------------------------------------------------------------

void drawGrid() {
  pushMatrix();
  int count = 50;
  //  translate(-player.pos.x, gridLevelY, -player.pos.y);
  translate(0, gridLevelY, 0);
  stroke(255);
  float size = (count -1) * player.bsize*2;
  for (int i = 0; i < count; i++) {
    float pos2 = map(i, 0, count-1, -0.5 * size, 0.5 * size);
    line(pos2, 0, -size/2, pos2, 0, size/2);
    line(-size/2, 0, pos2, size/2, 0, pos2);
  }
  popMatrix();
}

// -----------------------------------------------------------------

void decoration() {
  pushMatrix();
  // translate(-player.pos.x, 0, -player.pos.y);
  translate(-333, gridLevelY-320/2, 333);
  fill(0, 0, 255); 
  box(120, 320, 120);
  popMatrix();
}

// -----------------------------------------------------------------

int getSketchCenterX() {
  return
    myGLWindow.getX() + width / 2;
}

int getSketchCenterY() {
  return 
    myGLWindow.getY() + height / 2;
}

static final com.jogamp.newt.opengl.GLWindow getFrame(final PSurface surface) {
  // used only once ! 
  return 
    (com.jogamp.newt.opengl.GLWindow) surface.getNative();
}

float mouseChangeX() {
  return 
    getSketchCenterX() - (float) MouseInfo.getPointerInfo().getLocation().getX();
}

float mouseChangeY() {
  return
    getSketchCenterY() - (float) MouseInfo.getPointerInfo().getLocation().getY();
}

void updateRotation() {
  cameraRotateX -= mouseChangeX() * cameraSpeed;
  cameraRotateY += mouseChangeY() * cameraSpeed;
  cameraRotateY = constrain(cameraRotateY, -HALF_PI, 0);
}

// --------------------------------------------------------------------------

void mouseClicked() {
  //only used so I can keep track of coordinates in case I need to change things
  player.printPos(); 
  println("X" + mouseX);
  println("Y" + mouseY);
}

void keyPressed() {
  if (keyCode == SHIFT) {
    accelMag = 4;
  }

  switch(key) {

  case ' ':
    player.startJump(); 
    break; 

  case 'w':
    wPressed = true;
    pressedDir.y = -1;
    break;

  case 'a':
    aPressed = true;
    pressedDir.x = -1;
    break;

  case 's':
    sPressed = true;
    pressedDir.y = 1;
    break;

  case 'd':
    dPressed = true;
    pressedDir.x = 1;
    break;
  }
}

void keyReleased() {
  if (keyCode == SHIFT) {
    accelMag = 2;
  }

  switch(key) {

  case 'w':
    wPressed = false;
    pressedDir.y = sPressed ? 1 : 0;
    break;

  case 'a':
    aPressed = false;
    pressedDir.x = dPressed ? 1 : 0;
    break;

  case 's':
    sPressed = false;
    pressedDir.y = wPressed ? -1 : 0;
    break;

  case 'd':
    dPressed = false;
    pressedDir.x = aPressed ? -1 : 0;
    break;
  }
}

//==================================================================

class Player {

  PVector bpos = new PVector();
  float bsize  = 100;

  // jump 
  float playermoveY = 0; 
  boolean jump=false; 
  int jumpcounter=0;

  PVector pos, speed;

  // constr 
  Player() {
    //   bpos.y = height/2+bsize/2;
    pos = new PVector();
    speed = new PVector();
  } // constr 

  void display() {
    pushMatrix();
    //translate(bpos.x, bpos.y, bpos.z);
    // translate(player.pos.x, gridLevelY, player.pos.y); // ??? qqqq 
    translate(player.pos.x, bpos.y, player.pos.y); // ??? qqqq 
    translate(bpos.x, 
      gridLevelY-bsize/2, 
      bpos.z);
    stroke(255);
    fill(0);
    rotateY(atan2(speed.x, speed.y));
    box(bsize);
    popMatrix();
  }

  void move() {
    PVector accel = getMovementDir().rotate(cameraRotateX).mult(accelMag);
    speed.add(accel);
    pos.add(speed);
    speed.mult(0.9);
  }

  PVector getMovementDir() {
    return 
      pressedDir.copy().normalize();
  }

  void startJump() {
    // jump
    // if he's already jumping, no jump is allowed
    if (jump)
      return; // leave 
    // otherwise jump   
    jumpcounter=0;
    playermoveY=-21;
    jump=true;
  }

  void jumpManagement() {
    if (jump) {
      bpos.y += 
        playermoveY;
    }

    // stop the jump
    if (bpos.y>=0) {  //  gridLevelY-bsize/2
      bpos.y=0;
      if (jumpcounter==0) {
        // bounce
        playermoveY=-10;
        jumpcounter++;
      } else if (jumpcounter==1) {
        // bounce
        playermoveY=-7;
        jumpcounter++;
      } else {
        // stop jump
        jump=false;
        playermoveY=0;
      }
    } //if
    else {
      playermoveY++;
    }
  }//func 

  void printPos() {
    println("bpos.x " + bpos.x);
    println("bpos.y " + bpos.y);
    println("bpos.z " + bpos.z);
  }
  //
}
//

//
1 Like

Check out the maze runner example from QueasyCam – it implements a shooter camera through that library. You can then offset the camera position as much as you like.

1 Like

Thanks for that!

Actually, that is and First Person Shooter, not 3rd Person Shooter

How can I place my player visible always in front of the camera?

I made an mcve and tried to place a box as a player placeholder, but it doesn’t work, it’s at the very end where the stars are ******

Thanks!

Regards,

Chrisir


// ********************************************************************************
//         joined pde-file of folder C:\Desktop\mazeRunner1
// ********************************************************************************


// ********************************************************************************
// tab: mazeRunner1.pde main file (file name is folder name)




/*
 * NOTE: This doesn't generate real mazes; they're just
 * empty spaces in which to walk around. Enjoy! :)
 *
 */

import queasycam.*;

Player player;
Maze maze;

void setup() {
  size(1600, 800, P3D);
  strokeWeight(2);

  player = new Player(this);
  maze = new Maze(20);
  maze.setPlayerAtStart(player);
  noCursor();
}

void draw() {
  background(51);

  maze.update();
  maze.display();
  player.update();
}
//

// ********************************************************************************
// tab: Block.pde



class Block {

  PVector position;
  PVector dimensions;
  color fillColor;
  boolean visited;

  Block(float x, float y, float z, 
    float w, float h, float d) {
    position = new PVector(x, y, z);
    dimensions = new PVector(w, h, d);
    fillColor = color(random(150, 200));
    visited = false;
  }

  void update() {
    float playerLeft = player.position.x - player.dimensions.x/2;
    float playerRight = player.position.x + player.dimensions.x/2;
    float playerTop = player.position.y - player.dimensions.y/2;
    float playerBottom = player.position.y + player.dimensions.y/2;
    float playerFront = player.position.z - player.dimensions.z/2;
    float playerBack = player.position.z + player.dimensions.z/2;

    float boxLeft = position.x - dimensions.x/2;
    float boxRight = position.x + dimensions.x/2;
    float boxTop = position.y - dimensions.y/2;
    float boxBottom = position.y + dimensions.y/2;
    float boxFront = position.z - dimensions.z/2;
    float boxBack = position.z + dimensions.z/2;

    float boxLeftOverlap = playerRight - boxLeft;
    float boxRightOverlap = boxRight - playerLeft;
    float boxTopOverlap = playerBottom - boxTop;
    float boxBottomOverlap = boxBottom - playerTop;
    float boxFrontOverlap = playerBack - boxFront;
    float boxBackOverlap = boxBack - playerFront;

    if (((playerLeft > boxLeft && 
      playerLeft < boxRight || 
      (playerRight > boxLeft &&
      playerRight < boxRight)) &&

      ((playerTop > boxTop &&
      playerTop < boxBottom) || 
      (playerBottom > boxTop && 
      playerBottom < boxBottom)) && 

      ((playerFront > boxFront && 
      playerFront < boxBack) || 
      (playerBack > boxFront && 
      playerBack < boxBack)))) {

      float xOverlap = max(min(boxLeftOverlap, boxRightOverlap), 0);
      float yOverlap = max(min(boxTopOverlap, boxBottomOverlap), 0);
      float zOverlap = max(min(boxFrontOverlap, boxBackOverlap), 0);

      if (xOverlap < yOverlap && xOverlap < zOverlap) {
        if (boxLeftOverlap < boxRightOverlap) {
          player.position.x = boxLeft - player.dimensions.x/2;
        } else {
          player.position.x = boxRight + player.dimensions.x/2;
        }
      } else if (yOverlap < xOverlap && yOverlap < zOverlap) {
        if (boxTopOverlap < boxBottomOverlap) {
          player.position.y = boxTop - player.dimensions.y/2;
          player.velocity.y = 0;
          player.grounded = true;
        } else {
          player.position.y = boxBottom + player.dimensions.y/2;
        }
      } else if (zOverlap < xOverlap && zOverlap < yOverlap) {
        if (boxFrontOverlap < boxBackOverlap) {
          player.position.z = boxFront - player.dimensions.x/2;
        } else {
          player.position.z = boxBack + player.dimensions.x/2;
        }
      }
    }
  }

  void display() {
    pushMatrix();
    translate(position.x, position.y, position.z);
    //fill(fillColor, 200);
    fill(fillColor);
    box(dimensions.x, dimensions.y, dimensions.z);
    popMatrix();
  }

  void moveDown() {
    position.y += 5;
  }
}
//

// ********************************************************************************
// tab: Maze.pde




class Maze {
  
  Block[][] blocks;
  Block start;
  Block end;
  
  Maze(int size){
    blocks = new Block[size][size];
    
    for (int i=0; i<size; i++){
      for (int j=0; j<size; j++){
        float x = i * 5;
        float y = 0;
        float z = j * 5;
        blocks[i][j] = new Block(x, y, z, 5, 5, 5);
      }
    }
    
    int row = int(random(1, size-1));
    int col = int(random(1, size-1));
    start = blocks[row][col];
    
    for (int i=0; i<size*size*size/10; i++){
      if (!blocks[row][col].visited) blocks[row][col].moveDown();
      blocks[row][col].visited = true;
      
      if (random(0, 1) < 0.5){
        if (random(0, 1) < 0.5 && row > 1) row -= 1;
        else if (row < size-2) row += 1;
      } else {
        if (random(0, 1) < 0.5 && col > 1) col -= 1;
        else if (col < size-2) col += 1;
      }
    }
  }
  
  void update(){
    for (int i=0; i<blocks.length; i++){
      for (int j=0; j<blocks[i].length; j++){
        blocks[i][j].update();
      }
    }
  }
  
  void display(){
    for (int i=0; i<blocks.length; i++){
      for (int j=0; j<blocks[i].length; j++){
        blocks[i][j].display();
      }
    }
  }
  
  void setPlayerAtStart(Player player){
    player.position = PVector.add(start.position, new PVector(0, -15, 0));
  }
}
//

// ********************************************************************************
// tab: Player.pde




class Player extends QueasyCam {

  PVector dimensions;

  PVector velocity;
  PVector gravity;
  boolean grounded;

  Player(PApplet applet) {
    super(applet);
    speed = 0.04;
    dimensions = new PVector(1, 3, 1);
    velocity = new PVector(0, 0, 0);
    gravity = new PVector(0, 0.01, 0);
    grounded = false;
  }

  void update() {
    velocity.add(gravity);
    position.add(velocity);

    PVector c1 = new PVector(); // ********************************************************************************
    PVector v2 = new PVector(); // ********************************************************************************
    PVector.mult(velocity, 16, v2 ); // ********************************************************************************
    PVector.add(position, v2, c1 ); // ********************************************************************************

    pushMatrix();
    translate(c1.x, c1.y, c1.z); // ********************************************************************************
    box(.6);   // ********************************************************************************
    popMatrix();// ********************************************************************************

    if (grounded && keyPressed && key == ' ') {
      //key=0;
      grounded   = false;
      velocity.y = -0.5;
      position.y -= 0.1;
    }
  }
}
//

// End of joined file. ********************************************************************************

I see. In the example, class Player extends QuesyCam.

For a third-person camera, you can separate the player class from the camera class. Move the player around the space. The camera should then react to the player position and “follow.” The tricky move (which I haven’t tried) is how to react to player the turning. The simplest version might be to simply set the camera location to a defined point behind the player every time the player location / orientation updates.

1 Like