[Game] How do i make a camera.class?

Hello i am making a game like pokemon and im am stuck on how to scroll the map I have used g.translate(offsetX,offsetY); but if i want to add ai’s later on to follow the player it would not work well
can someone help me out?

if you can add me on skype : tommohawkaction

I mean a Player camera here is what my game is so far http://www.mediafire.com/download/js5na4pmy6eb8w5/test.jar I have made the map move by g.translate(xpos,ypos); however the player does not physically move what will make it hard to do ai so can you help me out.
I want to make a nice camera for the player thanks

if this doesnt make sense its because i had to qoute it from another website becuase they couldn’t help me
The game is using the pure java library
Source code for game is here
http://www.mediafire.com/download/46i80dfhdsnj0k4/TileGame.rar

For what it’s worth, here’s my Camera class. If any of it’s of use to you (or anyone), feel free to use it for whatever.

It assumes certain things about what a Person is in your game (namely, that it’s called Person, and it has getCenterX() and getCenterY() methods that tell you where the middle of the Person’s graphic is relative to the game board/level). It also assumes that you have a thing called GameBase that has getCnvW() and getCnvH() to get the width and height of the drawable area of the game window, a variable brd to keep track of the current Board/Level, a variable to keep track of the main player character, and some numbers to symbolize directions (north, south, east, west) which can be sent in to the board/level’s getBorder(directionNumber) method to find out where the edges of the level are.

package GameBase;

import Person.Person;

/**
 *
 * @author jemoore
 */
public class Camera {
    private static double x = 0;
    private static double y = 0;
    
    public static int getX() {
        return (int) x;
    }
    public static int getY() {
        return (int) y;
    }
    
    /*
     * Move Camera's top-left corner a given number of pixels,
     * but only if this wouldn't move the camera past the board edges.
     */
    public static void move(double x, double y) {
            //System.out.print("Camera moves from " + Camera.x + "," + Camera.y + " to ");
        set(Camera.x + x, Camera.y + y);
            //System.out.println(Camera.x + "," + Camera.y);
    }
    
    /*
     * Set Camera to have its top-left corner be a given position,
     * but only if this wouldn't move the camera past the board edges.
     */
    public static void set(double x, double y) {
        Board brd = GameBase.brd;
        int cnvW = GameBase.getCnvW();
        int cnvH = GameBase.getCnvH();
        double marginW = (cnvW - brd.getW())/2; //handle blank margins if canvas wider than board
        double marginH = (cnvH - brd.getH())/2;
            //System.out.print("Camera: cnvW,H="+cnvW+","+cnvH+" marginW,H="+marginW+","+marginH+" input x,y="+x+","+y);
        if(marginW < 0) { marginW = 0; }
        if(marginH < 0) { marginH = 0; }
        if(x + cnvW > brd.getBorder(GameBase.DIR_E) + marginW) { //can't move right
            x = brd.getBorder(GameBase.DIR_E) - cnvW + marginW;
        }
        if(x < brd.getBorder(GameBase.DIR_W) - marginW) { //can't move left
            x = brd.getBorder(GameBase.DIR_W) - marginW;
        }
        if(y < brd.getBorder(GameBase.DIR_N) - marginH) { //can't move up
            y = brd.getBorder(GameBase.DIR_N) - marginH;
        }
        if(y > brd.getBorder(GameBase.DIR_S) - cnvH + marginH) { //can't move down
            y = brd.getBorder(GameBase.DIR_S) - cnvH + marginH;
        }
        Camera.x = x;
        Camera.y = y;
            //System.out.println(" output x,y="+x+","+y);
    }
    
