钻石和方算法不工作 [英] Diamonds and Squares Algorithm not working

查看:137
本文介绍了钻石和方算法不工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的工作使分形地形,但无论怎样尝试,它总是找出来完全是随机的。我一直在关注钻石和广场算法解释这里。我能做些什么来解决这个问题?

这是我的地形类别:

 包游戏;

导入了java.util.Random;

公共类地形{

    面板面板;

    公共地形(面板面板){
        this.panel =面板;
        generateTerrain();
    }

    私人无效generateTerrain(){
        随机兰特=新的随机();

        INT种子= rand.nextInt(panel.colors.length);
        INT sideLength = panel.mapSize  -  1;
        INT halfSideLength;
        INT平均水平;

        panel.map [0] [0] =种子;
        panel.map [panel.mapSize  -  1] [0] =种子;
        panel.map [0] [panel.mapSize  -  1] =种子;
        panel.map [panel.mapSize  -  1] [panel.mapSize  -  1] =种子;

        而(sideLength大于0){
            halfSideLength = sideLength / 2;
            为(中间体X = 0 X  - 其中; panel.mapSize  -  1; X + = sideLength){
                对于(INT Y = 0; Y< panel.mapSize  -  1; Y + = sideLength){
                    平均= panel.map [X] [Y]
                            + panel.map [X + sideLength] [Y]
                            + panel.map [X] [Y + sideLength]
                            + panel.map [X + sideLength] [Y + sideLength]。
                    平均/ = 4;
                    平均+ = rand.nextInt(8);
                    panel.map [X + halfSideLength] [Y + halfSideLength] =平均值;
                }
            }
            为(中间体X = 0 X  - 其中; panel.mapSize  -  1; X + = sideLength){
                对于(INT Y = 0; Y< panel.mapSize  -  1; Y + = sideLength){
                    平均= panel.map [X] [Y]
                            + panel.map [X + halfSideLength] [Y]
                            + panel.map [X] [Y + sideLength]
                            + panel.map [X + halfSideLength] [Y + halfSideLength]。
                    平均/ = 4;
                    panel.map [X] [Y] =平均值;
                    如果(X == 0){
                        panel.map [panel.mapSize  -  1] [Y] =平均值;
                    }
                    如果(Y == 0){
                        panel.map [X] [panel.mapSize  -  1] =平均值;
                    }
                }
            }
            sideLength / = 2;
        }
    }
}
 

这是我的面板类:

 包游戏;

进口的javax.swing *。
进口java.awt中*。
导入java.awt.event中*。

公共类面板继承JPanel实现的ActionListener {

    地形currentTerrain;

    布尔高达pressed = FALSE;
    布尔右pressed = FALSE;
    布尔下跌pressed = FALSE;
    布尔左pressed = FALSE;

    INT tileSize = 1;
    INT mapSize = 1025;

    INT DELTAX = 0;
    INT移动deltaY = 0;
    INT X = 0;
    INT Y = 0;

    INT地图[] [] =新INT [mapSize] [mapSize];

    颜色[]颜色= {
        新的色彩(0,0,180),
        新的色彩(0,0,255),
        新颜色(0,150,255),
        新的色彩(255,255,180),
        新的色彩(220,220,120),
        新的色彩(200,200,60),
        新的色彩(0,200,0),
        新的色彩(0,180,0),
        新的色彩(0,160,0),
        新的色彩(0,140,0),
        新的色彩(0,120,0),
        新颜色(0,100,0),
        新的色彩(0,80,0),
        新的色彩(0,60,0),
        新颜色(0,40,0),
        新的色彩(40,40,40),
        新的色彩(60,60,60),
        新的色彩(80,80,80),
        新的色彩(100,100,100),
        新的色彩(120,120,120),
        新的色彩(255,255,255)
    };


    公共面板(主主){
        集preferredSize(main.getSize());
        setFocusable(真正的);
        的setBackground(Color.WHITE);
        添加(新的键绑定(本));
        currentTerrain =新的地形(本);
        新的Timer(1000/120号本)。启动();
    }

    私人无效打勾(){
        如果(最多pressed){
            移动deltaY + = 10;
        }否则,如果(下跌pressed){
            移动deltaY  -  = 10;
        }
        如果(右pressed){
            DELTAX  -  = 10;
        }否则,如果(左pressed){
            DELTAX + = 10;
        }
        重画();
    }

    @覆盖
    公共无效的actionPerformed(ActionEvent的E){
        蜱();
    }

    @覆盖
    公共无效的paintComponent(图形G){
        super.paintComponent方法(G);

        X = 0;
        Y = 0;

        对于(INT [] rowData:地图){
            对于(INT cellData:rowData){
                INT V = cellData;
                如果(ⅴ℃,){
                    ν= 0;
                }否则如果(V> colors.length  -  1){
                    V = colors.length  -  1;
                }
                g.setColor(颜色[V]);
                g.fillRect(X + DELTAX,Y +移动deltaY,tileSize,tileSize);
                X + = tileSize;
                如果(X == mapSize * tileSize){
                    X = 0;
                    Y + = tileSize;
                }
            }
        }
    }
}
 

解决方案

您已经得到了算法错误的钻石舞台。钻石的中心是正方形的各边的中点。他们是最好的处理在两个独立的循环:一个用于垂直midpoinst和第二的水平中点。在地图的边缘,diamnds的提示将伸出的地图。就可以解决,通过折叠成在地图上,以便例如在左边缘,右点被认为两次

