7

上周我正在开发一个世界生成器(用于 Minecraft 模组)。但是,我不只是在寻找 Perlin 噪声,而是基于单元噪声的东西。我想生成一种地下实验室,存在几个不同大小的房间。

为了解释这个问题,我使用了 2D 示例。

噪声生成器采用网格单元位置 ( int x, int y),并返回具有以下结构的对象:

boolean top;
boolean right;
boolean down;
boolean left;

int roomType;



4 个布尔值代表启用或禁用的墙壁:roomType分别代表房间的类型。

最终的结果应该是这样的: 这里,背景棋盘格图案代表基础网格,黑色线条代表墙壁。这只是一个可以生成的简单示例,但在实际情况下,网格在 x 和 y 方向上都是无限的在此处输入图像描述

我现在遇到的问题是噪声发生器只接受 x 和 y 坐标,这是它应该生成的网格单元的坐标。有一个种子,我可以为哈希函数生成更多随机种子:

long seed = 0x75fd239de48;

Random r = new Random(seed);
int seed1 = r.nextInt();
int seed2 = r.nextInt();
// etc.

我可以使用散列函数: ,它根据种子Hash.hash2D(int seed, int x, int y)返回随机坐标。double

这将提供为周围细胞生成信息的能力。

要轻松生成更大的房间,您可以为房间设置最大尺寸,并检查房间的面积是否大于 1x1。如果它们在那里,并将跨越到当前房间,则该房间将是另一个房间的延伸。但是,检查一个房间是否会扩展需要检查它是否尚未扩展(否则,不需要的房间扩展会出现在扩展另一个房间的基础上),这会陷入无限循环。

就我而言,有一张房间类型、大小和重量的给定表格。例子:

name:   size [weight]
room-1: 1x1  [128]
room-2: 1x1  [128]
room-3: 2x1  [16]
room-4: 1x2  [16]
room-5: 2x2  [8]
room-6: 3x1  [4]
room-7: 1x3  [4]

还有很多其他的,尺寸最大为 5x5,但我使用这个示例列表来回答我的问题。此示例中的最大尺寸为 3x3(即 max-width x max-height)。

在这里,我有一个 Java 基本设置的示例类:

public class RoomNoise {
    private final long seed;
    private final Random rand;
    public RoomNoise( long seed ) {
        this.seed = seed;
        this.rand = new Random( seed );
    }

    public enum RoomTypes {
        ROOM1( 1, 1, 128 ),
        ROOM2( 1, 1, 128 ),
        ROOM3( 2, 1, 16 ),
        ROOM4( 1, 2, 16 ),
        ROOM5( 2, 2, 8 ),
        ROOM6( 1, 3, 4 ),
        ROOM7( 3, 1, 4 );

        public final int width;
        public final int height;
        public final int weight;
        private RoomTypes( int w, int h, int weight ) {
            width = w;
            height = h;
            this.weight = weight;
        }
    }

    public static class Output {
        public final RoomTypes roomType;
        public final boolean upWall;
        public final boolean rightWall;
        public final boolean downWall;
        public final boolean leftWall;
        public Output( RoomTypes type, boolean u, boolean r, boolean d, boolean l ) {
            roomType = type;
            upWall = u;
            rightWall = r;
            downWall = d;
            leftWall = l;
        }
    }

    public Output generate( int x, int y ) {
        // What should be here
    }
}

我正在寻找generate方法的内容,为此我尝试了很多东西,但是每次我都变成了无限循环或者它不起作用。

有没有办法在小于无穷大的情况下产生这种O(N)噪音N?如果有办法,那是哪种方式,我该如何实施?我搜索了互联网并尝试了很多东西(现在已经 3 周),但仍然没有找到解决方案。

我使用 Java 1.8,但我更喜欢任何 C 风格的语言。

同样,我有这个哈希函数:

Hash.hash2D( int seed, int x, int y );

编辑:

预期结果: 蓝线是走廊,稍后生成。只是忘记他们。
在此处输入图像描述


笔记:

我无法手动加载和删除块(网格单元),基本 API(我的世界)正在为我做这件事。它只给了我坐标(这取决于玩家的互动),我应该归还一个适合该坐标处的块的(一部分)房间。我也知道,一旦生成了一个块,就不会再次生成它。

4

2 回答 2

1

我不确定我是否完全理解您要解决的问题,所以如果这没有达到标准,请随时发表评论:

如果您希望能够生成无限网格,则只能近似无限。我认为你有两个选择:

  1. 生成一个“足够大”的网格。这可能是时间/空间密集型的,但如果您需要多少有上限,并且一次完成所有操作是可行的,那么这是最简单的选择。例如,如果用户不可能使它离中心超过 1000 格,则生成 2000x2000。

