2017-05-10 8 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 좌표를 제공합니다. 버퍼링 된 이미지의 경우 이미지가 암시 적으로 위치를 가지지 않으므로 항상 0입니다. 레이블처럼 뭔가. 다음은 올바른 것입니다 버전입니다 :

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의 특성을 조정하게 될 것입니다. 결국 스프라이트는 위치를 가지며이 정보를 유지 관리해야합니다. 스프라이트 객체 외부에 그것을 유지하는 것은 객체 지향적 관점에서 이해가되지 않는다.

그래서 다음과 같이 클래스를 조정합니다

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에 스프라이트의 좌표를 사용하여 방법을 지우기. for 루프의

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

변수 스프라이트 (높이 sprite_x 0 내지 폭 stepper_x 0 내지 sprite_y 및 stepper_y)에 대해, 그리고 (스프라이트의 기본 좌표 세계 화상 대하여 조정 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"))); 

이제 프레임은 이전에 렌더링하는 시간이 주어집니다 스프라이트는 배경을 조절할 수 있습니다. 결과적으로 당신은 그 변화를 보지 못하게됩니다.

다른 방법으로 테스트 할 수 있습니다. 반 스프라이트가 작성되었을 때 짧은 잠을 추가 이제 다시 위 오초 잠을 제거하고 :

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

그런 다음 마지막 문제는 여기에 촬영 렌더링에 대한 접근 방식입니다. 스프라이트를 지우려면 배경 복사본을 유지 한 다음 명시 적으로 복사본의 스프라이트 영역을 복사본으로 교체하면 스프라이트가 지워집니다. 이것은 여러 스프라이트를 얻거나 이동하려고하면 어려운 일이 될 것입니다. 예를 들어 sprite가 spawn과 erase 호출 사이를 이동하면 완전히 지우지는 않습니다.

일반적으로 2D 렌더링에서 수행되는 작업은 하나 이상의 배경 레이어, 그리고 맨 위에있는 스프라이트를 순서대로 렌더링하는 것입니다. SNES 나 MegaDrive와 같은 구형 콘솔 용 에뮬레이터 나 NeoGeo 및 CPS-2와 같은 시스템 용 아케이드 에뮬레이터 (예 : MAME 또는 Kawaks)를 사용하여 약간의 게임을 즐겨보십시오. 종종 특정 레이어를 비활성화하고 사물이 어떻게 렌더링되는지 볼 수 있습니다.

체스 보드와 같이 대부분 정적 인 콘텐츠를 표시해야하는 매우 간단한 게임의 경우 배경을 렌더링 한 다음 위에 스프라이트를 렌더링하는 것이 대부분 효과가 있습니다. 그러나 빠르게 움직이는 프레임과 끊임없이 프레임을 업데이트하는 경우 화면에 출력 할 때 렌더링 단계의 위치에 따라 스프라이트가 누락되거나 깜박일 수 있습니다.

일반적인 해결책은 일부 프레임 버퍼를 사용하는 것입니다. 프레임은 일부 백그라운드 버퍼 이미지로 렌더링되며, 일단 준비되면 화면에 표시 할 수 있습니다. 그래서 같이 : 당신은 스윙 (및 AWT)에서이 작업을 수행 할 수 있지만

render loop

, 그것은 매우 성능이 좋은 방법이 아니다.어쨌든 그래픽 사용자 인터페이스를 작성하기위한 레이블 및 아이콘 대신에 조금 더 기본적인 구성 요소를 사용하고자 할 것입니다. 기존 스프라이트 라이브러리를 사용하고 싶지 않고 직접해야한다면 OpenGL과 같은 하드웨어 렌더링을위한 인터페이스를 조사하는 것이 가장 좋습니다. 사용할 수있는 Java 바인딩이 있습니다.

게임 개발 스택 교환을 확인하십시오. https://gamedev.stackexchange.com/

+0

감사합니다. :) –

+0

@ScottField이 질문이나 답변이 귀하의 질문을 해결했다면 [확인 표시를 클릭하여 수락하십시오.] (https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer -작업). 이는 해결책을 찾았으며 응답자와 자신에게 어느 정도의 평판을 제공한다는 것을 더 넓은 커뮤니티에 나타냅니다. 이를 수행 할 의무는 없습니다. –