Swing 2D游戏性能低下

时间:2015-05-27 21:07:01

标签: java performance swing awt repaint

我正在制作Flappy Bird的克隆。我的表现非常好:60 fps。这是它只有一个支柱/障碍的时候。一旦我添加其中3个,我的fps降至30以下。然后游戏现在无法播放。我知道这与repaint()一直有关。

以下是代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

/**
 * Created by Lazar on 25/05/15.
 */
public class Environment extends JComponent implements ActionListener {

    public static final Dimension dimension = new Dimension(800,600);
    BufferedImage img;
    BufferedImage ptica1;
    BufferedImage ptica2;
    double skokbrojac = 0; 
    int brzina = 4; // speed // MUST Background % brzina = 0
    int dx;
    int dx2;
    int pad = 0; //drop
    Timer timer;
    boolean parno;
    boolean skok = false;

    //Stubovi // Pillars
    Stub stub1 = new Stub();
    Stub stub2 = new Stub();
    Stub stub3 = new Stub();
    ArrayList<Stub>stubovi = new ArrayList<Stub>();
    int razmakStub; // Space between pillars

    public Environment() {
        setPreferredSize(dimension);
        img = Util.openImage("pozadina.png");
        ptica1 = Util.openImage("ptica1.png");
        ptica2 = Util.openImage("ptica2.png");

        stubovi.add(stub1);
        stubovi.add(stub2);
        stubovi.add(stub3);

        dx = img.getWidth()/2;
        timer = new Timer(1000/60,this); 
        timer.start();

        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                super.mousePressed(e);
                skok = true;  // start jump
                skokbrojac = 0; //jump frame counter
            }
        });

    }

    protected void paintComponent(Graphics g){
        Graphics2D g2d = (Graphics2D)g;
        //g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        //g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        if(dx == img.getWidth()){ //image horizontal scroll
            dx2 = 0;
        }
        if(dx2 == img.getWidth()/2){ //image horizontal scroll
            dx = dimension.width;
        }
        g2d.drawImage(img,getWidth() - dx, 0, null); //draw background
        if(dx >= img.getWidth()){
            g2d.drawImage(img,getWidth() - dx2, 0, null);
        }
        if(parno){
            g2d.drawImage(ptica1,dimension.width/2, 290 + pad, null); //draw bird
        }
        else{
            g2d.drawImage(ptica2,dimension.width/2, 290 + pad, null); //draw bird
        }
        stub1.postoji = true; //pillar1 exists?
        if(razmakStub > 240){
            stub2.postoji = true;
        }
        if(razmakStub > 480){ //pillar1 exists?
            stub3.postoji = true;
        }
        for(Stub i : stubovi){ //draw pillars if they exist
            if(i.postoji)
                i.crtaj(g2d);
        }
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        dx = dx + brzina; 
        dx2 = dx2 + brzina;

        if(skokbrojac > 5) // jump frame lenght
            skok = false;
        if(skok){
            pad -= 15; // jump height
        }
        else{  
            pad += 8; //rate of the fall
        }
        skokbrojac++;
        parno ^= true; // for different bird images
        if(290 + pad >= 536 || 290 + pad<= 3) //border hit detect
            timer.stop();
        razmakStub += brzina;
        for(Stub i : stubovi){ //reset pillars and make them move
            if(i.postoji){
                if(i.getDx() < -50){
                    i.setDx(800);
                    i.randomDy();
                }
                i.setDx(i.getDx() - brzina);
            }
        }   
        repaint();
    }
}

Complete project source

另外请记住,这是真正的未经修饰的版本,因此代码很难看。我正在寻找一种提高性能的解决方案。

主类:

import javax.swing.*;

/**
 * Created by Lazar on 25/05/15.
 */
public class Main {

    public static void main(String[] args){

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Frame(new Environment());
            }
        });
    }
}

框架类:

import javax.swing.*;

/**
 * Created by Lazar on 25/05/15.
 */
public class Frame extends JFrame{

    public Frame(JComponent content){
        setContentPane(content);
        setTitle("Flappy");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setSize(getPreferredSize());
        setResizable(false);
        setVisible(true);
        setLocationRelativeTo(null);
    }
}

Stub / Pillar类:

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;

