Java 为什么我不能擦除我的精灵
我已经尝试了一段时间让一个游戏在Java中运行,在对其他人的精灵功能感到痛苦之后,我自己制作了一个,不明白为什么我不能删除它。我知道它正在改变背景的像素来显示我的弓箭手精灵,因为它出现了,但无论出于什么原因,我不能改变像素回到以前的样子。有没有人知道为什么会这样,或者我如何解决? 链接到带有图像的google文档:Java 为什么我不能擦除我的精灵,java,jframe,sprite,bufferedimage,Java,Jframe,Sprite,Bufferedimage,我已经尝试了一段时间让一个游戏在Java中运行,在对其他人的精灵功能感到痛苦之后,我自己制作了一个,不明白为什么我不能删除它。我知道它正在改变背景的像素来显示我的弓箭手精灵,因为它出现了,但无论出于什么原因,我不能改变像素回到以前的样子。有没有人知道为什么会这样,或者我如何解决? 链接到带有图像的google文档: 代码中有几个问题共同导致了这种情况的发生。首先,您的擦除方法会擦除错误的部分。尝试在擦除中只写入白色像素,您将看到。发生的事情是,对于spawn方法,您提供坐标,但是对于erase
代码中有几个问题共同导致了这种情况的发生。首先,您的擦除方法会擦除错误的部分。尝试在擦除中只写入白色像素,您将看到。发生的事情是,对于spawn方法,您提供坐标,但是对于erase方法,您不提供坐标。getMinX和getMinY方法不提供精灵坐标,而是提供图像本身的最小X和Y坐标。对于缓冲图像,这始终为零,因为图像并不隐含有位置;就像标签一样。以下是一个正确的版本:
public void erase(JFrame frame, BufferedImage world, BufferedImage orig_world, int x, int y) throws IOException {
for (int stepper_y = 0; stepper_y < this.image.getHeight(); stepper_y++) {
for (int stepper_x = 0; stepper_x < this.image.getWidth(); stepper_x++) {
int sprite_pixel = orig_world.getRGB(x + stepper_x, y + stepper_y);
// get pixel from orginal sprite
int sprite_alpha = (sprite_pixel >> 24) & 0xff;
// get alpha value from original sprite
int sprite_red = (sprite_pixel >> 16) & 0xff;
// get red value from original sprite
int sprite_green = (sprite_pixel >> 8) & 0xff;
// get green value from original sprite
int sprite_blue = sprite_pixel & 0xff;
// get blue value from original sprite
int pixel = (sprite_alpha << 24) | (sprite_red << 16) | (sprite_green << 8) | sprite_blue;
// set the pixel equal to the old values
world.setRGB(x + stepper_x, y + stepper_y, pixel);
// place the pixel
}
}
}
然后在其swpan和erase方法中使用精灵的坐标
// to spawn a sprite on top of another image.
public void spawn(JFrame frame, BufferedImage world) throws IOException, InterruptedException {
for (int sprite_y = 0; sprite_y < this.image.getHeight(); sprite_y++) {
for (int sprite_x = 0; sprite_x < this.image.getWidth(); sprite_x++) {
int sprite_pixel = this.image.getRGB(sprite_x, sprite_y);
int sprite_alpha = (sprite_pixel >> 24) & 0xff;
int sprite_red = (sprite_pixel >> 16) & 0xff;
int sprite_green = (sprite_pixel >> 8) & 0xff;
int sprite_blue = sprite_pixel & 0xff;
int pixel = (sprite_alpha << 24) | (sprite_red << 16) | (sprite_green << 8) | sprite_blue;
world.setRGB(x + sprite_x, y + sprite_y, pixel);
}
}
}
public void erase(JFrame frame, BufferedImage world, BufferedImage orig_world) throws IOException {
for (int stepper_y = 0; stepper_y < this.image.getHeight(); stepper_y++) {
for (int stepper_x = 0; stepper_x < this.image.getWidth(); stepper_x++) {
int sprite_pixel = orig_world.getRGB(x + stepper_x, y + stepper_y);
// get pixel from orginal sprite
int sprite_alpha = (sprite_pixel >> 24) & 0xff;
// get alpha value from original sprite
int sprite_red = (sprite_pixel >> 16) & 0xff;
// get red value from original sprite
int sprite_green = (sprite_pixel >> 8) & 0xff;
// get green value from original sprite
int sprite_blue = sprite_pixel & 0xff;
// get blue value from original sprite
int pixel = (sprite_alpha << 24) | (sprite_red << 16) | (sprite_green << 8) | sprite_blue;
// set the pixel equal to the old values
world.setRGB(x + stepper_x, y + stepper_y, pixel);
// place the pixel
}
}
}
事情发生的顺序实际上是这样的:
使用世界精灵的背景图像创建标签,并将其设置为可见。它还没有上下文,所以你还没有真正看到它。
创建框架,将其设置为可见,设置其大小并添加标签。此时,框架的渲染将由Swing背景线程处理。您的代码现在继续。但帧尚未渲染。
读《弓箭手精灵》。
生成弓箭手精灵,这意味着它会覆盖作为标签图标的世界图像的一些像素。
直到现在,帧渲染才真正完成,背景已调整。
您可以通过在帧代码和获取弓箭手精灵之间的主线程中设置睡眠来测试这一点,如下所示:
int x, y;
BufferedImage image;
public Sprite(BufferedImage image, int x, int y) throws IOException {
this.image = image;
this.x = x;
this.y = y;
}
public BufferedImage getSprite() {
return this.image;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
frame.add(label);
Thread.sleep(5000);
Sprite archer = new Sprite(ImageIO.read(new File("C:/Users/sfiel42/Documents/game/archer.png")));
现在,在精灵可以调整背景之前,给了帧渲染时间。因此,你最终不会看到它的变化
这是另一种测试方法。再次移除上述5秒睡眠,现在在写入一半精灵后添加短暂睡眠:
public void spawn(JFrame frame, BufferedImage world, int x, int y) throws IOException, InterruptedException {
int orig_x = x;
for (int sprite_y = 0; sprite_y < this.image.getHeight(); sprite_y++) {
if (sprite_y == this.image.getHeight() / 2) {
Thread.sleep(100);
}
最后一个问题是这里采用的渲染方法。如果要删除精灵,请保留背景的副本,然后用副本的副本显式替换精灵区域,以擦除精灵。这将使事情变得困难,一旦你得到多个精灵或试图移动他们。例如,如果精灵在“繁殖”和“擦除”调用之间移动,则不会完全擦除它
在2D渲染中通常要做的是,你有一些层,这些层是按照给定的顺序渲染的:一个或多个背景层,然后是顶部的精灵。使用一些用于旧主机(如SNE或MegaDrive)的模拟器,或用于NeoGeo和CPS-2(如MAME或Kawaks)系统的arcade模拟器,进行一番尝试。您通常可以禁用特定层,并查看事物的渲染方式
对于一个非常简单的游戏,必须显示大部分静态内容,比如说,一个棋盘,渲染背景,然后顶部的精灵将大部分工作。但对于移动速度更快且不断更新帧的对象,可能会丢失精灵或闪烁,具体取决于输出到屏幕时处于渲染阶段的位置
通常的解决方案是使用一些帧缓冲区:帧被渲染成一些背景缓冲区图像,只有当它准备好后才允许在屏幕上显示。像这样:
虽然你可以在Swing和AWT中这样做,但这不是一种非常有效的方式。无论如何,您都希望使用更基本的组件,而不是用于组成图形用户界面的标签和图标。如果您不想使用现有的sprite库,而是自己做事情,那么最好还是研究硬件渲染的接口,例如OpenGL。有Java可用的绑定
还查看游戏开发堆栈交换:
@ ScottField,如果这个或任何答案已经解决了你的问题,请考虑。这向更广泛的社区表明,你已经找到了一个解决方案,并给回答者和你自己带来了一些声誉。没有义务这样做。frame.add(label);
Thread.sleep(5000);
Sprite archer = new Sprite(ImageIO.read(new File("C:/Users/sfiel42/Documents/game/archer.png")));
public void spawn(JFrame frame, BufferedImage world, int x, int y) throws IOException, InterruptedException {
int orig_x = x;
for (int sprite_y = 0; sprite_y < this.image.getHeight(); sprite_y++) {
if (sprite_y == this.image.getHeight() / 2) {
Thread.sleep(100);
}
Sprite archer = new Sprite(ImageIO.read(new File("C:/Users/sfiel42/Documents/game/archer.png")));
archer.spawn(frame, world.getSprite());
frame.repaint();
Thread.sleep(2000);
System.out.println("Erasing");
archer.erase(frame, world.getSprite(), orig_world.getSprite());
frame.repaint();