2015-02-08 25 views
0

我做了一个基于Java的游戏,利用包含JApplet的JFrame,该JApplet又包含一个JPanel,在该JPanel上绘制图形,但由于某种原因,图形的左侧偶尔会闪烁,弄清楚为什么。我想这可能是因为我实际上并没有使用EDT,我之后才意识到这一点,所以有人能够告诉我如何正确地将EDT的使用整合到程序中?在JApplet中正确使用Event Dispatch Thread?

我的主类的样子:

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

那不是双缓冲?

+0

'',它利用了包含JApplet的JFrame“' - 为什么这个*非常*奇怪而脆弱的设计?这不应该是一个拥有JPanel的JFrame吗? – 2015-02-08 15:12:31

+0

在将Applet移动到网页之前,我正在使用JFrame对其进行测试。 – user2649681 2015-02-08 15:46:53

+1

1.不要在paintComponent方法中执行BufferedImage绘图,即从'BufferedImage drawer = ....'到'g1.drawImage(...)'的所有内容。这应该在其他地方完成,可能在后台线程中。另外,尽管可以放弃BufferedImage的Graphics对象g1,但是在完成它之后,不要处置由JVM提供给您的Graphics对象,即g2对象。这将打破Swing的绘画树。 – 2015-02-08 16:16:40

回答

0

调用重绘将在EDT上排队事件。 。

如果你正在经历闪烁,我会看看渲染方法实际需要多长时间来执行(也许渲染到BufferedImage离屏,并让你的绘画方法只绘制。),在你正在绘画的bufferedimage以及在渲染完成时在屏幕上绘制的缓冲图像。 (这被称为双缓冲)。

你可以使用BufferStrategy使用(PS即时消息不是100%肯定,如果将用于小应用程序的工作,或者如果它需要一个完整的窗口。)

而这一切都是假设你没有做任何愚蠢的事情,例如抓住图形上下文被传递到paint方法中,然后在其他方法中使用它(比如你的渲染室方法),或者在这些方法的任何组件上调用“getGraphics()”。

检查的懒惰方式你在哪个线程在您直接访问图形对象的方法打印出来

Thread.currentThread().getName() 

Thread.dumpStack(); 

+0

我添加了'paintComponent()'方法,是不是已经使用双缓冲? – user2649681 2015-02-08 15:49:14

1

问题...

  • 小心更新从事件指派线程之外的比赛状态,这可能会导致脏涂料作为国家的一部分,是在油漆工艺更新
  • 不要处理您没有创建的Graphics上下文,处理系统Graphics上下文将影响其他组件的绘制方式,请记住,您可能不是在绘制周期中唯一绘制的内容,上下文是共享资源
  • 因为它的方式实施,repaint是一个线程安全的方法。绘画事件发布在Event Queue上,由Event Dispatching Thread处理,所以除非你做了一些可怕的错误(打印可能是规则的一个例外),绘画将始终发生在Event Dispatching的上下文中线程
  • paintComponent内没有任何关于BufferedImage的渲染,Swing组件已经是双缓冲。如果你想实现页面翻转,那么你应该画多于一个BufferedImage,但应该更新EDT之外的屏幕外缓冲区
  • 代码的基本结构没有意义,你给了很多控制到JAppletJFrame,但也不应该有什么,他们应该是没有什么比容器多为RenderPanel

可能的解决方案...

开始通过简化渲染/更新的过程。我认为使用Swing Timer作为更新过程的主要引擎,很简单,它触发了它在EDT内的更新,这意味着可以安全地更改游戏状态而不必担心竞争条件和肮脏的绘画。它可以让你直接利用paintComponent方法。

当你得到基本的进程在运行,你可以探索更多的提前更新引擎(如使用Thread)和翻页,technquies

改变你的程序的结构,从上面观的要求分离的核心程序( JAppletJFrame应该不外乎容器多为实际的程序,并且只包含足够的逻辑来构造和显示程序)

MVC

到它的进一步分离游戏逻辑流没有渲染逻辑的n个类。将控制器与渲染视图分开。该API将信息提供给控制器(例如鼠标事件和关键事件),这将更新模型。控制器也可以作为主要更新引擎,调度更新周期以便根据需要更新模型和视图。

请参阅Model-View-Controller了解更多详情。

+0

从这个角度来看,我不妨从头开始吧,呵呵? – user2649681 2015-02-10 13:16:18

+0

至少有一点切割和粘贴;) – MadProgrammer 2015-02-10 20:09:41

相关问题