/**
 * Created by Lazar on 26/05/15.
 */
public class Stub {

    BufferedImage dole;
    BufferedImage gore;
    Random r = new Random();
    int dx = 700;
    int dy = r.nextInt(250) + 250;
    boolean postoji = false;

    public void crtaj(Graphics2D g2d){
        dole = Util.openImage("stub_dole.png");
        gore = Util.openImage("stub_gore.png");
        g2d.drawImage(dole, dx, dy, null);
        g2d.drawImage(gore, dx, -(560-dy), null);
    }

    public void setDx(int dx) {
        this.dx = dx;
    }

    public void randomDy(){
        this.dy = r.nextInt(250) + 250;
    }

    public int getDx() {
        return dx;
    }
}

Ptica / Brid课程:

import java.awt.Graphics;
import java.awt.image.BufferedImage;



/**
 * Created by Lazar on 26/05/15.
 */

public class Ptica {

    BufferedImage ptica1;
    BufferedImage ptica2;
    boolean ptica;
    boolean skok = false;
    int pad = 0;
    double skokBrojac = 0;

    public Ptica(){
        ptica1 = Util.openImage("/slike/ptica1.png");
        ptica2 = Util.openImage("/slike/ptica2.png");
    }

    public void crtajPticu(Graphics g2d){

        ptica ^= true;

        if(ptica){
            g2d.drawImage(ptica1, Environment.dimension.width/2, Environment.dimension.height/2-110 + pad, null);
        }
        else{
            g2d.drawImage(ptica2, Environment.dimension.width/2, Environment.dimension.height/2-110 + pad, null);
        }

        System.out.println(pad);
    }


    public void setSkok(boolean skok) {
        this.skok = skok;
    }

    public void setSkokBrojac(double skokBrojac) {
        this.skokBrojac = skokBrojac;
    }

    public double getSkokBrojac() {
        return skokBrojac;
    }

    public boolean isSkok() {
        return skok;
    }

    public void setPad(int pad) {
        this.pad = pad;
    }

    public int getPad() {
        return pad;
    }


}

Util类:

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;

/**
 * Created by Lazar on 25/05/15.
 */
public class Util {

    public static BufferedImage openImage(String name){
        try {
            if(!name.startsWith("/slike/")){
                name="/slike/"+name;
            }
            return ImageIO.read(Util.class.getResource(name));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

3 个答案:

答案 0 :(得分:2)

  • 避免将所有类添加到默认包中,这可能会导致某些Java版本的类加载问题
  • 绘画应该描绘状态,不应该做出决定或改变状态
  • 不要反复加载资源

例如,您Stub Environment paintComponent调用crtaj的{​​{1}}课程,您可以执行以下操作...

public void crtaj(Graphics2D g2d){
    dole = Util.openImage("stub_dole.png");
    gore = Util.openImage("stub_gore.png");
    g2d.drawImage(dole, dx, dy, null);
    g2d.drawImage(gore, dx, -(560-dy), null);
}

加载图片可能需要一些时间。你应该有一个&#34;缓存&#34;管理它们的类(加载它们一次)或在创建Stub类时加载它们(我更喜欢缓存类,就像你创建和销毁许多Stub一样,加载资源在Stub类(例如构造函数)中可能会成为瓶颈

通过使用可重复使用的对象缓存(而不是重新创建对象并重新加载其资源),example能够从200-300个对象同时移动到4000以上)

答案 1 :(得分:0)

使用分析器确定代码实际花费时间的位置(请注意,YourKit提供15天免费试用许可证。)

一旦你知道你的瓶颈是什么,那么确定是否有一个简单的解决方案,如果不考虑更好的算法和数据结构,以降低代码的算法复杂性。

答案 2 :(得分:0)

按照@ alex-fitzpatrick的建议,分析总是很好。也:

  • 您的Util.openImage调用创建的图像类型是否与您绘制的graphics2D对象兼容?您可能会花一些时间进行转化 (image types)
  • 取消对getWidth()等的调用。在对象初始化后知道这些值,缓存它们。
  • 如果可能,请勿在整个组件上调用repaint。使用overloaded version that specifies the area to repaint
  • ...并考虑将JavaFX用于游戏: - )
相关问题