钻石和方块算法不起作用

时间:2015-05-14 02:16:54

标签: java algorithm

我正在制作分形地形,但无论尝试什么,它总是看起来完全随机。我按照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;
        }
    }
}

这是我的Panel Class:

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 :(得分:3)

你的算法钻石阶段错了。钻石的中心是方形边的中点。它们最好在两个独立的循环中处理:一个用于垂直midpoinst,第二个用于水平中点。在地图的边缘,diamnds的提示将伸出地图。您可以通过&#34;折叠&#34;来解决这个问题。它们进入地图,例如在左边缘,右边的点被认为是两次。

所以代码应该是:

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错误。但我认为这两个循环的想法很明确。)< / p>

这会给出非常嘈杂的地图。通常,每次将边长减半时,随机偏移量(代码中的8)会减少一个常数因子。可以使用此参数控制地图的平滑度,并且使用它很有趣。在您的情况下,参数是1.0。逼真地图的一个好因素是0.65左右。

您的方法存在另一个问题:您始终使用窄整数。我使用&#34; narrow&#34;这意味着,整数的粒度(即1)等于地图中的颜色变化。这并不能为一种颜色中的随机变化提供很多范围。最好使用浮点数或更宽范围的整数来生成地图,然后将这些值映射到您的颜色。

例如,假设您使用介于0.0和1.0之间的浮点数:然后您可以将低于0.1的值映射到深绿色,低于0.2将值映射到浅绿色等等,直到您为高于0.9的值获得白色。这样可以提供更平滑的带状外观,就像您链接的文章部分下方的云图片一样。

另一件事是你从角落的随机值开始,然后在打印地图时将值钳制到颜色范围。您添加的随机偏移量总是正数,因此如果您从&#34;高&#34;开始颜色,你很可能得到一个大多数白色的图片。事实上,你不会得到任何低于你的起始值的颜色。您可以从中间颜色开始,允许负向和正向随机偏移来规避这一点。或者,您可以在生成地图后根据最低和最高值对地图进行标准化。