基于 Java Tile 的游戏性能 [英] Java Tile Based Game Performance

查看:24
本文介绍了基于 Java Tile 的游戏性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在用 Java 试验基于 2D 瓷砖的横向滚动游戏,主要基于 David Brackeen 的Developing Games in Java"中的代码和示例

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

目前,地图文件的大小为 100x100 图块(每个图块为 64x64 像素).我已经将系统配置为仅显示玩家可见的图块.Graphics 系统由 ScreenManager 类管理,该类返回当前 BufferStrategy 的图形对象,如下所示:

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:

ScreenManager.java

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 的 draw 方法之后.

After the graphics from this ScreenManager is passed along in the game loop to the draw method of the TreeRenderer.

TreeMapRenderer.java

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 轴选择正确的 FROM 和 TO 值,从 10000 到 ~285 之间剔除所需的图块.

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.

我的问题是,即使这样,游戏在渲染图块时也只能以大约 8-10 FPS 的速度运行.如果我关闭图块渲染,系统会以 80 FPS 的速度运行(无事可做时很容易快速运行)

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)

您对如何加快此过程有什么想法吗?我希望看到至少在 30 FPS 附近的东西,以使其具有可玩性.

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.

最后,虽然我愿意使用 3rd 方库来做到这一点,但我想在承认失败之前尝试自己实现这个逻辑.

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.


根据这里的要求,关于 Image image = map.getTile(x, y).getImage(); 的调用如何工作的额外信息.


As requested here is the extra information for how the call for Image image = map.getTile(x, y).getImage(); works.

这里的ma​​p来自下面的TileMap类

The map here come from the following TileMap class

TileMap.java

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 是以下代码的一个实例.本质上这个类只保存可以通过添加覆盖层来更新的图像,总是使用 gc.createCompatibleImage(w, h, Transparency.TRANSLUCENT);和一个布尔值来判断它是否会阻止玩家.传入的图像也是以这种方式创建的.

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.

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

}

推荐答案

我会使用像素渲染引擎(google it ;D)

I would use a pixel rendering engine (google it ;D)

基本上你所做的,它有一个巨大的整数数组,对应于你正在绘制的图像.

Basically what you do, it have a giant array of intergers corresponding to the image you are drawing.

基本上,每个图块都有一个代表其像素的整数数组.当你渲染那个 tile 时,你复制"(比那稍微复杂一些)tile 数组到大数组:)

Basically you have each tile have an array of intergers representing its pixels. When you render that tile, you "copy" (it's slightly more complicated than that) the array of tile to the big array :)

然后,在将所有内容渲染到主阵列后,将其绘制在屏幕上.

Then once you are done rendering everything to the master array, you draw that on the screen.

这样,你每次画东西时只处理整数而不是整张图片.这使它更快.

This way, you are only dealing with integers and not whole pictures everytime you draw something. This makes it a lot faster.

我使用 MrDeathJockey 的 (youtube) 教程学习了这一点,并将它们与 DesignsbyZephyr 的(也是 youtube)相结合.虽然我不推荐使用他的技术(他只使用 4 种颜色和 8 位图形,就像 DeathJockey 的教程一样,你可以自定义图像的大小,甚至可以有多个不同分辨率的精灵表(对字体有用)

I learned this using MrDeathJockey's (youtube) tutorials and combining them with DesignsbyZephyr's (also youtube). Although I do not recommend using his technique (he only uses 4 colors and 8 bit graphics, as with deathJockey's tutorials you can customize the size of the images and even have multiple sprite sheets with different resolutions (useful for fonts)

不过,我确实使用了一些偏移量(使屏幕移动而不是播放器移动)和 Zephyr 的 InputHandler :)

I did however use some of the offset stuff (to make the screen move instead of the player) and the InputHandler by Zephyr :)

希望这有帮助!-Camodude009

Hope this helps! -Camodude009

这篇关于基于 Java Tile 的游戏性能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