I can't quite figure out how to render my entities and map...

Hey,

I’ve been having a bit of trouble figuring out how to use a camera to move around my map and render the player and other entities onto the map with working zooming.

If someone has some tips, suggestions, or sees a way to do this properly with my current code that would be appreciated.

Rendering the map:

package valkryst.area.map;

import java.awt.Graphics;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import valkryst.area.Area;
import valkryst.area.map.tile.GrassTile;
import valkryst.area.map.tile.VoidTile;
import valkryst.core.Camera;

/**
 * This class adds the map to the pixel array that will be rendered onto the screen.
 * 
 * @author Valkryst
 * --- Last Edit 24-Oct-2013
 */
public class RenderMap {
	public static String[] map;
	private static int currentMapID = -1;
	
	private static VoidTile voidTile = new VoidTile();
	private static GrassTile grassTile = new GrassTile();
	
	/**
	 * Renders the map to the canvas.
	 * @param g The graphics object to use to render things to the canvas with.
	 * @param area The area that the player is currently in.
	 * @param p The player.
	 * @param width The width of the canvas.
	 * @param height The height of the canvas.
	 */
	public void render(final Graphics g, final Area area, final Camera c, final int width, final int height, final int scaleMultiplier) {
		int areaID = area.getMapID();
		if(areaID != currentMapID || currentMapID == -1) {
			loadMapFromFile(areaID);
			currentMapID = areaID;
		}
		
		int xOffset = c.getOriginX() >> 5; // The x-axis tile coordinate of where the cameras origin is currently.
		int yOffset = c.getOriginY() >> 5; // The y-axis tile coordinate of where the cameras origin is currently.
		int numberOfXTiles = (width >> 5); // Takes whatever the width of the canvas is, divides it by 32 and then that's then number of XTiles to draw.
		int numberOfYTiles = (height >> 5); // Takes whatever the height of the canvas is, divides it by 32 and then that's then number of YTiles to draw.
		
		if(width > 0 && height > 0) { // If there is no room on the screen to render the map then don't bother rendering it.
			for(int y = 0 + yOffset; y < yOffset + numberOfYTiles + 2; y++) {
				for(int x = 0 + xOffset; x < xOffset + numberOfXTiles + 2; x++) {
					try {
						switch(map[y].charAt(x)) {
							case 'A': {
								g.drawImage(grassTile.getTileImage(), (x - xOffset) * scaleMultiplier, (y - yOffset) * scaleMultiplier, scaleMultiplier, scaleMultiplier, null);
								break;
							}
							default: {
								g.drawImage(voidTile.getTileImage(), (x - xOffset) * scaleMultiplier, (y - yOffset) * scaleMultiplier, scaleMultiplier, scaleMultiplier, null);
							} 
						}
					} catch(Exception e) {
						continue;
					}
				}
			}
		}
	}
	
	public void loadMapFromFile(final int mapID) {
		List<String> loadedMap = new ArrayList<String>();
		
		InputStream is = this.getClass().getClassLoader().getResourceAsStream("Areas/Area" + mapID + ".txt");
		try {
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
	        String line = null;
	        while ((line = bufferedReader.readLine()) != null) {
	            loadedMap.add(line);
	        }
	        bufferedReader.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		map = loadedMap.toArray(new String[loadedMap.size()]);
		System.out.println("Sucessfully finished loading Areas/Area" + mapID + ".txt");
	}
}

The Camera:

package valkryst.core;

import valkryst.area.Area;
import valkryst.core.graphics.Frame;

public class Camera {
	private int mapSizeInPixels;
	/** The origin for the are that the camera is currently viewing. */
	private int originX = 0, originY = 0;
	/** The start and end bounds of the map. */
	private int start = 0, end;
	
	public Camera(final Area currentAreaIn) {
		mapSizeInPixels = currentAreaIn.getMapSize() * (32 * Frame.scale);
		end = mapSizeInPixels;
	}
	
	/**
	 * Moves the camera's origin.
	 * @param x The value to add to the originX variable.
	 * @param y The value to add to the originY variable.
	 */
	public void moveOrigin(int x, int y) {
		originX += x;
		originY += y;
		
		checkBounds();
	}
	
	public void checkBounds() {
		// Checks if the originX is below 0, if it is then set it to 0.
		if(originX < start) {
			originX = start;
		}
		
		// Checks if the originY is below 0, if it is then set it to 0.
		if(originY < start) {
			originY = start;
		}
	}
	
	/**
	 * Sets the bounds of the map.
	 * @param currentArea The area to create bounds for.
	 */
	public void changeBounds(final Area currentAreaIn) {
		mapSizeInPixels = currentAreaIn.getMapSize() * (32 * Frame.scale);
		end = mapSizeInPixels;
	}
	
