web-dev-qa-db-fra.com

Vous cherchez un bon algorithme de génération de carte du monde

Je travaille sur un jeu de type civilisation et je recherche un bon algorithme pour générer des cartes du monde semblables à la Terre. J'ai expérimenté quelques alternatives, mais je n'ai pas encore trouvé de vrai gagnant.

Une option consiste à générer une carte de hauteur en utilisant bruit Perlin et à ajouter de l'eau à un niveau tel qu'environ 30% du monde soit terrestre. Bien que le bruit de Perlin (ou des techniques similaires basées sur les fractales) soit fréquemment utilisé pour le terrain et soit raisonnablement réaliste, il n'offre pas beaucoup de contrôle sur le nombre, la taille et la position des continents résultants, ce que j'aimerais avoir du point de vue du gameplay.

Perlin noise

Une deuxième option consiste à commencer avec une graine d'une tuile positionnée au hasard (je travaille sur une grille de tuiles), de déterminer la taille souhaitée pour le continent et à chaque tour d'ajouter une tuile qui est horizontalement ou verticalement adjacente au continent existant jusqu'à ce que vous avez atteint la taille souhaitée. Répétez pour les autres continents. Cette technique fait partie de l'algorithme utilisé dans Civilization 4. Le problème est qu'après avoir placé les premiers continents, il est possible de choisir un emplacement de départ qui est entouré par d'autres continents, et ne correspondra donc pas au nouveau. En outre, il a tendance à engendrer des continents trop proches les uns des autres, ce qui donne quelque chose qui ressemble plus à un fleuve qu'à des continents.

Random expansion

Quelqu'un connaît-il un bon algorithme pour générer des continents réalistes sur une carte basée sur une grille tout en gardant le contrôle sur leur nombre et leur taille relative?

96
FalconNL

Vous pouvez vous inspirer de nature et modifier votre deuxième idée. Une fois que vous générez vos continents (qui sont tous à peu près de la même taille), faites-les se déplacer et tourner de manière aléatoire et entrer en collision et se déformer les uns les autres et s'éloigner les uns des autres. (Remarque: ce n'est peut-être pas la chose la plus simple à implémenter.)

Edit: Voici une autre façon de le faire, avec une implémentation - Génération de cartes polygonales pour les jeux .

38
David Johnstone

Je vous suggère de sauvegarder et

  1. Pensez à ce qui fait de "bons" continents.
  2. Écrivez un algorithme qui peut distinguer une bonne configuration continentale d'une mauvaise.
  3. Affinez l'algorithme pour pouvoir quantifier la qualité d'une bonne mise en page.

Une fois que vous avez cela en place, vous pouvez commencer à implémenter un algorithme qui devrait avoir la forme suivante:

  • Générez des continents merdiques puis améliorez-les.

Pour l'amélioration, vous pouvez essayer toutes sortes de trucs d'optimisation standard, qu'il s'agisse de recuit simulé, de programmation génétique ou de quelque chose de complètement ad hoc , comme déplacer un Edge choisi au hasard carré de partout où il se trouve sur le continent jusqu'au bord opposé au centre de masse du continent. Mais la clé est de pouvoir écrire un programme qui puisse distinguer les bons continents des mauvais. Commencez avec les continents dessinés à la main ainsi que vos continents de test, jusqu'à ce que vous obteniez quelque chose que vous aimez.

11
Norman Ramsey

J'ai écrit quelque chose de similaire à ce que vous recherchez pour un clone automatisé de style économiseur d'écran de Civilization 1. Pour mémoire, j'ai écrit ceci dans VB.net mais puisque vous ne mentionnez rien sur la langue ou la plate-forme dans votre question, je garderai c'est abstrait.

La "carte" spécifie le nombre de continents, la variance de la taille des continents (par exemple, 1,0 conserverait tous les continents avec la même superficie approximative, jusqu'à 0,1 permettrait aux continents d'exister avec 1/10 de la masse du plus grand continent), la superficie maximale (en pourcentage) à générer, et le biais foncier central. Une "graine" est distribuée au hasard autour de la carte pour chaque continent, pondérée vers le centre de la carte selon le biais central (par exemple, un biais faible produit des continents distribués plus similaires à la Terre, où un biais central élevé ressemblera davantage à un Pangée). Ensuite, pour chaque itération de croissance, les "graines" attribuent des tuiles de terrain selon un algorithme de distribution (plus à ce sujet plus tard) jusqu'à ce qu'une surface de terrain maximale soit atteinte.