所以,code应该是:

 函数generateTerrain(){
    INT种子= rand.nextInt(co​​lors.length);
    INT sideLength = mapSize  -  1;
    INT halfSideLength;
    INT平均水平;
    INT偏移= 8;

    图[0] [0] =种子;
    图[mapSize  -  1] [0] =种子;
    地图[0] [mapSize  -  1] =种子;
    图[mapSize  -  1] [mapSize  -  1] =种子;

    而(sideLength大于0){
        halfSideLength / = 2;
        为(中间体X = 0 X  - 其中; mapSize  -  1; X + = sideLength){
            对于(INT Y = 0; Y< mapSize  -  1; Y + = sideLength){
                平均=地图[X] [Y]
                        +地图[X + sideLength] [Y]
                        +图[X] [Y + sideLength]
                        +地图[X + sideLength] [Y + sideLength]。
                平均/ = 4;
                平均+ = rand.nextInt(偏移);

                地图[X + halfSideLength] [Y + halfSideLength] =平均值;
            }
        }

        为(中间体X = 0 X  - 其中; mapSize  -  1; X + = sideLength){
            对于(INT Y = 0; Y< mapSize; Y + = sideLength){
                INT yTop = Y  -  halfSideLength;
                INT yBottom = Y + halfSideLength;

                如果(yTop℃,)yTop = Y + halfSideLength;
                如果(yBottom> mapSize  -  1)yBottom = Y  -  halfSideLength;

                平均=地图[X] [Y]
                        +地图[X + sideLength] [Y]
                        +地图〔X + halfSideLength] [yTop]
                        +地图〔X + halfSideLength] [yBottom];
                平均= / = 4;

                地图[X + halfSideLength] [Y] =平均值;
            }
        }

        为(中间体X = 0 X  - 其中; mapSize; X + = sideLength){
            对于(INT Y = 0; Y< mapSize  -  1; Y + = sideLength){
                INT xLeft = X  -  halfSideLength;
                INT xRight = X + halfSideLength;

                如果(xLeft℃,)xLeft = X + halfSideLength;
                如果(xRight> mapSize  -  1)xRight = X  -  halfSideLength;

                平均=地图[X] [Y]
                        +图[X] [Y + sideLength]
                        +图[xRight] [Y + halfSideLength]
                        +图[xRight] [Y + halfSideLength]。
                平均/ = 4;

                地图[X] [Y + halfSideLength] =平均值;
            }
        }

        sideLength / = 2;
    }
}
 

(注意:我从你的code的工作,但将它转换成JavaScript和背部,因此,有可能是一些Java的失误还在,但我认为这两个循环的思路是清晰的)

这会给很吵地图。通常,随机偏移(在code 8)由一个常数因子每次减半边长时间减少了。该图的平滑度,可以控制与该参数,这是有趣的发挥它。在你的情况下,参数为1.0。逼真的地图一个很好的因素是0.65左右。

这是你的方法的另一个问题:使用窄的整数贯穿始终。我用窄在这里的意思是,该整数,即1的粒度,等于在你的地图上的颜色变化。这并不在一个颜色给了很多的范围内随机变化​​。它可能是要么使用浮点数或更大范围的整数的映射生成,然后将这些值映射到你的颜色是一个好主意。

举例来说,假设你使用0.0和1.0之间的浮点数:你可以低于0.1,则映射值,以深绿色,下面0.2至浅绿色等,直到你得到白色大于0.9的值。这将使一个平滑的条带状appearence像下面你链接的文章的部分云图。

另一件事是,你开始在角落的一个随机值,然后打印地图时钳制值的颜色范围。随机偏移添加总是正的,所以如果你以一个高的色彩,你很可能得到一个白人图片。事实上,你不会得到下面的起始值任何颜色。您可以从一个中间色,让正反两方面的随机偏移量来规避这一点。或者你可以生成后恢复正常的基础上的最低和最高值映射。

I am working on making fractal terrain, but no matter what try, it always comes out looking completely random. I have been following the Diamonds and Squares algorithm as explained here. What can I do to fix this problem?

This is my Terrain Class:

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

and this is my 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;
                }
            }
        }
    }
}

解决方案

You've got the diamond stage of the algorithm wrong. The centres of the diamonds are the midpoints of the square's sides. They are best treated in two separate loops: One for the vertical midpoinst and the second for the horizontal midpoints. At the edge of the map, the tips of the diamnds will reach out of the map. You can solve that by "folding" them into the map, so that for example at the left edge, the right point is considered twice.

So the code should be:

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

(Note: I've worked from your code, but converted it into Javascript and back, so that there are probably some Java blunders still in it. But I think the idea of the two loops is clear.)

This will give very noisy maps. Usually, the random offset (8 in your code) is reduced by a constant factor every time you halve the side length. The smoothness of the map can be controlled with this parameter and it is interesting to play with it. In your case, the parameter is 1.0. A good factor for realistic maps is around 0.65.

There's another problem of your method: You use narrow integers throughout. I use "narrow" here to mean, that the granularity of the integers, namely 1, is equal to a colour change in your map. That doesn't give a lot of range for random variation within one colour. It might be a good idea to either use floating-point numbers or integers of a wider range for map generation and then map these values to your colours.

For example, say you use floating-point numbers between 0.0 and 1.0: You could then map values below 0.1 to dark green, below 0.2 to light green and so on until you get white for values above 0.9. That would give a smoother banded appearence like in the cloud picture below the section of the article you linked.

Another thing is that you start with a random value at the corners and then clamp values to the range of colours when printing the map. The random offset you add is always positive, so if you start with a "high" colour, you are very likely to get a mostly white picture. In fact, you won't get any colour below your starting value. You could start with a middle colour and allow both negative and positive random offsets to circumvent this. Or you could normalize the map based on the lowest and highest values after generating it.

这篇关于钻石和方算法不工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