java 当我重绘太多时停止摆动

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17965850/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-01 19:43:58  来源:igfitidea点击:

Stop flickering in swing when i repaint too much

javaswingconcurrencyrepaintevent-dispatch-thread

提问by sanchixx

I am making an RPG with a tilemap. To generate the tilemap i loop through a 2 dimensional array but that means that when I repaint I have to do that each time. If I repaint too much the screen flickers how could I stop this.

我正在制作带有 tilemap 的 RPG。为了生成tilemap,我循环遍历一个二维数组,但这意味着当我重新绘制时,我每次都必须这样做。如果我重绘太多屏幕闪烁,我怎么能阻止这种情况。

package sexyCyborgFromAnOtherDimension;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JPanel;

@SuppressWarnings("serial")
public class Game extends JPanel
{
    KeyLis listener;
    int mapX = 20;
    int mapY = 20;
    boolean up = false;
    boolean down = false;
    boolean right = false;
    boolean left = false;
    String[][] map;

    public Game()
    {
        super();
        try 
        {
            map = load("/maps/map1.txt");
        } 

        catch (IOException e) 
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        listener = new KeyLis();
        this.setFocusable(true);
        this.requestFocus();
        this.addKeyListener(listener);

        Timer timer = new Timer();
        TimerTask task = new TimerTask() 
        {
            @Override
            public void run() 
            {
                if(up)
                {
                    mapY++;
                    repaint();
                }

                if(down)
                {
                    mapY--;
                    repaint();
                }

                if(right)
                {
                    mapX--;
                    repaint();
                }

                if(left)
                {
                    mapX++;
                    repaint();
                }
            }
        };
        timer.scheduleAtFixedRate(task, 0, 10);
    }

    public void paint(Graphics g) 
    {
        super.paintComponent(g);
        setDoubleBuffered(true);
        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        for (int x = 0; x < map.length; x++) 
        {
            for (int y = 0; y < map[x].length; y++) 
            {
                switch(map[x][y])
                {
                case "0":
                    g.setColor(Color.GREEN);
                    break;
                case "1":
                    g.setColor(Color.GRAY);
                    break;
                }

                g.fillRect(y*20+mapX, x*20+mapY, 20, 20);
            }
        }
        g.setColor(Color.BLACK);
        g.fillRect(400, 400, 20, 20);
    }

    String[][] load(String file) throws IOException
    {
        BufferedReader br = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(file)));
        int lines = 1;
        int length = br.readLine().split(" ").length;
        while (br.readLine() != null) lines++;
        br.close();
        BufferedReader br1 = new BufferedReader(new InputStreamReader(getClass().getResourceAsStream(file)));
        String[][] map = new String[lines][length];
        for (int i = 0; i < lines; i++)
        {
            String line = br1.readLine();
            String[] parts = line.split(" ");
            for (int y = 0; y < length; y++)
            {
                map[i][y] = parts[y];
            }
        }
        br1.close();
        return map;
    }

    private class KeyLis extends KeyAdapter 
    {   
        @Override
        public void keyPressed(KeyEvent e) 
        {
            switch (e.getKeyCode())
            {
            case KeyEvent.VK_UP:
                up = true;
                break;
            case KeyEvent.VK_DOWN:
                down = true;
                break;
            case KeyEvent.VK_LEFT:
                left = true;
                break;
            case KeyEvent.VK_RIGHT:
                right = true;
                break;
            }
        }

        @Override
        public void keyReleased(KeyEvent e) 
        {
            switch (e.getKeyCode())
            {
            case KeyEvent.VK_UP:
                up = false;
                break;
            case KeyEvent.VK_DOWN:
                down = false;
                break;
            case KeyEvent.VK_LEFT:
                left = false;
                break;
            case KeyEvent.VK_RIGHT:
                right = false;
                break;
            }
        }
    }
}

Thank you for your help

谢谢您的帮助

Edit

编辑

Using javax.swing.Timerremoves all flickering even with a 10 ms delay.

javax.swing.Timer即使有 10 毫秒的延迟,使用也会消除所有闪烁。

