java游戏图像加载速度非常慢

我正在尝试开发一种从[100] [100]矩阵中导入背景图像的游戏。 矩阵将保持int值以与应在背景上绘制的内容相关联。 循环将图像绘制到canvas并根据用户的键输入更新它。 然而,一切都是油漆和移动都很好,很慢。 是否有更好的方法来加载图像而不是我正在做的方式?

这是主要的游戏类:

package com.game.src.main; import java.awt.Canvas; import java.awt.Dimension; import java.awt.Graphics; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.event.KeyEvent; import java.awt.image.BufferStrategy; import java.awt.image.BufferedImage; import java.io.IOException; import javax.swing.JFrame; public class Game extends Canvas implements Runnable{ static GraphicsEnvironment environment; static GraphicsDevice device; private static final long serialVersionUID = 1L; public static final int WIDTH = 320; public static final int HEIGHT = WIDTH / 12 * 9; public static final int SCALE = 2; public static final String TITLE = "fgfdsa"; private boolean running = false; private Thread thread; private Player p; private Background b; private Controller c; private BufferedImage spriteSheet; boolean isFiring = false; public void init(){ BufferedImageLoader loader = new BufferedImageLoader(); try{ spriteSheet = loader.loadImage("/sprite_sheet_test.png"); }catch(IOException e){ e.printStackTrace(); } requestFocus(); addKeyListener(new KeyInput(this)); c = new Controller(); p = new Player(getWidth() / 2, getHeight() / 2, this); b = new Background(this); } private synchronized void start(){ if(running) return; running = true; thread = new Thread(this); thread.start(); } private synchronized void stop(){ if(!running) return; running = false; try { thread.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.exit(1); } public void run(){ init(); long lastTime = System.nanoTime(); final double amountOfTicks = 60.0; double ns = 1000000000 / amountOfTicks; double delta = 0; int updates = 0; int frames = 0; long timer = System.currentTimeMillis(); while(running){ long now = System.nanoTime(); delta += (now - lastTime) / ns; lastTime = now; if(delta >= 1){ tick(); updates++; delta--; } render(); frames++; if(System.currentTimeMillis() - timer > 1000){ timer += 1000; System.out.println(updates + " Ticks, Fps " + frames); updates = 0; frames = 0; } } stop(); } public void tick(){ p.tick(); b.tick(); c.tick(); } public void render(){ BufferStrategy bs = this.getBufferStrategy(); if(bs == null){ createBufferStrategy(3); return; } Graphics g = bs.getDrawGraphics(); b.render(g); p.render(g); c.render(g); g.dispose(); bs.show(); } public void keyPressed(KeyEvent e){ int key = e.getKeyCode(); switch(key){ case 37: b.setX(5); break; case 38: b.setY(5); break; case 39: b.setX(-5); break; case 40: b.setY(-5); break; case 32: if(!isFiring){ c.addBullet(new Bullet(p.getX(), p.getY(), this)); isFiring = true; } } } public void keyReleased(KeyEvent e){ int key = e.getKeyCode(); switch(key){ case 37: b.setX(0); break; case 38: b.setY(0); break; case 39: b.setX(0); break; case 40: b.setY(0); break; case 32: isFiring = false; } } public static void main(String[] args){ Game game = new Game(); game.setPreferredSize(new Dimension(600, 600)); game.setMaximumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE)); game.setMinimumSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE)); JFrame frame = new JFrame(game.TITLE); frame.add(game); frame.pack(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setResizable(false); frame.setLocationRelativeTo(null); frame.setVisible(true); environment = GraphicsEnvironment.getLocalGraphicsEnvironment(); device = environment.getDefaultScreenDevice(); frame.setExtendedState(JFrame.MAXIMIZED_BOTH); game.start(); } public BufferedImage getSpriteSheet(){ return spriteSheet; } } 

这是用于将图像绘制到屏幕的背景类:

 package com.game.src.main; import java.awt.Graphics; import java.awt.image.BufferedImage; public class Background { private BufferedImage grass; private BufferedImage background; private BufferedImage tree; int[][] matrix; Game game; //original starting coordinates of matrix to be drawn int setX = -3200; int setY = -3200; //integers used to update coordinates of the matrix to be drawn int helpX = 0; int helpY = 0; public Background(Game game){ this.game = game; // load matrix into matrix array GetMatrix gm = new GetMatrix(); matrix = gm.getMatrix(); //import the sprite from game class background = game.getSpriteSheet(); //call sprite sheet class SpriteSheet ss = new SpriteSheet(background); //get coordinates of grass image grass = ss.grabImage(1, 1, 32, 32); // get coordinates of tree image tree = ss.grabImage(4, 1, 32, 32); } public void tick(){ //update the start pixel of the background setX += helpX; setY += helpY; if(setX > 0) setX = 0; if(setX  0) setY = 0; if(setY < -5340) setY = -5340; } public void render(Graphics g){ int x = 0; int y = 0; for(int i = setX; i < setX + 6400; i +=64){ x = 0; for(int j = setY; j < setY + 6400; j += 64){ switch(matrix[x][y]){ case 0: g.drawImage(grass, i, j, i + 64, j + 64, 0, 0, 32, 32, null); break; case 1: g.drawImage(grass, i, j, i + 64, j + 64, 0, 0, 32, 32, null); g.drawImage(tree, i, j, i + 64, j + 64, 0, 0, 32, 32, null); } x++; } y++; } } //sets the background start coordinates from key input public void setX(int x){ helpX = x; } public void setY(int y){ helpY = y; } } 

SpriteSheet#grabImage(...)作用并不明显。 但我很确定有一些涉及BufferedImage#getSubImage(...)调用。 是对的吗?

如果这是正确的,这里有两个潜在的问题:

  1. 使用ImageIO加载图像时,生成的图像的类型未知。 通过“type”我引用BufferedImage#getType() 。 此类型可能是BufferedImage.TYPE_CUSTOM ,特别是当它是具有透明度的PNG时。 当绘制具有此类型的图像时,此绘画非常慢,因为内部已完成一些颜色转换。

  2. 当您调用BufferedImage#getSubImage(...) ,您调用此方法的图像将变为“非托管”。 这意味着实际的图像数据不能再直接保存在video内存中。 (这个细节背后有一些非常复杂的技术细节。这些细节可能会在不同的JRE版本之间发生变化。例如,它们在Java 6和Java 7之间发生了变化。但是,经验法则是:如果你想要绘制高图像性能,不要在你想要绘制的图像上调用BufferedImage#getSubImage(...)

这两个问题的解决方案可以是将图像转换为BufferedImage.TYPE_INT_ARGB类型的托管图像。 因此,对于您想要绘制的每个图像,您都可以调用

 BufferedImage toPaint = convertToARGB(originalImage); 

用这种方法:

 public static BufferedImage convertToARGB(BufferedImage image) { BufferedImage newImage = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g = newImage.createGraphics(); g.drawImage(image, 0, 0, null); g.dispose(); return newImage; } 

在您的示例中,您可以将其应用于grasstree图像。

另一个(可能更重要的)问题是你似乎正在绘制缩放的瓷砖:你似乎画了一个尺寸为32×32的64×64像素的精灵。 如果这是正确的,那么您可以考虑重新调整输入图像一次 ,然后绘制原始大小为32×32的图块。

无论如何,很难预测这些变化实际带来多少加速,但它们应该值得一试。