2015-05-14 41 views
2

我正在製作分形地形,但不管做什麼嘗試,它總是會看起來完全隨機。我一直遵循here中所述的鑽石和方塊算法。我能做些什麼來解決這個問題?鑽石和方塊算法不起作用

這是我的地形類別:

package game; 

import java.util.Random; 

public class Terrain { 

    Panel panel; 

    public Terrain(Panel panel) { 
     this.panel = panel; 
     generateTerrain(); 
    } 

    private void generateTerrain() { 
     Random rand = new Random(); 

     int seed = rand.nextInt(panel.colors.length); 
     int sideLength = panel.mapSize - 1; 
     int halfSideLength; 
     int average; 

     panel.map[0][0] = seed; 
     panel.map[panel.mapSize - 1][0] = seed; 
     panel.map[0][panel.mapSize - 1] = seed; 
     panel.map[panel.mapSize - 1][panel.mapSize - 1] = seed; 

     while (sideLength > 0) { 
      halfSideLength = sideLength/2; 
      for (int x = 0; x < panel.mapSize - 1; x += sideLength) { 
       for (int y = 0; y < panel.mapSize - 1; y += sideLength) { 
        average = panel.map[x][y] 
          + panel.map[x + sideLength][y] 
          + panel.map[x][y + sideLength] 
          + panel.map[x + sideLength][y + sideLength]; 
        average /= 4; 
        average += rand.nextInt(8); 
        panel.map[x + halfSideLength][y + halfSideLength] = average; 
       } 
      } 
      for (int x = 0; x < panel.mapSize - 1; x += sideLength) { 
       for (int y = 0; y < panel.mapSize - 1; y += sideLength) { 
        average = panel.map[x][y] 
          + panel.map[x + halfSideLength][y] 
          + panel.map[x][y + sideLength] 
          + panel.map[x + halfSideLength][y + halfSideLength]; 
        average /= 4; 
        panel.map[x][y] = average; 
        if (x == 0) { 
         panel.map[panel.mapSize - 1][y] = average; 
        } 
        if (y == 0) { 
         panel.map[x][panel.mapSize - 1] = average; 
        } 
       } 
      } 
      sideLength /= 2; 
     } 
    } 
} 

,這是我的面板類:

package game; 

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.*; 

public class Panel extends JPanel implements ActionListener { 

    Terrain currentTerrain; 

    boolean upPressed = false; 
    boolean rightPressed = false; 
    boolean downPressed = false; 
    boolean leftPressed = false; 

    int tileSize = 1; 
    int mapSize = 1025; 

    int deltaX = 0; 
    int deltaY = 0; 
    int x = 0; 
    int y = 0; 

    int map[][] = new int[mapSize][mapSize]; 

    Color[] colors = { 
     new Color(0, 0, 180), 
     new Color(0, 0, 255), 
     new Color(0, 150, 255), 
     new Color(255, 255, 180), 
     new Color(220, 220, 120), 
     new Color(200, 200, 60), 
     new Color(0, 200, 0), 
     new Color(0, 180, 0), 
     new Color(0, 160, 0), 
     new Color(0, 140, 0), 
     new Color(0, 120, 0), 
     new Color(0, 100, 0), 
     new Color(0, 80, 0), 
     new Color(0, 60, 0), 
     new Color(0, 40, 0), 
     new Color(40, 40, 40), 
     new Color(60, 60, 60), 
     new Color(80, 80, 80), 
     new Color(100, 100, 100), 
     new Color(120, 120, 120), 
     new Color(255, 255, 255) 
    }; 


    public Panel(Main main) { 
     setPreferredSize(main.getSize()); 
     setFocusable(true); 
     setBackground(Color.WHITE); 
     add(new KeyBindings(this)); 
     currentTerrain = new Terrain(this); 
     new Timer(1000/120, this).start(); 
    } 

    private void tick() { 
     if (upPressed) { 
      deltaY += 10; 
     } else if (downPressed) { 
      deltaY -= 10; 
     } 
     if (rightPressed) { 
      deltaX -= 10; 
     } else if (leftPressed) { 
      deltaX += 10; 
     } 
     repaint(); 
    } 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     tick(); 
    } 

    @Override 
    public void paintComponent(Graphics g) { 
     super.paintComponent(g); 

     x = 0; 
     y = 0; 

     for (int[] rowData : map) { 
      for (int cellData : rowData) { 
       int v = cellData; 
       if(v < 0) { 
        v = 0; 
       } else if(v > colors.length - 1) { 
        v = colors.length - 1; 
       } 
       g.setColor(colors[v]); 
       g.fillRect(x + deltaX, y + deltaY, tileSize, tileSize); 
       x += tileSize; 
       if(x == mapSize * tileSize) { 
        x = 0; 
        y += tileSize; 
       } 
      } 
     } 
    } 
} 
+1

你可以顯示一些示例輸出或給我們提示哪些代碼行可能有問題嗎? –

+0

@TimBiegeleisen我相信我給「平均值」添加一個隨機值的行可能是問題。至於輸出,它看起來有點像一個顏色版本的靜態 – jklsfdgs

回答

3

