2012-04-06 3 views
2

Я в настоящее время в процессе экспериментирования с боковой прокруткой игрушке 2D плитки в Java, в первую очередь на основе кода и примеры из «Разработка игр в Java» Дэвид BrackeenJava плитки на основе игры Производительность

В настоящее время моментальные файлы карт размером 100x100 (каждый фрагмент - 64x64 пикселя). Я уже настроил систему, чтобы отображать только те плитки, которые видны игроку. Графическая система управляется классом ScreenManager, который возвращает графический объект текущего BufferStrategy следующим образом:

ScreenManager.java

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. 
* <p> 
* 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; 
    } 
} 

После графики из этого ScreenManager передается вместе в цикле игры к метод рисования TreeRenderer.

TreeMapRenderer.java

/** 
    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<lastTileY; y++) { 
     for (int x=firstTileX; x <= 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); 

Алгоритм работает правильно выбрать правильное от и до значений для X и Y оси отбирая необходимые плитки от 10000 до ~ 285.

Моя проблема заключается в том, что даже при этом игра будет работать только при 8-10 FPS, пока отображаются фрагменты. Если я отключу отрисовку плит, чем система работает на 80 FPS (легко работать быстро, когда нечего делать)

Есть ли у вас какие-либо идеи по ускорению этого процесса? Я хотел бы увидеть что-то, по крайней мере, вокруг отметки 30 FPS, чтобы сделать эту игру.

И, наконец, хотя я открыт для использования сторонних библиотек для этого, я хотел бы попытаться реализовать эту логику самостоятельно, прежде чем допускать поражение.

EDIT:
В соответствии с просьбой здесь дополнительную информацию о том, как призыв к Image image = map.getTile(x, y).getImage(); работ.

карта сюда приезжают из следующего класса TileMap

TileMap.java

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 < 0 || x >= getWidth() || 
     y < 0 || y >= 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); 
    } 
} 

... 

С Tile здесь является экземпляром следующего кода. По существу этот класс просто содержит изображение, которое можно обновить, добавив к нему слой overlay, всегда используя gc.createCompatibleImage (w, h, Transparency.TRANSLUCENT); и логическое значение, чтобы определить, заблокирует ли он игрока. Образ, который передается, также создается таким образом.

Tile.java

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; 
} 

}

+0

Возможно, просто сделайте свои плитки более крупными, чтобы вы могли сделать меньше вызовов 'g.drawImage(), чтобы повысить производительность. – ulmangt

+0

Кроме того, нужно пробовать профилирование, чтобы увидеть, что такое узкое место на самом деле? Проверьте JVisualVM, если вам нужен простой бесплатный профайлер (он поставляется с JVM). – ulmangt

+0

Очень верно. По этой причине я перешел из 32x32 плит, но я могу только стать таким большим, пока я не потеряю способность создавать более подробные карты. Я думаю, я мог бы просто сделать лучшие тайеты :) – gruntled

ответ

0

Создание прозрачных изображений (не прозрачные), так как полупрозрачные изображения требуют более высокого барана и сохраняются в системной памяти вместо стандартного Java кучи. Для создания полупрозрачных изображений требуется несколько собственных вызовов для доступа к собственной системной памяти. Используйте BufferedImages вместо Image. Вы можете в любой момент переместить BufferedImage в Image.

1

Я хотел бы использовать рендеринг пикселей двигатель (Google его; D)

В основном то, что вы делаете, это есть гигантский массив intergers, соответствующий изображению вы чертите.

В принципе, у вас есть каждая плитка с массивом промежуточных элементов, представляющих ее пиксели. Когда вы делаете эту плитку, вы «копируете» (это немного сложнее) массив черепицы в большой массив :)

Затем, как только вы закончите рендеринг всего в главный массив, вы нарисуете это на экран.

Таким образом, вы имеете дело только с целыми числами, а не целыми картинками каждый раз, когда вы рисуете что-то. Это делает его намного быстрее.

Я узнал об этом, используя учебники MrDeathJockey (youtube) и объединив их с DesignsbyZephyr's (также youtube). Хотя я не рекомендую использовать его технику (он использует только 4 цвета и 8 бит графики, как и в учебниках deathJockey, вы можете настроить размер изображений и даже иметь несколько листов спрайтов с различными разрешениями (полезно для шрифтов)

I однако использовали некоторые вещи смещения (чтобы заставить экран двигаться вместо игрока) и InputHandler по Zephyr :)

Надеюсь, это поможет! -Camodude009