0
\$\begingroup\$

After making some progress in my game, I was looking around for camera scrolling methods to use for my TDS game and found one that I thought would work well for it.

This is an example of what sort of scrolling I want: enter image description here

The creator of the document called it a target-focus camera (I do not know if there is an official name for it), and I was wondering how to create camera scrolling like that featured in the example and if anyone has examples that I can look at to see how it works and such.

Please note that there are quite a few things in here that will be removed due to not being needed. Most of them are the "is never used" stuff.

Player class.

    import java.awt.*;
    import javax.swing.*;
    import java.awt.image.*;
    import javax.imageio.*;
    import java.io.*;
    import java.awt.event.MouseEvent;
    import java.lang.Math;

   public class Player 
    {
    // prototype of the mouse location information.
     PointerInfo a = MouseInfo.getPointerInfo();
     Point b = a.getLocation();
     int mX = (int)b.getX();
     int mY = (int)b.getY();



     private int xPos, yPos, r;
     int dist = 1;
     //information on what part of the world the player is at.
     private int secX,secY;





private int dx,dy;
//speed will depend on what ship Player is using and what items it has.
private int speed;
//   private int xLoc, yLoc;
//  private Sector location;



//prototype variables to dictate where the camera will center
int cX = (mX - xPos) * dist + xPos;
int cY = (mY - yPos) * dist + yPos;

private String userName;

 private Ship ship;
// private Weapon weapon;
// private Engine engine;
private boolean left;
private boolean right;
private boolean up;
private boolean down;
//  private boolean firing;

private Color color1;
private Color color2;
// Variable to use when finding the angle needed to turn.
private double angleToTurn;
double mx, my;

public Player(Ship s){
 setShip(s);
 //  x = GameData.WIDTH / 2;
  //  y = GameData.HEIGHT / 2;
 xPos = s.getX();
 yPos = s.getY();
 r = s.getR();


    dx = 0;
    dy = 0;
    speed = 5;


    color1 = Color.WHITE;
    color2 = Color.RED;

  //  firing = false;
  //  firingTimer = System.nanoTime();
  //  firingDelay = 200;

  //   recovering = false;
  //  recoveryTimer = 0;

 //   score = 0;
    }

public void setShip(Ship s){
ship = s;
}



//  public int getXLoc(){return xLoc;}
 //   public int getYLoc(){return yLoc;}
 //   public Sector getLocation(){return location;}
public int getx() { return xPos; }
public int gety() {return yPos;}
public int getr() { return r; }

 public void setLeft(boolean b) { left = b; }
 public void setRight(boolean b) { right = b; }
 public void setUp(boolean b) { up = b; }
 public void setDown(boolean b) { down = b; }


 //TODO Add controls for mouse based on what class of ship it is.   
  public void update() {

    if(left) {
        dx = -speed;
    }
    if(right) {
        dx = speed;
    }
    if(up) {
        dy = -speed;
    }
    if(down) {
        dy = speed;
    }

    xPos += dx;
    yPos += dy;

    if(xPos < ship.getX2() ){ 

      xPos = ship.getX2();
   } 
    if(yPos    < ship.getY2()  ){ 
      yPos = ship.getY2();

    } 


    if(xPos > GameData.WIDTH - r){xPos = GameData.WIDTH - r;
     }
    if(yPos > GameData.HEIGHT - r){
    yPos = GameData.HEIGHT - r;

    } 

    dx = 0;
    dy = 0;

    // firing
    /*
     if(firing){long elapsed = (System.nanoTime() - firingTimer) / 1000000;
        GamePanel.bullets.add(new Bullet(270, x, y));
     if(elapsed > firingDelay) {



        firingTimer = System.nanoTime();



        }






    }
   */



    /*
    if(recovering) {
        long elapsed = (System.nanoTime() - recoveryTimer) / 1000000;
        if(elapsed > 2000) {
            recovering = false;
            recoveryTimer = 0;
        }
    }
    */


}

 public void draw(Graphics2D g) {


    g.drawImage(ship.buff, xPos, yPos, null);


    /*
         g.setColor(color1);
        g.fillOval(x - r, y - r, 2 * r, 2 * r);

        g.setStroke(new BasicStroke(3));
        g.setColor(color1.darker());
        g.drawOval(x - r, y - r, 2 * r, 2 * r);
        g.setStroke(new BasicStroke(1));
    */


}


public void moveToNextLoc(Sector m){}

//TODO add mouse rotation to the player (to start out with)
}

