首页 > 解决方案 > 使用 Slick2d 渲染大量图像的最佳方法?

问题描述

所以我正在尝试使用 Slick2d 制作一个自上而下的 2D 游戏。

我有一个世界对象,它包含一个包含瓷砖列表的块列表。那些瓷砖是渲染的。我将平铺位置存储在 Vector2f 中。每个块也有一个 Vector2f 位置。这是我当前的工作代码:

主要的:

public class Main extends BasicGame {
public static Main instance;
private Renderer renderer;
private long lastFrameTime;
private float delta;

public Main() {
    super("Test");
}

public static void main(String[] arguments) {
    try {
        instance = new Main();
        AppGameContainer app = new AppGameContainer(instance);
        app.setDisplayMode(1240, 720, false);
        app.setShowFPS(true);
        app.start();
        app.setAlwaysRender(true);
    } catch (SlickException e) {
        e.printStackTrace();
    }
}

@Override
public void init(GameContainer container) throws SlickException {
    renderer = new Renderer(new World(), new Player());
    for(Chunk chunk : renderer.getWorld().getChunks()) {
        System.out.println(chunk.getPosition().x + "," + 
chunk.getPosition().y + "," + chunk.getTiles().size());
    }

}

@Override
public void update(GameContainer container, int delta)
        throws SlickException {
    long currentFrameTime = getCurrentTime();
    this.delta = (currentFrameTime - lastFrameTime)/1000f;
    lastFrameTime = currentFrameTime;
    renderer.move();
}

public void render(GameContainer container, Graphics g)
        throws SlickException {
    renderer.update(g);
}

public float getFrameTimeSeconds() {
    return delta;
}

private static long getCurrentTime() {
    return Sys.getTime()*1000/Sys.getTimerResolution();
}

public Renderer getRenderer() {
    return renderer;
}
}

渲染器类:

public class Renderer {
World world;
Player player;

public Renderer(World world, Player player) {
    this.world = world;
    this.player = player;
}

public World getWorld() {
    return world;
}

public void setWorld(World world) {
    this.world = world;
}

public Player getPlayer() {
    return player;
}

public void setPlayer(Player player) {
    this.player = player;
}

public void update(Graphics g) {
    for (int i = 0; i < 2; i++) {
        for (int i2 = 0; i2 < 2; i2++) {
            Vector2f pos = player.getPos();
            Chunk chunk = world.getChunk(new Vector2f((int) Math
                    .floor(pos.x / 16) + i, (int) Math.floor(pos.y / 16)
                    + i2));
            if (chunk != null) {
                for (Tile tile : chunk.getTiles()) {
                    if (tile.getPos().x < player.getPos().x + 
((Display.getWidth() / 2) + 64 + 50) &&
                            tile.getPos().x > player.getPos().x - 
((Display.getWidth() / 2) + 64 + 50) &&
                            tile.getPos().y < player.getPos().y + 
((Display.getHeight() / 2) + 64 + 50) &&
                            tile.getPos().y > player.getPos().y - 
((Display.getHeight() / 2) + 64 + 50))
                    g.drawImage(tile.getTexture().getScaledCopy(64, 64), 
(pos.x - tile.getPos().x)*64+Display.getWidth(),
                            (pos.y - 
tile.getPos().y)*64+Display.getHeight());
                }
            }
        }
    }
}

public void move() {
    Vector2f pos = player.getPos();
    if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
        pos.y += 1 * Main.instance.getFrameTimeSeconds();
    } else if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
        pos.y -= 1 * Main.instance.getFrameTimeSeconds();
    }

    if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
        pos.x += 1 * Main.instance.getFrameTimeSeconds();
    } else if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
        pos.x -= 1 * Main.instance.getFrameTimeSeconds();
    }
    int width = Display.getWidth();
    int height = Display.getHeight();
    Vector2f npos2;
    Vector2f constant = new Vector2f((int) Math.floor((width / 2 - 
pos.x)/256), (int) Math.floor((height / 2 - pos.y)/256));
    Vector2f npos = constant;
    if (world.getChunk(npos) == null) {
        npos2 = npos;
        npos2.x += 1;
        //Right
        if (world.getChunk(npos2) == null) {
            for (int i = 0; i < 2; i++) {
                System.out.println("Generating new chunk! (1) " + npos2.x + 
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " + 
player.getPos().y);
                npos.x += i;
                world.generateChunk(npos);
            }
        }
        npos2.x -= 1;
        npos2.y -= 1;
        //Down
        if (world.getChunk(npos2) == null) {
            for (int i = 0; i < 2; i++) {
                System.out.println("Generating new chunk! (2) " + npos2.x + 
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " + 
player.getPos().y);
                npos.y -= i;
                world.generateChunk(npos);
            }
        }
    }
    npos = constant;
    if (world.getChunk(npos) == null) {
        npos2 = npos;
        npos2.x -= 1;
        //Left
        if (world.getChunk(npos2) == null) {
            for (int i = 0; i < 2; i++) {
                System.out.println("Generating new chunk! (3) " + npos2.x + 
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " + 
player.getPos().y);
                npos.x -= i;
                world.generateChunk(npos);
            }
        }
        npos2.x += 1;
        npos2.y += 1;
        //Up
        if (world.getChunk(npos2) == null) {
            for (int i = 0; i < 2; i++) {
                System.out.println("Generating new chunk! (4) " + npos2.x + 
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " + 
player.getPos().y);
                npos.y += i;
                world.generateChunk(npos);
            }
        }
    }
}
}