	///////////////////////////////////////// Get methods here: ////////////////////////////////////////////////////
	
	 /**
     * Returns the originX variable of the camera.
     * @return Returns the originX variable of the camera.
     */
	public int getOriginX() {
		return originX;
	}
	
	/**
     * Returns the originY variable of the camera.
     * @return Returns the originY variable of the camera.
     */
	public int getOriginY() {
		return originY;
	}
}

The Player:

package valkryst.entity;

import java.awt.Graphics;
import java.awt.event.KeyEvent;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import valkryst.area.Area;
import valkryst.core.Camera;
import valkryst.core.graphics.Frame;
import valkryst.enums.BattleDecision;
import valkryst.enums.CombatType;

/**
 * This class defines a player object.
 * 
 * @author Valkryst
 * --- Last Edit 24-Oct-2013
 */
public class Player extends Entity {
    private BattleDecision battleDecision = null;
    
    /**
     * The constructor for the player object.
     * @param nameIn The name of the player being constructed.
     * @param playerLevelIn The level of the player being constructed.
     * @param playerMaxLevelIn The maximum level of the player being constructed.
     * @param combatTypeIn The combat type of the player being constructed.
     */
    public Player(final String nameIn, final int playerLevelIn, final int playerMaxLevelIn, final CombatType combatTypeIn) {
		setName(nameIn);
		setUsesArmor(true);
		setUsesWeapon(true);
		setLevel(playerLevelIn);
		setMaxLevel(playerMaxLevelIn);
		setExperience(0);
		double health = (playerLevelIn * 25.0);
		setHealth(health);
		setMaxHealth(health);
	
		
		switch (combatTypeIn) {
        	case MELEE:
        	    setEnergy(100.0);
        	    setMaxEnergy(100.0);
        	    setUsesEnergy(true);
        	    break;
        	case RANGED:
        	    setEnergy(100.0);
        	    setMaxEnergy(100.0);
        	    setUsesEnergy(true);
        	    break;
        	case CASTER:
        	    setMana(100.0);
        	    setMaxMana(100.0);
        	    setUsesMana(true);
        	    break;
        	default: {
        		final Logger LOGGER = LoggerFactory.getLogger(Player.class);
        		LOGGER.info("Unsupported type of CombatType: " + combatTypeIn);
        	}
		}
		
		setBaseDamage(3.5 * getLevel());
		setArmorPoints(0.0);
		setup(true);
    }

    /**
     * Updates the player in preparation to be rendered.
     */
    public void updateLogic(final Area currentArea, final Camera camera) {
    	// Checks whether the player entity is visible on the screen or not.
    	if(xCoord + sprite.getSpriteImage(0).getWidth(null) < camera.getOriginX() && yCoord + sprite.getSpriteImage(0).getHeight(null) < camera.getOriginY()) {
    		isVisible = false;
    	} else if (!isVisible){
    		isVisible = true;
    	}
    	
    	// Moves + or - one pixel and + or - 1/32 of a tile for every pixel moved.
    	if(Frame.KEYBOARD_INPUT.isKeyPressed(KeyEvent.VK_UP)) {
			move(0, -1, 0, -0.03125);
			camera.moveOrigin(0, -1);
		}
		if(Frame.KEYBOARD_INPUT.isKeyPressed(KeyEvent.VK_DOWN)) {
			move(0, 1, 0, 0.03125);
			camera.moveOrigin(0, 1);
		}
		if(Frame.KEYBOARD_INPUT.isKeyPressed(KeyEvent.VK_LEFT)) {
			move(-1, 0, -0.03125, 0);
			camera.moveOrigin(-1, 0);
		}
		if(Frame.KEYBOARD_INPUT.isKeyPressed(KeyEvent.VK_RIGHT)) {
			move(1, 0, 0.03125, 0);
			camera.moveOrigin(1, 0);
		}
		
		// Checks if the player entity is off of bounds and if it is then move it back onto the map.
		if(xCoord < 0 || tileX < 0) {
			xCoord = 0;
			tileX = 0;
		}
		
		if(yCoord < 0 || tileY < 0) {
			yCoord = 0;
			tileY = 0;
		}
		
		int mapSize = currentArea.getMapSize();
		int scale = (32 * Frame.scale);
		int mapSizeInPixels = (mapSize * scale);
		
		if(xCoord > mapSizeInPixels || tileX > mapSize) {
			System.out.println("Test1");
			xCoord = mapSizeInPixels;
			tileX = mapSize-1;
		}
		
		if(yCoord > mapSizeInPixels || tileY > mapSize) {
			System.out.println("Test2");
			yCoord = mapSizeInPixels;
			tileY = mapSize - 1;
		}
    }
    