GameData

import javax.swing.JPanel;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.util.*;
//import javax.swing.*;
//import javax.imageio.*;
//import java.io.*;

import java.awt.event.MouseEvent;
//import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;


public class GameData extends JPanel implements Runnable, KeyListener, MouseMotionListener
{
// instance variables - replace the example below with your own
  //   MouseListener mouse = new MouseMotionListener();
  Point playerPoint;
  Point turretPoint;

public static int WIDTH = 400;
public static int HEIGHT = 400; // stuff here may change
public static int MINWIDTH = 0;
public static int MINHEIGHT = 0; 
private int FPS = 30;
// private double averageFPS;
private Thread thread;
private boolean running;

private BufferedImage image;
private Graphics2D g;


public static Player userPlayer;
public static ArrayList<Bullet> bullets;
public static ArrayList<Text> texts;




 //  float angle = (float)(Math.atan2(userPlayer.gety() - mouse.y, userPlayer.getx() - mouse.x));
 public GameData() {
 super();
 setPreferredSize(new Dimension(WIDTH, HEIGHT));
 setFocusable(true);
 requestFocus();

 }

public void addNotify() {
super.addNotify();
if(thread == null) {
thread = new Thread(this);
thread.start();
}
addKeyListener(this);
}

public void run() {

this.requestFocus();
running = true;

image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
g = (Graphics2D) image.getGraphics();
g.setRenderingHint(
 RenderingHints.KEY_ANTIALIASING,
 RenderingHints.VALUE_ANTIALIAS_ON);
 g.setRenderingHint(
 RenderingHints.KEY_TEXT_ANTIALIASING,
 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);



userPlayer = new Player(sampleShip);
//bullets = new ArrayList<Projectile>();
//enemies = new ArrayList<Enemy>();
//powerups = new ArrayList<PowerUp>();
//explosions = new ArrayList<Explosion>();
texts = new ArrayList<Text>();



long startTime;
long URDTimeMillis;
long waitTime;
long totalTime = 0;

int frameCount = 0;
int maxFrameCount = 30;

long targetTime = 1000 / FPS;

//hue = 0;

// GAME LOOP
while(running) {

startTime = System.nanoTime();

gameUpdate();
gameRender();
gameDraw();

URDTimeMillis = (System.nanoTime() - startTime) / 1000000;

waitTime = targetTime - URDTimeMillis;

try {
Thread.sleep(waitTime);
}
catch(Exception e) {
}

frameCount++;
if(frameCount == maxFrameCount) {
//   averageFPS = 1000.0 / ((totalTime / frameCount) / 1000000);
 frameCount = 0;
totalTime = 0;
}

}

g.setColor(new Color(0, 100, 255));
g.fillRect(0, 0, WIDTH, HEIGHT);
g.setColor(Color.WHITE);
g.setFont(new Font("Century Gothic", Font.PLAIN, 16));
String s = "G A M E   O V E R";
int length = (int) g.getFontMetrics().getStringBounds(s, g).getWidth();
g.drawString(s, (WIDTH - length) / 2, HEIGHT / 2);
//  s = "Final Score: " + userPlayer.getScore();
length = (int) g.getFontMetrics().getStringBounds(s, g).getWidth();
g.drawString(s, (WIDTH - length) / 2, HEIGHT / 2 + 30);
gameDraw();

}

private void gameUpdate() {

 // new wave


  // create enemies
  /*
   if(waveStart && enemies.size() == 0) {
   createNewEnemies();
   }
   */
  // player update
  userPlayer.update();

  // bullet update
  /*
   for(int i = 0; i < bullets.size(); i++) {
   boolean remove = bullets.get(i).update();
   if(remove) {
   bullets.remove(i);
   i--;
   }
   }
   */




  /*
   // enemy update
   for(int i = 0; i < enemies.size(); i++) {
   enemies.get(i).update();
   }
   */
  // powerup update

  /*
   for(int i = 0; i < powerups.size(); i++) {
   boolean remove = powerups.get(i).update();
   if(remove) {
   powerups.remove(i);
   i--;
   }
   }
   */

  /*
   * 
   // explosion update
   for(int i = 0; i < explosions.size(); i++) {
   boolean remove = explosions.get(i).update();
   if(remove) {
   explosions.remove(i);
   i--;
   }
   }

   */


  // text update

  for(int i = 0; i < texts.size(); i++) {
    boolean remove = texts.get(i).update();
    if(remove) {
      texts.remove(i);
      i--;
    }
  }

  // bullet-enemy collision

  /*
   for(int i = 0; i < bullets.size(); i++) {

  Bullet b = bullets.get(i);
  double bx = b.getx();
  double by = b.gety();
  double br = b.getr();

   for(int j = 0; j < enemies.size(); j++) {

   Enemy e = enemies.get(j);
   double ex = e.getx();
   double ey = e.gety();
   double er = e.getr();

   double dx = bx - ex;
   double dy = by - ey;
   double dist = Math.sqrt(dx * dx + dy * dy);

   if(dist < br + er) {
   e.hit();
   bullets.remove(i);
   i--;
   break;
   }

   }

   }
   */

   /*
  // check dead enemies
  for(int i = 0; i < enemies.size(); i++) {

  if(enemies.get(i).isDead()) {

   Enemy e = enemies.get(i);

  // chance for powerup
  double rand = Math.random();
  if(rand < 0.001) powerups.add(new PowerUp(1, e.getx(), e.gety()));
  else if(rand < 0.020) powerups.add(new PowerUp(3, e.getx(), e.gety()));
  else if(rand < 0.120) powerups.add(new PowerUp(2, e.getx(), e.gety()));
  else if(rand < 0.130) powerups.add(new PowerUp(4, e.getx(), e.gety()));

  player.addScore(e.getType() + e.getRank());
  enemies.remove(i);
  i--;

  e.explode();
  explosions.add(new Explosion(e.getx(), e.gety(), e.getr(), e.getr() + 30));

  }

  }
  */
  // check dead player
  /*
 if(userPlayer.isDead()) {
 running = false;
 }
 */



  // player-enemy collision
  /*
 if(!player.isRecovering()) {
 int px = player.getx();
 int py = player.gety();
 int pr = player.getr();
 for(int i = 0; i < enemies.size(); i++) {

 Enemy e = enemies.get(i);
 double ex = e.getx();
 double ey = e.gety();
 double er = e.getr();

 double dx = px - ex;
 double dy = py - ey;
 double dist = Math.sqrt(dx * dx + dy * dy);

  if(dist < pr + er) {
   player.loseLife();
  }

  }
  }
  */
  // player-powerup collision
  /*
  int px = player.getx();
  int py = player.gety();
  int pr = player.getr();

  for(int i = 0; i < powerups.size(); i++) {
  PowerUp p = powerups.get(i);
  double x = p.getx();
  double y = p.gety();
  double r = p.getr();
  double dx = px - x;
  double dy = py - y;
  double dist = Math.sqrt(dx * dx + dy * dy);

  // collected powerup
  if(dist < pr + r) {

   int type = p.getType();

   if(type == 1) {
   player.gainLife();
   texts.add(new Text(player.getx(), player.gety(), 2000, "Extra Life"));
   }
   if(type == 2) {
   player.increasePower(1);
   texts.add(new Text(player.getx(), player.gety(), 2000, "Power"));
   }
   if(type == 3) {
   player.increasePower(2);
   texts.add(new Text(player.getx(), player.gety(), 2000, "Double Power"));
   }
   if(type == 4) {
   slowDownTimer = System.nanoTime();
   for(int j = 0; j < enemies.size(); j++) {
   enemies.get(j).setSlow(true);
   }
   texts.add(new Text(player.getx(), player.gety(), 2000, "Slow Down"));
   }

   powerups.remove(i);
   i--;

   }

   }
   */



    }


