最近在尝试用Java做出一些经典小游戏,在被画面闪烁折磨之后,上网搜索学会了(大概)双缓冲,成功消除了画面闪烁。
接着发现了两个问题:
1.在之后的另一个代码中,我图省事(),没有专门做一个继承Canvas的类,而是把主类继承JFrame,直接重写了JFrame中的paint(Graphics g)方法和update(Graphics g),在程序中直接调用repaint()方法。但是发现闪烁又出现了。
我在VS Code里面按住Ctrl点击repaint()查看repaint()的代码,发现这个里面似乎没有调用update和paint
public void repaint() {
repaint(0, 0, 0, width, height);
}
public void repaint(long tm, int x, int y, int width, int height) {
if (this.peer instanceof LightweightPeer) {
// Needs to be translated to parent coordinates since
// a parent native container provides the actual repaint
// services. Additionally, the request is restricted to
// the bounds of the component.
if (parent != null) {
if (x < 0) {
width += x;
x = 0;
}
if (y < 0) {
height += y;
y = 0;
}
int pwidth = (width > this.width) ? this.width : width;
int pheight = (height > this.height) ? this.height : height;
if (pwidth <= 0 || pheight <= 0) {
return;
}
int px = this.x + x;
int py = this.y + y;
parent.repaint(tm, px, py, pwidth, pheight);
}
} else {
if (isVisible() && (this.peer != null) &&
(width > 0) && (height > 0)) {
PaintEvent e = new PaintEvent(this, PaintEvent.UPDATE,
new Rectangle(x, y, width, height));
SunToolkit.postEvent(SunToolkit.targetToAppContext(this), e);
}
}
}
后面把代码里面的repaint()全部替换成了update(getGraphics())解决了这个问题,但还是有点不清楚(还有Canvas的repaint()好像只有定义)
2.在替换之后,虽然闪烁没了,但是感觉画面清晰度降低了很多。
我猜可能是因为系统缩放的问题,但是不清楚怎么解决……
(方法倒也有,把缓冲用的图片分辨率加大,然后对应的把paint()里面的所有东西都放大,最后按照正常大小显示在屏幕上。但是似乎很费手)
// --------------用于绘图的部分
@Override // 重写paint
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
// 绘制玩家
g2.rotate(-Math.atan(1.0 * vy / VX), p.x + p.width / 2, p.y + p.height / 2);
g2.fill(p);
g2.drawLine(p.x + p.width / 2, p.y + p.height / 2, p.x + p.width / 2 + 30, p.y + p.height / 2);
g2.rotate(Math.atan(1.0 * vy / VX), p.x + p.width / 2, p.y + p.height / 2);
if (game == Game.START) {
g2.drawString("按下鼠标开始", W / 2, H / 2);
} else if (game == Game.RUNNING || game == Game.ZT) {
g2.setColor(Color.GREEN);
g2.fillRect((int) (objx1), 0, (int) (objW), (int) objy1);
g2.fillRect((int) objx1, (int) (objy1 + objH), (int) (objW), (int) (H - objH - objy1));
g2.fillRect((int) objx2, 0, (int) objW, (int) objy2);
g2.fillRect((int) objx2, (int) (objy2 + objH), objW, (int) (H - objH - objy2));
} else if (game == Game.AGAIN) {
g2.setColor(Color.GREEN);
g2.fillRect((int) (objx1), 0, (int) (objW), (int) objy1);
g2.fillRect((int) objx1, (int) (objy1 + objH), (int) (objW), (int) (H - objH - objy1));
g2.fillRect((int) objx2, 0, (int) objW, (int) objy2);
g2.fillRect((int) objx2, (int) (objy2 + objH), objW, (int) (H - objH - objy2));
g2.setColor(Color.RED);
g2.drawString("游戏结束!", W / 2, H / 2);
g2.drawString("你获得了 " + s + " 分!", W / 2 - 10, H / 2 + 15);
if (s > maxs) {
g2.setColor(Color.YELLOW);
g2.drawString("并且获得了最高分!", W / 2 - 20, H / 2 + 30);
}
}
// 暂停键
g2.setColor(Color.black);
g2.fillRect(0, 20, 10, 30);
g2.fillRect(20, 20, 10, 30);
// 计分
if (s <= maxs) {
g2.setColor(Color.black);
} else {
g2.setColor(Color.BLACK);
g2.fillRect(W-30, 30, 30, 10);
g2.setColor(Color.yellow);
}
g2.drawString(String.format("%03d", s), W - 30, 40);
}
// 缓存刷新防闪烁
@Override
public void update(Graphics g) {
r = null;
r = this.createImage(W, H);
Graphics og = r.getGraphics();
paint(og);
g.drawImage(r, 0, 0, this);
}
在资源管理器中可执行文件上点鼠标右键、属性、兼容性、更改高DPI设置、……