1

我刚刚完成了一款扫雷游戏,除了一件事,将图像加载到游戏中的速度之外,一切都运行良好。我注意到如果我在游戏中有大量单元格,鼠标单击单元格后加载速度会非常慢,如果单元格数量较少,加载速度会更快。有没有其他方法可以使加载图像比我使用的更快?这是我用来将图像加载到游戏中的方法:

   private void draw(Graphics g) {
        BufferedImage gRec =null, flag=null, mine=null, aCount0=null,
            aCount1=null,aCount2 =null,aCount3 =null,aCount4 =null,aCount5 =null,
            aCount6 =null,aCount7 =null,aCount8 = null;
        try {
           gRec = ImageIO.read(new File("/Users/msa_666/Desktop/blank.gif"));
                flag = ImageIO.read(new File("/Users/msa_666/Desktop/bombflagged.gif"));
                mine = ImageIO.read(new File("/Users/msa_666/Desktop/bombdeath.gif"));
                flag = ImageIO.read(new File("/Users/msa_666/Desktop/bombflagged.gif"));
                aCount0 = ImageIO.read(new File("/Users/msa_666/Desktop/open0.gif"));
                aCount1 = ImageIO.read(new File("/Users/msa_666/Desktop/open1.gif"));
                aCount2 = ImageIO.read(new File("/Users/msa_666/Desktop/open2.gif"));
                aCount3 = ImageIO.read(new File("/Users/msa_666/Desktop/open3.gif"));
                aCount4 = ImageIO.read(new File("/Users/msa_666/Desktop/open4.gif"));
                aCount5 = ImageIO.read(new File("/Users/msa_666/Desktop/open5.gif"));
                aCount6 = ImageIO.read(new File("/Users/msa_666/Desktop/open6.gif"));
                aCount7 = ImageIO.read(new File("/Users/msa_666/Desktop/open7.gif"));
                aCount8 = ImageIO.read(new File("/Users/msa_666/Desktop/open8.gif"));
                }
                 catch (IOException e) { 
              e.printStackTrace();
           } 


        if (getCovered() == true && getMarked () == false) {    // gray rectangle
           g.drawImage(gRec,getX(),getY(),w,h,null);

        }
        else if (getCovered()==true && getMarked() ==  true) {  //flag
           g.drawImage(flag,getX(),getY(),w,h,null);

        }
        else if (getCovered()== false && getMined()== true){        //mine
           g.drawImage(mine,getX(),getY(),w,h,null);

        }
        else if ( getCovered() == false && getMined() == false) {   // adjacency count image
            switch (getAdjCount()){
                case 0:
           g.drawImage(aCount0,getX(),getY(),w,h,null);
                break;
                case 1:
           g.drawImage(aCount1,getX(),getY(),w,h,null);
                break;
                case 2:
           g.drawImage(aCount2,getX(),getY(),w,h,null);
                break;
                case 3:
           g.drawImage(aCount3,getX(),getY(),w,h,null);
                break;

                case 4:
           g.drawImage(aCount4,getX(),getY(),w,h,null);
                break;
                case 5:
           g.drawImage(aCount5,getX(),getY(),w,h,null);
                break;
                case 6:
           g.drawImage(aCount6,getX(),getY(),w,h,null);
                break;
                case 7:
           g.drawImage(aCount7,getX(),getY(),w,h,null);
                break;
                case 8:
           g.drawImage(aCount8,getX(),getY(),w,h,null);
                break;


        }
     }
    }

这是单击每个单元格后重新绘制每个单元格的鼠标侦听器:

   public void mouseClicked(MouseEvent e) {
      int sRow, sCol;
      sRow= e.getX() / cellHeight;
      sCol = e.getY()/ cellWidth;
      System.out.println(e.getX() +"," +sCol);
      System.out.println(e.getY()+","+sRow);
      if (e.getButton() == MouseEvent.BUTTON1) {
        if( cells[sRow][sCol].getMarked() == false)     
           uncoverCell(cells[sRow][sCol]);
          // cells[sRow][sCol].setCovered(false);
        System.out.println(cells[sRow][sCol].getMined());
        repaint();
     }
     else if (e.getButton() == MouseEvent.BUTTON2) {
     }
     else if (e.getButton() == MouseEvent.BUTTON3) {
        if (cells[sRow][sCol].getMarked() == false){
           cells[sRow][sCol].setMarked(true);
                       repaint();

        }
        else {
           cells[sRow][sCol].setMarked(false);          
           repaint();           
        }
     }

     if (allMinesMarked() && allNonMinesUncovered()){
        System.out.println("You Win");
     }
  }


  public void paintComponent(Graphics g) { 
     for ( int i=0 ; i <rowCount; i++ ) {
        for (int j=0; j<columnCount; j++) {
           cells[i][j].draw(g);
        }
     }

  }
4

2 回答 2

2

您需要告诉我们:

  • 只是在哪里draw(...)叫?
  • 如何获得传递给 draw 方法参数的 Graphics 对象 g?

我在这里猜测是因为我们没有所有相关代码,但看起来你每次想要显示图像时都在重新阅读图像。如果是这样,请不要这样做。在程序开始时只读取一次图像,然后使用图像或更好的 ImageIcons,在您需要它们时获得。

编辑
感谢您发布更多代码,这实际上证实了我的怀疑:您在每次重新绘制 GUI 时重新读取图像文件。这是非常低效的,并且会使您的程序慢下来。同样,您应该将图像读入您的程序一次,然后多次使用它们。

我自己会从图像中创建 ImageIcons,然后将它们显示在 JLabel 中。当需要交换图像时,只需调用setIcon(...)JLabel。这样就不用乱搞了paintComponent(...)

编辑 2
例如(编译并运行它):

import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.*;