    /*
     * Reset Camera to be centered on subject's position,
     * but only if this wouldn't move the camera past the board edges.
     */
    public static void centerOnCharacter(Person subject) {
        centerOnCharacter(subject, 0, 0);
    }
    public static void centerOnCharacter(Person subject, double offsetX, double offsetY) {
        int cnvW = GameBase.getCnvW();
        int cnvH = GameBase.getCnvH();
        double newX = subject.getCenterX() - cnvW/2 + offsetX;
        double newY = subject.getCenterY() - cnvH/2 + offsetY;
        Camera.set(newX, newY);
        //System.out.println("cam xy: " + Camera.getDrawX() + ", " + Camera.getDrawY());
    }
    
    public static void centerOnMain() {
        centerOnCharacter(GameBase.mainPlayer);
    }
}

thanks but would this work for a 2d tile array have a look at my source code please and you will see what i mean also how does this make the screen move???

Ah, I’ve been typing up an answer for you! Here’s how I do the making the screen move:

My game’s camera works more or less like Pokémon’s, with the player (or whoever we’re looking at right now) staying at the center of the screen. The player and everybody else keep track of their own position relative to the level they’re on. (My game calls these levels “Boards”, but you could call them levels, maps, boards, areas, whatever you want.) If a Person (a character) is at x: 0, y: 0, that means they’re at the very top left of the board.

When you update the screen, draw everything with an offset equal to the camera’s x and y. Example:

drawThing(thing.getImage(), thing.x - Camera.getX(), thing.y - Camera.getY())

Looking at your source code, you would want to change this line in your Player.render(Graphics g):

g.drawImage(cheapAnimation(), x, y, width, height, null);

to something like this:

g.drawImage(cheapAnimation(), x - Camera.getX(), y - Camera.getY(), width, height, null);

This would mean that as you move the camera to the right, the player image moves to the left, and vice versa. You can do the same thing to your maps as well, so that as the camera moves, they move. If you want to keep a player or AI at the center of the screen, move the camera to their current position. Mine accomplishes this by Camera.centerOnCharacter(Person subject). If I want it to stay locked on to the player, I use that once each time I’m getting ready to draw everything. You could do the same (or modify my code if you like) by changing Person to Player and giving Player getCenterX() and getCenterY() methods so that the Camera knows where the middle of the player is, so that it can move to it.

How would I add it to my game could you please explain more?
I won’t respond anymore now because its like 2am and im tried so add me on skype would be good : tommohawkaction
if not please could you explain this and look into my source code for how i could do it. thanks a bunch man

package com.hawk.tilegame.level.LevelGen;

import java.awt.Graphics;
import java.util.Random;

import com.hawk.tilegame.entity.Tile;
import com.hawk.tilegame.gfx.SpriteSheet;
import com.hawk.tilegame.window.Window;

public class LevelGen {
	
	/*
	 * Grass < 100
	 * Dirt > 100
	 * Stone > 110
	 */

	public static SpriteSheet tileSheet = new SpriteSheet("SpriteSheet/tileSheet.png",
			16, 16);

