【问题标题】:Graphics painting strategy(performance)图形绘画策略(性能)
【发布时间】:2013-12-20 04:17:44
【问题描述】:

在我的游戏中,我有一个绘制地图图层的游戏面板:“地面/建筑物/对象图层”,然后是玩家精灵,然后是敌人/NPC/小怪,然后是“图层上方(在玩家上方绘制的瓷砖)”。这运行良好且运行顺利。

然后我开始制作小地图 JInternalFrame。它实际上看起来很适合我的需要,但我关心性能。添加小地图后,我注意到绘画速度有所放缓。我最大的分辨率支持以下地图:

else if (scrnsize.width >= 1440 && scrnsize.height >= 1024){ //large&wide
    //45x29(32x32px tiles)
    //1440, 1024

基本上我的问题是,有没有更好的方法可以做到这一点(比下面的代码)或我可以调用屏幕外缓冲区或其他方法的方法?

这是小地图代码。如您所见,除非 refreshMinimap == true(玩家移动或死亡),否则我在paintComponent 中有逻辑不重绘。这有助于消除最明显的滞后,但我仍然注意到一些。任何帮助将不胜感激。

public MinimapGamePanel() {
    super();
    logger.addAppender(GUILog4JFileHelper.fileAppender);
    gamePanelImage = new BufferedImage(32 * MyClient.xTiles, 32 * MyClient.yTiles, BufferedImage.TYPE_INT_ARGB);
    setLayout(new BorderLayout());
    setBorder(BorderFactory.createLineBorder(Color.black));
}

public void paintComponent(Graphics g) {
    logger.trace("begin: REPAINTNG...");
    g2 = (Graphics2D)g;
    //map

    if (refreshMinimap){
        RefreshMinimap();
        refreshMinimap = false;
    }
    g2.drawImage(gamePanelImage, 0, 0, null);

    g2.dispose();

    logger.trace("end: REPAINTNG...");
}

private void RefreshMinimap() {
    logger.trace("Map drawing started.");
    int count = (int) ((MyClient.characterX - (MyClient.xTiles*1.5)) + ((MyClient.characterY - (MyClient.yTiles*2)) * MyClient.mapWidth));
    for (int x = 0; x < MyClient.xTiles*3; x++){
        for (int y = 0; y < MyClient.yTiles*4; y++){
            if (count > -1  && count < (MyClient.mapWidth * MyClient.mapHeight)){
                if (!MyClient.groundLayer[count].equals("0")){ //don't draw full transparent tiles
                    //SpriteStore.get().getSprite("images/tiles/" + MyClient.groundLayer[count] + ".png").draw(gamePanelImage, x, y);
                    SpriteStore.get().getSprite("images/tiles/" + MyClient.groundLayer[count] + ".png").drawFirstPixel(gamePanelImage, x, y);   
                }
                if (!MyClient.buildingLayer[count].equals("0")){ //don't draw full transparent tiles
                    SpriteStore.get().getSprite("images/tiles/" + MyClient.buildingLayer[count] + ".png").drawFirstPixel(gamePanelImage, x, y);
                }
                if (!MyClient.objectLayer[count].equals("0")){ //don't draw full transparent tiles
                    SpriteStore.get().getSprite("images/tiles/" + MyClient.objectLayer[count] + ".png").drawFirstPixel(gamePanelImage, x, y);
                }
            } else {
                SpriteStore.get().getSprite("images/tiles/" + MyClient.groundLayer[0] + ".png").drawFirstPixel(gamePanelImage, x, y);
            }
            count += MyClient.mapWidth;
        }
        count -= MyClient.yTiles * 4 * MyClient.mapWidth;
        count++;
    }
    logger.trace("Map drawing done.");
}

这是仅绘制像素 0,0 的绘图代码

public void drawFirstPixel(BufferedImage gamePanelImage, int xDraw, int yDraw) {
    BufferedImage bufferedVersion = (BufferedImage) image;
    gamePanelImage.getGraphics().drawImage(bufferedVersion.getSubimage(0, 0, 1, 1), xDraw, yDraw, null);    
}

实际上,我对仅获取像素 0,0 的外观非常满意。为质量差、摇摇晃晃的手机图片道歉。

【问题讨论】:

  • 将您的小地图放在另一个线程上的单独 JFrame 中。
  • 哦,这真是个好主意...您会认为制作一个 mmorpg 并在服务器上处理线程我会考虑在客户端使用一个...大声笑不。你认为还有什么可以帮助的吗?我的逻辑合理吗?
  • Put your minimap in a separate JFrame on another thread - 应用程序应该只有一个主 JFrame。为子窗口使用 JDialog。
  • @camickr 感谢您的澄清...现在我的 GUI 是 Frame -> JDesktopPane... 然后我将 JInternalFrames 添加到... theDesktop.add(minimapFrame, JLayeredPane.DEFAULT_LAYER);这是一个好的策略吗?
  • 使用 JInternalFrame 很好,尽管您只需将内部框架直接添加到桌面窗格。我不确定您为什么要引用 DEFAULT_LAYER。阅读 How to Use Internal Frames 上的 Swing 教程,了解一个工作示例。

标签: java graphics


【解决方案1】:

添加小地图后,我注意到绘画速度有所放缓

SpriteStore.get().getSprite("images/tiles/" + MyClient.buildingLayer

不要在绘画方法中进行 I/O。创建类时,应将所有图像读入内存。

【讨论】:

  • 是的,我的 SpriteStore 类将图像保存在内存中...我在开头预加载它们: for (int count = 0; count
  • public Sprite getSprite(String ref) { // 如果我们已经在缓存中获得了精灵 // 然后只返回现有版本 if (sprites.get(ref) != null) {返回 (Sprite) sprites.get(ref); }
【解决方案2】:

这符合你们建议我做的事情吗?

public class MinimapGamePanel extends JPanel {
    ...
    private static Thread t;

    public MinimapGamePanel() {
        super();
        logger.addAppender(GUILog4JFileHelper.fileAppender);
        gamePanelImage = new BufferedImage(320, 320, BufferedImage.TYPE_INT_ARGB);
        //TODO:: change values above upon picking minimap size
        setLayout(new BorderLayout());
        setBorder(BorderFactory.createLineBorder(Color.black));
        t = new Thread(new RefreshMinimapThread());
    }

    public void paintComponent(Graphics g) {
        logger.trace("begin: REPAINTNG...");
        g2 = (Graphics2D)g;

        if (refreshMinimap){

            t.start();
            //RefreshMinimap();
            refreshMinimap = false;
        }
        g2.drawImage(gamePanelImage, 0, 0, null);

        g2.dispose();

        logger.trace("end: REPAINTNG...");
    }

    private class RefreshMinimapThread implements Runnable {
        @Override
        public void run() {
            RefreshMinimap();
        }
    }

【讨论】:

  • 您的 MiniMap 是一个单独的组件。您不应该尝试从您的绘画方法中控制该组件的绘画。您的每个代码都是游戏窗格上的调用 repaint() 的地方,您也应该在迷你面板中进行重绘。最终,所有组件的所有绘制都在 Event Dispatch Thread 上完成。当您有大量 CPU 密集型处理来处理您的游戏逻辑时,您可以使用单独的线程。然后调用 repaint() 并将绘画请求放回 EDT。
  • 好的,谢谢您的回复。我会再读几遍,直到它有意义为止。我很困惑我没有按照你说的去做。我现在在线程中运行 RefreshMinimap() 方法 - 这是 cpu 密集型任务。我将继续破解我的方式。我要做一些打印来检查时间。再次感谢所有的帮助。真的很感激!
  • I am running the RefreshMinimap() method in a thread now,您正在从 paintComponent() 方法调用线程。不应该从那里完成。
猜你喜欢
  • 2015-09-12
  • 1970-01-01
  • 2023-03-26
  • 2011-02-04
  • 1970-01-01
  • 1970-01-01
  • 2012-06-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多