2012-03-04 143 views
3

我只是使用双缓冲开始,直到我想一个JScrollPane添加到屏幕上,这样我以后可以做一些镜头运动的一切工作就好了。除了JScrollPane的ScrollBars以外,一切都很好(我的精灵)。我希望他们被展示!的Java Swing的双缓冲

但是如果我调整窗口的大小,滚动条闪烁,所以我知道他们在那里!如果我足够快,我甚至可以使用它们。他们怎么没有出现在渲染? :(

这是问题的一个SSCCE:

public class BufferStrategyDemo extends JFrame 
{ 
    private BufferStrategy bufferStrategy; 
    private JPanel gameField; 
    private JScrollPane scroll; 

    public BufferStrategyDemo() 
    { 

     this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   
     this.getContentPane().setPreferredSize(new Dimension(800, 600)); 
     this.pack(); 
     this.createBufferStrategy(2); 

     this.gameField = new JPanel(); 
     this.gameField.setPreferredSize(new Dimension(1400, 600)); 

     this.scroll = new JScrollPane(gameField); 
     this.add(scroll, BorderLayout.CENTER); 

     this.setVisible(true); 
     this.bufferStrategy = this.getBufferStrategy(); 

     setupGameFieldContent(); 

     new Renderer(this, scroll , bufferStrategy).start(); 
    } 

    // Add some contents to gameField that shows up fine 
    private void setupGameFieldContent() 
    { 
     // For (all my sprites) 
     //  gameField.add(sprite); 

    } 

    private class Renderer extends Thread 
    { 
     private BufferStrategy bufferStrategy; 
     private JFrame window; 
     private JScrollPane scroll; 
     private Image imageOfGameField; 

     public Renderer(JFrame window, JScrollPane scroll, BufferStrategy bufferStrategy) 
     { 
      this.bufferStrategy = bufferStrategy; 
      this.window = window; 
      this.scroll = scroll; 
     } 

     public void run() 
     { 
      while (true) 
      { 
       Graphics g = null; 
       try 
       { 
        g = bufferStrategy.getDrawGraphics(); 
        drawSprites(g); 
       } 
       finally { g.dispose(); } 

       bufferStrategy.show(); // Shows everything except the scrollbars.. 
       Toolkit.getDefaultToolkit().sync(); 

       try { Thread.sleep(1000/60) ; } 
       catch(InterruptedException ie) {} 
      } 
     } 

     private void drawSprites(Graphics g) 
     { 
      Graphics2D g2d=(Graphics2D)g; 

      //Also tried using the JPanels (gameField) createImage with same result 
      imageOfGameField = this.scroll.createImage(800, 600); 
      Graphics screen = (Graphics2D)imageOfGameField.getGraphics(); 

      /** 
      * For all my sprites, draw on the image 
      for (Sprite current : sprites) 
       screen.drawImage(current.getImage(), current.getX(), current.getY(), current.getWidth() , current.getHeight(), null); 
      */ 

      g2d.drawImage(imageOfGameField, 0, 0 , null); 

     } 
    } 
} 
+0

制作游戏,theese尺寸是当前水平的宽度和高度。我想在屏幕上显示800像素,如果玩家到达任何边缘,则进一步滚动。 – 2012-03-04 13:31:36

回答

5

绘图上整个帧区域进行,而不是在gameField。实际上,出现临时JScrollPane的唯一原因是它会在鼠标拖动处理程序中的某处调用paintImmediately

,想到的第一件事是与Canvas更换JPanel,如I_Love_Thinking写,然后从帆布实例得到bufferStategy并用它进行渲染。但不幸的是,它不能按预期工作。轻量级JScrollPane无法正常驱动重量级Canvas。画布拒绝在区域外绘制内容(0,0,viewport_width,viewport_height)。这是已知的bug,它不会修复。将混合重量级组件与轻量级混合是不好的主意。

重量级ScrollPane更换JScrollPane似乎工作。几乎。在某些情况下,滚动条上有明显的渲染工件。

在这一点上,我已经决定停止跳动的墙壁和放弃。滚动窗格是许多问题的来源,不值得用于懒惰滚动。现在是从另一个角度看滚动的时候了。 Canvas不是游戏场本身,而是我们用来观察游戏世界的窗口。这是gamedev中众所周知的渲染策略。画布的大小与可观察区域完全相同。当需要滚动时,我们只是用一些偏移量渲染世界。偏移值可能来自某个外部滚动条。

我建议未来的变化:

  1. 扔掉JScrollPane
  2. 直接添加画布框。
  3. 在画布下添加单个水平JScrollBar
  4. 使用滚动条的值来改变渲染。

该示例基于您的代码。该实现不考虑调整帧的大小。在现实生活中,一些地方需要与视口的大小同步(这是我们之前为之做的scrollpane)。此外,示例违反了Swing线程规则,并从呈现线程读取滚动条的值。

import java.awt.BorderLayout; 
import java.awt.Canvas; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.Toolkit; 
import java.awt.image.BufferStrategy; 
import java.awt.image.BufferedImage; 

import javax.swing.JFrame; 
import javax.swing.JScrollBar; 

public class BufferStrategyDemo extends JFrame { 
    private BufferStrategy bufferStrategy; 
    private Canvas gameField; 
    private JScrollBar scroll; 

    public BufferStrategyDemo() { 
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     getContentPane().setLayout(new BorderLayout()); 
     getContentPane().setPreferredSize(new Dimension(800, 600)); 

     gameField = new Canvas(); 
     gameField.setIgnoreRepaint(true); 
     gameField.setPreferredSize(new Dimension(800, 580)); 
     getContentPane().add(gameField, BorderLayout.CENTER); 

     scroll = new JScrollBar(JScrollBar.HORIZONTAL); 
     scroll.setPreferredSize(new Dimension(800, 20)); 
     scroll.setMaximum(1400 - 800); // image width - viewport width 
     getContentPane().add(scroll, BorderLayout.SOUTH); 

     this.pack(); 

     gameField.createBufferStrategy(2); 
     bufferStrategy = gameField.getBufferStrategy(); 

     new Renderer().start(); 
    } 

    private class Renderer extends Thread { 
     private BufferedImage imageOfGameField; 

     public Renderer() { 
      // NOTE: image size is fixed now, but better to bind image size to the size of viewport 
      imageOfGameField = new BufferedImage(1400, 580, BufferedImage.TYPE_INT_ARGB); 
     } 

     public void run() { 
      while (true) { 
       Graphics g = null; 
       try { 
        g = bufferStrategy.getDrawGraphics(); 
        drawSprites(g); 
       } finally { 
        g.dispose(); 
       } 

       bufferStrategy.show(); 
       Toolkit.getDefaultToolkit().sync(); 

       try { 
        Thread.sleep(1000/60); 
       } catch (InterruptedException ie) { 
       } 
      } 
     } 

     private void drawSprites(Graphics g) { 
      Graphics2D g2d = (Graphics2D) g; 

      Graphics g2d2 = imageOfGameField.createGraphics(); 
      g2d2.setColor(Color.YELLOW); // clear background 
      g2d2.fillRect(0, 0, 1400, 580); // again, fixed width/height only for SSCCE 
      g2d2.setColor(Color.BLACK); 

      int shift = -scroll.getValue(); // here it is - get shift value 

      g2d2.fillRect(100 + shift, 100, 20, 20); // i am ugly black sprite 
      g2d2.fillRect(900 + shift, 100, 20, 20); // i am other black sprite 
                // located outside of default view 

      g2d.drawImage(imageOfGameField, 0, 0, null); 

      g2d2.dispose(); 
     } 
    } 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      public void run() { 
       BufferStrategyDemo mf = new BufferStrategyDemo(); 
       mf.setVisible(true); 
      } 
     }); 
    } 
} 

