Java JPanel闪烁问题
对于我的游戏,我在Java JPanel闪烁问题,java,swing,jpanel,Java,Swing,Jpanel,对于我的游戏,我在java.awt.Image上绘制图像,然后将图像绘制到JPanel上。我这样做有两个原因,主要是因为我不想让游戏渲染占用EDT上的cpu周期,以及为了可移植性 出现了一个问题,在使用时导致java.awt.JPanel上闪烁 图形#绘图图像(图像img、int x、int y、int width、int height、, 图像观察者(观察员) 但是, 图形#绘图图像(图像img、int x、int y、ImageObserver) 没有导致此问题 这是我的密码: Sandb
java.awt.Image
上绘制图像,然后将图像绘制到JPanel上。我这样做有两个原因,主要是因为我不想让游戏渲染占用EDT上的cpu周期,以及为了可移植性
出现了一个问题,在使用时导致java.awt.JPanel
上闪烁
图形#绘图图像(图像img、int x、int y、int width、int height、,
图像观察者(观察员)
但是,
图形#绘图图像(图像img、int x、int y、ImageObserver)
没有导致此问题
这是我的密码:
Sandbox.java
public class Sandbox implements Paintable {
public static void main(String[] args) throws Exception {
GameWindow window = new GameWindow("Test", 800, 600);
GameScreen screen = new GameScreen(800, 600);
Sandbox sandbox = new Sandbox();
window.add(screen);
window.setVisible(true);
boolean running = true;
while(running) {
sandbox.update();
screen.getPaintBuffer().clear();
screen.getPaintBuffer().paint(sandbox);
screen.repaint();
Thread.sleep(1000 / 60);
}
}
private int x = 0, y = 0;
public void update() {
x++;
y++;
}
public void paint(Graphics g) {
g.drawRect(x, y, 50, 50);
}
}
GameWindow.java
public class GameWindow extends JFrame {
public GameWindow(String title, int width, int height) {
setTitle(title);
setSize(width, height);
setResizable(false);
setLocationByPlatform(true);
setDefaultCloseOperation(3);
}
}
GameScreen.java
public class GameScreen extends JPanel {
private ImageBuffer buffer;
public GameScreen(int width, int height) {
buffer = new ImageBuffer(width, height);
}
public void paint(Graphics g) {
g.drawImage(getPaintBuffer().getBuffer(), 0, 0, getWidth(), getHeight(), null);
}
public ImageBuffer getPaintBuffer() {
return buffer;
}
}
Paintable.java
public interface Paintable {
public void paint(Graphics g);
}
ImageBuffer.java
public class ImageBuffer {
private final Image buffer;
private int width, height;
public ImageBuffer(int width, int height) {
buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
this.width = width;
this.height = height;
}
public void paint(Paintable paintable) {
paintable.paint(buffer.getGraphics());
}
public void clear() {
buffer.getGraphics().clearRect(0, 0, width, height);
}
public Image getBuffer() {
return buffer;
}
}
将
GameScreen
的paint
方法更改为
paintComponent
super.paintComponent
,以维护油漆链的合同作为ImageObsever
参数传递给drawImage
public class GameScreen extends JPanel {
private ImageBuffer buffer;
public GameScreen(int width, int height) {
buffer = new ImageBuffer(width, height);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(getPaintBuffer().getBuffer(), 0, 0, getWidth(), getHeight(), this);
}
public ImageBuffer getPaintBuffer() {
return buffer;
}
}
查看和了解有关绘画工作原理的更多详细信息
已更新
基本问题是缩放问题
您用来绘制的图像是800x600
,但游戏屏幕实际上是794x572
(在我的电脑上),这会导致图像缩放。现在,Swing的默认缩放并不漂亮,通常是基于速度而不是质量
现在,有很多方法可以改进这一点,但是一个快速的方法是应用一些更高级的渲染提示,例如
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2d.drawImage(getPaintBuffer().getBuffer(), 0, 0, getWidth(), getHeight(), this);
g2d.dispose();
}
现在,我已经做得太过火了,所以你可能想删除一些,看看有什么变化
已更新
渲染提示可能会减慢渲染过程,因此更好的解决方案可能是覆盖GameScreen
的getPreferredSize
方法并返回预期大小
public static class GameScreen extends JPanel {
private ImageBuffer buffer;
private Dimension size;
public GameScreen(int width, int height) {
buffer = new ImageBuffer(width, height);
this.size = new Dimension(width, height);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
然后,只需调用pack
GameWindow window = new GameWindow("Test");
GameScreen screen = new GameScreen(800, 600);
Sandbox sandbox = new Sandbox();
window.add(screen);
window.pack();
window.setVisible(true);
这样,每个人的大小都相同将GameScreen
的paint
方法更改为
替代paintComponent
调用super.paintComponent
,以维护油漆链的合同
将此作为ImageObsever
参数传递给drawImage
例如
public class GameScreen extends JPanel {
private ImageBuffer buffer;
public GameScreen(int width, int height) {
buffer = new ImageBuffer(width, height);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(getPaintBuffer().getBuffer(), 0, 0, getWidth(), getHeight(), this);
}
public ImageBuffer getPaintBuffer() {
return buffer;
}
}
查看和了解有关绘画工作原理的更多详细信息
已更新
基本问题是缩放问题
您用来绘制的图像是800x600
,但游戏屏幕实际上是794x572
(在我的电脑上),这会导致图像缩放。现在,Swing的默认缩放并不漂亮,通常是基于速度而不是质量
现在,有很多方法可以改进这一点,但是一个快速的方法是应用一些更高级的渲染提示,例如
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
g2d.drawImage(getPaintBuffer().getBuffer(), 0, 0, getWidth(), getHeight(), this);
g2d.dispose();
}
现在,我已经做得太过火了,所以你可能想删除一些,看看有什么变化
已更新
渲染提示可能会减慢渲染过程,因此更好的解决方案可能是覆盖GameScreen
的getPreferredSize
方法并返回预期大小
public static class GameScreen extends JPanel {
private ImageBuffer buffer;
private Dimension size;
public GameScreen(int width, int height) {
buffer = new ImageBuffer(width, height);
this.size = new Dimension(width, height);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
然后,只需调用pack
GameWindow window = new GameWindow("Test");
GameScreen screen = new GameScreen(800, 600);
Sandbox sandbox = new Sandbox();
window.add(screen);
window.pack();
window.setVisible(true);
这样,每个人都是一样大小的我会说这是一个侥幸,一个成功了,另一个没有发生过一次,它们不容易复制,这个错误是。伙计,如果你对此有信心,那么提交一份错误报告我会说这是一个侥幸,一个成功了,另一个没有发生过一次,它们不容易复制,这个错误是。伙计,如果您对此很有信心,那么就提交一份bug报告。我尝试过使用paintComponent,但它不会改变任何事情。我不想使用ImageObserver,这与任何方面都无关。另外,我不调用super.paint(g),因为我不打算向这个类添加任何组件。不管怎样,这些更改都不相关,也无法解决我的问题。@ChrisGray绘制比“绘制”组件更复杂。你有责任维护油漆链,否则奇怪和美妙的事情可能会出错。ImageObsever
向API提供了一些相关信息,以便在Image
发生更改时组件可以自行更新。这是一个很好的实践,让他们都明白这一点。我在秋千和AWT上画画已经有一段时间了。现在,这个图像被用作缓冲区,我不希望它在完成之前被显示或重新绘制到屏幕上。每次更改图像时更新JPanel都是对处理能力的浪费,没有必要这样做。有些时候,那些“良好的实践”应该被忽略。当然,只要你不介意什么时候出错。你应该考虑使用<代码> BufferStrategy <代码>,并对绘画过程进行完全控制,这就是它的设计目的。无论如何,您的问题不是bug,而是理解错误或看得太多,两个drawImage
方法之间的差异,以及可能忘记了JFrame
在框架的边界内绘制窗口装饰,使其内容小于实际窗口边界。你可以通过在游戏屏幕中使用getPreferredSize
来解决这个问题,我知道,我选择不使用画布和缓冲策略(我不想在评论中列出我的所有原因)。我还可以调用getContentPane().setPreferredSize(新维度(宽度、高度);然后调用pack().但为了简单起见,所以我可以很快写出来,我选择了退出。我尝试过使用paintComponent,但它不会改变任何东西。我不想使用ImageObserver,这与任何方面都不相关。此外,我也不叫super.paint(g)be