     private void gameRender() {

    // draw background
       g.setColor(new Color(0, 100, 255));
       g.fillRect(0, 0, WIDTH, HEIGHT);

       // draw slowdown screen


       // draw player
       userPlayer.draw(g);

       // draw bullet

       /*
        for(int i = 0; i < bullets.size(); i++) {
        bullets.get(i).draw(g);
        }

        // draw enemy
        for(int i = 0; i < enemies.size(); i++) {
        enemies.get(i).draw(g);
        }

        // draw powerups
        /*
        for(int i = 0; i < powerups.size(); i++) {
        powerups.get(i).draw(g);
        }
        */
       /*
        // draw explosions
        for(int i = 0; i < explosions.size(); i++) {
        explosions.get(i).draw(g);
        }

        // draw text
        for(int i = 0; i < texts.size(); i++) {
        texts.get(i).draw(g);
        }



          // draw player lives
          for(int i = 0; i < player.getLives(); i++) {
          g.setColor(Color.WHITE);
          g.fillOval(20 + (20 * i), 20, player.getr() * 2, player.getr() * 2);
          g.setStroke(new BasicStroke(3));
          g.setColor(Color.WHITE.darker());
          g.drawOval(20 + (20 * i), 20, player.getr() * 2, player.getr() * 2);
           g.setStroke(new BasicStroke(1));
           }

           // draw player power
           g.setColor(Color.YELLOW);
           g.fillRect(20, 40, player.getPower() * 8, 8);
           g.setColor(Color.YELLOW.darker());
           g.setStroke(new BasicStroke(2));
           for(int i = 0; i < player.getRequiredPower(); i++) {
           g.drawRect(20 + 8 * i, 40, 8, 8);
           }
           g.setStroke(new BasicStroke(1));

           // draw player score
           g.setColor(Color.WHITE);
           g.setFont(new Font("Century Gothic", Font.PLAIN, 14));
           g.drawString("Score: " + player.getScore(), WIDTH - 100, 30);
           */



              }


