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

时间:2014-03-06 16:51:12

标签: java performance swing awt mixing

我正在尝试开发一种从[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;
}
}

1 个答案:

答案 0 :(得分:3)

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

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

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

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

  3. 这两个问题的解决方案可以是将图像转换为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图片。

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

    在任何情况下,很难预测这些变化实际带来多少加速,但它们应该值得一试。