在平面地图上随机生成块 [英] Randomly generate blocks on a flat map

查看:166
本文介绍了在平面地图上随机生成块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在平面地图上随机生成块,并使它们彼此不重叠。
我做了一个矩阵(c#数组)的大小的地图(500x500),块的比例在1和5之间。
代码工作,但如果生成的块与另一个重叠,被毁坏,而不是在其他地方重新生成。

I'm trying to randomly generate blocks on a flat map and make it so that they don't overlap each other. I have made a matrix (c# array) of the size of the map (500x500), the blocks have a scale between 1 and 5. The code works but if a generated block overlaps another one, it is destroyed and not regenerated somewhere else.

我尝试生成的1000个块中只有大约80个不与另一个块重叠。

Only around 80 of the 1000 blocks I try to generate don't overlap another block.

以下是生成大约80个块的地图图,绿色正方块是块

Here is a picture of the map with around 80 blocks generated, the green squares are blocks

void generateElement(int ratio, int minScale, int maxScale, GameObject g) {
    bool elementFound = false;
    for (int i = 0; i < ratio * generationDefault; i++) {
        GameObject el;
        // Randomly generate block size and position
        int size = Random.Range(minScale, maxScale + 1);
        int x = Random.Range(0, mapSizex + 1 - size);
        int y = Random.Range(0, mapSizey + 1 - size);

        // Check if there is already an element 
        for (int j = x; j < x + size; j++)
            for (int k = y; k < y + size; k++)
                if (map[j][k] != null)
                    elementFound = true;
        if (elementFound)
            continue;
        else {
            el = (GameObject)Instantiate(g, new Vector3(x + (float)size / 2, (float)size / 2, y + (float)size / 2), Quaternion.Euler(0, 0, 0));
            el.transform.localScale *= size;
        }
        // Create element on map array
        for (int j = x; j < x + size; j++)
            for (int k = y; k < y + size; k++)  
                if (map[j][k] == null) {
                    map[j][k] = el.GetComponent<ObjectInterface>();
                }
    }
}

我想到了3个可能的修复

I thought of 3 possible fixes


  • 我应该根据它的位置来设置块的大小。

  • 我应该使用其他随机化算法。

  • 我没有这样做。

认为是最好的主意?

更新

我的代码工作得更好了我现在尝试多次实例化块(如果需要时最多5次),并且我修复了错误。如果地图上已经有很多元素,它们并不总是被实例化,这就是我想要的,我只需要找到正确的次数来实例化块。

I got the code working much better. I now try to instantiate the blocks multiple times if needed (maximum 5 for the moment) and I fixed the bugs. If there are already many elements on the map, they will not always be instantiated and that's what I wanted, I just have to find the right amount of times it will try to instantiate the block.

我尝试在500x500地图上实例化1280个元素。它只需要约1.5秒,它实例化了1278/1280块(99.843%)。

I tried instantiating 1280 elements on a 500x500 map. It takes only about 1.5 second and it instantiated 1278/1280 blocks (99.843%).

void generateElement(int ratio, int minScale, int maxScale, GameObject g) {
bool elementFound = false;
int cnt = 0;
// Generate every block
for (int i = 0; i < ratio * generationDefault; i++) {
    GameObject el = null;
    // Randomly generate block size and position
    int size, x, y, tryCnt = 0;

    // Try maximum 5 times to generate the block
    do {
        elementFound = false;
        // Randomly set block size and position
        size = Random.Range(minScale, maxScale + 1);
        x = Random.Range(0, mapSizex + 1 - size);
        y = Random.Range(0, mapSizey + 1 - size);

        // Check if there is already an element 
        for (int j = x; j < x + size; j++)
            for (int k = y; k < y + size; k++)
                if (map[j][k] != null)
                    elementFound = true;
        tryCnt++;
    } while (elementFound && tryCnt < 5);
    if (tryCnt >= 5 && elementFound) continue;

    // Instantiate the block
    el = (GameObject)Instantiate(g, new Vector3(x + (float)size / 2, (float)size / 2, y + (float)size / 2), Quaternion.Euler(0, 0, 0));
    el.transform.localScale *= size;
    // Create element on map array
    for (int j = x; j < x + size; j++)
        for (int k = y; k < y + size; k++)  
            if (map[j][k] == null) {
                map[j][k] = el.GetComponent<ObjectInterface>();
            }
    cnt++;
}
print("Instantiated " + cnt + "/" + ratio * generationDefault);

}

推荐答案

这很难做得很好

这是一个快速的解决方案,你可能会喜欢...取决于你

Here's a quick solution you'll maybe like ... depending on your scene.

actualWidth = 500 //or whatever. assume here is square
// your blocks are up to 5 size
chunkWidth = actualWidth / 5
// it goes without saying, everything here is an int
kChunks = chunkWidth*chunkWidth
List<int> shuf = Enumerable.Range(1,kChunks).OrderBy(r=>Random.value).ToList();
howManyWanted = 1000
shuf = shuf.Take(howManyWanted)
foreach( i in shuf )
   x = i % actualWidth
   y = i / actualWidth
   make block at x y
   put block in list allBlocks

HOWEVER ........ ....

HOWEVER ............

......你会看到这看起来像是常规,所以这样做:

...... you'll see that this looks kind of "regular", so do this:

只是随机扰乱所有的块。记住,视频游戏编程是关于聪明的技巧!

Just randomly perturb all the blocks. Remember, video game programming is about clever tricks!

理想情况下,你必须从中间开始工作,在任何情况下,你不能只是一行就行。洗牌是可以的所以,这样做..

Ideally, you have to start from the middle and work your way out; in any event you can't just do them in a line. Shuffling is OK. So, do this ..

   harmonic = 3  //for example. TRY DIFFERENT VALUES

   function rh = Random.Range(1,harmonic) (that's 1 not 0)

   function rhPosNeg
       n = rh
       n = either +n or -n
       return n

   function onePerturbation
   {
   allBlocks = allBlocks.OrderBy(r => Random.value) //essential
   foreach b in allBlocks
      newPotentialPosition = Vector2(rhPosNeg,rhPosNeg)
      possible = your function to check if it is possible
           to have a block at newPotentialPosition,
           however be careful not to check "yourself"
      if possible, move block to newPotentialPosition
   }

最简单的方法就是运行 onePerturbation ,比如说三次。在每次跑步之间看看它。另外尝试谐波调整因子的不同值。

The simplest approach is just run onePerturbation, say, three times. Have a look at it between each run. Also try different values of the harmonic tuning factor.

有很多方法来扰乱不同的字段

There are many ways to perturb fields of differently-sized blocks, above is a KISS solution that hopefully looks good for your situation.

编码注意事项上面的KISS解决方案希望对您的情况看起来很好。 ..

Coding note...

只是为了解释这一行代码.. 。

Just to explain this line of code...

List<int> shuf = Enumerable.Range(1,kChunks).OrderBy(r=>Random.value).ToList();

如果你是新的编码:说你想这样做:得到一百个随机数,从1到百万,但不重复。

If you are new to coding: say you want to do this: "get a hundred random numbers, from 1 to million, but with no repeats".

幸运的是,这是一个非常有名的问题,使用非常简单的解决方案

Fortunately, this is a very well known problem with a very simple solution.

您不用重复的方式获取数字,只需轻轻轻轻地整理所有数字 然后取你想要的顶点数。

The way you get numbers with no repeats, is simply shuffle all the numbers, and then take how many you want off the top.

例如,说你需要一个1-10位数的随机数,但不要重复。

For example, say you need a random couple of numbers from 1-10 but with no repeats.

所以,这里是数字1-10洗牌:3,8,6,1,2,7,10,9,4,5

So, here's the numbers 1-10 shuffled: 3,8,6,1,2,7,10,9,4,5

只需把你需要的东西从前面:所以,3,8,6,6等。

Simply take what you need off the front: so, 3, 8, 6 etc.

所以举一个例子,我们假设你想要十二个数字,不要重复,从1到75.所以第一个问题是,你想要一个列表,所有的数字高达75,但洗牌。实际上你这样做是这样的。

So to make an example let's say you want twelve numbers, no repeats, from 1 through 75. So the first problem is, you want a List with all the numbers up to 75, but shuffled. In fact you do that like this ..

List<int> shuf = Enumerable.Range(1,75).OrderBy(r=>Random.value).ToList();

所以这个列表是75个项目长。您可以通过说 foreach(在shuf中的int r)Debug.Log(r); 来检查它。接下来在这个例子中,你只需要12个数字。幸运的是有一个列表调用,这样做:

So that list is 75 items long. You can check it by saying foreach(int r in shuf) Debug.Log(r);. Next in the example you only want 12 of those numbers. Fortunately there's a List call that does this:

shuf = shuf.Take(12)

所以,就是这样 - 你现在有12个数字,不重复, 1和75.再次,您可以使用 foreach(int r in shuf)查看Debug.Log(r);

So, that's it - you now have 12 numbers, no repeats, all random between 1 and 75. Again you can check with foreach(int r in shuf) Debug.Log(r);

简而言之,当你想要n数字时,不要重复,在1和Max之间,所有你必须这样做:

In short, when you want "n" numbers, no repeats, between 1 and Max, all you have to so is this:

List<int> shuf = Enumerable.Range(1,Max).OrderBy(r=>Random.value).ToList();
shuf = shuf.Take(n);

etvoilà,您可以使用 foreach(int r in shuf )Debug.Log(r);

et voilà, you can check the result with foreach(int r in shuf) Debug.Log(r);

我只是解释这一点,因为问题经常被问到如何获得随机数独特。这是一个古老的编程技巧,答案只是您 shuffle 所有涉及的整数的数组。

I just explain this at length because the question is often asked "how to get random numbers that are unique". This is an "age-old" programming trick and the answer is simply that you shuffle an array of all the integers involved.

有趣的是,如果你google这个问题(如何获得随机数是独一无二的),这是罕见的情况之一,其中谷歌没有太多的帮助,因为:当这个问题被问及时,你会得到大量的敏锐的新程序员(谁没有听到简单的伎俩来正确地做...)写出巨大的复杂的想法,导致进一步的混乱和复杂化。

Interestingly, if you google this question ("how to get random numbers that are unique") it's one of those rare occasions where google is not much help, because: whenever this question is asked, you get a plethora of keen new programmers (who have not heard the simple trick to do it properly!!) writing out huge long complicated ideas, leading to further confusion and complication.

所以这就是你做随机数,不重复,幸好是微不足道的。

So that's how you make random numbers with no repeats, fortunately it is trivial.

这篇关于在平面地图上随机生成块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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