	public static int tileGenAmount = 40;
	static int map[][] = new int[tileGenAmount][tileGenAmount];
	public static Tile mapLevel[][] = new Tile[tileGenAmount][tileGenAmount];
	public static Random r = new Random();
	public static int randomMapNumber;
	public static int sizeOfBlock = 12;
	public LevelGen(){
		// Random Generation
		for (int i = 0; i < tileGenAmount; i++) {
			for (int j = 0; j < tileGenAmount; j++) {
				if(j == 0|| j == tileGenAmount -1 || i == 0 && j <= tileGenAmount || i == tileGenAmount -1 && j <= tileGenAmount){
					if(map[j][i] == 0){
						map[j][i] = 106;
					}
				}
				randomMapNumber = r.nextInt(106);
				if(map[j][i] == 0){
				map[j][i] = randomMapNumber;
				}
				
			}
		}
	}
	public static void createWorld(Graphics g) {
		for (int i = 0; i < tileGenAmount; i++) {
			for (int j = 0; j < tileGenAmount; j++) {
				if (map[j][i] <= 100) { //Grass
					if (mapLevel[j][i] == null) {
						mapLevel[j][i] = new Tile(tileSheet.images[0], i * sizeOfBlock
								* Window.SCALE, j * sizeOfBlock * Window.SCALE,
								sizeOfBlock * Window.SCALE, sizeOfBlock * Window.SCALE);
						break;
					}
				}
				if (map[j][i] > 100 && map[j][i] <= 105) { // Dirt
					if (mapLevel[j][i] == null) {
						mapLevel[j][i] = new Tile(tileSheet.images[1], i * sizeOfBlock
								* Window.SCALE, j * sizeOfBlock * Window.SCALE,
								sizeOfBlock * Window.SCALE, sizeOfBlock * Window.SCALE);
						break;
					}
				}
				if (map[j][i] > 105 && map[j][i] <= 106) { //Stone
					if (mapLevel[j][i] == null) {
						mapLevel[j][i] = new Tile(tileSheet.images[2], i * sizeOfBlock
								* Window.SCALE, j * sizeOfBlock * Window.SCALE,
								sizeOfBlock * Window.SCALE, sizeOfBlock * Window.SCALE);
						break;
					}
				}
			}
		}
		if (mapLevel[tileGenAmount-1][tileGenAmount-1] != null) {
			renderMap(g);
		}
	}

	private static void renderMap(Graphics g) {
		for (int i = 0; i < tileGenAmount; i++) {
			for (int j = 0; j < tileGenAmount; j++) {
				mapLevel[j][i].tick();
				mapLevel[j][i].render(g);
			}
		}
	}
}

again thanks :slight_smile: I shall look more into this tomorrow could you explain a bit more how the camera kinda works as i am new to the camera side of things also could you rate my code /10 please and the game out of /10 im just curios

Take a look when you’ve had some sleep; I think you might be able to figure it out. Sorry I won’t be analyzing your code or game, but here are some hints on the camera:

The Camera represents where the person who is playing the game is. When that person moves, the things in the game world don’t. When the things in the game world move, the person playing does not. But, when you move, it looks like everything else does, because they are being “drawn” through your eyes and in your brain based both on where they are and where you are. So, when we draw something in the game, we should draw it based both on where it is and where the human player is. Thus, g.drawImage(cheapAnimation(), x - Camera.getX(), y - Camera.getY(), width, height, null); . The x and y are where the thing is, and the Camera.getX() and Camera.getY() are where the human player is looking from.

Your Tile.render() should draw its image with its x and y minus the Camera’s x and y. This way, if the camera moves to the right, the level gets drawn to the left, for example, making it look like your view has moved to the right.

Anything else that should be part of the game world – Player, NPC, whatever – should also draw its images minus the Camera’s x and y.

Anything that should be part of the HUD, rather than the game world, such as menus, should not draw minus the Camera’s x and y. This way, even if the Camera moves, the menus and stuff will stay in the same spot on the screen.

The principle here is that when something moves (changes its x and y position), that means that it has moved around in the game world. The Player and the Tiles don’t move when the Camera does. They stay in the same spot. But, when the Camera moves, while their x and y stay the same, they get drawn with an offset based on where the Camera is. This way, if you move your Camera, you don’t have to move all your Players and Tiles, but it’s like the person playing the game moves around instead.

Thanks mate just woke up and i think i have figured it out.
Thanks again :smiley:

Hello again im not completely sure how you make the camera directly on the player i looked at your source code but the public static void centerOnCharacter(Person subject, double offsetX, double offsetY) didn’t make much sense to me

DONE IT ;D ;D ;D ;D

How would you do animation like thorugh bufferedimage arrays?

Glad to hear you got it working! So, if you centerOnCharacter(), it snaps the camera over to the Player you specify then? Obviously, you had to do a bit of converting to make it work with your classes.

For updating the screen, here’s the approach I took:

  1. Every time we are ready to update the screen, loop through all things that should be drawn and call their draw() methods.
  2. In a draw() method, you do the work of picking the right image and drawing it. I believe you already do this.
  3. When everything has drawn, flip the buffer strategy or whatever it is you need to do to put the new drawing on the screen.

For individual animations, here’s the approach I took:

  1. I made an Animation class.
  2. Each thing that should draw has at least one instance of the animation class. Tiles would usually only need one, Players will need one for each pose and direction combination they can have (for example, walking west, idling facing north).
  3. When a thing draws, it checks which animation it is currently using (which can be changed when, for example, a Player turns to a different direction or changes poses)…
  4. …then, it tells that animation to draw its next frame, giving it the x and y location minus the Camera’s x and y. The animation grabs a BufferedImage, which it then draws.
  5. This brings us to Animations themselves:
    5a. An animation needs to know several things. First, which images it needs to use (or which directory to automatically load them from).
    5b. It needs to know how many frames per second to play and whether it should go back to the first frame when it’s finished and loop around, or if it should stay at the last frame when it’s done.
    5c. If you’re allowing the players to pause, it needs to keep track of when it was last paused, last resumed, and be able to take that into account to avoid skipping frames, because…
    5d. …it needs to keep track of when it handed over the last frame to be drawn and what number that frame was…
    5e. …so it can decide what frame to give you when you ask it for the nextFrame(). It compares the current time, the lastFrameTime, and playRate to decide whether to give you the last frame again or move on to the next one. If it’s time for a new frame, it needs to make sure that it either loops back to the beginning or gives you the final frame again if it’s at the final frame, depending on whether it is a looping animation or not.

I’ve found this to be a pretty convenient way of doing things, once I got it working.

thanks I managed to get animation working i am now stuck this
private static void renderMap(Graphics g) {
for (int i = 0; i < tileGenAmount; i++) {
for (int j = 0; j < tileGenAmount; j++) {
mapLevel[j][i].tick();
mapLevel[j][i].render(g);
}
}
}
I create the map using another method however I want it so i can render a massive map without fps lag so i want to limit how many tiles need rendering I have tried a few thing but nothing has work any ideas?

You don’t have to just get the players x,y and center it on screen translate with Graphics!

I want to be able to limit what renders on the screen
but I want to see the graphics what the play can see

I did something similar with my tile grid. (I have the map as a single image, but a bunch of grid outlines placed over it that slowed things down on large maps.)

You need to, before drawing each tile (either in renderMap() or in Tile.render()), calculate whether the tile is on the screen and only draw it if it is.

The information you need:

  1. The tile’s sides (the x of the left and of the right, the y of the top and of the bottom) or the x,y of a corner and the width and height for you to calculate this from.
  2. The width and height of the draw area. I.e., if your window is 1200x800, this might be something like 1192x780. The space that graphics go. This is the cnvW and cnvH (from GameBase.getCnvW() and getCnvH()) in the Camera code I gave you, but you may have it labeled something different or may need to calculate it still by subtracting the frame from the window or something.
  3. The position of the Camera. In the Camera code I gave you, I think it’s the Camera.getX() and Camera.getY() that give you the top-left corner of the area you want to draw (i.e. the camera location).

Using that, can you figure out how to tell if a Tile is at least partially on-screen before bothering to draw it?

thank you it worked again :slight_smile: You are just amazing dude thanks again

Okay i was wondering how would i create like a biome in my game like one whole area covered in sand and the rest in grass ect
I am using a 2d array for rendering

Glad to help!

I’ll let someone else answer your latest question though. Since you’re asking multiple questions on one thread, try editing/modifying your original post so you can change the title to reflect your current problem. That way, people know to stop by if they think they can help.

And you can always Appreciate my posts to get me imaginary medals if they’ve been helpful. :wink:

If you have a separate question, then please start a new thread so we can keep the forum organized and easily accessible :point: