我打算写一个简单的太空射击游戏。我已经读过repaint()方法只是一个请求,并且每次调用它都不会执行。我相信我注意到了这一点的影响,因为当我移动时,我的宇宙飞船往往会略微滞后。目前我只是在一个JPanel的paintComponent()方法中绘制我的船,并且定期调用repaint()(我的面板也是Runnable)。看起来像重绘()可能会让我感到困惑,我正试图找到一种方法来解决它,但是我已经没有了想法。我到目前为止的代码:
private void renderGraphics() {
if (MyImage == null) {
MyImage = new BufferedImage(getPreferredSize().width,
getPreferredSize().height, BufferedImage.TYPE_INT_RGB);
}
MyGraphics = MyImage.getGraphics();
MyGraphics.setColor(Color.BLACK);
MyGraphics.fillRect(0, 0, getPreferredSize().width, getPreferredSize().height);
MyGraphics.drawImage(ship.getImage(), ship.getCurrentX(), ship.getCurrentY(), null);
}
我的想法是创建自己的图形,然后让JPanel绘制它,并在我的run()方法中继续调用它而不是repaint(),但我不知道如何做到这一点。我会对此事的任何意见表示赞同。
答案 0 :(得分:4)
有多种方法可以解决这个问题。
最好的可能是使用BufferStrategy
并绘制到那个,其中包含了一个适合您的代码段。
你可以更进一步,完全放弃Swing,只需使用Frame / BufferStrategy。在我的问题中有一个完整的工作示例(从中获取和修改了代码片段):
AWT custom rendering - capture smooth resizes and eliminate resize flicker
无论如何,这是一个实现BufferStrategy
,您应该可以直接进入:
// you should be extending JFrame
public void addNotify() {
super.addNotify();
createBufferStrategy(2);
}
private synchronized void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy==null) return;
sizeChanged = false;
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
MyGraphics draw = strategy.getDrawGraphics();
draw.setColor(Color.BLACK);
draw.fillRect(0, 0, getPreferredSize().width, getPreferredSize().height);
draw.drawImage(ship.getImage(), ship.getCurrentX(), ship.getCurrentY(), null);
draw.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
答案 1 :(得分:2)
任何绘图仍将在Swing Thread中执行,因此无论您尝试解决什么,它都无济于事。
确保你没有在swing线程中进行任何冗长的计算,这可能会在需要执行时立即停止重绘
答案 2 :(得分:2)
将所有逻辑分成两部分。静态和动态。 (例如海运和移动船舶。船舶在海洋静态图像上改变形状/位置)
在图像中绘制一次静态内容并使用paintComponent()中的图像。在静态图像之后调用动态部件绘制。
使用setClip()限制重绘区域。
答案 3 :(得分:2)
仅针对您在此处发布的代码:
1 /如果您想显示Image/ImageIcon,那么最好也是最简单的方式是Use Labels
2 /正如您所提到的Runnable{...}.start();
Swing是简单的线程,所有输出到GUI都必须在EDT上完成;您必须查看Concurrency in Swing,结果是BackGround Task(s)
的所有输出都必须包含在invokeLater()
中,如果有问题,则会出现问题,然后进入invokeAndWait()
3 /如果你切换(在JComponents
之间)/添加/删除/更改Layout
,那么你必须在具体代码块中调用revalidate() + repaint()
作为最后一行
编辑:
脏黑客将是paintImmediately()
答案 4 :(得分:2)
调用不带任何参数的重绘意味着重新绘制整个面板。
如果你需要重新绘制部分屏幕(宇宙飞船已移动到不同的位置),你应该确保只重新绘制屏幕的那些部分。不应触及保持不变的区域。
重绘会获取应重新绘制的矩形的坐标。移动船舶时,您应该知道船舶的旧坐标和船舶应移动的坐标。
repaint( oldShipCoordinateX, oldShipCoordinateY, shipWidth, shipLength );
repaint( newShipCoordinateX, newShipCoordinateY, shipWidth, shipLength );
这通常比不带参数调用repaint()快得多。但是,您需要额外的努力来记住船的最后位置,并且必须能够计算船的新位置。
另请参阅:http://download.oracle.com/javase/tutorial/uiswing/painting/index.html - 尤其是第3步
答案 5 :(得分:1)
我已经读过repaint()方法只是一个请求,并且每次调用它都不会执行
它将多个repaint()请求合并为一个以提高效率。
我相信我注意到了这一点的影响,因为当我移动飞船时,我的飞船往往会略微落后。
然后发布演示此问题的SSCCE。我怀疑问题是你的代码。
关于您接受的解决方案,请查看Charles上次发布的帖子:Swing/JFrame vs AWT/Frame for rendering outside the EDT比较Swing与AWT解决方案。