回答by MadProgrammer

A number of small things jump out at me.

一些小事情突然出现在我身上。

Firstly. You might be better using javax.swing.Timerinstead of java.util.Timer, this will at least allow the events to flow a little better.

第一。您可能会更好地使用javax.swing.Timer而不是java.util.Timer,这至少可以让事件更好地流动。

Secondly, 10 milliseconds is to short a time period, seriously, you don't need 100fps, 60fps is about 17 milliseconds, I normally use 40 milliseconds for 25fps. This might give the EDT some breathing room to actually respond to the repaint requests.

其次,10毫秒是缩短一个时间段,说真的,你不需要100fps,60fps大约是17毫秒,我一般用40毫秒换25fps。这可能会给 EDT 一些喘息的空间来实际响应重绘请求。

Thirdly, you should be using paintComponentinstead of paint. It's low enough in the call chain to guaranteed to be double buffered

第三,你应该使用paintComponent而不是paint. 它在调用链中足够低以保证双缓冲

Fourthly, you should avoid calling any method that might reschedule a repaint(like setDoubleBufferedfor example, to this in the constructor if you must, but, Swing components are double buffered by default)

第四,您应该避免调用任何可能重新安排 a 的方法repaintsetDoubleBuffered例如,如果必须的话,可以在构造函数中调用this,但是,Swing 组件默认情况下是双缓冲的)

Fifthly, where possible, paint all "static" or slow changing content to a backing buffer and paint that instead. This will increase the speed at which paintcan work as it doesn't get stuck in a lot of small loops.

第五,在可能的情况下,将所有“静态”或缓慢变化的内容绘制到后备缓冲区并进行绘制。这将提高paint可以工作的速度,因为它不会卡在很多小循环中。

You may want to take a look at Painting in AWT and Swingfor more details about the paint process

您可能需要查看在 AWT 和 Swing中绘制以了解有关绘制过程的更多详细信息

Some additional examples...

一些额外的例子...

And because kleo scares me (and it's also a good idea), you should also take a look at How to use Key Bindings, which will solve your focus issues with the KeyListener

因为 kleo 吓到我了(这也是个好主意),你还应该看看如何使用键绑定,这将解决你的焦点问题KeyListener

回答by Qwerky

You have set double buffering on the panel, but the things you are drawing on it yourself are not being double buffered. Calling setDoubleBuffered(true)only applies buffering for child components of the container.

您在面板上设置了双缓冲,但您自己在其上绘制的内容并未被双缓冲。调用setDoubleBuffered(true)仅对容器的子组件应用缓冲。

You need manually buffer stuff you draw yourself.

您需要手动缓冲您自己绘制的内容。

I would suggest moving away from a JPaneland use a Canvasinstead. Have a look at how a Canvasis used with a buffer strategy in this class, where it can handle hundreds of things being drawn on screen with perfect smoothness and no flickering (the game runs on a 10ms sleep loop too).

我建议远离 aJPanel并使用 aCanvas代替。看看 aCanvas是如何与此类中的缓冲策略一起使用的,它可以处理在屏幕上绘制的数百个事物,并且具有完美的平滑度和无闪烁(游戏也以 10 毫秒的睡眠循环运行)。

回答by Loki

Not really sure if it solves your problem, but you can consider to use a 1dimensional array.

不确定它是否能解决您的问题,但您可以考虑使用一维数组。

when int width;is the width of your screen/tile/whatever you loop and int heightis the height you coud do the following:

什么时候int width;是你的屏幕/瓷砖/任何你循环的宽度,int height是你可以执行以下操作的高度:

instead of, what you do:

而不是,你做什么:

for(int x = 0; x < width; x++)
{
    for(int y = 0; y < height; y++)
    {
        // dosomething with your array
        myarray[x][y];
    }
}

you could go through this way

你可以通过这种方式

for(int y = 0; y < height; y++)
{
    for(int x = 0; x < width; x++)
    {
        // dosomething with your array
        myarray[x + y * width];
    }
}