Algorithm 寻找一个好的世界地图生成算法

Algorithm 寻找一个好的世界地图生成算法,algorithm,map,terrain,Algorithm,Map,Terrain,我正在做一个类似文明的游戏,我正在寻找一个生成类似地球的世界地图的好算法。我已经尝试了一些替代方案,但还没有找到真正的赢家 一种选择是使用水生成高度图,并在水平面上添加水,这样世界上大约30%的面积是陆地。虽然柏林噪声(或类似的基于分形的技术)经常被用于地形,并且相当逼真,但它并不能控制产生的大陆的数量、大小和位置,这是我希望从游戏性的角度来看的 第二种选择是从随机定位的一个瓷砖种子开始(我正在处理一个瓷砖网格),确定大陆的所需大小,然后每转一圈添加一个与现有大陆水平或垂直相邻的瓷砖,直到达到

我正在做一个类似文明的游戏,我正在寻找一个生成类似地球的世界地图的好算法。我已经尝试了一些替代方案,但还没有找到真正的赢家

一种选择是使用水生成高度图,并在水平面上添加水,这样世界上大约30%的面积是陆地。虽然柏林噪声(或类似的基于分形的技术)经常被用于地形,并且相当逼真,但它并不能控制产生的大陆的数量、大小和位置,这是我希望从游戏性的角度来看的

第二种选择是从随机定位的一个瓷砖种子开始(我正在处理一个瓷砖网格),确定大陆的所需大小,然后每转一圈添加一个与现有大陆水平或垂直相邻的瓷砖,直到达到所需大小。对其他大陆重复上述步骤。这项技术是文明4中使用的算法的一部分。问题是,在放置前几个大陆之后,可能会选择一个被其他大陆包围的起始位置,因此不适合新的位置。此外,它有一种倾向,即繁殖的大陆过于靠近,导致一些东西看起来更像一条河流,而不是大陆

有没有人知道一个好的算法,可以在基于栅格的地图上生成真实的大陆,同时控制它们的数量和相对大小?

你可以借鉴并修改你的第二个想法。一旦你生成了你的大陆(大小都差不多),让它们随机移动、旋转、相互碰撞、变形、彼此漂移。(注意:这可能不是最容易实现的。)


编辑:这里有另一种方法,包括一个实现-。

在这里即兴思考:

选择一些起点,并为每个起点分配一个随机抽取的(希望的)大小。如果需要,您可以为计划的大陆和计划的岛屿维护单独的大小绘制