该代码运行良好,但问题是帧速率为 4-5 fps。我有一个想法尝试将纹理缝合在一起并像这样渲染它们,这是我的新渲染器代码:

public class Renderer {
World world;
Player player;

public Renderer(World world, Player player) {
    this.world = world;
    this.player = player;
}

public World getWorld() {
    return world;
}

public void setWorld(World world) {
    this.world = world;
}

public Player getPlayer() {
    return player;
}

public void setPlayer(Player player) {
    this.player = player;
}

public void update(Graphics g) {
    for (int i = 0; i < 2; i++) {
        for (int i2 = 0; i2 < 2; i2++) {
            Vector2f pos = player.getPos();
            Chunk chunk = world.getChunk(new Vector2f((int) Math
                    .floor(pos.x / 16) + i, (int) Math.floor(pos.y / 16)
                    + i2));
            if (chunk != null) {
                Image image = null;
                try {
                    image = new Image(4096, 4096);
                } catch (SlickException e1) {
                    e1.printStackTrace();
                }
                Graphics ig = null;
                try {
                    ig = image.getGraphics();
                } catch (SlickException e) {
                    e.printStackTrace();
                }
                for (Tile tile : chunk.getTiles()) {
                    if (tile.getPos().x < player.getPos().x + 
((Display.getWidth() / 2) + 64 + 50) &&
                            tile.getPos().x > player.getPos().x - 
((Display.getWidth() / 2) + 64 + 50) &&
                            tile.getPos().y < player.getPos().y + 
((Display.getHeight() / 2) + 64 + 50) &&
                            tile.getPos().y > player.getPos().y - 
((Display.getHeight() / 2) + 64 + 50))
                    ig.drawImage(tile.getTexture().getScaledCopy(64, 64), 
(pos.x - tile.getPos().x)*64+Display.getWidth(),
                            (pos.y - 
tile.getPos().y)*64+Display.getHeight());
                }
                ig.flush();
                g.drawImage(image, chunk.getPosition().x*256 - pos.x, 
chunk.getPosition().y*256-pos.y);
            }
        }
    }
}

public void move() {
    Vector2f pos = player.getPos();
    if (Keyboard.isKeyDown(Keyboard.KEY_W)) {
        pos.y += 1 * Main.instance.getFrameTimeSeconds();
    } else if (Keyboard.isKeyDown(Keyboard.KEY_S)) {
        pos.y -= 1 * Main.instance.getFrameTimeSeconds();
    }

    if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
        pos.x += 1 * Main.instance.getFrameTimeSeconds();
    } else if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
        pos.x -= 1 * Main.instance.getFrameTimeSeconds();
    }
    int width = Display.getWidth();
    int height = Display.getHeight();
    Vector2f npos2;
    Vector2f constant = new Vector2f((int) Math.floor((width / 2 - 
pos.x)/256), (int) Math.floor((height / 2 - pos.y)/256));
    Vector2f npos = constant;
    if (world.getChunk(npos) == null) {
        npos2 = npos;
        npos2.x += 1;
        //Right
        if (world.getChunk(npos2) == null) {
            for (int i = 0; i < 2; i++) {
                System.out.println("Generating new chunk! (1) " + npos2.x + 
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " + 
player.getPos().y);
                npos.x += i;
                world.generateChunk(npos);
            }
        }
        npos2.x -= 1;
        npos2.y -= 1;
        //Down
        if (world.getChunk(npos2) == null) {
            for (int i = 0; i < 2; i++) {
                System.out.println("Generating new chunk! (2) " + npos2.x + 
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " + 
player.getPos().y);
                npos.y -= i;
                world.generateChunk(npos);
            }
        }
    }
    npos = constant;
    if (world.getChunk(npos) == null) {
        npos2 = npos;
        npos2.x -= 1;
        //Left
        if (world.getChunk(npos2) == null) {
            for (int i = 0; i < 2; i++) {
                System.out.println("Generating new chunk! (3) " + npos2.x + 
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " + 
player.getPos().y);
                npos.x -= i;
                world.generateChunk(npos);
            }
        }
        npos2.x += 1;
        npos2.y += 1;
        //Up
        if (world.getChunk(npos2) == null) {
            for (int i = 0; i < 2; i++) {
                System.out.println("Generating new chunk! (4) " + npos2.x + 
", " + npos2.y + ", Player: x: " + player.getPos().x + ", y: " + 
player.getPos().y);
                npos.y += i;
                world.generateChunk(npos);
            }
        }
    }
}
}

问题是,当我在图块上移动太多时,程序不仅会崩溃,而且我只能得到 1 fps,这根本不是更好。所以我的问题是,以体面的帧速率做到这一点的最佳方法是什么?

使用第二种方法程序崩溃时出错:

#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000006996dfce, 
pid=10024, tid=0x0000000000001ec8
#
# JRE version: Java(TM) SE Runtime Environment (8.0_161-b12) (build 
1.8.0_161-b12)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.161-b12 mixed mode windows- 
amd64 compressed oops)
# Problematic frame:
# C  [atio6axx.dll+0x102dfce]

更新:我没有运气,但是看着其他 2D 游戏渲染数百张图像而不会丢失很多帧,我认为有一些方法可以大大加快我的代码速度。我不渲染屏幕瓷砖,只尝试渲染我需要的尽可能多的内容。

更新 #2:我切换到 libgdx,现在它运行起来就像一个魅力。libgdx 中包含的批处理渲染更适合我的目的。

标签: java2dslick2d

解决方案


推荐阅读