public class SwapIcons {
   private static final int CELL_SIDE_COUNT = 3;
   private ImageCell[] imageCells = new ImageCell[CELL_SIDE_COUNT * CELL_SIDE_COUNT];
   private JPanel mainPanel = new JPanel();

   public SwapIcons(final GetImages getImages) {
      mainPanel.setLayout(new GridLayout(CELL_SIDE_COUNT, CELL_SIDE_COUNT));
      mainPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));

      for (int i = 0; i < imageCells.length; i++) {
         imageCells[i] = new ImageCell(getImages);
         mainPanel.add(imageCells[i].getImgLabel());
      }
   }

   public JComponent getMainComponent() {
      return mainPanel;
   }

   private static void createAndShowGui(GetImages getImages) {
      SwapIcons swapIcons = new SwapIcons(getImages);

      JFrame frame = new JFrame("Click on Icons to Change");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(swapIcons.getMainComponent());
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      try {
         final GetImages getImages = new GetImages();
         SwingUtilities.invokeLater(new Runnable() {
            public void run() {
               createAndShowGui(getImages);
            }
         });
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

class ImageCell {
   private JLabel imgLabel = new JLabel();
   private GetImages getImages;
   private int iconIndex = 0;

   public ImageCell(final GetImages getImages) {
      this.getImages = getImages;
      imgLabel.setIcon(getImages.getIcon(0));
      imgLabel.addMouseListener(new MyMouseListener());
   }

   public JLabel getImgLabel() {
      return imgLabel;
   }

   private class MyMouseListener extends MouseAdapter {
      @Override
      public void mousePressed(MouseEvent e) {
         iconIndex++;
         iconIndex %= getImages.getIconListSize();
         imgLabel.setIcon(getImages.getIcon(iconIndex));
      }
   }
}

// Simply gets a SpriteSheet and subdivides it into a List of ImageIcons
class GetImages {
   private static final String SPRITE_PATH = "http://th02.deviantart.net/"
         + "fs70/PRE/i/2011/169/0/8/blue_player_sprite_sheet_by_resetado-d3j7zba.png";
   public static final int SPRITE_ROWS = 6;
   public static final int SPRITE_COLS = 6;
   public static final int SPRITE_CELLS = 35;

   private List<ImageIcon> iconList = new ArrayList<ImageIcon>();

   public GetImages() throws IOException {
      URL imgUrl = new URL(SPRITE_PATH);
      BufferedImage mainImage = ImageIO.read(imgUrl);

      for (int i = 0; i < SPRITE_CELLS; i++) {
         int row = i / SPRITE_COLS;
         int col = i % SPRITE_COLS;
         int x = (int) (((double) mainImage.getWidth() * col) / SPRITE_COLS);
         int y = (int) ((double) (mainImage.getHeight() * row) / SPRITE_ROWS);
         int w = (int) ((double) mainImage.getWidth() / SPRITE_COLS);
         int h = (int) ((double) mainImage.getHeight() / SPRITE_ROWS);
         BufferedImage img = mainImage.getSubimage(x, y, w, h);
         ImageIcon icon = new ImageIcon(img);
         iconList.add(icon);
      }
   }

   // get the Icon from the List at index position
   public ImageIcon getIcon(int index) {
      if (index < 0 || index >= iconList.size()) {
         throw new ArrayIndexOutOfBoundsException(index);
      }

      return iconList.get(index);
   }

   public int getIconListSize() {
      return iconList.size();
   }

}
于 2013-03-30T12:33:05.100 回答
0

充满鳗鱼的气垫船答案很好并且会起作用。并且适用于独立应用程序,但对于小程序或 Web 启动应用程序可以通过拥有一个大图像然后将其部分复制到可见的图形对象来进一步优化,考虑网格并使用 java.awt.Graphics 对象中的函数(来自javadoc):

公共抽象布尔drawImage(图像img,int dx1,int dy1,int dx2,int dy2,int sx1,int sy1,int sx2,int sy2,ImageObserver观察者)

绘制当前可用的指定图像的指定区域,动态缩放以适应目标可绘制表面的指定区域。透明像素不会影响已经存在的任何像素。

此方法在所有情况下都会立即返回,即使要绘制的图像区域尚未针对当前输出设备进行缩放、抖动和转换。如果当前输出表示尚未完成,则 drawImage 返回 false。随着更多图像可用,加载图像的进程会通知指定的图像观察者。

此方法始终使用图像的未缩放版本来渲染缩放的矩形并即时执行所需的缩放。它不使用缓存的、缩放版本的图像进行此操作。执行从源到目标的图像缩放,使得源矩形的第一坐标映射到目标矩形的第一坐标,并且第二源坐标映射到第二目标坐标。根据需要对子图像进行缩放和翻转以保留这些映射。

参数: img - 要绘制的指定图像。如果 img 为 null,则此方法不执行任何操作。dx1 - 目标矩形第一个角的 x 坐标。dy1 - 目标矩形第一个角的 y 坐标。dx2 - 目标矩形第二个角的 x 坐标。dy2 - 目标矩形第二个角的 y 坐标。sx1 - 源矩形第一个角的 x 坐标。sy1 - 源矩形第一个角的 y 坐标。sx2 - 源矩形第二个角的 x 坐标。sy2 - 源矩形第二个角的 y 坐标。观察者 - 当更多图像被缩放和转换时要通知的对象。返回: 如果图像像素仍在变化,则返回 false;否则为真。

这更好,因为建立新连接并通过 Internet 下载图像需要几秒钟,所以如果您有一个主图像,其中所有子图像都在一个大表中,那么下载、加载和渲染的总时间会更少。从一个区域复制的额外逻辑是微不足道的,也许 .1KB 的 jar 文件空间:)

于 2013-03-30T16:07:30.350 回答