在土地元素上循环,如果它们尚未达到计划大小,则添加一个正方形。但有趣的部分是权衡每个相邻元素成为唯一元素的可能性。一些建议可能会影响:

  • 到最近的“其他”土地的距离。越远越好,生成的海洋空间越宽。越近越好,通道越窄。你必须决定是否也让比特合并
  • 离种子的距离。离得越近意味着地块越紧凑,离得越远意味着排得越长
  • 相邻的现有土地广场数量。权重有利于许多相邻的正方形,这会让你的海岸变得平滑,而偏好少数的正方形会让你拥有大量的入口和半岛
  • 附近是否有“资源”广场?取决于游戏规则,何时生成资源方,以及是否希望使其变得简单
  • 你会允许比特接近或与磁极连接吗
  • ???不知道还有什么
  • 继续,直到所有地块达到计划规模或由于某种原因无法再增长

    请注意,将参数修改为这些权重因子可以调整生成的世界类型,这是我喜欢的一些CIV的一个特性


    这样,您将需要分别在每个位上生成地形

    我建议你回去

  • 想想是什么造就了“好”的大陆
  • 写一个算法,可以区分一个好的大陆布局和一个坏的
  • 优化算法,以便量化好的布局有多好
  • 一旦你准备好了,你就可以开始实现一个算法,它的形状应该是这样的:

    • 生成糟糕的大陆,然后改进它们。

    为了提高性能,你可以尝试各种标准的优化技巧,无论是模拟退火、遗传编程,还是完全特殊的方法,比如将随机选择的边正方形从大陆上的任何位置移动到大陆质心对面的边上。但关键是要能够编写一个程序,能够区分好大陆和坏大陆。从手绘大陆和测试大陆开始,直到你得到你喜欢的东西。

    我会根据一些你知道“有效”的布局放置分形地形(例如2x2网格、菱形等,有一些抖动),但具有高斯分布阻尼峰向下靠近大陆中心的边缘。将水位放低,这样在靠近边缘之前,大部分都是陆地。

    我认为您可以在这里使用“动态规划”风格的方法

    先解决小问题,然后合并 巧妙地解决更大问题的解决方案 问题

    这将是非常好的,看看一些“图形布局算法”


    你可以根据自己的目的修改它们。

    我写了一些类似于你想要的自动屏幕保护程序风格的文明1克隆。作为记录,我在VB.net中写了这篇文章,但由于你的问题中没有提到任何关于语言或平台的内容,我将保持它的抽象性

    “地图”规定了大陆的数量、大陆大小差异(例如1.0将保持所有大陆具有相同的近似陆地面积,降到0.1将允许大陆以最大大陆质量的1/10存在)、生成的最大陆地面积(以百分比表示)和中央陆地偏差。每个大陆的“种子”随机分布在地图周围,根据中心偏差向地图中心加权(例如,低偏差产生更类似于地球的分布大陆,而高中心偏差将更类似于盘古大陆)。然后,对于每一次生长迭代,“种子”根据一种分布算法(稍后将详细介绍)分配地砖,直到最大土地面积h
    A1= [elliptical rectangular random ... ]// list of continents with area A1 approx. 
    A2= [elliptical rectangular random ... ]// list of continents with area A2 approx.
    A3= [elliptical rectangular random ... ]// list of continents with area A3 approx.
    ...
    An= [elliptical rectangular random ... ]// list of continents with area An approx.
    
    // note that elliptical is approximately elliptical in shape and same for the other shapes.
    
    Choose one/more randomly from each of the lists (An).
    
    Now you have control over number and area of continents.
    
    You can use genetic algorithm for positioning them 
    as you see "fit" ;)
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Untitled Document</title>
    
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <style type="text/css">
        #stage{
            font-family: Courier New, monospace;
        }
        span{
            display: none;
        }
        .tile{
            float:left;
            height:10px;
            width:10px;
        }
        .water{
            background-color: #55F;
        }
        .earth{
            background-color: #273;
        }
    </style>
    </head>
    
    <body>
    
    
    <div id="stage">
    
    </div>
    
    <script type="text/javascript">
    
    var tileArray = new Array();
    var probabilityModifier = 0;
    var mapWidth=135;
    var mapheight=65;
    var tileSize=10;
    
    var landMassAmount=2; // scale of 1 to 5
    var landMassSize=3; // scale of 1 to 5
    
    
    $('#stage').css('width',(mapWidth*tileSize)+'px');
    
    
    for (var i = 0; i < mapWidth*mapheight; i++) {
    
        var probability = 0;
        var probabilityModifier = 0;
    
        if (i<(mapWidth*2)||i%mapWidth<2||i%mapWidth>(mapWidth-3)||i>(mapWidth*mapheight)-((mapWidth*2)+1)){
    
            // make the edges of the map water
            probability=0;
        }
        else {
    
            probability = 15 + landMassAmount;
    
            if (i>(mapWidth*2)+2){
    
                // Conform the tile upwards and to the left to its surroundings 
                var conformity =
                    (tileArray[i-mapWidth-1]==(tileArray[i-(mapWidth*2)-1]))+
                    (tileArray[i-mapWidth-1]==(tileArray[i-mapWidth]))+
                    (tileArray[i-mapWidth-1]==(tileArray[i-1]))+
                    (tileArray[i-mapWidth-1]==(tileArray[i-mapWidth-2]));
    
                if (conformity<2)
                {
                    tileArray[i-mapWidth-1]=!tileArray[i-mapWidth-1];
                }
            }
    
            // get the probability of what type of tile this would be based on its surroundings 
            probabilityModifier = (tileArray[i-1]+tileArray[i-mapWidth]+tileArray[i-mapWidth+1])*(19+(landMassSize*1.4));
        }
    
        rndm=(Math.random()*101);
        tileArray[i]=(rndm<(probability+probabilityModifier));
    
    }
    
    for (var i = 0; i < tileArray.length; i++) {
        if (tileArray[i]){
            $('#stage').append('<div class="tile earth '+i+'"> </div>');
        }
        else{
            $('#stage').append('<div class="tile water '+i+'"> </div>');
        }
    }
    
    </script>
    
    </body>
    </html>