Я в настоящее время в процессе экспериментирования с боковой прокруткой игрушке 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;
}
}
Возможно, просто сделайте свои плитки более крупными, чтобы вы могли сделать меньше вызовов 'g.drawImage(), чтобы повысить производительность. – ulmangt
Кроме того, нужно пробовать профилирование, чтобы увидеть, что такое узкое место на самом деле? Проверьте JVisualVM, если вам нужен простой бесплатный профайлер (он поставляется с JVM). – ulmangt
Очень верно. По этой причине я перешел из 32x32 плит, но я могу только стать таким большим, пока я не потеряю способность создавать более подробные карты. Я думаю, я мог бы просто сделать лучшие тайеты :) – gruntled