    /**
     * Renders the player to the screen.
     */
    public void render(final Graphics g, final int scaleMultiplier) {
    	if(isVisible) {
    		g.drawImage(getSprite().getSpriteImage(0), xCoord, yCoord, scaleMultiplier, scaleMultiplier , null);
    	}
    }
    
    ///////////////////////////////////////// Get methods here: ////////////////////////////////////////////////////
    public BattleDecision getBattleDecision() {
    	return battleDecision;
    }

    ///////////////////////////////////////// Set methods here: ////////////////////////////////////////////////////
    public void setMyBattleDecision(final BattleDecision b) {
    	battleDecision = b;
    }
}

The Creature:

package valkryst.entity;

import java.util.Random;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import valkryst.enums.CombatType;
import valkryst.systems.Drops;
import valkryst.systems.battle.AITactics;

/**
 * This class defines a creature object.
 * 
 * @author Valkryst
 * --- Last Edit 24-Oct-2013
 */
public class Creature extends Entity {
	public final AITactics TACTICS = new AITactics();
	/** The id of the creature. */
	final int CREATURE_ID;
	/** The drop table of the creature. */
	private Drops drops;
	/** Whether the creature has been looted or not. If the creature has been looted then it can be removed from the area it's on. */
	private boolean hasBeenLooted = false;

	/**
	 * The constructor method for the Creature object.
	 * @param creatureIdIn The ID of the creature being created.
	 * @param nameIn The name of the creature being created.
	 * @param creatureLevelIn The level of the creature being created.
	 * @param creatureMaxLevelIn The maximum level of the creature being created.
	 * @param combatTypeIn The combat type of the creature being created.
	 */
    public Creature(final int creatureIdIn, final String nameIn, final int creatureLevelIn, final int creatureMaxLevelIn, final CombatType combatTypeIn) {
    	CREATURE_ID = creatureIdIn; // Set the creature's ID.
        setName(nameIn);// Set the creature's name.

        int level = -1;
        while(level <= 0) { // Prevents the creature's level from being 0 or below.
        	// Generates a random integer that is within playerLevel-3 and playerLevel+3.
        	level = (creatureLevelIn - 3) + (int) (Math.random() * (((creatureLevelIn + 3) - (creatureLevelIn - 3)) + 1));
        }
        setLevel(level);
        setMaxLevel(creatureMaxLevelIn);
        setExperience(0);

        // Figure out the creature's health.
        final Random RANDOM = new Random();
        double health = 0;
        while(health <= 0 || health == -1) { 
        	health = (level * 25.0) + RANDOM.nextInt((level * 10) + 1);
        }
        setHealth(health);
        setMaxHealth(health);
        
        // Figure out the creature's aggressiveness. REWORK LATER ON
        if(level >= creatureLevelIn) { 
        	setAggressiveness(Math.random() + (level - creatureLevelIn)/100); // Tested multiple times and has never gone to 1.0 or above. Do extensive testing if it ever does tho.
        } else if(getAggressiveness() == -1) { 
        	setAggressiveness(Math.random()); 
        }
        
        // Set the creature's energy/mana depending on it's combatType.
        switch (combatTypeIn) {
            case MELEE: {
                setEnergy(100.0);
                setMaxEnergy(100.0);
                setUsesEnergy(true);
                break;
            }
            case RANGED: {
                setEnergy(100.0);
                setMaxEnergy(100.0);
                setUsesEnergy(true);
                break;
            }
            case CASTER: {
                setMana(100.0);
                setMaxMana(100.0);
                setUsesMana(true);
                break;
            }
            default: {
        		final Logger LOGGER = LoggerFactory.getLogger(Creature.class);
        		LOGGER.info("Unsupported type of CombatType: " + combatTypeIn);
        	}
        }

        setBaseDamage(2.5 * level); // Set the creature's base damage.
        setArmorPoints(0.0); // Set the armor points of the creature.
        setup(false);
    }
    
    public void createDropTable(final int[] dropTable, final double[] dropChance) {
    	drops.createDropTable(dropTable, dropChance);
    }

    /**
     * Updates the creature in preparation to be rendered.
     */
    public void updateLogic() {
    }
    
    /**
     * Renders the player to the screen.
     */
    public void render() {
    	
    }
    
    ///////////////////////////////////////// Set methods here: ////////////////////////////////////////////////////
    