            private void gameDraw() {
              Graphics g2 = this.getGraphics();
              g2.drawImage(image, 0, 0, null);
              g2.dispose();
             //  adapter.render(g);
            }


            public void keyTyped(KeyEvent key) {}
            public void keyPressed(KeyEvent key) {
              int keyCode = key.getKeyCode();
              if(keyCode == KeyEvent.VK_LEFT) {
                userPlayer.setLeft(true);
              }
              if(keyCode == KeyEvent.VK_RIGHT) {
                userPlayer.setRight(true);
              }
              if(keyCode == KeyEvent.VK_UP) {
                userPlayer.setUp(true);
              }
              if(keyCode == KeyEvent.VK_DOWN) {
                userPlayer.setDown(true);
              }
              /*
               if(keyCode == KeyEvent.VK_Z) {
               userPlayer.setFiring(true);
               }
               */
            }
            public void keyReleased(KeyEvent key) {
              int keyCode = key.getKeyCode();
              if(keyCode == KeyEvent.VK_LEFT) {
                userPlayer.setLeft(false);
              }
              if(keyCode == KeyEvent.VK_RIGHT) {
                userPlayer.setRight(false);
              }
              if(keyCode == KeyEvent.VK_UP) {
                userPlayer.setUp(false);
              }
              if(keyCode == KeyEvent.VK_DOWN) {
                userPlayer.setDown(false);
              }
              /*
               if(keyCode == KeyEvent.VK_Z) {
               userPlayer.setFiring(false);
               }
               */
            }
            // for mouse. MOUSE PROGRESS FTW!

            public void mouseClicked(MouseEvent e) {

            }
            public void mouseExited(MouseEvent e) {

            }
            public void mouseEntered(MouseEvent e) {


            }
            public void mousePressed(MouseEvent e) {
    playerPoint = e.getPoint();
     float angle = (float)(Math.atan2(userPlayer.gety() - playerPoint.y, userPlayer.getx() - playerPoint.x));
    int x = e.getX();
    int y = e.getY();
            }
            public void mouseReleased(MouseEvent e) {
     playerPoint = null;

            }
            public void mouseMoved(MouseEvent e) {
              int x = e.getX();
              int y = e.getY();
              float angle = (float)(Math.atan2(userPlayer.gety() - playerPoint.y, userPlayer.getx() - playerPoint.x));
   // ^^^ p used to be mouse so watch out on that.
    }
            public void mouseDragged(MouseEvent e) {
  turretPoint = e.getPoint();
    float angle = (float)(Math.atan2(userPlayer.gety() - turretPoint.y, userPlayer.getx() - turretPoint.x));
            }   


