2014-02-06 4 views
1

저는 2D 타일 기반 게임을 만들려고 노력 했었지만, 몇 가지 문제가 발생하기 전에는 아직 멀었습니다. 게임은 매우 느리고 타일 사이에 공백이 계속 나타나는 것을 제외하고는 괜찮습니다. 모든 타일 이미지를 하나의 이미지로 옮겨서 더 부드럽게 만들기 위해로드하려고했지만 작동하지 않았습니다. 더 나은 fps를 위해 게임을 최적화하는 방법에 대한 도움이 필요합니다.Java 2D 게임 타일 렌더링 최적화

Player p = new Player(); 
static Map m = new Map(); 
Hotbar h = new Hotbar(); 
static Zombie z = new Zombie(); 

public static void main(String args[]) { 
    Thread t1 = new Thread(new Display()); 
    t1.start(); 
    Thread t2 = new Thread(z); 
    t2.start(); 
    Thread t3 = new Thread(m); 
    t3.start(); 
} 

@SuppressWarnings("static-access") 
public void run() { 
    while (true) { 
     try { 
      oldTime = currentTime; 
      currentTime = System.currentTimeMillis(); 
      elapsedTime = currentTime - oldTime; 
      fps = (int) ((1000/40) - elapsedTime); 
      Thread.sleep(fps); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     if (started) { 
      p.playerMovement(); 
      p.width = getWidth(); 
      p.height = getHeight(); 
      h.width = getWidth(); 
      h.height = getHeight(); 
      if (p.maptiles != null) { 
       z.maptiles = new Rectangle[p.maptiles.length]; 
       z.maptiles = p.maptiles; 
      } 
     } else { 

     } 
     repaint(); 
    } 
} 

대부분의지도의 클래스 다음

BufferedImage backLayer; 
BufferedImage backLayer2; 

@SuppressWarnings("static-access") 
public void createBack() { 
    backLayer = new BufferedImage(mapwidth * 32, mapheight * 32, BufferedImage.TYPE_INT_ARGB); 
    Graphics g = backLayer.getGraphics(); 
    Graphics2D gd = (Graphics2D) g; 
    Color daycolor = new Color(0, 150, 255, 255); 
    Color nightcolor = new Color(0, 50, 100, Math.abs(time/daylength - 510/2)); 
    Color backtilecolor = new Color(10, 10, 20, Math.abs(time/daylength - 510/4)); 
    g.setColor(daycolor); 
    g.fillRect(0, 0, p.width, p.height); 
    g.setColor(nightcolor); 
    g.fillRect(0, 0, p.width, p.height); 
    time++; 
    if (time > 510 * daylength) 
     time = 0; 
    if (time < 100 * daylength || time > 410 * daylength) { 
     z.SpawnZombie(); 

    } else { 

    } 
    g.setColor(backtilecolor); 
    int i = 0; 
    for (int x = 0; x < mapwidth; x++) { 
     for (int y = 0; y < mapheight; y++) { 
      if (mapdatasky[i] == 0) { 
       p.mapsky[i] = new Rectangle(x * 32 - p.playerX, y * 32 - p.playerY, 32, 32); 
      } 
      if (mapdatasky[i] >= 1) { 
       g.drawImage(tiles[mapdatasky[i]], x * 32 - p.playerX, y * 32 - p.playerY, 32, 32, null); 
       mapsky[i] = new Rectangle(x * 32 - p.playerX, y * 32 - p.playerY, 32, 32); 
       p.mapsky[i] = new Rectangle(x * 32 - p.playerX, y * 32 - p.playerY, 32, 32); 
       gd.fill(mapsky[i]); 
      } 
      i++; 
     } 
    } 
    backLayer2 = backLayer; 
} 

BufferedImage middleLayer; 
BufferedImage middleLayer2; 

@SuppressWarnings("static-access") 
public void createMiddle() { 
    middleLayer = new BufferedImage(mapwidth * 32, mapheight * 32, BufferedImage.TYPE_INT_ARGB); 
    Graphics g = middleLayer.getGraphics(); 
    Color fronttilecolor = new Color(20, 20, 40, Math.abs(time/daylength - 510/4)); 
    int i = 0; 
    for (int x = 0; x < mapwidth; x++) { 
     for (int y = 0; y < mapheight; y++) { 
      if (mapdata[i] == -1) { 
       spawnX = x * 32 - p.width; 
       spawnY = y * 32 - p.height; 
       p.spawnX = spawnX; 
       p.spawnY = spawnY; 
      } 
      if (mapdata[i] == 0) { 
       p.mapsky[i] = new Rectangle(x * 32 - p.playerX, y * 32 - p.playerY, 32, 32); 
      } 
      if (mapdata[i] > 0) { 
       g.drawImage(tiles[mapdata[i]], x * 32 - p.playerX, y * 32 - p.playerY, 32, 32, null); 
       maptiles[i] = new Rectangle(x * 32 - p.playerX, y * 32 - p.playerY, 32, 32); 
       p.maptiles[i] = new Rectangle(x * 32 - p.playerX, y * 32 - p.playerY, 32, 32); 
      } 
      if (!p.breaking) { 
       tileid = -1; 
      } 
      if (tileid == i) { 
       g.setColor(new Color(100, 0, 0, 100)); 
       g.fillRect(x * 32 - p.playerX + 16 - timer/(breaktime/16), y * 32 - p.playerY + 16 - timer/(breaktime/16), (timer/(breaktime/16)) * 2, (timer/(breaktime/16)) * 2); 
      } 
      i++; 
     } 
    } 
    g.setColor(fronttilecolor); 
    g.fillRect(0, 0, p.width, p.height); 
    middleLayer2 = middleLayer; 
} 


BufferedImage both; 
BufferedImage both2; 

public void mergeLayers() { 
    both = new BufferedImage(mapwidth * 32, mapheight * 32, BufferedImage.TYPE_INT_ARGB); 
    Graphics g = both.getGraphics(); 
    g.drawImage(backLayer2, 0, 0, mapwidth * 32, mapheight * 32, null); 
    g.drawImage(middleLayer2, 0, 0, mapwidth * 32, mapheight * 32, null); 
    both2 = both; 
} 

public void renderMap(Graphics g) { 
    g.drawImage(both2, 0, 0, mapwidth * 32, mapheight * 32, null); 
    if (placetimer > 0) 
     placetimer--; 
} 
@Override 
public void run() { 
    while (true) { 
     try { 
      oldTime = currentTime; 
      currentTime = System.currentTimeMillis(); 
      elapsedTime = currentTime - oldTime; 
      fps = (int) ((1000/100) - elapsedTime); 
      Thread.sleep(fps); 
      if (!mapset) { 
       setMap(); 
       mapset = true; 
      } 
      createBack(); 
      createMiddle(); 
      mergeLayers(); 
     } catch(Exception e) { 
      e.printStackTrace(); 
     } 
    } 
} 

게임 스크린 샷 타일

사이의 렌더링 오류의 사진입니다 디스플레이 클래스의 대부분 : Screenshot

+0

코드 검토 사이트에 더 적합합니다. –

답변

2

I 리뷰가 여기에서 독점하라.

일반적인 힌트 : 성능상의 이유로 "병합"(createBack/createMiddle/mergeLayers)을 추가 한 경우 :하지 마십시오! 여기서 모든 타일을 페인팅합니다 (어쨌든 보이지 않는 일부 타일도 g.drawImage로 직접 그려지면 잘릴 수 있습니다). 많은 작은 이미지를 큰 이미지로 그린 다음 화면에 큰 이미지를 페인팅하면 처음에는 작은 이미지를 직접 화면에 그릴 때보 다 더 빠를 수 없습니다 ....

"병합 "나타나는 줄무늬"를 해결하기 위해 :하지 마! 줄무늬는 이미지를 그리는 스레드와 다른 스레드에서 변경된 좌표에서옵니다. 타일과 좌표를 계산하는 방법을 변경하여이 문제를 피할 수 있습니다. 코드는 약간을 지적하기에 너무 ... "복잡한", 그래서 여기에 몇 가지 의사 코드를 사용합니다 :

void paintTiles(Graphics g) 
{ 
    for (Tile tile : allTiles) 
    { 
     g.drawImage(tile, player.x, player.y, null); 
    } 
} 

문제는 여기에 동안 그림 스레드가 모든 타일의를 통해 반복되고 있다는 것입니다 다른 스레드는 플레이어 좌표를 변경할 수 있습니다. 예를 들어, 타일 중 일부는 player.x=10player.y=20으로 그릴 수 있으며, 나머지 스레드는 플레이어 좌표를 변경하므로 나머지 타일은 player.x=15player.y=25으로 그려집니다. 그러면 타일 사이에 나타나는 "줄무늬"로 알게됩니다. 최선의 경우

이 오히려 쉽게 해결할 수 있습니다 타일 반복하면서

void paintTiles(Graphics g) 
{ 
    int currentPlayerX = player.x; 
    int currentPlayerY = player.y; 

    for (Tile tile : allTiles) 
    { 
     g.drawImage(tile, currentPlayerX, currentPlayerY, null); 
    } 
} 

이 방법은, "현재"플레이어의 좌표가 동일하게 유지됩니다.

+0

감사합니다. 다른 스레드와 다른 플레이어 좌표를 사용한다는 것을 결코 깨닫지 못했습니다. 현재 쓰레드를 유지하거나 함께 쓰도록 제안 하시겠습니까? – NickM13

+0

게임의 (의도 된) 아키텍처를 알지 못하여 권장 사항을 제시 할 수 없습니다. "일반적으로"렌더링 스레드 (이벤트 발송 스레드)와 단일 게임 스레드가 있습니다. "단순한"타일 게임의 경우 충분해야합니다. 스레드가 많을수록 동기화면에서 더 복잡한 작업을하는 경향이 있습니다. 실제로 수행해야 할 시간이 거의 들지 않는 한 여러 스레드로 인해 번거롭게 할 수있는 이점은 거의 없지만 모든 것 게임에 대한 지식없이 – Marco13