私は文明のようなゲームに取り組んでおり、地球のような世界地図を生成するための優れたアルゴリズムを探しています。私はいくつかの選択肢を試しましたが、本当の勝者にはまだヒットしていません。
1つのオプションは、 Perlin noise を使用してハイトマップを生成し、世界の約30%が陸地になるように水を追加することです。パーリンノイズ(または同様のフラクタルベースのテクニック)は地形によく使用され、かなり現実的ですが、得られる大陸の数、サイズ、位置を制御する方法はあまりありません。ゲームプレイの観点から持っています。
2番目のオプションは、ランダムに配置された1タイルシードから開始し(タイルのグリッドで作業しています)、大陸の希望サイズを決定し、各ターンで既存の大陸に水平または垂直に隣接するタイルを追加します希望のサイズに達しました。他の大陸についても繰り返します。この手法は、Civilization 4で使用されるアルゴリズムの一部です。問題は、最初のいくつかの大陸を配置した後、他の大陸に囲まれているため新しい場所に適合しない開始場所を選択できることです。また、大陸同士が近づきすぎてスポーンする傾向があり、その結果、大陸よりも川のように見えます。
数と相対サイズを制御しながら、グリッドベースのマップ上に現実的な大陸を生成するための優れたアルゴリズムを知っている人はいますか?
nature からヒントを得て、2番目のアイデアを修正できます。大陸(すべてほぼ同じサイズ)を生成したら、それらをランダムに移動および回転させ、衝突および変形させ、互いから離れるようにします。 (注:これは実装するのが最も簡単な方法ではないかもしれません。)
Edit:これを行う別の方法は、実装を完了します— ゲームの多角形マップ生成 。
バックアップすることをお勧めします
それを設定したら、次のような形にすべきアルゴリズムの実装を開始できます。
改善するには、シミュレートされたアニーリング、遺伝的プログラミング、または完全にアドホックであるかどうかにかかわらず、ランダムに選択されたEdgeを移動するなど、大陸上のどこからでも、大陸の重心の反対側の端までの正方形。しかし重要なのは、良い大陸と悪い大陸を区別できるprogramを書くことができることです。好きなものが手に入るまで、手描きの大陸とテスト大陸から始めてください。
Civilization 1の自動スクリーンセーバースタイルクローンの目的に似たものを書きました。記録のためにVB.netでこれを書きましたが、質問で言語やプラットフォームについては何も触れないので、続けます。それは抽象的です。
「マップ」は、大陸の数、大陸サイズの分散を指定します(たとえば、1.0はすべてのおおよその陸地をほぼ同じ面積に保ち、0.1までは大陸が最大大陸の1/10の質量で存在できるようにします)、最大陸地面積(割合として)生成するため、および中央の土地バイアス。 「シード」は、各大陸のマップの周囲にランダムに分布し、中央バイアスに応じてマップの中心に向かって重み付けされます(たとえば、低バイアスは、地球に似た分散大陸を生成します。パンゲア)。その後、成長の各反復に対して、「シード」は、最大の土地面積に達するまで、分布アルゴリズム(後で詳しく説明します)に従って土地タイルを割り当てます。
土地配分アルゴリズムは、必要に応じて正確にすることができますが、さまざまな遺伝的アルゴリズムを適用してサイコロを転がすと、より興味深い結果が見つかりました。 Conwayの「Game of Life」は、非常に簡単に開始できます。大陸が相互に成長することを避けるために、グローバルに認識されたロジックを追加する必要がありますが、ほとんどの場合、物事は自分で処理します。より多くのフラクタルベースのアプローチで見つけた問題(これは私の最初の傾向)は、結果がパターン化されすぎているか、または十分に動的に感じられない結果を得るためにハック感の回避策を必要とするシナリオが多すぎることです。使用するアルゴリズムによっては、結果に「ぼかし」パスを適用して、豊富な単一正方形の海のタイルや市松模様の海岸線などを排除することができます。いくつかの大陸に囲まれ、成長する余地がない大陸のようなものが発生した場合、シードをマップ上の新しいポイントに再配置し、成長パスを継続します。はい、それはあなたが時々計画よりも多くの大陸で終わることを意味することができますが、それが本当にあなたがしっかりと望んでいないものである場合、それを避けるための別の方法は成長アルゴリズムにバイアスをかけることで、他に最も近い方向の成長を優先します種。最悪の場合(とにかく私の意見では)、シードが成長して新しいマップを生成する場所が残っていないときに、シリーズに無効のフラグを立てることができます。最大試行回数を設定するようにしてください。非現実的なものが指定された場合(10x10のボードに50の均等に重み付けされた大陸を収めるなど)、有効なソリューションを見つけようとして永遠に費やされることはありません。
私はCivなどがそれをどのように行うかを保証することはできません。もちろん、気候、土地の年齢などのことをカバーしていませんが、種子成長アルゴリズムをいじることによって、大陸、群島などに似たかなり興味深い結果を得ることができます同じアプローチを使用して、「オーガニック」に見える川、山脈なども作成します。
JavaScriptで最初の画像に似たものを作成しました。それは非常に洗練されていませんが、動作します:
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>
ここでカフを考えてみましょう:
いくつかの開始点を選択し、それぞれにランダムに描画(希望)されたサイズを割り当てます。必要に応じて、計画された大陸と計画された島の個別のサイズの引き分けを維持できます。
土地要素をループし、まだ計画されたサイズになっていない場所に1つの正方形を追加します。しかし、楽しいのは、隣接する各要素が1つになる可能性を評価することです。考慮に入れるかもしれないいくつかの提案された事柄:
すべての陸地が計画されたサイズに達するか、何らかの理由で成長できなくなるまで続けます。
パラメーターをこれらの重み係数に変更すると、生成されるワールドの種類を調整できることに注意してください。これは、私がCivのいくつかについて気に入った機能です。
この方法では、各ビットで個別に地形生成を行う必要があります。
地殻プレートの答えに似たマップ作成のアイデアがありました。次のようになりました:
これは、3D空間で重力がどのように機能するかに似ています。かなり複雑です。ニーズに応じたより単純なアルゴリズムは、次のように機能します。
これがどのように機能するか教えてください。自分で試したことはありません。
PS。これはあなたが試したものに似ていると思います。開始前に一度にすべてのシードを設定することを除き、大陸は十分に離れており、マップが十分に満たされると停止します。
ダイアモンドスクエアアルゴリズムまたはパーリンノイズを試して、高さマップのようなものを生成できます。次に、範囲値をマップに表示されるものに割り当てます。 「高さ」が0〜100の場合、0〜20の水、20〜30のビーチ、30〜80の草、80〜100の山を作ります。ノッチはミニクラフトでこれと似たようなことをしたと思いますが、私は専門家ではありません。
ここで「動的プログラミング」スタイルのアプローチを使用できると思います。
最初に小さな問題を解決し、解決策をスマートに組み合わせて大きな問題を解決します。
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" ;)
いくつかの「グラフレイアウトアルゴリズム」を見てみると非常に良いでしょう。
これらを目的に合わせて変更できます。
開発中のゲーム用にこのようなものを実装しようとしているので、これが私が考えていることです。 :
世界は地域に分かれていました。世界のサイズに応じて、地域の数が決まります。この例では、6つの地域を持つ中規模の世界を想定します。各グリッドゾーンは9つのグリッドゾーンに分割されます。これらのグリッドゾーンはそれぞれ9つのグリッドに分割されます。 (これはキャラクターの移動用ではなく、単にマップ作成用です)グリッドはバイオーム用であり、グリッドゾーンは陸地の特徴をオーバーアーチ(大陸対海洋)に使用し、地域は気候全体に使用します。グリッドはタイルに分割されます。
ランダムに生成された地域には、論理的な気候セットが割り当てられます。たとえば、グリッドゾーンはランダムに割り当てられます。海または陸。グリッドには、グリッドゾーンと気候に基づいた修飾子を使用して、バイオームがランダムに割り当てられます。これらは、森林、砂漠、平野、氷河、沼地、火山です。これらの基本がすべて割り当てられたら、タイルセットを埋めるランダムなパーセンテージベースの関数を使用して、それらを一緒にブレンドします。例えば;砂漠バイオームの隣に森林バイオームがある場合、タイルが「森林」である可能性が高いフードを減らし、「砂漠」になることを増やすアルゴリズムがあります。そのため、それらの間の約半分で、2つのバイオームを組み合わせて、それらの間のやや滑らかな移行をオフにする、一種のブレンド効果が表示されます。たとえば、あるグリッドゾーンから次のグリッドゾーンへの移行では、ロジックの陸地形成を保証するために、もう少し作業が必要になります。たとえば、バイオームの中心からバイオームの端まで50個のタイルがあります。つまり、接触しているエッジから次のバイオームの中心まで50個のタイルがあります。論理的には、あるバイオームから次のバイオームに100%の変化が残ります。そのため、タイルが2つのバイオームの境界に近づくにつれて、割合は約60%程度に狭まります。境界線から遠く離れたバイオームを横切る可能性が高すぎるのは賢明ではないと思いますが、境界線をある程度ブレンドする必要があります。グリッドゾーンの場合、パーセンテージの変化はより顕著になります。 %が約60%に低下する代わりに、約80%に低下するだけです。そして、二次チェックを実行して、何らかのロジックがなければ、海の隣の陸地バイオームの中央にランダムな水タイルがないことを確認する必要があります。そのため、その水タイルを海洋マスに接続して、水タイルを説明するチャネルを作成するか、完全に削除します。水ベースのバイオームの土地は、岩の露頭などを使用して説明する方が簡単です。
ああ、ちょっと馬鹿げてごめんなさい。
「機能する」ことがわかっているレイアウト(たとえば、2x2グリッド、ダイヤモンドなど、ジッター)に従ってフラクタル地形を配置しますが、ガウス分布の減衰は大陸の中心の端に向かって減衰します。水位を低くして、端に近づくまでほとんどが陸地になるようにします。
私は実際にこれを試したことはありませんが、構造プレートに関するデイビッド・ジョンストンの答えに触発されました。古いCivプロジェクトに自分で実装してみましたが、衝突の処理に関しては別のアイデアがありました。タイルを直接生成する代わりに、各大陸はノードで構成されます。各ノードに質量を分配し、2Dメタボールアプローチを使用して一連の「blob」大陸を生成します。テクトニクスと大陸移動は、ノードを移動するだけで、とんでもなく簡単に「偽造」できます。どの程度複雑にしたいかによっては、電流のようなものを適用してノードの動きを処理し、プレート境界の重なりに対応する山の範囲を生成することもできます。おそらくゲームプレイ側にはそれほど多くは追加しませんが、純粋にアカデミックな観点から興味深いマップを生成できます:)
メタボールを使用したことがない場合のメタボールの説明: