作为我的 CS 项目,我正在创建一个完整的东方风格的子弹躲避游戏,其中涉及在 JPanel 上的正确坐标上渲染数千个子弹图像。幸运的是,JVM 可以容纳数以万计的 bufferedImage,而不会出现任何明显的帧丢失,所以我没想到会遇到这个巨大的障碍:旋转图像。
我最初想要实现的是旋转敌人子弹的BufferedImage;我在其他 Stack Overflow 问题上使用了小样本的旋转方法,效果很好。当我尝试旋转子弹对象的 ArrayList 中的数千个子弹精灵时,问题出现了。数以万计的新 BufferedImage 和 Graphics2D 创建在运行时完全停止了 JVM。
我研究了与 Java 图像旋转相关的所有问题,以找到一种不会导致严重丢帧或彻底的堆空间问题的轻量级方法。然而,这些方法都至少包含某种形式的对象创建或操作,而程序根本无法接受。
我确实尝试自己制作一个轻量级的轮换方法,牺牲了两周的时间和至少七个智商点。尽管如此,在没有对计算机科学更内在的理解的情况下,我能得到的“最佳”性能就是这种方法,修改现场图像:
public Bullet(... , double deg, ... , BufferedImage shape /*actual bullet sprite*/, String tag, BufferedImage emp /*empty bufferedimage to act as a template to modify image then redraw*/ ) throws IOException
{
rotor = emp;
img = shape;
rotate(deg);
setDeg(deg);
this.deg = deg;
...
}
public void rotate(double angle) { //tried AffineTransform and image Op and everything but all the same...
Graphics2D g = rotor.createGraphics();
g.setBackground(new Color(255, 255, 255, 0));
g.clearRect(0,0, rotor.getWidth(), rotor.getHeight());
g.rotate(Math.toRadians(angle), img.getWidth() / 2, img.getHeight() / 2);
g.drawImage(img, null, img.getWidth() - rotor.getWidth(), img.getHeight() - rotor.getHeight());
g.dispose();
img = rotor;
}
尽管如此,由于需要渲染如此多的子弹(至少 10,000 个),该方法并没有带来任何创新性的差异。有什么方法可以使图像旋转尽可能轻,以免给渲染添加相关的权重(并希望将项目从注定的厄运中拯救出来)?
考虑在游戏初始化期间预先计算和存储旋转图像,以有效地旋转。这种方法最大限度地减少了实时计算负载以提高性能。以下是实现此方法的方法:
预计算阶段:
以固定间隔(例如,每 1 度)预先计算每个子弹精灵的旋转版本。 将这些预先旋转的图像存储在数组或地图中,其中每个索引/键代表一个旋转角度。 实施示例:
// Precompute rotated images
BufferedImage[] preRotatedImages = new BufferedImage[360];
for (int i = 0; i < 360; i++) {
preRotatedImages[i] = rotateImage(originalImage, i);
}
// Rotate function
private BufferedImage rotateImage(BufferedImage img, int angle) {
int w = img.getWidth();
int h = img.getHeight();
int newW = (int) Math.ceil(Math.sqrt(w * w + h * h));
BufferedImage rotated = new BufferedImage(newW, newW, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = rotated.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.rotate(Math.toRadians(angle), newW / 2, newW / 2);
g2d.drawImage(img, (newW - w) / 2, (newW - h) / 2, null);
g2d.dispose();
return rotated;
}
// During gameplay, fetch pre-rotated images
public void render(Graphics g, int angle, int x, int y) {
g.drawImage(preRotatedImages[angle % 360], x, y, null);
}
优化:
正如 @Abra 建议的那样,仅旋转和存储可见项目符号的图像,从而减少内存使用。 使用具有 Java 内置功能的硬件加速图像绘制。 此方法通过利用预先计算的资源来减少运行时开销,即使屏幕上有数千个子弹,也能确保更流畅的游戏体验。测试内存使用情况和性能,看看这种方法是否适合您。