或者:

  1. 使用惰性求值。这意味着,在需要之前不要生成任何东西。如果用户靠近尚未生成的区域,则生成它。另一方面,如果您需要释放资源,您还可以丢弃用户不太可能返回的旧部分。例如:将您的区域切割成正方形(例如 20x20 或 1000x1000),并根据需要生成额外的相邻正方形,因为玩家靠近它们,或者地图在那个方向上平移等等。
于 2018-05-07T19:31:53.937 回答
0

好的,我想我已经自己解决了。

这是网格,在这里不是无限的,但它可能是。
在此处输入图像描述

在这个网格中,您有一些想要扩展的单元格。这是由散列函数直接定义的: 但是,某些扩展确实与其他扩展重叠。这是我们实际上不知道的事情。
在此处输入图像描述

优先考虑每个单元格。您可以通过以下几种方式做到这一点:

  • 只需使用哈希函数给它们一个随机优先级

  • 优先考虑每种可能的房间类型/大小(例如,较大的房间具有更高的优先级)。

优先级仅对想要扩展的单元格很重要。在我的示例中,较大的单元格具有更高的优先级。

然后我们有输入坐标,也就是蓝色单元格:
在此处输入图像描述

好的,知道这一点,您必须执行一些步骤。我们知道最大尺寸是 3x3。

如果输入单元格不想扩展

  1. 如果有任何单元格试图扩展到此单元格,请检查最大大小的区域:在这种情况下,它存在。
    在此处输入图像描述

  2. 知道了这一点,我们需要检查一个找到的单元格是否可以延伸到这个单元格。要检查这一点,请检查是否希望此单元格成为扩展,然后执行以下步骤并将检查单元格作为输入坐标。在示例中,扩展单元能够扩展。我们还知道扩展单元将扩展输入单元,因此输入单元是扩展。

  3. 现在我们可以很容易地检查哪些墙存在,哪些不存在。在这种情况下,每一面墙都消失了,因为它是这个扩展房间的中心:
    在此处输入图像描述

其他一些例子:

输入坐标。他们都不想要扩展: 检查区域: 如您所见,一个单元格找到了扩展室,但该扩展室没有扩展该输入单元格。这使得所有这些房间成为 1x1 房间:
在此处输入图像描述

在此处输入图像描述

在此处输入图像描述

如果输入单元格想要扩展

以及如何检查单元格是否可以扩展

  1. 做一个严格的检查。硬检查检查输入单元格正下方的扩展区域(在本例中为 3x3)大小,以查找其他尝试扩展的单元格。如果有,请检查该是否可以扩展。如果可以,则输入单元格无法扩展并继续执行输入单元格上的非扩展单元格的步骤。为了节省内存和时间,您可以跳过检查找到的单元格是否可以扩展(也许这会陷入无限循环),而直接采用 1x1 单元格(此时不需要非扩展单元格步骤)。
    在这种情况下,这是扩展区域/硬检查区域。这里没有扩展单元格,因此单元格可以扩展。
    在此处输入图像描述

  2. 现在做一个软检查。软检查检查在单元格左下方和右上方的最大大小区域中是否扩展单元格: 同样在这里,您可以检查它们是否扩展,但这需要大量内存和时间。 对于每个找到的单元格,检查它们正下方的扩展区域是否将扩展到输入单元格将扩展的任何扩展单元格。这将检查两个扩展区域是否重叠。如果没有,您的单元格可以扩展,您可以跳过第 3 步并转到第 4 步。如果有,请转到第 3 步。在这种情况下,发现重叠: 这里,带有红色轮廓的黄色单元格是发现的重叠。
    在此处输入图像描述


    在此处输入图像描述

  3. 你发现了重叠。在这里,优先级将发挥作用。优先考虑哪个区域与输入单元区域重叠的扩展单元。还要考虑输入单元格本身的优先级。比较它们。如果输入单元格的优先级大于另一个优先级,则输入单元格可以扩展,您可以转到步骤 4。如果输入单元格的优先级较低,则无法扩展,您可以将其设置为 1x1 房间(或您可以执行非扩展单元格步骤,如果您在硬检查中验证找到的单元格,这是必要的)。如果优先级相等,则取 X 或 Y 坐标最高的单元格,或其他东西。
    在我的示例中,输入单元格具有最高优先级,因为它更大。

  4. 最后一步。保证单元格扩展。你可以计算出存在哪些墙。请注意,左侧和顶壁始终存在,因为扩展单元始终位于左上角。
    我们的单元格可以扩展,我们得到以下结果:
    在此处输入图像描述

另一个例子

输入单元格: 进行严格检查: 哦,它找到了一个,所以它无法扩展并变成一个 1x1 的房间:
在此处输入图像描述

在此处输入图像描述

在此处输入图像描述


就是这样,其实。我希望我已经足够清楚了。除了使用正方形,您也可以使用矩形,或更复杂的形状,如 L 形房间。

这是我的示例的最终结果:
在此处输入图像描述

于 2018-05-09T08:45:46.330 回答