L'algorithme de distribution des terres peut être aussi précis que vous le souhaitez, mais j'ai trouvé des résultats plus intéressants en appliquant divers algorithmes génétiques et en lançant les dés. Le "Game of Life" de Conway est vraiment très facile à démarrer. Vous devrez ajouter QUELQUE logique consciente à l'échelle mondiale pour éviter que des continents comme les continents ne se croisent, mais pour la plupart, les choses prennent soin d'elles-mêmes. Le problème que j'ai trouvé avec des approches plus fractales (ce qui était ma première inclinaison) était que les résultats semblaient trop structurés, ou conduisaient à trop de scénarios nécessitant des règles de contournement hacky pour obtenir un résultat qui ne semblait toujours pas assez dynamique. Selon l'algorithme que vous utilisez, vous souhaiterez peut-être appliquer une passe de "flou" sur le résultat pour éliminer des éléments tels que des tuiles océaniques à un seul carré abondantes et des côtes à damiers. Dans le cas où quelque chose comme un continent est apparu entouré de plusieurs autres et n'ayant nulle part où se développer, déplacez la graine à un nouveau point sur la carte et continuez les passes de croissance. Oui, cela peut signifier que vous vous retrouvez parfois avec plus de continents que prévu, mais si c'est vraiment quelque chose que vous ne voulez vraiment pas, une autre façon d'éviter cela est de biaiser les algorithmes de croissance afin qu'ils favorisent la croissance dans la direction la moins proche des autres. des graines. Au pire (à mon avis en tout cas), vous pouvez signaler une série comme invalide lorsqu'une graine n'a plus nulle part où se développer et générer une nouvelle carte. Assurez-vous simplement de définir un nombre maximum de tentatives, donc si quelque chose d'irréaliste est spécifié (comme installer 50 continents de poids égal sur une carte 10x10), il ne passera pas éternellement à chercher une solution valide.

Je ne peux pas garantir comment Civ etc. le fait, et bien sûr ne couvre pas des choses comme le climat, l'âge des terres, etc., mais en jouant avec l'algorithme de croissance des graines, vous pouvez obtenir des résultats assez intéressants qui ressemblent à des continents, des archipels, etc. Vous pouvez utiliser la même approche pour produire des rivières, des chaînes de montagnes, etc. "organiques".

10
nathanchere

J'ai créé quelque chose de similaire à votre première image en JavaScript. Ce n'est pas super sophistiqué mais ça marche:

http://jsfiddle.net/AyexeM/zMZ9y/

<!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>
10
AyexeM

Génération de cartes polygonales l'article décrit la génération de cartes étape par étape en utilisant les polygones Voronoi.

Ce gars donne également tous les codes sources. C'est Flash (ActionScript 3/ECMAScript) mais transposable à tout autre langage orienté objet

Ou essayez d'utiliser des algorithmes implémentés dans certains logiciels d'environnement fractal comme TerraJ

8
mems

Je pense juste au pied levé ici:

Choisissez des points de départ et attribuez à chacun une taille tirée au sort (espérée). Vous pouvez conserver un dessin de taille distinct pour les continents et les îles planifiés si vous le souhaitez.

Faites une boucle sur les éléments terrestres et, lorsqu'ils ne sont pas encore à la taille prévue, ajoutez un carré. Mais la partie amusante pèse la chance que chaque élément voisin soit celui-là. Certains ont suggéré quelque chose qui pourrait prendre en compte:

  1. Distance au "autre" terrain le plus proche. En outre, il est préférable de générer de larges espaces océaniques. Plus on est proche, mieux on fait des canaux étroits. Vous devez également décider si vous allez également laisser les bits fusionner.
  2. Distance de la graine. Plus proche est meilleur signifie des masses terrestres compactes, plus loin est meilleur signifie des morceaux allongés
  3. Nombre de carrés de terrain existants adjacents. La pondération en faveur de nombreux carrés adjacents vous donne une côte lisse, préférant quelques-uns vous donne beaucoup d'entrées et de péninsules.
  4. Présence de carrés "ressources" à proximité? Cela dépend des règles du jeu, lorsque vous générez un carré de ressources et si vous voulez le rendre facile.
  5. Autoriserez-vous les bits à s'approcher ou à rejoindre les pôles?
  6. ??? je ne sais pas quoi d'autre

