1

我正在尝试绘制背景图像,然后在其上绘制角色。我的代码一直在工作,直到我添加了睡眠我没有得到 1500 fps。

package sylvyrfysh.screen;

import javax.swing.*;
import java.awt.*;
import game.infos.Information;

public class ImageLoadDraw extends JFrame{
/**
 * 
 */
private static final long serialVersionUID = 1L;
public static void main(){
    DisplayMode dm=new DisplayMode(Information.sX,Information.sY,16,DisplayMode.REFRESH_RATE_UNKNOWN);
    ImageLoadDraw i = new ImageLoadDraw();
    i.run(dm);
}
public void run(DisplayMode dm){
    setBackground(Color.PINK);
    setForeground(Color.WHITE);
    setFont(new Font("Arial",Font.PLAIN,24));
    s=new Screen();
    try{
        loadpics();
        s.setFullScreen(dm,this);
        try{
            Thread.sleep(10000);
        }finally{
            doRun=false;
            s.restoreScreen();
        }
    }catch(Exception e){
        e.printStackTrace();
    }
}
private void loadpics() {
    bg=new ImageIcon("src/sylvyrfysh/screen/maze_icon.png").getImage();
    chara=new ImageIcon("src/sylvyrfysh/screen/char.png").getImage();
    repaint();
}
public void paint(Graphics g){
    if(g instanceof Graphics2D){
        Graphics2D g2=(Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    }
    while(true&&doRun){
        g.drawImage(bg,0,0,null);
        g.drawImage(bg,0,480,null);
        g.drawImage(bg,360,0,null);
        g.drawImage(bg,360,480,null);
        g.drawImage(bg,720,0,null);
        g.drawImage(bg,720,480,null);
        g.drawImage(chara,imgX,imgY,null);
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
private Screen s;
public static int imgX=0;
private int imgY=525;
private Image bg,chara;
private Boolean doRun=true;
}

关于导致它的原因以及如何解决它的任何想法?就像我说的,不睡觉它工作正常。

4

3 回答 3

1

Swing 是一个单线程框架。也就是说,所有 UI 交互和修改都应该在 Event Dispatching Thread 的内容中执行,包括重绘请求。

任何停止、阻塞或以其他方式阻止此线程运行的行为都会阻止 EDT 处理新事件,包括重绘请求。从本质上讲,这将使您的程序看起来像挂起(或停止响应),因为它已经挂起。

在您的run方法中,您正在调用Thread.sleep. 这可能会阻止 EDT 处理新事件,但是因为您实际上已经从“主”线程调用了该方法,所以它实际上可能会起作用,但是......

在你paint的方法中,你有一个无限循环和一个Thread.sleep调用。这些将停止 EDT 运行,正如paint在 EDT 上下文中调用的那样。

在某些系统上,绘制不会总是立即发生,直到paint方法返回,它可能不会被推送到设备进行输出,因此,即使是在paint. ,无论如何都是个坏主意。

与其他一些 UI 框架不同,您不需要实现“主循环”,Swing 会为您处理这些。

相反,在您的paint方法中,您应该简单地绘制该循环所需绘制的内容并退出。

相反,你应该做类似的事情......

public void paint(Graphics g){
    // Painting is a complex series of chained methods, failing to call super.paint
    // to cause significant issues
    super.paint(g);
    // Graphics is guaranteed to be an instance of Graphics2D since I think 1.4
    // You should create a copy, so any changes you make are not carried onto the
    // next component, Graphics is shared between all the components being painted
    // in this paint cycle.
    Graphics2D g2=(Graphics2D)g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    g2.drawImage(bg,0,0,null);
    g2.drawImage(bg,0,480,null);
    g2.drawImage(bg,360,0,null);
    g2.drawImage(bg,360,480,null);
    g2.drawImage(bg,720,0,null);
    g2.drawImage(bg,720,480,null);
    g2.drawImage(chara,imgX,imgY,null);
    // If you create, you should dispose of it...
    g2.dispose();
}

...反而

而不是Thread.sleep你应该使用类似的东西javax.swing.Timer,例如......

s=new Screen();
try{
    loadpics();
    s.setFullScreen(dm,this);
    Timer timer = new Timer(10000, new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
            s.restoreScreen();
        }
    });
    timer.setRepeats(false);
    timer.start();
}catch(Exception e){
    e.printStackTrace();
}

查看Swing 中的并发以获取更多详细信息。

您还应该避免覆盖顶级容器,尤其是覆盖paint. 绘制一系列复杂的链接方法调用,每个方法调用都执行特定的工作,相互叠加以产生最终结果。

相反,您应该从某种自定义组件开始,JPanel例如从扩展,并覆盖它的paintComponent方法,确保super.paintComponent在您自己进行任何绘画之前调用。

看看Performing Custom Painting了解更多细节

于 2013-10-23T22:22:52.270 回答
0

那么这个Thread.sleep(10000);关闭一切10秒。这不是你想要的。即使执行 30 毫秒(大约 30 帧/秒)也不是您想要的,因为它甚至会停止输入等。

你应该使用定时器。它在另一个线程中运行计时,并且仅在给定的毫秒数内自动休眠和唤醒该线程,因此它不会影响您的程序,并且只能在例如 30 毫秒后调用它。

仍然有很好的应用,这个计时器应该有一个低值,你应该计算经过多长时间System.nanoTime(),例如每 30 毫秒重绘一次,但是每 5 毫秒读取一次输入等。

于 2013-10-23T21:40:00.107 回答
0

我想->这是因为你需要调用loadpics()并行,试试这个

public void run(DisplayMode dm){
    setBackground(Color.PINK);
    setForeground(Color.WHITE);
    setFont(new Font("Arial",Font.PLAIN,24));
    s=new Screen();
        loadpics();
        s.setFullScreen(dm,this);
        try{
            new Thread(new Runnable() {
      @Override
      public void run() {try {Thread.sleep(1000);doRun=false;s.restoreScreen();} catch (Exception e) {}
      }
    }).start();
}

也将其设置doRun为易失性。 private Boolean doRun=true;

于 2013-10-23T22:02:53.180 回答