如何解决 Java awt/swing 图像打印的滞后问题?

Posted

技术标签:

【中文标题】如何解决 Java awt/swing 图像打印的滞后问题?【英文标题】:How do you solve lagging in Java awt/swing image printing? 【发布时间】:2018-05-23 13:09:52 【问题描述】:

我正在尝试使用 Java swing/awt 制作一个简单的游戏。 在屏幕上打印和移动图像时出现滞后问题。

下面是我的代码:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.File;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.*;




public class StarDef extends JFrame implements Runnable, KeyListener
    private BufferedImage back;
    private boolean start = false, end = false;
    public int w = 1500, h = 800, commandx = 200, commandy = 100, ground = 500,  mineral = 100;
    private int mineralx = 0, mineraly = commandy + 104;
    private int dronecnt = 0;
    private ArrayList<Drone> DrList = null;
    private ArrayList<Enemy> EnList = null;
    private ArrayList<Building> BuildList = null;
    private ArrayList<Allies> AlyList= null;
    public Image imagearr[] = new Image[10];
    private boolean makedrone = false, NeedMinerals = false;
    public int picnum = 1;
    private int OrderBuild = 0;

    public static void main(String[] args)
        Thread t = new Thread(new StarDef());
        t.start();
    

    public StarDef()
        back = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        DrList = new ArrayList<>();
        BuildList = new ArrayList<>();
        EnList = new ArrayList<>();
        AlyList = new ArrayList<>();
        this.addKeyListener(this);
        this.setSize(w,h);
        this.setTitle("Starcraft");
        this.setResizable(false);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
        try 
            imagearr[0] = ImageIO.read(new File("Char/Command.png"));
            imagearr[1] = ImageIO.read(new File("Char/droneleft.png"));
            imagearr[2] = ImageIO.read(new File("Char/droneright.png"));
            imagearr[3] = ImageIO.read(new File("Char/Mineral.png"));
            imagearr[4] = ImageIO.read(new File("Char/barracks.png"));
         catch (Exception e)
            e.printStackTrace();
        
    

    public void initGame()
        DrList.clear();
        mineral = 100;

    
    public void draw()
        Graphics gs = back.getGraphics();
        gs.setColor(Color.white);
        gs.fillRect(0,0,w,h);
        gs.setColor(Color.DARK_GRAY);
        gs.fillRect(0,ground,w,200);

        if (!end) 
            gs.drawImage(imagearr[0], commandx, commandy, null); // First Image-Command Center
            gs.drawImage(imagearr[3], mineralx,mineraly,null); // Second Image-Mineral

            for (int i = 0; i < DrList.size(); i++)  //Printing Drones
                Drone m = DrList.get(i);
                gs.drawImage(imagearr[m.state], m.x, m.y, null); //Drawing Drones
                m.moveDr(); // Moving the Drone
            

            for (int i = 0; i < BuildList.size(); i++) //Printing Building
                Building bd = BuildList.get(i);
                if(bd.buildingtype == 'R')
                    gs.drawImage(imagearr[4], bd.x, bd.y, null); // Drawing Building-Problem starts..?
                
            
            gs.drawImage(imagearr[0], commandx, commandy, null);
        

        gs.setColor(Color.black);
        gs.drawString("mineral : " + mineral, 10,50);
        gs.drawString("Drones : " + DrList.size(), 10, 70);

        Graphics ge = this.getGraphics();
        ge.drawImage(back, 0,0,w,h,this);
    

    public void run()

        try
            int timer = 10;

            while (true)
                Thread.sleep(timer);
                if(start)
                    if (makedrone) 
                        makedrone();
                    
                    if (OrderBuild>0)
                        makeBuilding(OrderBuild);
                    
                
                draw();
            
         catch (Exception e)
            e.printStackTrace();
        
    

    public void makeBuilding(int buildingnumber)
        int bdx, bdy;
        char BuildingType;
        if(buildingnumber == 1)
            bdx = 500;
            bdy = 100;
            BuildingType = 'R';
            Building barracks = new Building(this, bdx, bdy, BuildingType);
            BuildList.add(barracks);
        
    
    public void makedrone() 

        if (mineral >= 50) 
            int dronex = commandx;
            int droney = commandy+129;
            Drone dr = new Drone(this ,dronex, droney);
            DrList.add(dr);
            dronecnt++;
            mineral -= 50;
            makedrone = false;

         else if (mineral < 50) 
            NeedMinerals = true;
            makedrone = false;
        

    

    public void keyPressed(KeyEvent ke)
        switch (ke.getKeyCode())
            case KeyEvent.VK_ENTER:
                start = true;
                end = false;
                break;
            case KeyEvent.VK_D:
                makedrone = true;
                break;
            case KeyEvent.VK_R:
                OrderBuild = 1;
                break;
        
    

    public void keyReleased(KeyEvent ke)
        switch ((ke.getKeyCode()))

        
    
    public void keyTyped(KeyEvent ke) 
    

编译代码时,前几张静止图像看起来不错。

在移动图像(无人机)很好地出现后,但当你召唤下一个静止图像(建筑物)时,开始出现严重滞后,移动无人机的速度明显降低。

建筑大约300*150像素,无人机40*30像素。

这个问题的原因是什么?是因为代码(调用图片的方式),还是图片的大小,还是电脑(我用的是笔记本(LG Gram 14in))?

【问题讨论】:

1) 为自包含示例获取图像的一种方法是热链接到在this Q&A 中看到的图像。 2) 请参阅Detection/fix for the hanging close bracket of a code block 了解我无法再费心修复的问题。 【参考方案1】:

首先不要使用Graphics ge = this.getGraphics();

因为您还使用自己的Thread,所以您面临线程竞争条件的风险,这可能导致脏读/绘制。

首先了解 Swing 绘画的实际工作原理以及在 API 功能中的工作原理。

首先查看Performing Custom Painting、Painting in AWT and Swing 和Concurrency in Swing

KeyListener 也是监控键输入的糟糕选择,您应该使用 Key Bindings API - 有关详细信息,请参阅How to use key bindings。

ArrayList 添加内容会导致ArrayList 经历一个增长周期,这会消耗时间并强制延长GC 周期。考虑用初始容量播种ArrayList,这将有助于减少生长周期之间的间隔。

专注于将“更新”逻辑与“绘制”逻辑分开,它可以帮助您发现性能问题

你也可以看看

java what is heavier: Canvas or paintComponent()? Swing animation running extremely slow Rotating multiple images causing flickering. Java Graphics2D

这展示了在 Swing 中提高渲染性能的大量技术。

如果这些仍然无法让您达到您想要的水平,那么您将需要使用 BufferStrategy 进行探索

【讨论】:

以上是关于如何解决 Java awt/swing 图像打印的滞后问题?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Java AWT / Swing垂直对齐面板

java awt;java applet;java swing分别是啥?他们之间有啥联系和区别?

14.1-全栈Java笔记: Java语言中GUI到底是神马鬼?| AWT | Swing

Eclipse中如何配置SWT

如何在 JavaFX 中以大频率显示图像?

Java语言中的GUI总结