你已經得到了算法錯誤的鑽石舞臺。鑽石的中心是廣場兩側的中點。他們最好在兩個獨立的循環中處理:一個用於垂直中點,另一個用於水平中點。在地圖的邊緣,直線的尖端將伸出地圖。您可以通過將它們「摺疊」到地圖中來解決這個問題,例如在左邊緣,右邊的點被認爲是兩次。

因此,代碼應該是:

function generateTerrain() { 
    int seed = rand.nextInt(colors.length); 
    int sideLength = mapSize - 1; 
    int halfSideLength; 
    int average; 
    int offset = 8; 

    map[0][0] = seed; 
    map[mapSize - 1][0] = seed; 
    map[0][mapSize - 1] = seed; 
    map[mapSize - 1][mapSize - 1] = seed; 

    while (sideLength > 0) { 
     halfSideLength /= 2; 
     for (int x = 0; x < mapSize - 1; x += sideLength) { 
      for (int y = 0; y < mapSize - 1; y += sideLength) { 
       average = map[x][y] 
         + map[x + sideLength][y] 
         + map[x][y + sideLength] 
         + map[x + sideLength][y + sideLength]; 
       average /= 4; 
       average += rand.nextInt(offset); 

       map[x + halfSideLength][y + halfSideLength] = average; 
      } 
     } 

     for (int x = 0; x < mapSize - 1; x += sideLength) { 
      for (int y = 0; y < mapSize; y += sideLength) { 
       int yTop = y - halfSideLength; 
       int yBottom = y + halfSideLength; 

       if (yTop < 0) yTop = y + halfSideLength; 
       if (yBottom > mapSize - 1) yBottom = y - halfSideLength; 

       average = map[x][y] 
         + map[x + sideLength][y] 
         + map[x + halfSideLength][yTop] 
         + map[x + halfSideLength][yBottom]; 
       average = /= 4; 

       map[x + halfSideLength][y] = average; 
      } 
     } 

     for (int x = 0; x < mapSize; x += sideLength) { 
      for (int y = 0; y < mapSize - 1; y += sideLength) { 
       int xLeft = x - halfSideLength; 
       int xRight = x + halfSideLength; 

       if (xLeft < 0) xLeft = x + halfSideLength; 
       if (xRight > mapSize - 1) xRight = x - halfSideLength; 

       average = map[x][y] 
         + map[x][y + sideLength] 
         + map[xRight][y + halfSideLength] 
         + map[xRight][y + halfSideLength]; 
       average /= 4; 

       map[x][y + halfSideLength] = average; 
      } 
     } 

     sideLength /= 2; 
    } 
} 

(注:我從你的代碼的工作,但將它轉換成JavaScript和背部,使那裏它可能是一些Java的失誤仍然不過我。認爲這兩個循環的想法是清楚的。)

這將給非常嘈雜的地圖。通常,每當您將邊長減半時,隨機偏移量(代碼中的8)會減少一個常數因子。地圖的平滑度可以用這個參數來控制,玩起來很有趣。在你的情況下,參數是1.0。現實地圖的一個很好的因素是大約0.65。

你的方法還有另外一個問題:整個使用狹義整數。我在這裏使用「窄」來表示整數的粒度,即1,等於地圖中的顏色變化。這不會給一個顏色內隨機變化的範圍很大。將浮點數或更大範圍的整數用於地圖生成,然後將這些值映射到您的顏色可能是一個好主意。例如,假設您使用0.0到1.0之間的浮點數:然後,您可以將0.1以下的值映射爲深綠色,低於0.2以淺綠色等等,直到您獲得高於0.9的值爲白色。這樣可以更平滑地出現在您鏈接的文章部分下方的雲圖中。

另一件事是,你在角落開始一個隨機值,然後在打印地圖時將值限制在顏色範圍內。您添加的隨機偏移總是正面的,所以如果以「高」顏色開始,您很可能會獲得大部分爲白色的圖片。事實上,你的初始值不會低於任何顏色。您可以從中間顏色開始,同時允許負面和正面的隨機偏移來規避這種情況。或者,您可以在生成地圖後根據最低和最高值對地圖進行標準化。

+0

當我嘗試減少偏移量時,它給了我一個錯誤,說明rand.nextInt()的邊界必須是正數。你有什麼建議我做,以規避這一點?另外,我不確定你是否看過我的顏色陣列,但我正在做一些與你所提到的非常相似的東西。例如:0,1和2都是藍色陰影。 – jklsfdgs

+0

當你減小偏移量時,你應該強制它至少爲1.我不知道你的地圖有多大,但是當你開始一個8,並用0乘以偏移量。對於每一邊的長度爲65,由於整數截斷,你會很快得到一個。我認爲你應該讓你的「高度」浮點數,並且只能在繪製顏色時將它們截斷爲整數。 –

+0

「粒度」與您選擇的顏色沒有任何關係。這只是表示。因爲你有21個不同的值可供選擇,這不是很多,你甚至可以從一個隨機值開始進一步限制。當你使用浮點數或者說從0到255的整數時,你將會得到一個更逼真的地形。 (當然,不是插值「高度」,你可以內插顏色的RGB值,但不清楚顏色的偏移是什麼意思。) –