简体   繁体   English

摇摆2D游戏性能低下

[英]Swing 2D game low performance

I am making a clone of Flappy Bird. 我正在克隆飞扬的小鸟。 I was doing just fine performance-wise: 60 fps. 我在性能方面做得很好:60 fps。 This was when it had 1 pillar/obstacle only. 当时只有1个支柱/障碍物。 As soon as I added 3 of them my fps dropped to 30 and below. 一加3,我的fps就降到30以下。 Then game is unplayable now. 然后游戏现在无法播放。 I get that this has something to do with doing repaint() all the time. 我知道这与一直执行repaint()有关。

Here is the code: 这是代码:

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 完整的项目来源

Also bear in mind this is really unpolished version so the code is ugly. 还请记住,这实际上是未经抛光的版本,因此代码很丑陋。 I am looking for a solution to boost performance. 我正在寻找提高性能的解决方案。

Main Class: 主类:

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());
            }
        });
    }
}

Frame class: 车架类:

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 class: 存根/支柱类:

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 class: 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 class: 实用程序类:

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;
    }
}
  • Avoid adding all you classes to the default package, this could cause issues with class loading on some versions of Java 避免将所有类添加到默认包中,这可能会导致某些版本的Java上的类加载出现问题
  • Painting should paint the state and should not be making decisions or changing the state 绘画应该描绘状态,而不应该做出决定或改变状态
  • Don't, repeatedly, load resources 不要反复加载资源

For example, from you Stub class, which Environment 's paintComponent calls crtaj , you do the following... 例如,从您的Stub类( EnvironmentpaintComponent调用crtaj ,您可以执行以下操作...

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);
}

Loading the images can take time. 加载图像可能会花费一些时间。 You should either have a "cache" class which managers them (loading them once) or load them when the Stub class is created (I'd prefer the cache class, as if you create and destroy many Stub s, loading the resources within the Stub class (constructor for example) could become a bottle neck 您应该有一个“缓存”类来管理它们(一次加载),或者在创建Stub类时加载它们(我更喜欢使用cache类,就像您创建和销毁许多Stub ,将资源加载到Stub中) Stub类(例如构造函数)可能会成为瓶颈

For example , which was able to go from 200-300 objects moving simultaneously, to over 4000 through the use of a re-usable object cache (rather the re-creating the objects and re-loading their resources) 例如 ,通过使用可重复使用的对象缓存,它可以将200-300个对象同时移动到4000个以上(而不是重新创建对象并重新加载其资源)

Use a profiler to determine where you code is actually spending time (Note that YourKit has a 15 day free trial license available). 使用探查器来确定您在代码上实际花费的时间(请注意,YourKit具有15天的免费试用许可证)。

Once you know what your bottleneck is then determine if there's an easy fix, if not consider better algorithms and data-structures to reduce the algorithmic complexity of your code. 一旦知道了瓶颈,然后确定是否有一个简单的解决方案,如果不考虑更好的算法和数据结构,则可以降低代码的算法复杂性。

Profiling, as suggested by @alex-fitzpatrick, is always good. 正如@ alex-fitzpatrick所建议的那样,分析始终是好的。 Also: 也:

  • Is the type of images created by your Util.openImage call compliant with the graphics2D object you paint on? Util.openImage调用创建的图像类型是否与您绘制的graphics2D对象兼容? You may be spending some with the conversions (image types) . 您可能在转换(图像类型)上花了一些钱。
  • eliminate calls to getWidth() etc. You know these values after object initialization, cache them. 消除对getWidth()等的调用。在对象初始化之后就知道了这些值,并对其进行缓存。
  • If possible, don't call repaint on the entire component. 如果可能,请勿在整个组件上调用repaint Use the overloaded version that specifies the area to repaint . 使用指定要重新粉刷区域重载版本
  • ... and consider using JavaFX for games :-) ...并考虑将JavaFX用于游戏:-)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM