2017-05-10 35 views
0

我一直在嘗試一段時間,以獲得一個在Java中的遊戲,在對其他人民的痛苦之後,我做了我自己的雪碧功能,不明白爲什麼我不能擦除它。我知道它會改變背景的像素以顯示我的射手精靈,因爲它顯示出來,但無論出於什麼原因,我都無法將像素更改回原來的樣子。有沒有人有一個想法,爲什麼這是或我可以如何解決它? 鏈接到谷歌文檔與圖像: https://docs.google.com/document/d/1eU6faW1d7valq1yE_Bo09IPMbXuuZ6ZgqUu3BesaJUw/edit?usp=sharing爲什麼我無法刪除我的雪碧

import javax.swing.*; 
import javax.imageio.*; 
import java.io.*; 
import java.awt.image.BufferedImage; 

public class Sprite { 
BufferedImage image; 
public Sprite(BufferedImage image) throws IOException{ 
this.image = image; 
} 
public BufferedImage getSprite(){ 
return this.image; 
} 
public int getX(){ 
return this.image.getMinX(); 
} 
public int getY(){ 
return this.image.getMinY(); 
} 

//to spawn a sprite on top of another image. 
public void spawn(JFrame frame, BufferedImage world,int x, int y) throws 
IOException{ 
int orig_x = x; 
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,y,pixel); 
    x++; 
    } 
    y++; 
    x = orig_x; 
    } 
    } 

    public void erase(JFrame frame,BufferedImage world, BufferedImage 
    orig_world) throws IOException{ 
    int sprite_x = this.image.getMinX(); 
    int sprite_y = this.image.getMinY(); 
    int orig_sprite_x = sprite_x; 
    for (int stepper_y = this.image.getMinY(); stepper_y < 
    this.image.getHeight(); stepper_y++){ 
     for (int stepper_x = this.image.getMinX(); stepper_x < 
     this.image.getWidth(); stepper_x++){ 
     int sprite_pixel = orig_world.getRGB(sprite_x,sprite_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(sprite_x,sprite_y,pixel); 
     //place the pixel 
     sprite_x++; 
     } 
    sprite_x = orig_sprite_x; 
    // setting equal to original is so that at the end of each row it resets 
    to the farthest left pixel. 
    sprite_y++; 
    } 
} 

public static void main(String[] args) throws IOException{ 

    Sprite orig_world = new Sprite(ImageIO.read(new 
    File("C:/Users/sfiel42/Documents/game/castles.png"))); 
    Sprite world  = new Sprite(ImageIO.read(new 
    File("C:/Users/sfiel42/Documents/game/castles.png"))); 

    JLabel label  = new JLabel(); 
    label.setLocation(0,0); 
    label.setIcon(new ImageIcon(world.getSprite())); 
    label.setVisible(true); 

    JFrame frame  = new JFrame(); 
    frame.setVisible(true); 
    frame.setSize(783,615); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    frame.add(label); 

    Sprite archer  = new Sprite(ImageIO.read(new 
    File("C:/Users/sfiel42/Documents/game/archer.png"))); 
    archer.spawn(frame,world.getSprite(),250,400); 
    archer.erase(frame,world.getSprite(),orig_world.getSprite()); 

    } 
} 

回答

1

有一對夫婦與代碼相結合,使這種情況發生的問題。首先是你的擦除方法擦除錯誤的部分。嘗試在擦除時只寫白色像素,你會看到。發生什麼事是你提供座標的產卵方法,但是你沒有使用的擦除方法。 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 
     } 
    } 
} 

不過,更好的是使x和y座標一個Sprite的性質。畢竟,精靈有一個位置並且必須保持這個信息。將它保持在Sprite對象之外對於面向對象的觀點來說沒有任何意義。

那麼調整你的類是這樣的:

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; 
} 

然後用精靈的座標在其swpan和清除方法。

// 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 
     } 
    } 
} 

在for循環的變量是相對於子畫面(sprite_y和stepper_y從0到高度,sprite_x和stepper_x從0到寬度),並調整相對於所述子畫面的基本座標世界圖像( x和y)。


第二個問題。

當前代碼中真正發生的事情是,您從未真正呈現過背景,然後將精靈呈現給它。這聽起來很奇怪,因爲你看到了,對吧?但是發生什麼事情是一個競爭條件。 Java Swing使用單獨的線程進行渲染,這意味着當你看到某些東西時,你不能保證它會在你的代碼繼續之前被渲染。

正是這一部分在這裏:

JLabel label  = new JLabel(); 
label.setLocation(0,0); 
label.setIcon(new ImageIcon(world.getSprite())); 
label.setVisible(true); 

JFrame frame  = new JFrame(); 
frame.setVisible(true); 
frame.setSize(783,615); 
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
frame.add(label); 

Sprite archer = new Sprite(ImageIO.read(new File("C:/Users/sfiel42/Documents/game/archer.png"))); 
archer.spawn(frame,world.getSprite(),250,400); 
archer.erase(frame,world.getSprite(),orig_world.getSprite()); 

,事情發生的順序實際上是這樣的:

  1. 與背景圖像(世界精靈)創建標籤,並將其設置爲可見。它沒有上下文,所以你還沒有看到它。
  2. 創建框架,將其設置爲可見,設置其大小並添加標籤。 此時,框架的渲染將由Swing後臺線程處理。你的代碼現在繼續。但框架還沒有渲染。
  3. 閱讀射手精靈。
  4. 生成了射手精靈,這意味着它會覆蓋作爲標籤圖標的世界圖像的一些像素。
  5. 只有現在幀渲染實際上完成,與調整後的背景。

您可以通過將一個睡在你的主線程框架代碼之間並獲得射手精靈,像這樣的測試:

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); 
     } 

您可能會看到半精靈與另一半失蹤。所有這些都將取決於渲染線程的時間安排,計算機的速度和其他方面,所以結果可能無法預測。如果閱讀射手精靈文件的速度較慢,那麼您有可能永遠不會看到您的精靈開始。

當您將某物更改爲世界圖像時,該框架和圖標不會自動更新;你直接寫入一些緩衝圖像,所以使用它的組件不知道有什麼改變,他們應該改變它們在屏幕上的表示。渲染之後調用的更新:

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(); 

那麼最後一個問題是這種方法在這裏渲染服用。如果要刪除精靈,則通過保留背景的副本,然後明確地將精靈區域替換爲副本的精靈區域來刪除精靈。一旦你得到多個精靈或試圖移動它們,這會讓事情變得困難。例如,如果精靈在產卵和擦除調用之間移動,則不會完全擦除它。

通常在2D渲染中做的事情是,你有層,按照給定的順序渲染:一個或多個背景層,然後精靈在頂部。用SNES或MegaDrive等較老式控制檯的某些模擬器或NeoGeo和CPS-2(例如MAME或Kawaks)等系統的街機模擬器玩一下。您通常可以禁用特定的圖層並查看渲染的方式。

對於一個非常簡單的遊戲,必須顯示大多數靜態內容,如棋盤,渲染背景,然後在頂部的精靈將主要工作。但是對於移動速度更快並且不斷更新幀的內容,當您輸出到屏幕時,您可能會丟失精靈或閃爍,這取決於您在渲染階段的位置。

通常的解決方案是使用一些幀緩衝區:幀被渲染到一些背景緩衝區圖像中,只有一旦準備就緒,它才允許顯示在屏幕上。像這樣:

render loop

雖然你可以在Swing(和AWT)做到這一點,它不是一個非常高性能的方式。無論如何,你會希望使用更基本的組件,而不是標籤和圖標,這些組件是用來組成圖形用戶界面的。如果你不想使用現有的精靈庫,而是自己做一些事情,那麼最好還是研究硬件渲染的接口,比如OpenGL。有可用的Java綁定。

還檢查了遊戲開發堆棧交換:https://gamedev.stackexchange.com/

+0

謝謝,這使得現在很有意義! :) –

+0

@ScottField如果這個或任何答案已解決您的問題,請考慮[通過點擊複選標記接受它](https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer -工作)。這向更廣泛的社區表明,您已經找到了解決方案,併爲答覆者和您自己提供了一些聲譽。沒有義務這樣做。 –