title: Java Tile Based Game Performance
body: <p>I am currently in the process of experimenting with a 2D tile based side scrolling game in Java, primarily based on the code and examples from "Developing Games in Java" by David Brackeen</p>
  
  <p>At the moment the map files are 100x100 tiles in size (each tile is 64x64 pixels). I have already configured the system to only display the tiles which are visible to the player. The Graphics system is managed by a ScreenManager class that returns the graphics object of the current BufferStrategy as follows:</p>
  
  <p>ScreenManager.java</p>
  
  <pre><code>private GraphicsDevice device;
  
  ...
  
  /**
   * Gets the graphics context for the display. The
   * ScreenManager uses double buffering, so applications must 
   * call update() to show any graphics drawn.
   * &lt;p&gt;
   * The application must dispose of the graphics object.
   */
  public Graphics2D getGraphics(){
      Window window = device.getFullScreenWindow();
      if(window != null){
          BufferStrategy strategy = window.getBufferStrategy();
          return (Graphics2D)strategy.getDrawGraphics();
      }
      else{
          return null;
      }
  }
  </code></pre>
  
  <p>After the graphics from this ScreenManager is passed along in the game loop to the draw method of the TreeRenderer.</p>
  
  <p>TreeMapRenderer.java</p>
  
  <pre><code>/**
      Draws the specified TileMap.
  */
  public void draw(Graphics2D g, TileMap map,
      int screenWidth, int screenHeight, float fr)
  {
      Sprite player = map.getPlayer();
      int mapWidth = tilesToPixels(map.getWidth());
      int mapHeight = tilesToPixels(map.getHeight());
  
      // get the scrolling position of the map
      // based on player's position
      int offsetX = screenWidth / 2 -
          Math.round(player.getX()) - TILE_SIZE;
      offsetX = Math.min(offsetX, 0);
      offsetX = Math.max(offsetX, screenWidth - mapWidth);
  
      // get the y offset to draw all sprites and tiles
      int offsetY = screenHeight /2 -
              Math.round(player.getY()) - TILE_SIZE;
      offsetY = Math.min(offsetY,0);
      offsetY = Math.max(offsetY, screenHeight - mapHeight);
  
      // draw the visible tiles
      int firstTileY = pixelsToTiles(-offsetY);
      int lastTileY = firstTileY + pixelsToTiles(screenHeight) +1;
  
      int firstTileX = pixelsToTiles(-offsetX);
      int lastTileX = firstTileX +
          pixelsToTiles(screenWidth) + 1;
  
      //HERE IS WHERE THE SYSTEM BOGS dOWN (checking ~280 tiles per iteration)
      for (int y=firstTileY; y&lt;lastTileY; y++) {
          for (int x=firstTileX; x &lt;= lastTileX; x++) {
              if(map.getTile(x, y) != null){
                  Image image = map.getTile(x, y).getImage();
                  if (image != null) {
                      g.drawImage(image,
                          tilesToPixels(x) + offsetX,
                          tilesToPixels(y) + offsetY,
                          null);
                  }
              }
          }
      }
  
      // draw player
      g.drawImage(player.getImage(),
         Math.round(player.getX()) + offsetX,
         Math.round(player.getY()) + offsetY,
         null);
  </code></pre>
  
  <p>The algorithm works correctly selecting the correct FROM and TO values for the X and Y axis culling the needed tiles from 10000 to ~285. </p>
  
  <p>My problem is that even with this the game will only run at around 8-10 FPS while the tiles are being rendered. If I turn off tile rendering than the system runs at 80 FPS (easy to run fast when there is nothing to do)</p>
  
  <p>Do you have any ideas on how to speed up this process? I would like to see something at least around the 30 FPS mark to make this playable.  </p>
  
  <p>And finally although I am open to using 3rd party libraries to do this I would like to try and implement this logic myself before admitting defeat.</p>
  
  <p><strong>EDIT:</strong><br>
  As requested here is the extra information for how the call for <code>Image image = map.getTile(x, y).getImage();</code> works.</p>
  
  <p>The <strong>map</strong> here come from the following TileMap class</p>
  
  <p>TileMap.java</p>
  
  <pre><code>public class TileMap {
  
  private Tile[][] tiles;
  private LinkedList sprites;
  private Sprite player;
  private GraphicsConfiguration gc;
  
  /**
      Creates a new TileMap with the specified width and
      height (in number of tiles) of the map.
  */
  public TileMap(GraphicsConfiguration gc, int width, int height) {
      this.gc = gc;
      tiles = new Tile[width][height];
      overlayer = new Tile[width][height];
      sprites = new LinkedList();
  }
  
  
  /**
      Gets the width of this TileMap (number of tiles across).
  */
  public int getWidth() {
      return tiles.length;
  }
  
  
  /**
      Gets the height of this TileMap (number of tiles down).
  */
  public int getHeight() {
      return tiles[0].length;
  }
  
  
  /**
      Gets the tile at the specified location. Returns null if
      no tile is at the location or if the location is out of
      bounds.
  */
  public Tile getTile(int x, int y) {
      if (x &lt; 0 || x &gt;= getWidth() ||
          y &lt; 0 || y &gt;= getHeight())
      {
          return null;
      }
      else {
          return tiles[x][y];
      }
  }
  
  
  /**
   * Helper method to set a tile. If blocking is not defined than it is set to false.
   * 
   * @param x
   * @param y
   * @param tile
   */
  public void setTile(int x, int y,Image tile){
      this.setTile(x,y,tile,false);
  }
  
  /**
      Sets the tile at the specified location.
  */
  public void setTile(int x, int y, Image tile, boolean blocking) {
      if(tiles[x][y] == null){
          Tile t = new Tile(gc, tile, blocking);
          tiles[x][y] = t;
      }
      else{
          tiles[x][y].addImage(tile);
          tiles[x][y].setBlocking(blocking);
      }
  }
  
  ...
  </code></pre>
  
  <p>With the Tile here being an instance of the following code. Essentially this class just holds the Image which can be updated by adding an overlay layer to it always using gc.createCompatibleImage(w, h, Transparency.TRANSLUCENT); and a boolean to tell if it will block the player. The image that is passed in is also created in this manner. </p>
  
  <p>Tile.java</p>
  
  <pre><code>public class Tile {
  private Image image;
  private boolean blocking = false;
  private GraphicsConfiguration gc;
  
  /**
   * Creates a new Tile to be used with a TileMap
   * @param image The base image for this Tile
   * @param blocking Will this tile allow the user to walk over/through
   */
  public Tile(GraphicsConfiguration gc, Image image, boolean blocking){
      this.gc = gc;
      this.image = image;
      this.blocking = blocking;
  }
  
  public Tile(GraphicsConfiguration gc, Image image){
      this.gc = gc;
      this.image = image;
      this.blocking = false;
  }
  
  /**
  Creates a duplicate of this animation. The list of frames
  are shared between the two Animations, but each Animation
  can be animated independently.
  */
  public Object clone() {
  
      return new Tile(gc, image, blocking);
  }
  
  /**
   * Used to add an overlay to the existing tile
   * @param image2 The image to overlay
   */
  public void addImage(Image image2){
      BufferedImage base = (BufferedImage)image;
      BufferedImage overlay = (BufferedImage)image2;
  
      // create the new image, canvas size is the max. of both image sizes
      int w = Math.max(base.getWidth(), overlay.getWidth());
      int h = Math.max(base.getHeight(), overlay.getHeight());
      //BufferedImage combined = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
      BufferedImage combined = gc.createCompatibleImage(w, h, Transparency.TRANSLUCENT);
      // paint both images, preserving the alpha channels
      Graphics g = combined.getGraphics();
      g.drawImage(image, 0, 0, null);
      g.drawImage(overlay, 0, 0, null);
  
      this.image = (Image)combined;
  }
  
  public boolean isBlocking(){
      return this.blocking;
  }
  
  public void setBlocking(boolean blocking){
      this.blocking = blocking;
  }
  
  public Image getImage(){
      return this.image;
  }
  </code></pre>
  
  <p>}</p>