            Ship sampleShip = new Ship(83,83,83,0,0,false,"C:\\Users\\Kenny\\Desktop\\ProjectProto\\ProtoGame\\Proto\\Images\\Ships\\ATN_FIGHTER_LOWRES.PNG");
           //TODO: have file directory change to allow other devices to be able to get the files.
    }

Camera.

  import java.awt.*;





  public class Camera{
  private int x,y;
  private Player player;
  private int xPos, yPos;
  MapSector sec;
   GameData data;
  public  int xmax = sec.XSIZE;
  public int ymax = sec.YSIZE;
  public int dist = 1;
  // prototype of the mouse location information.
  private  PointerInfo a;
  private Point b;
  private int mX,mY,cX,cY;
 private int dirX

  public int xView;
  public int yView;

  //Location of the center of an Image
  private  int iX, iY;
  public Camera(int x, int y, Player player, int xPos,int yPos){
    this.x = x;
    this.y = y;
    this.player = player;
    xPos = player.getx();
    yPos = player.gety();
    xView = data.WIDTH;
    yView = data.HEIGHT;
    iX = player.ship.img.getCenterX();
    iY = player.ship.img.getCenterY();
  }

  public int getX(){return x;}
  public int getY(){return y;}

  public void setX(int x){this.x = x;}
  public void setY(int y){this.y = y;}

  public void updateCamera(){
    a = MouseInfo.getPointerInfo();
    b = a.getLocation();
    mX = (int)b.getX();
    mY = (int)b.getY();
    cX = (mX - xPos) * dist + xPos;
    cY = (mY - yPos) * dist + yPos;
  }

  public void defaultCamera(){
    cX = iX;
    cY = iY;
  }
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Lets start of by defining a few variables. Lets say that o is the position of the player, d is the point that we want the player to look at, c is the position of the camera and dist is how much do we want to move c towards d, were dist is a float between 0 and 1.

float cX = (dX - oX) * dist + oX;
float cY = (dY - oY) * dist + oY;

In case you want to move smoothly towards that point you can use the following method, where L the previous camera position, C is the one we just calculates using the method above, out is the smooth position, and minDist the distance from C that when reached the camera will stop moving:

float dirX = (cX - lX);
float dirY = (cY - lY);
float length = (float)Math.sqrt(dirX * dirX + dirY * dirY) - 0.6f;
dirX /= length;
dirY /= length;
dirX *= delta;
dirY *= delta;
float outX = lX + dirX;
float outY = lY + dirY;

In sum, all of the above would look something like this:

float cX = (dX - oX) * dist + oX;
float cY = (dY - oY) * dist + oY;

float dirX = (cX - lX);
float dirY = (cY - lY);
float length = (float)Math.sqrt(dirX * dirX + dirY * dirY) - 0.6f;

float outX = lX;
float outY = lY;

if(length > minDist) {
    dirX /= length;
    dirY /= length;
    dirX *= delta + 0.5f;
    dirY *= delta + 0.5f;
    outX = lX + dirX;
    outY = lY + dirY;
}

Now, in your case there are a few things you should change:

public void updateCamera(){
 a = MouseInfo.getPointerInfo();
 b = a.getLocation();
 mX = (int)b.getX();
 mY = (int)b.getY();
 cX = (mX - xPos) * dist + xPos;
 cY = (mY - yPos) * dist + yPos;

 float dirX = (cX - lX);
 float dirY = (cY - lY);
 float length = (float)Math.sqrt(dirX * dirX + dirY * dirY) - 0.6f;

 float outX = lX;
 float outY = lY;

 if(length > minDist) {
     dirX /= length;
     dirY /= length;
     dirX *= delta + 0.5f;
     dirY *= delta + 0.5f;
     outX = lX + dirX;
     outY = lY + dirY;
 }
 x = (int)outX;
 y = (int)outY;
}

Also, before you ask: I have taken a look at your code and haven't found the camera being used anywhere. So I thought you were having problems with that as well. Here is a solution: You can add an x and y offset to the position of every thing that is rendered, like entities and the player, but not the UI. And in each update you set that x and y offset to the negative x and y position of the camera. In that way the world will move around, creating the feeling that there is a camera.

\$\endgroup\$
1
  • \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$
    – DMGregory
    Commented Oct 10, 2018 at 15:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .