Tiled map with camera following player

Hello, i’m very new to java and have been programing for a few days, and, im a bit stuck…
I want to draw tiles in the background, and make the player move around and have the screen follow the player.
The tiles should be in a fixed XY as the player move around to increase its position XY…

I’ve searched the forum for examples but all links and downloads where outdated/not working and was from <2003…


import java.applet.Applet;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.net.URL;

public class Game extends Applet implements Runnable, KeyListener
{
	Thread gameloop;
	BufferedImage backbuffer;
	Graphics2D g2d;
	AffineTransform identity = new AffineTransform();
	Player player = new Player();
	
	boolean leftPressed = false;
	boolean upPressed = false;
	boolean rightPressed = false;
	boolean downPressed = false;
	boolean spacePressed = false;
	
    int mapArray[][] = new int[MAP_WIDTH][MAP_HEIGHT];
    static final int MAP_WIDTH = 100;
    static final int MAP_HEIGHT = 100;
    static final int TILE_WIDTH = 32;
    static final int TILE_HEIGHT = 32;
    static final int GRASS_TILE = 0;
    static final int WALL_TILE = 1;
    int tileOffsetX = 0;
    int tileOffsetY = 0;
    int startTileX = 0;
    int startTileY = 0;
    int VIEW_LIMIT_X = 0;
    int VIEW_LIMIT_Y = 0;
    private Image tilesheet;
	
	public static void main(String[] args)
	{
		new Game().run();
	}
	
	public void init()
	{
		gameloop = new Thread(this);
		backbuffer = new BufferedImage(800, 600, BufferedImage.TYPE_INT_RGB);
		g2d = backbuffer.createGraphics();
		setSize(800, 600);
		
		URL urlImage = getClass().getResource("img/tilesheet.png");
		tilesheet = Toolkit.getDefaultToolkit().getImage(urlImage);
		
        // Horizontal walls
        for(int i = 0; i < MAP_WIDTH; i++)
        {
            mapArray[i][0] = WALL_TILE;
            mapArray[i][MAP_HEIGHT-1] = WALL_TILE;
        }
        
		// Fill remaining tiles
		for(int i = 1; i < MAP_WIDTH - 1; i++)
        {
			for(int j = 1; j < MAP_HEIGHT - 1; j++)
			{
            	mapArray[i][j] = GRASS_TILE;
            }
        }
		
		player.setX(800 / 2);
		player.setY(600 / 2);
		
		addKeyListener(this);
	}
	
	public void run()
	{	
		Thread thread = Thread.currentThread();
		
		while (thread == gameloop)
		{
			try
			{
				gameUpdate();
				Thread.sleep(1000 / 60);
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
			
			repaint();
		}
	}

	public void update(Graphics g)
	{
		g2d.setTransform(identity);
        g.translate(-(int)player.getX() + 800 / 2, -(int)player.getY() + 600 / 2);
		
		g2d.setPaint(Color.DARK_GRAY);
		g2d.fillRect(0, 0, getSize().width, getSize().height);
        
        int srcX = 0;
        int tileX = 0;
        int tileY = 0;
       
		
        for(int x = tileOffsetX; x < VIEW_LIMIT_X; x += TILE_WIDTH)	
        {
            tileY = startTileY;
            for(int y = tileOffsetY; y < VIEW_LIMIT_Y; y += TILE_HEIGHT)
            { 
                srcX = mapArray[tileX][tileY]*TILE_WIDTH;
                
                g2d.drawImage(tilesheet, x, y, x+TILE_WIDTH, y+TILE_HEIGHT, srcX, 0, srcX+TILE_WIDTH, TILE_HEIGHT, null);
                tileY++;
            }
            tileX++;
        }
        
        
		g2d.setFont(new Font("Arial", Font.PLAIN, 9));
		g2d.setColor(Color.WHITE);
		g2d.drawString("Player: " + Math.round(player.getX()) + "," + Math.round(player.getY()) , 10, 50);
        
		drawPlayer();
        
		paint(g);
	}
	
	private void gameUpdate()
	{
		if(leftPressed)
		{
			player.setX(player.getX() - 1);
		}
		if(rightPressed)
		{
			player.setX(player.getX() + 1);
		}
		if(upPressed)
		{
			player.setY(player.getY() - 1);
		}
		if(downPressed)
		{
			player.setY(player.getY() + 1);
		}
	}
	
	public void start()
	{
		gameloop.start();
	}

	public void paint(Graphics g)
	{
		g.drawImage(backbuffer, 0, 0, this);
	}
	
	public void drawPlayer()
	{
		g2d.setTransform(identity);
		g2d.translate(player.getX(), player.getY());
		g2d.rotate(Math.toRadians(player.getFaceAngle()));
		g2d.setColor(Color.RED);
		g2d.draw(player.getShape());
	}

	public void keyPressed(KeyEvent e)
	{
		int keyCode = e.getKeyCode();
		switch (keyCode)
		{
			case KeyEvent.VK_ESCAPE:
				stop();
				break;
			case KeyEvent.VK_LEFT:
				leftPressed = true;
				break;
			case KeyEvent.VK_RIGHT:
				rightPressed = true;
				break;
			case KeyEvent.VK_UP:
				upPressed = true;
				break;
			case KeyEvent.VK_DOWN:
				downPressed = true;
				break;
			case KeyEvent.VK_A:
				leftPressed = true;
				break;
			case KeyEvent.VK_D:
				rightPressed = true;
				break;
			case KeyEvent.VK_W:
				upPressed = true;
				break;
			case KeyEvent.VK_S:
				downPressed = true;
				break;
			case KeyEvent.VK_SPACE:
				spacePressed = true;
				break;
		}
		e.consume();
	}
	
	public void keyReleased(KeyEvent k)
	{
		switch(k.getKeyCode())
		{
			case KeyEvent.VK_LEFT:
				leftPressed = false;
				break;
			case KeyEvent.VK_UP:
				upPressed = false;
				break;
			case KeyEvent.VK_RIGHT:
				rightPressed = false;
				break;
			case KeyEvent.VK_DOWN:
				downPressed = false;
				break;
			case KeyEvent.VK_A:
				leftPressed = false;
				break;
			case KeyEvent.VK_W:
				upPressed = false;
				break;
			case KeyEvent.VK_D:
				rightPressed = false;
				break;
			case KeyEvent.VK_S:
				downPressed = false;
			break;
			case KeyEvent.VK_SPACE:
				spacePressed = false;
				break;
			default:
				break;
		}
	}
	
	public void keyTyped(KeyEvent e)
	{
	}
}

It is generally better to do it the other way around. So the map moves, and the player is static in the middle.
So what you do is draw the player at the exact middle of the screen, and use playerPosition and middleOfScreenPoint to translate the rendering of the tiles.

I prefer moving the player around.

But no matter which way you do it, you’re going to have to move the whole game world around to accommodate the “physical” screen. The monitor can’t move, so the game has to shift the world ever which way to look like its moving.

I dont want to move the world around, anyhows here’s another problem.
The screen follow my ship but doesnt draw anything outside the screen box or something.
Also the score and other texts doesnt follow the screen, its printed at a fixed position… Any help or fix?