Continuez jusqu'à ce que toutes les masses terrestres aient atteint la taille prévue ou ne puissent plus grandir pour une raison quelconque.

Notez que le fait de régler le paramètre sur ces facteurs de pondération vous permet de régler le type de monde généré, ce qui est une fonctionnalité que j'aimais dans certaines Civs.

De cette façon, vous devrez générer séparément le terrain sur chaque bit.

5
dmckee

J'ai eu une idée de création de carte similaire à la réponse des plaques tectoniques. Il est allé quelque chose comme ça:

  1. balayer les carrés de la grille en donnant à chaque carré un carré "terre" si rnd <= 0,292 (le pourcentage réel de terre sèche sur la planète terre).
  2. Migrez chaque morceau de terre d'une case vers son plus grand voisin le plus proche. Si les voisins sont équidistants, allez vers le plus gros morceau. Si les morceaux sont de taille égale, choisissez-en un au hasard.
  3. si deux carrés de terre se touchent, groupez-les en un morceau, en déplaçant tous les carrés comme un à partir de maintenant.
  4. répétez à partir de l'étape 2. Arrêtez-vous lorsque tous les morceaux sont connectés.

Ceci est similaire au fonctionnement de la gravité dans un espace 3D. C'est assez compliqué. Un algorithme plus simple pour vos besoins fonctionnerait comme suit:

  1. Déposez n carrés de terrain de départ à des positions x, y aléatoires et à des distances acceptables les unes des autres. Ce sont des graines pour vos continents. (Utilisez le théorème de Pythagore pour vous assurer que les graines ont une distance minimale entre elles et toutes les autres.)
  2. faire apparaître un carré terrestre à partir d'un carré terrestre existant dans une direction aléatoire, si cette direction est un carré océanique.
  3. répétez l'étape 2. Arrêtez-vous lorsque les carrés de terrain remplissent 30% de la taille totale de la carte.
  4. si les continents sont assez proches les uns des autres, déposez des ponts terrestres comme vous le souhaitez pour simuler un effet de type Panama.
  5. Ajoutez des îles aléatoires plus petites à votre guise pour un look plus naturel.
  6. pour chaque carré "île" supplémentaire que vous ajoutez, découpez les mers intérieures et les carrés des lacs des continents en utilisant le même algorithme à l'envers. Cela maintiendra le pourcentage de terrain au montant souhaité.

Faites-moi savoir comment cela fonctionne. Je ne l'ai jamais essayé moi-même.

PS. Je vois que cela ressemble à ce que vous avez essayé. Sauf qu'il met en place toutes les graines à la fois, avant de commencer, les continents seront donc suffisamment éloignés et s'arrêteront lorsque la carte sera suffisamment remplie.

3
Kevnar

Vous pouvez essayer un algorithme carré de diamant ou un bruit perlin pour générer quelque chose comme une carte de hauteur. Ensuite, affectez des valeurs de plages à ce qui apparaît sur la carte. Si votre "hauteur" passe de 0 à 100, faites 0 - 20 eau, 20 - 30 plage, 30 - 80 herbe, 80 - 100 montagnes. Je pense que notch a fait quelque chose de similaire dans le minicraft, mais je ne suis pas un expert, je suis juste dans un état d'esprit carré de diamant après l'avoir finalement fait fonctionner.

3
user137

Je pense que vous pouvez utiliser ici une approche de style "programmation dynamique".

Résolvez d'abord les petits problèmes et combinez intelligemment les solutions pour résoudre des problèmes plus importants.

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" ;)

Il sera très intéressant de jeter un œil à certains "algorithmes de mise en page de graphes"

Vous pouvez les modifier en fonction de votre objectif.

2
Pratik Deoghare

Voici ce que je pense, car je suis sur le point d'implémenter quelque chose comme ça que j'ai pour un jeu en développement. :

Le monde divisé en régions. en fonction de la taille du monde, il déterminera le nombre de régions. Pour cet exemple, nous supposerons un monde de taille moyenne, avec 6 régions. Chaque zone de grille se divise en 9 zones de grille. ces zones de grille se divisent en 9 grilles chacune. (ce n'est pas pour le mouvement des personnages, mais simplement pour la création de cartes) Les grilles sont pour les biomes, les zones de grille sont pour les caractéristiques terrestres plus arquées, (continent vs océan) et les régions sont pour le climat global. Les grilles se décomposent en tuiles.

Générées aléatoirement, les régions reçoivent des ensembles climatiques logiques assignés. Les zones de grille sont assignées au hasard, par exemple; océan ou terre. Les grilles reçoivent des biomes assignés au hasard avec des modificateurs en fonction de leurs zones de grille et de leur climat, qui sont des forêts, des déserts, des plaines, des glaciers, des marécages ou des volcans. Une fois que toutes ces bases sont attribuées, il est temps de les mélanger ensemble, en utilisant une fonction basée sur un pourcentage aléatoire qui remplit les ensembles de tuiles. Par exemple; si vous avez un biome forestier, à côté d'un biome désertique, vous avez un algorithme qui diminue le capot probable qu'une tuile sera "forestière" et augmente qu'elle sera "la liberté". Donc, à mi-chemin entre eux, vous verrez une sorte d'affect mixte combinant les deux biomes pour une transition quelque peu fluide entre eux. La transition d'une zone de grille à la suivante nécessiterait probablement un peu plus de travail pour assurer des formations de masses terrestres logiques, comme, par exemple, un biome d'une zone de grille qui touche le biome d'une autre, au lieu d'avoir un pourcentage de commutation simple basé sur la proximité. Par exemple, il y a 50 carreaux du centre du biome au bord du biome, ce qui signifie qu'il y en a 50 du bord qu'il touche au centre du biome suivant. Cela laisserait logiquement un changement de 100% d'un biome à l'autre. Ainsi, à mesure que les tuiles se rapprochent de la frontière des deux biomes, le pourcentage se réduit à environ 60%. Il serait, je pense, imprudent de donner trop de probabilité de traverser des biomes loin de la frontière, mais vous voudrez que la frontière soit quelque peu mélangée. Pour les zones de grille, la variation en pourcentage sera beaucoup plus prononcée. Au lieu de descendre à environ 60%, il ne descendrait qu'à environ 80%. Et une vérification secondaire devrait alors être effectuée pour s'assurer qu'il n'y a pas une tuile d'eau aléatoire au milieu d'un biome terrestre à côté de l'océan sans logique. Donc, soit connectez cette tuile d'eau à la masse océanique pour créer un canal pour expliquer la tuile d'eau, soit supprimez-la complètement. La terre dans un biome à base d'eau est plus facile à expliquer à l'aide d'affleurements rocheux et autres.

Oh, un peu stupide, désolé.

2
Kevin Quinn

Je placerais le terrain fractal selon une disposition que vous connaissez "fonctionne" (par exemple grille 2x2, diamant, etc., avec un peu de gigue) mais avec une distribution gaussienne amortissant les pics vers les bords des centres du continent. Placez le niveau d'eau plus bas de sorte que ce soit principalement de la terre jusqu'à ce que vous vous rapprochiez des bords.

1
Rex Kerr

Je n'ai pas vraiment essayé cela, mais il a été inspiré par la réponse de David Johnstone concernant les plaques tectoniques. J'ai essayé de l'implémenter moi-même dans mon ancien projet Civ et quand il s'agissait de gérer les collisions, j'ai eu une autre idée. Au lieu de générer directement des tuiles, chaque continent se compose de nœuds. Distribuez la masse à chaque nœud puis générez une série de continents "blob" en utilisant une approche de métabille 2D. La tectonique et la dérive des continents seraient ridiculement faciles à "simuler" simplement en déplaçant les nœuds. Selon la complexité que vous souhaitez atteindre, vous pouvez même appliquer des éléments tels que les courants pour gérer le mouvement des nœuds et générer des chaînes de montagnes qui correspondent aux limites des plaques qui se chevauchent. N'ajouterait probablement pas grand-chose au côté gameplay, mais cela pourrait générer une génération de cartes intéressante d'un point de vue purement académique :)

Une bonne explication des métabilles si vous n'avez jamais travaillé avec elles auparavant:

http://www.gamedev.net/page/resources/_//feature/fprogramming/exploring-metaballs-and-isosurfaces-in-2d-r2556

1
nathanchere