当前位置: 首页 > 知识库问答 >
问题:

Java 2D游戏磁贴渲染优化

姜育
2023-03-14

我一直在尝试制作一款基于2D Tile的游戏,但在出现问题之前并没有走得太远。游戏很好,除了它非常慢,并且空间不断出现在瓷砖之间。我尝试将所有磁贴图像放入一个图像中以加载以使其更平滑,但它不起作用。我需要有关如何优化游戏以获得更好的fps的帮助。

大多数显示类

Player p = new Player();
static Map m = new Map();
Hotbar h = new Hotbar();
static Zombie z = new Zombie();

public static void main(String args[]) {
    Thread t1 = new Thread(new Display());
    t1.start();
    Thread t2 = new Thread(z);
    t2.start();
    Thread t3 = new Thread(m);
    t3.start();
}

@SuppressWarnings("static-access")
public void run() {
    while (true) {
        try {
            oldTime = currentTime;
            currentTime = System.currentTimeMillis();
            elapsedTime = currentTime - oldTime;
            fps = (int) ((1000 / 40) - elapsedTime);
            Thread.sleep(fps);
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (started) {
            p.playerMovement();
            p.width = getWidth();
            p.height = getHeight();
            h.width = getWidth();
            h.height = getHeight();
            if (p.maptiles != null) {
                z.maptiles = new Rectangle[p.maptiles.length];
                z.maptiles = p.maptiles;
            }
        } else {

        }
        repaint();
    }
}

大多数地图类

BufferedImage backLayer;
BufferedImage backLayer2;

@SuppressWarnings("static-access")
public void createBack() {
    backLayer = new BufferedImage(mapwidth * 32, mapheight * 32, BufferedImage.TYPE_INT_ARGB);
    Graphics g = backLayer.getGraphics();
    Graphics2D gd = (Graphics2D) g;
    Color daycolor = new Color(0, 150, 255, 255);
    Color nightcolor = new Color(0, 50, 100, Math.abs(time / daylength - 510 / 2));
    Color backtilecolor = new Color(10, 10, 20, Math.abs(time / daylength - 510 / 4));
    g.setColor(daycolor);
    g.fillRect(0, 0, p.width, p.height);
    g.setColor(nightcolor);
    g.fillRect(0, 0, p.width, p.height);
    time++;
    if (time > 510 * daylength)
        time = 0;
    if (time < 100 * daylength || time > 410 * daylength) {
        z.SpawnZombie();

    } else {

    }
    g.setColor(backtilecolor);
    int i = 0;
    for (int x = 0; x < mapwidth; x++) {
        for (int y = 0; y < mapheight; y++) {
            if (mapdatasky[i] == 0) {
                p.mapsky[i] = new Rectangle(x * 32 - p.playerX, y * 32 - p.playerY, 32, 32);
            }
            if (mapdatasky[i] >= 1) {
                g.drawImage(tiles[mapdatasky[i]], x * 32 - p.playerX, y * 32 - p.playerY, 32, 32, null);
                mapsky[i] = new Rectangle(x * 32 - p.playerX, y * 32 - p.playerY, 32, 32);
                p.mapsky[i] = new Rectangle(x * 32 - p.playerX, y * 32 - p.playerY, 32, 32);
                gd.fill(mapsky[i]);
            }
            i++;
        }
    }
    backLayer2 = backLayer;
}

BufferedImage middleLayer;
BufferedImage middleLayer2;

@SuppressWarnings("static-access")
public void createMiddle() {
    middleLayer = new BufferedImage(mapwidth * 32, mapheight * 32, BufferedImage.TYPE_INT_ARGB);
    Graphics g = middleLayer.getGraphics();
    Color fronttilecolor = new Color(20, 20, 40, Math.abs(time / daylength - 510 / 4));
    int i = 0;
    for (int x = 0; x < mapwidth; x++) {
        for (int y = 0; y < mapheight; y++) {
            if (mapdata[i] == -1) {
                spawnX = x * 32 - p.width;
                spawnY = y * 32 - p.height;
                p.spawnX = spawnX;
                p.spawnY = spawnY;
            }
            if (mapdata[i] == 0) {
                p.mapsky[i] = new Rectangle(x * 32 - p.playerX, y * 32 - p.playerY, 32, 32);
            }
            if (mapdata[i] > 0) {
                g.drawImage(tiles[mapdata[i]], x * 32 - p.playerX, y * 32 - p.playerY, 32, 32, null);
                maptiles[i] = new Rectangle(x * 32 - p.playerX, y * 32 - p.playerY, 32, 32);
                p.maptiles[i] = new Rectangle(x * 32 - p.playerX, y * 32 - p.playerY, 32, 32);
            }
            if (!p.breaking) {
                tileid = -1;
            }
            if (tileid == i) {
                g.setColor(new Color(100, 0, 0, 100));
                g.fillRect(x * 32 - p.playerX + 16 - timer / (breaktime / 16), y * 32 - p.playerY + 16 - timer / (breaktime / 16), (timer / (breaktime / 16)) * 2, (timer / (breaktime / 16)) * 2);
            }
            i++;
        }
    }
    g.setColor(fronttilecolor);
    g.fillRect(0, 0, p.width, p.height);
    middleLayer2 = middleLayer;
}


BufferedImage both;
BufferedImage both2;

public void mergeLayers() {
    both = new BufferedImage(mapwidth * 32, mapheight * 32, BufferedImage.TYPE_INT_ARGB);
    Graphics g = both.getGraphics();
    g.drawImage(backLayer2, 0, 0, mapwidth * 32, mapheight * 32, null);
    g.drawImage(middleLayer2, 0, 0, mapwidth * 32, mapheight * 32, null);
    both2 = both;
}

public void renderMap(Graphics g) {
    g.drawImage(both2, 0, 0, mapwidth * 32, mapheight * 32, null);
    if (placetimer > 0)
        placetimer--;
}
@Override
public void run() {
    while (true) {
        try {
            oldTime = currentTime;
            currentTime = System.currentTimeMillis();
            elapsedTime = currentTime - oldTime;
            fps = (int) ((1000 / 100) - elapsedTime);
            Thread.sleep(fps);
            if (!mapset) {
                setMap();
                mapset = true;
            }
            createBack();
            createMiddle();
            mergeLayers();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

这是图块之间渲染错误的图片

共有1个答案

汪永春
2023-03-14

我同意在这里进行审查可能是适当的。

但一般提示:如果您出于性能原因添加了此“合并”(createBack/createMiddle/mergeLayers):请不要这样做!在那里,您正在绘制所有瓷砖(也可能是一些无论如何都不可见的瓷砖,当它们直接使用g.drawImage绘制时可能会被剪掉)。将许多小图像绘制成大图像,然后在屏幕上绘制大图像,很难比首先直接在屏幕上绘制小图像更快。

如果您添加此“合并”来解决出现的“条纹”:不要这样做!条纹来自于从不同的线程更改的坐标,而不是绘制图像的线程。您可以通过更改计算图块及其坐标的方式来避免这种情况。代码有点太......“复杂”,所以我将在这里使用一些伪代码:

void paintTiles(Graphics g)
{
    for (Tile tile : allTiles)
    {
        g.drawImage(tile, player.x, player.y, null);
    }
}

这里的问题是,当绘画线程在所有图块上迭代时,另一个线程可能会更改播放器坐标。例如,一些图块可能使用 player.x=10player.y=20 进行绘制,然后另一个线程更改播放器坐标,因此剩余的图块绘制为 player.x=15player.y=25 - 您会注意到这是在图块之间出现的“条纹”。

在最好的情况下,这可以很容易地解决:

void paintTiles(Graphics g)
{
    int currentPlayerX = player.x;
    int currentPlayerY = player.y;

    for (Tile tile : allTiles)
    {
        g.drawImage(tile, currentPlayerX, currentPlayerY, null);
    }
}

这样,“当前”玩家坐标将在迭代瓦片时保持不变。

 类似资料:
  • 我正在开发一个基于2D图块的游戏,其中所有图块旋转45度。 我想这样做: 原因是为了在2D游戏中创建不同的视角。 有相当多的基于瓷砖的游戏教程,但我不知道有像这样的旋转瓷砖。 所以我的问题是:如何创建旋转45度的瓷砖?我会感谢任何代码或链接。 编辑:忘了说我用的是Java Graphics2D,不是OpenGL

  • 优化提升渲染性能,不仅能让页面更快的展现、可交互,同时能提升用户操作滚动的流畅度,对提升用户体验至关重要。 避免不必要的更新对比 Rax 同 React 一样,render 时会有 vdom 对比,如果对比发现 DOM 没有变化时,不会去真正更新页面。而本身 vdom 对比也是不小的消耗,你应该避免这种不必要的更新对比,使用 shouldComponentUpdate 方法明确标识你的组件什么时候

  • 我目前正在从事一个项目,该项目涉及将LWJGL游戏场景渲染为视频流而不是窗口。我相信,如果将游戏场景渲染为中间格式(如 ByteBuffer),我就能实现这一目标。我正在尝试扩展LWJGL 演示作为概念证明。 我发现了一个类似的SO问题和一个论坛帖子,但我无法做到这一点。我是OpenGL和LWJGL的初学者,我正在努力寻找关于这方面的可理解文档。 在渲染循环()的开始,调用函数。根据我的理解,它将

  • 直接把我去年的面试题从知乎上搬过来了。有些题目可能没有固定答案 #24届软开秋招面试经验大赏##我的实习求职记录##引擎开发实习##图形引擎实战#

  • 我正在做一个2D平台,只是增加了重力 然而,如果空格键在玩家完成跳跃后仍然被按住,玩家就会在半空中继续跳跃。 我知道我需要检查球员是否真的在地面上,但当我这样做时,它总是返回“false”,如我的跳跃方法所述:

  • 在reactjs中,我试图从贴图对象渲染组件。更具体地说,按下一个按钮,我创建了一个新的“FormRow”组件,为了方便起见,我将其存储在javascript映射对象中(因为稍后我将使用它)。无论何时,我都想渲染刚刚添加的新组件,但我不知道如何从贴图中获取它。 我尝试以不同的方式解决我的问题,使用: myMap.for每个(key= 我没有尝试的是: https://stackoverflow.c