【问题标题】:Flickering when updating overlapping JPanels inside a JLayeredPane using timestep使用时间步更新 JLayeredPane 内的重叠 JPanel 时闪烁
【发布时间】:2013-12-30 05:36:05
【问题描述】:

我正在用 Java 制作游戏。基本上,我需要处理两个不同的更新“平面”。基础层是实际的游戏绘画本身。它只是一个覆盖整个 JFrame 的 JPanel,并使用其 Graphics 对象进行绘制。

我使用固定的时间步来处理这些第一次图形更新。我已经覆盖了 paintComponent() 方法,完全没有做任何事情,因为我已经编写了一个自定义的 render(float interpolation) 方法来处理这个问题,以防止不需要的更新。

但是,除了原始的鼠标点击和键盘输入之外,此面板不接受任何输入。我需要能够在屏幕上创建各种菜单、文本框等。比如各种能力,甚至是大多数游戏左上角通常出现的“菜单”按钮。

为了处理该输入,例如创建按钮,我有第二个 JPanel,它应用了 setOpaque(false)。然后我创建了我可能需要的各种 Swing 组件,例如 JButton。

为了包含两个 JPanel,我使用了 JLayeredPane,并适当地设置了它们的层,如下所示。这样,输入层应该始终位于实际游戏层之上。

下面的代码展示了我如何创建 Swing 组件并将它们相互添加。 addLoginDialog() 是一种为登录添加 Swing 组件的方法。它已经过测试并且工作正常,不是问题。

private void initComponents()
{
    //This code is inside of the JFrame
    wholePane = new JLayeredPane();
    add(wholePane);
    guiPanel = new GUIPanel();
    guiPanel.setOpaque(false);
    gamePanel = new RPGPanel();
    gamePanel.setOpaque(false);
    wholePane.add(gamePanel, JLayeredPane.DEFAULT_LAYER);
    wholePane.add(guiPanel, JLayeredPane.POPUP_LAYER);
    guiPanel.addLoginDialog();
}

所以当我运行代码时,我得到了可怕的闪烁。这是从我的固定时间步长约每秒 60 次运行的代码。

public void handleRepaint()
{
    //I don't use repaint() for the game drawing so I can be sure that fps is controlled.
    Graphics g = gamePanel.getGraphics();
    gamePanel.render(g);
    g.dispose();
    wholePane.repaint();
}

我认为,问题在于两种不同的屏幕更新系统正在发生冲突。标准的paintComponent() 系统非常适合更多静态屏幕,但是当我需要持续更新并跟踪 fps 时,我不能随机更新。

但是,对于输入层,我只想像 Swing 一样进行更新。当鼠标移动到按钮上时,当 I 组件被移动或调整大小等时。

另外,请注意屏幕闪烁的方式:背景图像变为空白,然后反复出现。输入面板总是在那里,但实际上是在游戏绘图后面绘制的,这不应该发生,因为它被放置在默认图层中。我知道它没有完全消失的原因是因为游戏画面是部分透明的,所以我可以看到它的下面,而且我添加的按钮仍然存在。

我的主要问题是,我怎样才能停止闪烁? 为什么在 JLayeredPane 中较低层的 Panel 上进行游戏绘图时,游戏绘图被绘制在输入组件的顶部? 我想最重要的是,是什么导致了闪烁?感谢您的帮助。

【问题讨论】:

  • 如需尽快获得更好的帮助,请发帖SSCCE
  • wholePane.setSize(getSize()); 布局管理器更有可能遵守所引用的尺寸,而不是尺寸,但我们也不应该设置。见Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?(是的。)
  • @AndrewThompson 尺寸不是问题,所以我现在不太担心。
  • @AndrewThompson 实际上我已经阅读了多个教程,他们都告诉我,当仅涉及绘图代码时,使用渲染方法并确保不要使用 paintComponent 以避免不必要的绘画调用。
  • @user3144349 你只是“禁用”了渲染引擎,这不是 Swing 的设计方式。它是按照这种方式工作的。你不妨试着在冰箱里烤一烤……对不起,但事实就是如此。我想任何告诉你避免使用paintComponent 的教程也应该告诉你改用BufferStrategy。这是控制油漆过程的唯一方法。这里的问题是,Swing 组件被设计为使用被动渲染引擎...catch 22...

标签: java swing jpanel jlayeredpane


【解决方案1】:

为什么游戏绘图是在输入组件之上绘制的 当在较低的面板上进行游戏绘图时 JLayeredPane 中的图层?

主要是因为您绕过了 Swing 的工作方式。

让我们从 Graphics 上下文是一个共享资源的事实开始(通常每个本地对等点有一个 Graphics 上下文),也就是说,每个组件都获得相同的上下文,这意味着,当你使用你的自己的绘画程序,你基本上是在画其他所有东西。

RepaintManager 负责决定应该绘制什么以及何时绘制。所以你现在有两个艺术家在同一张画布上争吵,想同时在上面画画,这只会弄得一团糟。

加上 Swing 的绘制过程不是线程安全的,你最终会变得一团糟。

如果您绝对必须拥有控制权,那么您需要从等式中删除 Swing 的绘制引擎。

先看看Passive vs. Active Rendering

ps- 使用BufferStrategy 也有硬件加速的好处

【讨论】:

  • 这是我之前看过的教程。 +1 对于“两位艺术家”的描述,我不知道为什么,但这确实有助于澄清我的情况。谢谢你。所以如果我要完全转换为主动渲染,我还能使用 JButtons、JTextAreas 等吗?如果是这样,我将如何去做?
  • 是和不是。没有布线进入使它们有用的组件。因为混合重型和轻型组件存在问题,并且因为您现在基本上充当重绘管理器,所以您将负责确保使用BufferStrategy 绘制它们。我没有亲自尝试过,但在让这种事情发挥作用方面我没有看到很多成功,并不意味着它不能完成
  • 非常感谢。这是否意味着我正在使用的 Graphics 对象已附加到每个组件?我想我不太明白您所说的“本地同行”是什么意思。另外,针对您的第二条评论,最好使用 BufferStrategy 直接绘制到 JFrame 上吗?然后使用 BufferStrategy 将组件绘制到 JFrame 上?我不确定这会如何工作,也许我误解了你在说什么。
  • This 可能会有所帮助
  • 原生对等体是重量级的、操作系统特定的组件,所有 Swing 组件都在其上绘制。这通常由窗口表示。所有 Swing 组件共享相同的 Graphics 上下文,RepaintManager 做了一些技巧,让它们按照需要以正确的顺序进行绘制...
猜你喜欢
  • 2014-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-18
  • 2017-08-06
  • 2014-02-01
  • 1970-01-01
相关资源
最近更新 更多