在JApplet中正确使用Event Dispatch Thread?

时间:2015-02-08 15:11:00

标签: java swing applet japplet event-dispatch-thread

我制作了一个基于Java的游戏,它利用了一个包含JApplet的JFrame,而JApplet又包含一个绘制图形的JPanel,但由于某种原因,图形的左侧偶尔会闪烁,我无法弄清楚为什么会这样。我想这可能是因为我实际上没有使用过EDT,后来我才意识到这一点,那么有人能够告诉我如何将EDT的使用正确地整合到程序中吗?

我的主要课程如下:

public class Main extends JFrame{

public static final int HEIGHT = 600;
public static final int WIDTH = 800;
public static RApplet app;

public Main(){
    setTitle("RCrawl");
    Container c = getContentPane();
    c.setPreferredSize(new Dimension(WIDTH, HEIGHT));
    pack();
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLocationRelativeTo(null);
    setResizable(false);
    final Player p = new Player(50, 70, true);
    final Room r = new Room(0xff222244);
    r.addEntity(new EntityRock(200, 150));
    app = new RApplet(WIDTH, HEIGHT);
    app.setRoom(r);
    app.setPlayer(p);
    app.setFps(25);
    add(app);
    setVisible(true);
    new UpdateThread().start();
    System.out.println("thread run");
}

public static void main(String[] args){
    Main m = new Main();
}

class UpdateThread extends Thread{
    public void run(){
        while(true)update();
    }

    public void update(){
        app.refresh();
    }
}
}

虽然RApplet类看起来像:

public class RApplet extends JApplet{
public int width, height, fps;
public long curTime, delta;
public RenderPanel panel;
public Room curRoom;
public Player player;
public boolean isSingle;
public InputHandler input;

public RApplet(int x, int y){
    width = x;
    height = y;
    panel = new RenderPanel(width, height);
    fps = 30;
    isSingle = true;
    input = new InputHandler();
    addKeyListener(input);
    setFocusable(true);
    add(panel);
}

public void setPlayer(Player p){
    player = p;
    curRoom.addPlayer(p);
}

public void setFps(int f){
    fps = f;
}

public void initialize(){
    curTime = System.currentTimeMillis();
    delta = curTime;
}

public void refresh(){
    delta = (int)(System.currentTimeMillis() - curTime);
    if(delta > 1000 / fps){
        curTime = System.currentTimeMillis();
        render();
        tick();
    }
}

public void setRoom(Room r){
    curRoom = r;
}

public void render(){
    panel.renderStart(curRoom);
    panel.renderRoom(curRoom);
    panel.renderEnd();
    panel.repaint();
}

public void tick(){
    curRoom.tick(this);
}
}

那么为了正确使用EDT我想做什么?我使用invokeLater()invokeAndWait()的不同组合尝试了一些事情,但无法让它们发挥作用。如果您能提供帮助,我们非常感谢您的帮助。

编辑:这是RenderPanel

中的渲染方法
public void paintComponent(Graphics g){
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    BufferedImage drawer = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    Graphics g1 = drawer.getGraphics();
    g1.drawImage(canvas, 0, 0, this);
    g2.drawImage(drawer, 0, 0, this);
    g1.dispose();
    g2.dispose();
}

那不是双缓冲吗?

2 个答案:

答案 0 :(得分:1)

的问题...

  • 小心地从事件调度线程外部更新游戏状态,这可能会导致脏涂料作为状态的一部分在绘制过程中更新
  • 不要处置您未创建的Graphics个上下文,处理系统Graphics上下文会影响其他组件的绘制方式,请记住,您可能不是唯一被绘制的内容绘制周期和Graphics上下文是共享资源
  • 由于它的实现方式,repaint是一种线程安全的方法。 paint事件发布在Event Queue上,由Event Dispatching Thread处理,所以除非你做了一些可怕的错误(打印是规则的一个可能的例外),否则绘制将始终在事件调度的上下文中进行螺纹
  • BufferedImage内向paintComponent呈现没有任何意义,Swing组件已经被双重缓冲。如果你想实现页面翻转,那么你应该绘制到多个BufferedImage,但应该更新EDT之外的屏幕外缓冲区
  • 代码的基本结构没有意义,你对JAppletJFrame给予了很多控制,但是它们都没有,它们应该只是容器的RenderPanel

可能的解决方案......

首先简化渲染/更新过程。我考虑使用Swing Timer作为更新过程的主要引擎,它很简单,它触发了EDT中的更新,这意味着可以安全地更改游戏状态而不必担心竞争条件和脏画。它允许您直接利用paintComponent方法。

当您运行基本流程时,您可以探索更多高级更新引擎(例如使用Thread)和页面翻转技术

更改程序的结构,将核心程序与上层视图要求分开(JAppletJFrame应该只是实际程序的容器,并且只包含足够的逻辑来构造和显示该计划)

MVC

进一步将游戏逻辑分成它自己的没有渲染逻辑的类。将控制器与渲染视图分开。 API将向控制器提供信息(例如鼠标事件和关键事件),这将更新模型。控制器还可以作为主要更新引擎,安排更新周期,以便根据需要更新模型和视图。

有关详细信息,请参阅Model-View-Controller

答案 1 :(得分:0)

调用重绘会在EDT上排队一个事件。

如果您遇到闪烁,我会看看渲染方法实际执行的时间长度(并且可能渲染到屏幕外的BufferedImage并让您的绘制方法只绘制它。),在您绘制的bufferedimage之间切换以及渲染完成后在屏幕上绘制的缓冲图像。 (这称为双缓冲。)

你可以使用BufferStrategy(p.s. im我不是100%肯定,如果它适用于applet或者它需要一个完整的窗口。)

这就是假设你没有做任何愚蠢的事情,例如抓住传递给paint方法的图形上下文,然后在其他方法中使用它(比如你的渲染室方法)或调用“getGraphics( “关于这些方法的任何组成部分。

一种检查你所在线程的懒惰方式是打印出来

Thread.currentThread().getName() 

Thread.dumpStack();

在直接访问图形对象的方法中。