2

今天我一直在与一群同学讨论这个话题,他们每个人都在研究不同的想法,以“滥用”Java2D API 来制作一个简单的 2D 游戏。

我正在使用一个类扩展JComponent并覆盖该paintComponent()方法来获取Graphics2D对象并从那里定义我的渲染逻辑。修改后JComponent的是contentPane我的覆盖JFrame

我的问题来了: call 和 in the 的后端对我的屏幕有什么paint(Graphics)不同的影响repaint()?我提到,当从我的“帧定时器”调用任何东西(每秒调用 50 次有问题的方法)时,屏幕的某些部分有时会闪烁,我清楚地告诉它渲染的某些东西是不可见的(或快速闪烁)和一切都感觉不对。这里有什么区别?我试图深入了解 AWT 后端的源代码,直到它以某种方式进行管理,但我停在那里以使我的大脑免受极其丑陋的代码的影响。update(Graphics)JFramerepaint()EventQueuePaintEvent

此外,在讨论整个repaint()事情时,我们谈到了仅重新绘制与模型中的明确变化相对应的屏幕位置的“策略”,从而节省了我们的 CPU/GPU 功率。尽管完成这些事情(以及动画)仍然需要逻辑,但下一个问题是我如何“访问”FrameBuffer已绘制屏幕的内容,以便我可以参考我的渲染已经完成的工作。

是的,我们已经经常听到 Java 可能不是以最佳方式支持我们正在寻找的所有操作的编程语言......

4

1 回答 1

6

我的问题来了:在 JFrame 的后端调用paint(Graphics)、repaint() 和 update(Graphics) 对我的屏幕有什么不同?我提到,当从我的“帧计时器”(每秒调用 50 次相关方法)调用除 repaint() 之外的任何内容时,屏幕的某些部分有时会闪烁,我明确告诉它渲染的某些内容不可见(或闪烁迅速),一切都感觉不对。这里有什么区别?我试图深入挖掘 AWT 后端的源代码,直到 EventQueue 以某种方式管理 PaintEvent,但我停在那里以使我的大脑免受极其丑陋的代码的影响。

在 Swing 中调度绘画是RepaintManager. 它(除其他外)负责确定应用程序的哪些区域需要重新绘制,并安排这些更新在事件调度线程的上下文中发生。

何时可能发生重绘完全取决于重绘管理器。重绘哪些区域也部分取决于重绘管理器,并且重绘管理器可以选择将多个重绘请求合并到单个重绘事件中,从而节省时间和 CPU。

一般来说,你不应该调用paint(Graphics)or update(Graphics),除了你实际上不能创建图形上下文之外,重绘管理器会为你做这件事。即使你想打印屏幕,你也应该使用组件的print(Graphics)方法,除了它不是双缓冲之外,还有与尝试将缓冲区复制回本机对等相关的问题;)

闪烁通常是因为您正在从非双缓冲上下文进行绘制,例如覆盖paint而不是paintComponent. 一般来说,在极少数情况下,您实际上需要覆盖paint顶级容器的方法,例如JFrame

此外,在讨论整个 repaint() 事情时,我们来到了“策略”,即仅重新绘制与模型中的明确变化相对应的屏幕位置,从而节省 CPU/GPU 功率。尽管完成这些事情(以及动画)仍然需要逻辑,但下一个问题是我如何“访问”绘制屏幕的 FrameBuffer,以便可以参考我的渲染已经完成的工作。

通常,除非您确实需要这样做,否则不要担心。如果你对你的重绘代码很小心(你实际上可以安排一个区域被重绘,而不是重绘整个组件),你不应该真的需要关心。确保您使用的是该方法的祖先JComponent并使用该paintComponent方法,并且您获得了自动双缓冲...

另一个问题是,您真的不知道组件何时可能会请求重新绘制它自己,例如响应鼠标移动或组件属性的更改......

访问“帧缓冲区”可能真的不是一个好主意,更好的是生成自己的缓冲区(使用类似 a 的东西BufferedImage),渲染到它,然后将其渲染到屏幕上(屏幕外缓冲)。这样您就可以生成自己的“FrameBuffer”

现在,如果你真的很绝望,你可以看看BufferStrategy

但我建议您在深入研究之前先看看在 AWT 和 Swing 中的绘画,这将使您更深入地了解绘画的工作原理。

此外,永远不要认为你在控制,你不是。如果您尝试“控制”,请准备好让事情迅速发生在您的脸上。了解流程并使用它。

看看这些(简单的)例子......

于 2013-01-11T23:44:48.760 回答