2D game camera logic

I am trying to implement a camera for a 2D game that I am making ... The goal is for the cam to keep the player centered and sprites relative to the camera.

To get the normalocity status information , I tried to start with a simple one by running the Camera Test project, where I simulated a camera by drawing a sprite on a JPanel and moving the camera object (which is a JPanel) around and setting the x, y sprite to this.

The camera, as I said, is JPanel ... and I added a "world" which is a class with x,y of 0,0 and w=1000, h=1000 . I included the location of the sprites relative to the world, as well as the camera. When I move the camera up, the sprite moves down, and the player stays in the middle as expected.

enter image description here

But if I keep clicking, the sprite seems to continue to draw on its own.

enter image description here

My questions:

  • Am I on the right track when implementing the camera with the code below?
  • Why does the sprite start painting itself there? It should just fade with viewPort / JPanel

Thanks!

Now that PaintComponent(g) added, now my gray JPanel bg is gray. Is this supposed to happen?

enter image description here


EDIT : SSCCE of my program:

Main class:

 import java.awt.Dimension; import java.awt.Toolkit; import javax.swing.JFrame; @SuppressWarnings("serial") public class MainSSCCE extends JFrame { static MainSSCCE runMe; public MainSSCCE() { JFrame f = new JFrame("Camera Test"); CameraSSCCE cam = new CameraSSCCE(0, 0, 500, 500); f.add(cam); f.setSize(cam.getWidth(), cam.getHeight()); f.setVisible(true); f.setResizable(false); f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize(); f.setLocation( (screensize.width - f.getWidth())/2, (screensize.height - f.getHeight())/2-100 ); } public static void main(String[] args) { runMe = new MainSSCCE(); } } 

Camera class:

 import java.awt.Color; import java.awt.Graphics; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import javax.swing.JPanel; //Camera is the JPanel that will draw all objects... each object location will be in relation to the World public class CameraSSCCE extends JPanel implements KeyListener { //add world to camera... private static final long serialVersionUID = 1L; private int camX, camY, camH, camW; private SpriteSSCCE sprite; private PlayerSSCCE player; private WorldSSCCE world; public CameraSSCCE(int x, int y, int w, int h) { camX = x; camY = y; camW = w; camH = h; sprite = new SpriteSSCCE(this, 300, 300, 20, 20); player = new PlayerSSCCE(this, camW/2, camH/2, 25, 40); world = new WorldSSCCE(this, 0, 0, 1000, 1000); addKeyListener(this); setFocusable(true); } public int getWidth() { return camW; } public int getHeight() { return camH; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); //cam is 500 x 500 g.setColor(Color.gray); g.fillRect(camX, camY, camW, camH); //draw sprite at JPanel location if in camera sight if (((sprite.getX()-camX) >= camX) && ((sprite.getX()-camX) <= (camX+camW)) && ((sprite.getY()-camY) >= camY) && ((sprite.getY()-camY) <= (camY+camH))) { g.setColor(Color.green); g.fillRect(sprite.getX()-camX, sprite.getY()-camY, 20, 20); //Cam Sprite Location g.setColor(Color.white); g.drawString("Camera Sprite Location: (" + (sprite.getX()-camX) + ", " + (sprite.getY()-camY) + ")", sprite.getX()-camX, sprite.getY()-camY); } //Player location (center of Camera... Camera follows player) g.setColor(Color.cyan); g.fillRect(player.getX()-player.getWidth(), player.getY()-player.getWidth(), player.getWidth(), player.getHeight()); g.setColor(Color.white); //World Sprite Location g.drawString("World Sprite Location: (" + sprite.getX() + ", " + sprite.getY() + ")", sprite.getX(), sprite.getY()); //Cam Player Location g.drawString("Cam Player Location: (" + (camW/2-player.getWidth()) + ", " + (camH/2-player.getHeight()) + ")", camW/2-player.getWidth(), camH/2-player.getHeight()); } public void keyPressed(KeyEvent e) { //move camera right in relation to World if (e.getKeyCode() == KeyEvent.VK_RIGHT) { camX+=5; } //move camera left in relation to World if (e.getKeyCode() == KeyEvent.VK_LEFT) { camX-=5; } //move camera up in relation to World if (e.getKeyCode() == KeyEvent.VK_UP) { camY-=5; } //move camera down in relation to World if (e.getKeyCode() == KeyEvent.VK_DOWN) { camY+=5; } repaint(); } public void keyReleased(KeyEvent e) {} public void keyTyped(KeyEvent e) {} } 

