【问题标题】:java game images loading very slowlyjava游戏图像加载非常缓慢
【发布时间】:2014-04-09 11:10:24
【问题描述】:

我正在尝试开发一个从 [100][100] 矩阵导入背景图像的游戏。该矩阵将保存 int 值以与应在背景上绘制的内容相关联。一个循环将图像绘制到画布上,并根据用户的键输入对其进行更新。一切都可以很好地绘制和移动,但是速度很慢。有没有更好的方法来加载图像而不是我这样做的方式?

这是主要的游戏类:

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 < -4500)
        setX = -4500;
    if(setY > 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;
}
}

【问题讨论】:

  • 那么是绘画慢还是从磁盘加载图像慢?
  • 神圣的未签名长蝙蝠侠! Short, Self Contained, Correct (Compilable), Examples的制作方法请参考sscce.org
  • 我相信它是图像的加载。当我绕过图像并打印填充矩形时,我的刻度保持在 60,我的 fps 下降到 350 左右。当我不绘制任何东西时,我的刻度为 60,我的 fps 为 2500。当我从文件中绘制图像时我的滴答声下降到 12 左右,我的 fps 下降到 15 左右
  • 哈哈,请记住这一点,我一直认为代码越多越好,这样您就可以轻松编译它。我会在未来修复它。
  • 可能是渲染方法中的双重for循环

标签: java performance swing awt mixing


【解决方案1】:

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

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

  1. 当您使用 ImageIO 加载图像时,生成的图像的类型是未知的。通过“类型”,我指的是BufferedImage#getType()。这种类型可能是BufferedImage.TYPE_CUSTOM,尤其是当它是具有透明度的PNG 时。当绘制这种类型的图像时,这种绘制速度非常慢,因为在内部进行了一些颜色转换。

  2. 当您调用BufferedImage#getSubImage(...) 时,调用此方法的图像将变为“非托管”。这意味着实际的图像数据不能再直接保存在视频内存中。 (这背后有一些非常复杂的技术细节。这些细节可能会在不同的 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 图像。

另一个(也许更重要的)问题是您似乎在绘制您的图块缩放:您似乎绘制了一个 64x64 像素、大小为 32x32 的精灵。如果这是正确的,那么您可以考虑重新缩放输入图像一次,然后绘制原始大小为 32x32 的图块。

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

【讨论】:

  • 这正是我设置它的方式。 SpriteSheet 使用一种方法返回精灵表的一部分,然后将其绘制在屏幕上。我可以通过直接在循环中写入坐标来绘制背景来轻松绕过它。但是,当我尝试转换使用 TYPE_INT_RGB 加载的缓冲图像时,整个屏幕都会变黑。
  • 所以当你用grass = convertToARGB(ss.grabImage(1, 1, 32, 32));替换grass = ss.grabImage(1, 1, 32, 32);时它不再起作用了?这很难想象,你能提供更多信息吗(也许是一个例子,连同一张图片,可以重现这种行为)?
  • 哈哈是我的错,我在测试它并忘记将我的循环放回绘制它。它正在工作,但它仍然很慢。但是,比以前快一点
  • 啊,我采取缩放并根据尺寸直接涂上它们。现在速度快了很多。你先生是个天才。感谢您的帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-11
  • 1970-01-01
  • 2019-12-21
  • 2022-10-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多