而且another example,主要是基于代码Java Games: Active Rendering文章。它与JScrollBar的值和Canvas的大小正确同步。此外,它直接绘制到Graphics,从BufferStrategy实例获得(而不是中间对象BuffereImage)。其结果是这样的:

Active Rendering Demo

+0

这正是我所需要的。 JPanel没有任何BufferStrategy,这就是为什么我使用框架来绘制。但你的解决方案给了我很多选择,欢呼队友:) – 2012-03-04 20:10:34

+1

这里是伟大的[Java游戏:主动渲染](http://www.gamedev.net/page/resources/_/technical/general-programming/java- games-active-rendering-r2418)文章。它包含了非屏幕图像的主动渲染演示。你会将渲染部分作为模板,因为它会产生更少的开销 - 渲染循环只包含必需的操作。祝你好运:) – Mersenne 2012-03-04 20:32:31

+0

这是更好,非常感谢:) – 2012-03-04 20:39:42

1

你的游戏油漆在你的滚动窗格
窗口大小调整显示了一个时刻你的滚动窗格,因为它正常的油漆方法被调用(没有双缓冲)
你有一些可能性...第二个是漂亮,但它是你的选择,我不知道究竟你想用它做什么...

如果你想控制所有的组件你自己的jframe:

  • 覆盖涂料(图形G)方法,只是让它空
  • 包括滚动面板拉丝的漆面环(法“跑”)

如果你想使用双缓冲在另一个组件上,您不必使用双缓冲直接管理其他组件。
最好的组件是Canvas。它已经有了可以使用的双缓冲方法。
很久以前,我做了这个,所以我不能完全告诉你它是如何工作的。但只要看一下,这并不难。

+0

你的意思是覆盖JFrames paint或JPanel(gameField)?关于第二个选项,我以为我是通过从scroll,scroll.createImage(..)获取图像来做到这一点的? – 2012-03-04 13:40:35

1

您的代码正在后台线程上操作Swing对象。这不起作用。

阅读Event Dispatch ThreadConcurrency in Swing

+0

一切都很好。我猜这可能会给我未来的问题,但它不是显示滚动条的主要原因。 – 2012-03-04 13:46:46

+0

@Devon_C_Miller:Hasslam试图利用[“主动渲染”](http://docs.oracle.com/javase/tutorial/extra/fullscreen/rendering.html)范式。尽管不像传统的被动渲染那样广泛传播,但它起作用。将常用的Swing组件与手工渲染组件混合时,大多数情况下都会出现警告。 – Mersenne 2012-03-04 16:16:20

+0

这就是我喜欢的SO。我总是在学习新东西!我没有意识到“主动渲染”绕过了美国东部时间的规则。 – 2012-03-06 17:21:02