World class:

 public class WorldSSCCE { private int x, y, w, h; private CameraSSCCE camera; public WorldSSCCE(CameraSSCCE cam, int x, int y, int w, int h) { camera = cam; this.x = x; this.y = y; this.w = w; this.h = h; } public int getX() { return this.x; } public int getY() { return this.y; } public int getWidth() { return this.w; } public int getHeight() { return this.h; } } 

Player Class:

 import java.awt.Dimension; public class PlayerSSCCE { private int x, y, w, h; private CameraSSCCE cam; public PlayerSSCCE(CameraSSCCE cm, int x, int y, int w, int h) { cam = cm; this.x = x; this.y = y; this.w = w; this.h = h; } public int getX() { return this.x; } public int getY() { return this.y; } public int getWidth() { return this.w; } public int getHeight() { return this.h; } public void setX(int val) { this.x += val; } public void setY(int val) { this.y += val; } } 

Sprite Class:

 import java.awt.Color; import java.awt.Graphics; public class SpriteSSCCE { private int xLoc, yLoc, width, height; private CameraSSCCE world; public SpriteSSCCE(CameraSSCCE wld, int x, int y, int w, int h) { xLoc = x; yLoc = y; width = w; height = h; world = wld; } public int getX() { return xLoc; } public int getY() { return yLoc; } public int getWidth() { return width; } public int getHeight() { return height; } public void paintComponent(Graphics g) { g.setColor(Color.green); g.fillRect(xLoc, yLoc, width, height); } } 
+4
source share
2 answers

1) You did not complete the drawing chain by calling super.paintComponent(g) in paintComponent(..) :

 @Override protected void paintComponent(Graphics g) { super.paintComponent(g); //do drawing here } 

According to Java docs :

protected void paintComponent(Graphics g)

In addition, if you are not making a super call, you must observe the opaque property, that is, if this component is opaque, you must completely fill the background with an opaque color. Unless you read the opaque property, you are likely to see visual artifacts.

2) Also pay attention to the @Override annotation added by me and the fact that I changed the public modifier to protected , because this is that the access level is defined as in the implementation class, which we must observe, if only for a certain reason.

3) Also Swing uses Keybinding to read How to use key bindings

4) Also read Concurrency in Swing , namely Thread Dispatch Thread , which determines that all swing components will be created on EDT using the SwingUtillities.invokeXXX(..) block:

 SwingUtilities.invokeLater(new Runnable() { @Override public void run() { //create and manipulate swing components here } }); 

5) You extend the JFrame class and create an instance, this is not what you need, but remove the extends JFrame from the class declaration:

 public class MainSSCCE extends JFrame { //<-- Remove extends JFrame public MainSSCCE() { JFrame f = new JFrame("Camera Test");//<-- instance is created here } } 
+3
source

Your world is a virtual area larger than the screen (or your jpanel for something important). The positions of all objects relative to the world. Let them be called absolute coordinates.

Your camera is a small rectangular part of the world (your panel). Moving it, you see different parts of the world. If you can move the camera, as in the message you are referring to, then at some point you will not be able to see either the player or the other sprite.

Since your goal is to keep the player in the center of the screen, what does this mean for our world? This means that the player and camera move together in relation to the world.

Given the above, it makes no sense to draw a camera sprite, as in your first screenshot. The camera’s sprite must be either invisible or drawn in the same position using the player’s sprite. It also makes no sense to change the absolute coordinates of the camera without changing the player. These two are moving together. (note this in your keyPressed() methods)

Now, when you draw, you draw from the point of view of the camera (or, in other words, in the coordinate system of the camera). From this point of view, the camera always sees the rectangle (0, 0, cameraWidth, cameraHeight) . This is what you should use when cleaning the gray area. This will fix the problem with the moving background. Since the camera and the player always have the same absolute coordinates, the player will always be in the same place (this is what we want). The remaining sprites will be visible relative to the camera.

For each of them, you translate them into the camera coordinate system when you do (sprite.x - cam.x) and (sprite.y - cam.y). Since they are translated, you only need to check if they are inside the camera rectangle (0, 0, cameraWidth, cameraHeight) . If it's you, go ahead and draw them. If you do not ignore them.

I hope this helps

Note. cameraWidth, cameraHeight - your jpanel sizes

+1
source

Source: https://habr.com/ru/post/1494353/


All Articles