    /**
     * Sets whether the creature has been looted or not.
     * @param b The variable to set the hasBeenLooted variable to.
     */
    public void setHasBeenLooted(boolean b) {
    	hasBeenLooted = b;
    }
    
    ///////////////////////////////////////// Get methods here: ////////////////////////////////////////////////////
    
    /**
     * Returns the drop variable of the creature.
     * @return Returns the drop variable of the creature.
     */
    public Drops getDrops() {
    	return drops;
    }
    
    /**
     * Returns whether the creature has been looted or not.
     * @return Returns whether the creature has been looted or not.
     */
    public boolean getHasBeenLooted() {
    	return hasBeenLooted;
    }
}

The Game Loop:

package valkryst.core.graphics;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;

import javax.swing.SwingUtilities;

import valkryst.area.Area;
import valkryst.area.map.RenderMap;
import valkryst.core.Camera;
import valkryst.entity.Player;
import valkryst.enums.CombatType;
import valkryst.spell.Cast;


/**
 * @author Valkryst
 * --- Last Edit 24-Oct-2013
 */
public class Screen extends Canvas implements Runnable{
	private static final long serialVersionUID = 4532836895892068039L;
	
	private Thread gameThread;
	private boolean isGameRunning = false;
	
	private int WIDTH = Frame.WIDTH, HEIGHT = Frame.HEIGHT, SCALE = Frame.scale;
	
	private BufferStrategy BS = getBufferStrategy();
	private Area currentArea = new Area(0, 10, "TestArea"); // area shouldn't be created here.
	int lastFrameScale = 0, scaleMultiplier = (32 * Frame.scale);
	
	
	private RenderMap renderMap = new RenderMap();
	private Camera camera;
	
	public Screen() {   
		setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
		setFocusable(true);
		setVisible(true);
	}

	public void run() {
		long lastLoopTime = System.nanoTime();
	    final int TARGET_FPS = 60;
		final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;   
		double delta = 0;
		
		// Keep looping until the game ends.
		while (isGameRunning) {
			long now = System.nanoTime();
			long updateLength = now - lastLoopTime;
			lastLoopTime = now;
		    delta += updateLength / ((double)OPTIMAL_TIME); // Work out how long its been since the last update. This will be used to calculate how far the entities should move this loop.
		    
		    //Update the game's logic and then render the screen.
		    while(delta >= 1) {
		    	updateLogic(delta);
		    	delta--;
		    }
		    
		    render();
		      
		    // we want each frame to take 10 milliseconds, to do this
		    // we've recorded when we started the frame. We add 10 milliseconds
		    // to this and then factor in the current time to give 
		    // us our final value to wait for
		    // remember this is in ms, whereas our lastLoopTime etc. vars are in ns.
		    try {
		    	long tempLong = (lastLoopTime-System.nanoTime() + OPTIMAL_TIME)/1000000;
		    	if(tempLong <= 0) { continue; } // Skips the sleep()
				Thread.sleep(tempLong);
			} catch (InterruptedException e) {
				continue;
			}
		}
		stop();
	}
	
	public synchronized void start() {
		// TEMPORARY
		currentArea.addPlayerToArea(new Player("Test", 1, (short)60, CombatType.CASTER));
		// END OF TEMPORARY
		
		camera = new Camera(currentArea);
		
		setBackground(Color.black);
		isGameRunning = true;
		gameThread = new Thread(this, "Display");
		gameThread.start();
	}

	public synchronized void stop() {
		try {
			gameThread.join();
		} catch(InterruptedException e) {
			e.printStackTrace();
		}
	}

	// When called this updates all of the game's logic.
	public void updateLogic(double delta) {
		Cast.setCurrentTime();
		currentArea.pruneCreatures(); // Removes all creatures that have been looted from the area.
		currentArea.updateLogic(camera);
	}

	// When called this updates the screen.
	public void render() {
		// If Frame.scale has changed then re-size all tile images.
		if(Frame.scale != lastFrameScale) {
			scaleMultiplier = (32 * Frame.scale);
		}
		lastFrameScale = Frame.scale;
				
		// Forces the canvas to use triple buffering.
		BS = getBufferStrategy();
        if (BS == null) {
        	SwingUtilities.invokeLater(new Runnable() {
        	    public void run() {
        	        createBufferStrategy(3);
        	    }
        	});
        	return;
        }
		
        // Creates the graphics object and then clears the screen.
        Graphics g = BS.getDrawGraphics();
        g.clearRect(0, 0, getWidth(), getHeight());
        renderMap.render(g, currentArea, camera, getWidth(), getHeight(), scaleMultiplier);
		currentArea.render(g, scaleMultiplier);
        g.dispose();
		BS.show();
	}
}

Thanks for any replies!