	public void update(Graphics g)
	{
		//start off transforms at identity
		g2d.setTransform(identity);
		
		//erase the background
		g2d.setPaint(Color.BLACK);
		g2d.fillRect(0, 0, getSize().width, getSize().height);
		

		g.translate(-(int)ship.getX() + ScreenWidth / 2, -(int)ship.getY() + ScreenHeight / 2);
		// Set Background IMG
		g2d.drawImage(bg, 0, 0, null);
		
		
		// Calc FPS
		long currentTime = System.currentTimeMillis(); 
		if (currentTime > nextSecond)
		{ 
			nextSecond += 1000; 
			frameInLastSecond = framesInCurrentSecond; 
			framesInCurrentSecond = 0; 
		} 
		framesInCurrentSecond++; 
	    
		// FPS
		g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
		g2d.setColor(Color.WHITE);
		g2d.setFont(new Font("Arial", Font.PLAIN, 20));
		g2d.drawString(frameInLastSecond + " fps", 10, 30);
		g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
	    
		//print some status information
		g2d.setFont(new Font("Arial", Font.PLAIN, 9));
		g2d.setColor(Color.LIGHT_GRAY);
		g2d.drawString("Ship: " + Math.round(ship.getX()) + "," + Math.round(ship.getY()) , 10, 60);
		g2d.drawString("Move angle: " + Math.round(ship.getMoveAngle())+90, 10, 70);
		g2d.drawString("Face angle: " + Math.round(ship.getFaceAngle()), 10, 80);
		g2d.drawString("Ship VelX: " + Math.round(ship.getVelX()), 10, 90);
		g2d.drawString("Ship VelY: " + Math.round(ship.getVelY()), 10, 100);
		g2d.drawString("Version: " + GameVersion, 10, 110);
		g2d.drawString("Asteroids: " + AsteroidsAlive, 10, 120);
		
		// Print Score
		g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
		g2d.setColor(Color.WHITE);
		g2d.setFont(new Font("Arial", Font.PLAIN, 20));
		g2d.drawString("Score: " + Score, 360, 30);
		
		// Draw the game graphics
		drawShip();
		drawBullets();
		drawAsteroids();
		
		// Repaint the applet window
		paint(g);
		
	}

You have to render things with different translation-points.
Now that it seems like you can move around (?), you will want your HUD (head-up-display with score and health and such) to always be rendered in the same place.

I’ve changed a few things in your code here, and added a bunch of comments to tell you what is happening.

Pastebin Link

Extra things:
An update-method shouldn’t do drawing, it should do game-logic. Although it seems you have no game-logic in this update-method, it is a rather confusing name for a method that draws everything.
The normal game-loop setup looks like this:

registerControls(); // Logs which keys are pressed, for use in the update-method. Some people do this in their update-method, and some do it separately.

update(long deltaTime); // updates everything according to the time passed since the last frame finished. This makes sure your game runs at a steady pace, and doesn’t go slower when the computer lags and skips some frames. This means that even if your computer slows to 5 fps for 1 second, it’ll still move things the same amount of pixels as if it was running 60fps, because the updates are now relative to time, and not to how many times you’ve looped through this game-loop.

draw(Graphics2D g); // draws everything, and does not under any circumstance have any logic-calculations. Only things that have to do with graphics.

Then some sort of sleep-function to keep the framerate steady.

Repeat

This is a very simple demonstration-setup, and I really recommend you read up on game-loops, as they are crucial to how your game runs. Also, if you’re not overriding the paint-method, you should probably look into that.

Well unless you want the character to walk outside of the field of view, you’re going to have to.

I do move my character, but then I translate my world to follow his position. That’s one way to do it.

Still essentially “moving”