2

私は現在、主に David Brackeen による「Developing Games in Java」のコードと例に基づいて、Java で 2D タイル ベースの横スクロール ゲームを試しています。

現時点では、マップ ファイルのサイズは 100x100 タイルです (各タイルは 64x64 ピクセルです)。プレイヤーに表示されるタイルのみを表示するようにシステムを構成済みです。Graphics システムは、現在の BufferStrategy のグラフィック オブジェクトを次のように返す ScreenManager クラスによって管理されます。

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 メソッドに渡された後。

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 までの必要なタイルをカリングします。

私の問題は、これを使用しても、タイルがレンダリングされている間、ゲームが約 8 ~ 10 FPS でしか実行されないことです。タイル レンダリングをオフにすると、システムは 80 FPS で実行されます (何もすることがないときは簡単に高速に実行できます)。

このプロセスをスピードアップする方法について何かアイデアはありますか? これをプレイ可能にするために、少なくとも 30 FPS マーク前後のものを見たいと思っています。

そして最後に、私はこれを行うためにサードパーティのライブラリを使用することにオープンですが、敗北を認める前にこのロジックを自分で実装してみたいと思います.

編集:
ここで要求されているように、呼び出しがどのように機能するかについての追加情報があります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);
    }
}

...

ここでのタイルは、次のコードのインスタンスです。基本的に、このクラスは常に gc.createCompatibleImage(w, h, Transparency.TRANSLUCENT); を使用してオーバーレイ レイヤーを追加することで更新できる画像を保持するだけです。プレイヤーをブロックするかどうかを示すブール値。渡されるイメージもこの方法で作成されます。

タイル.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;
}

}

4

2 に答える 2

1

ピクセル レンダリング エンジンを使用します (google it ;D)

基本的にあなたがしていることは、あなたが描いている画像に対応する整数の巨大な配列を持っています.

基本的に、各タイルには、そのピクセルを表す整数の配列があります。そのタイルをレンダリングするときは、タイルの配列を大きな配列に「コピー」します(それよりも少し複雑です:)

次に、マスター配列へのすべてのレンダリングが完了したら、それを画面に描画します。

この方法では、何かを描画するたびに画像全体ではなく、整数のみを処理します。これにより、はるかに高速になります。

MrDeathJockey の (YouTube) チュートリアルを使用してこれを学び、DesignsbyZephyr の (これも YouTube) と組み合わせました。彼のテクニックの使用はお勧めしませんが (彼は 4 色と 8 ビットのグラフィックのみを使用します。デスジョッキーのチュートリアルと同様に、画像のサイズをカスタマイズしたり、解像度の異なる複数のスプライト シートを使用したりすることもできます (フォントに便利))。

ただし、オフセットのもの(プレーヤーの代わりに画面を移動させるため)とZephyrのInputHandlerを使用しました:)

お役に立てれば!-カモデュード009

于 2013-09-22T18:24:52.430 に答える
0

半透明のイメージはより高い RAM を必要とし、標準の Java ヒープではなくシステム メモリに格納されるため、透明なイメージ (半透明ではない) を作成します。半透明のイメージを作成するには、ネイティブ システム メモリにアクセスするためにいくつかのネイティブ呼び出しが必要です。Image の代わりに BufferedImages を使用します。BufferedImage を Image にいつでもキャストできます。

于 2012-06-02T01:33:47.243 に答える