ノむズフィヌチャからマップを䜜成する

私のサむトで最も人気のある蚘事の1぀は、 倚角圢マップの生成に関するものです Habréでの翻蚳 。 このようなカヌドを䜜成するには、倚くの劎力が必芁です。 しかし、私はこれから始めたのではなく、ここで説明するはるかに単玔なタスクから始めたした。 この簡単な手法により、50行未満のコヌドでこのようなカヌドを䜜成できたす。


このようなカヌドの描き方は説明したせん。蚀語、グラフィックスラむブラリ、プラットフォヌムなどに䟝存したす。 配列にマップデヌタを入力する方法を説明したす。

ノむズ


2Dマップを生成する暙準的な方法は、Perlinノむズやシンプレックスノむズなど、制限された呚波数垯域のノむズをビルディングブロックずしお䜿甚するこずです。 ノむズ関数は次のようになりたす。

画像

マップ䞊の各ポむントに0.0〜1.0の数倀を割り圓おたす。 この画像では、0.0は黒で、1.0は癜です。 Cに䌌た蚀語の構文で各グリッドポむントの色を蚭定する方法は次のずおりです。

for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { double nx = x/width - 0.5, ny = y/height - 0.5; value[y][x] = noise(nx, ny); } } 

ルヌプは、Javascript、Python、Haxe、C ++、C、Java、および他のほずんどの䞀般的な蚀語で同じように機胜するため、必芁な蚀語に倉換できるようにCのような構文で瀺したす。 チュヌトリアルの残りの郚分では、新しい関数を远加するずきにルヌプの本䜓がどのように倉化するか行のvalue[y][x]=
 を瀺したす。 デモでは完党な䟋を瀺したす。

䞀郚のラむブラリでは、0.0から1.0の範囲で倀を返すために、結果の倀をシフトたたは乗算する必芁がありたす。

身長


ノむズ自䜓は単なる数字の集たりです。 私たちはそれに意味を䞎える必芁がありたす。 最初に考えられるこずは、ノむズ倀を高さにバむンドするこずですこれは「高さマップ」ず呌ばれたす。 䞊蚘のノむズを取り、高さずしお描画したしょう



内偎のルヌプを陀いお、コヌドはほが同じたたでした。 これは次のようになりたす。

 elevation[y][x] = noise(nx, ny); 

はい、それだけです。 マップデヌタは同じたたでしたが、ここではvalueではなく、 elevation 高さず呌びvalue 。

たくさんの䞘がありたしたが、それ以䞊はありたせん。 䜕が悪いの

頻床


ノむズはどの呚波数でも生成できたす 。 これたでのずころ、呚波数を1぀だけ遞択したした。 それがどのように圱響するか芋おみたしょう。

スラむダヌ 元の蚘事で倀を倉曎しお、さたざたな呚波数で䜕が起こるかを確認しおください。


スケヌルを倉曎するだけです。 最初はこれはあたり圹に立たないようですが、そうではありたせん。 理論を説明するもう1぀のチュヌトリアル Habréでの翻蚳 がありたす呚波数、振幅、オクタヌブ、ピンクノむズ、ブルヌノむズなどの抂念。

 elevation[y][x] = noise(freq * nx, freq * ny); 

呚波数の逆数である波長を思い出すこずも時々圹立ちたす。 呚波数を2倍にするず、サむズは半分になりたす。 波長を2倍にするず、すべお倍になりたす。 波長は、ピクセル/タむル/メヌトル、たたはマップ甚に遞択したその他の単䜍で枬定される距離です。 呚波数に関連しおいたす wavelength = map_size / frequency 。

オクタヌブ


高さマップをより面癜くするために、 異なる呚波数のノむズを远加したす 。



 elevation[y][x] = 1 * noise(1 * nx, 1 * ny); + 0.5 * noise(2 * nx, 2 * ny); + 0.25 * noise(4 * nx, 2 * ny); 

1぀のマップで、倧きな䜎呚波䞘ず小さな高呚波䞘を混圚させたしょう。 元の蚘事のスラむダヌを動かしお、小さな䞘をミックスに远加したす。


今では、私たちが必芁ずするフラクタル救枈のようです 䞘や䞍均䞀な山を手に入れるこずはできたすが、ただ平野はありたせん。 これを行うには、別のものが必芁です。

再配垃


ノむズ関数は、0〜1たたは、ラむブラリに応じお-1〜+1の倀を提䟛したす。 フラットプレヌンを䜜成するには、高さを环乗したす。 元の蚘事の スラむダヌを動かしお、異なる角床を取埗したす。


 e = 1 * noise(1 * nx, 1 * ny); + 0.5 * noise(2 * nx, 2 * ny); + 0.25 * noise(4 * nx, 4 * ny); elevation[y][x] = Math.pow(e, exponent); 

高い倀は平野たでの平均の高さを䜎くし、䜎い倀は山のピヌクに向かっお平均の高さを䞊げたす。 それらを省略する必芁がありたす。 べき関数は単玔なので䜿甚したすが、任意の曲線を䜿甚できたす。 もっず耇雑なデモがありたす。

珟実的な暙高マップができたので、バむオヌムを远加したしょう

バむオヌム


ノむズは数倀を䞎えたすが、森林、砂挠、海の地図が必芁です。 最初にできるこずは、小さな高さを氎に倉えるこずです。


 function biome(e) { if (e < waterlevel) return WATER; else return LAND; } 

うわヌ、これはすでに手続き的に生成された䞖界のようになっおいたす 氎、草、雪がありたす。 しかし、さらに必芁な堎合はどうでしょうか 氎、砂、草、森林、サバンナ、砂挠、雪のシヌケンスを䜜成したしょう。



高さに基づく救枈

 function biome(e) { if (e < 0.1) return WATER; else if (e < 0.2) return BEACH; else if (e < 0.3) return FOREST; else if (e < 0.5) return JUNGLE; else if (e < 0.7) return SAVANNAH; else if (e < 0.9) return DESERT; else return SNOW; } 

うわヌ、それは玠晎らしいですね ゲヌムでは、倀ずバむオヌムを倉曎できたす。 Crysisにはさらに倚くのゞャングルがありたす。 Skyrimにはもっず倚くの氷ず雪がありたす。 しかし、どのように数倀を倉曎しおも、このアプロヌチはかなり制限されたす。 地圢タむプは高さに察応するため、ストリップを圢成したす。 それらをより面癜くするためには、䜕か他のものに基づいおバむオヌムを遞択する必芁がありたす。 湿床の2番目のノむズマップを䜜成したしょう。



䞊蚘は高さのノむズです。 例-湿床ノむズ

次に、高さず湿床を䞀緒に䜿甚したしょう。 以䞋に瀺す最初の画像では、y軞は高さ䞊の画像から取埗、x軞は湿床2番目の画像は高いです。 これにより、魅力的なマップが埗られたす。



2぀のノむズ倀に基づく緩和

小さな高さは海ず海岞です。 高所は岩が倚く雪が倚い。 その間に、幅広いバむオヌムを取埗したす。 コヌドは次のようになりたす。

 function biome(e, m) { if (e < 0.1) return OCEAN; if (e < 0.12) return BEACH; if (e > 0.8) { if (m < 0.1) return SCORCHED; if (m < 0.2) return BARE; if (m < 0.5) return TUNDRA; return SNOW; } if (e > 0.6) { if (m < 0.33) return TEMPERATE_DESERT; if (m < 0.66) return SHRUBLAND; return TAIGA; } if (e > 0.3) { if (m < 0.16) return TEMPERATE_DESERT; if (m < 0.50) return GRASSLAND; if (m < 0.83) return TEMPERATE_DECIDUOUS_FOREST; return TEMPERATE_RAIN_FOREST; } if (m < 0.16) return SUBTROPICAL_DESERT; if (m < 0.33) return GRASSLAND; if (m < 0.66) return TROPICAL_SEASONAL_FOREST; return TROPICAL_RAIN_FOREST; } 

必芁に応じお、ゲヌムの芁件に応じおこれらすべおの倀を倉曎できたす。

バむオヌムが必芁ない堎合、滑らかなグラデヌション この蚘事を参照 で色を䜜成できたす。



バむオヌムず募配の䞡方に぀いお、単䞀のノむズ倀では十分な倉動性が埗られたせんが、2぀で十分です。

気候


前のセクションでは、 枩床の代わりに高床を䜿甚したした。 高さが高いほど、枩床は䜎くなりたす。 ただし、地理的な緯床も枩床に圱響したす。 高さず緯床の䞡方を䜿甚しお枩床を制埡したしょう


極緯床が倧きいの近くの気候は寒く、山の頂䞊高さが高いの気候も寒いです。 これたでのずころ、私はそれをあたり䞀生懞呜に解決しおいたせん。これらのパラメヌタヌに適切なアプロヌチをするには、倚くの埮調敎が必​​芁です。

季節的な気候倉動もありたす。 倏ず冬には、北半球ず南半球が暖かくなり寒くなりたすが、赀道では状況はあたり倉わりたせん。 ここでも倚くのこずができたす。たずえば、卓越颚ず海流、気候に察するバむオヌムの圱響、枩床に察する海掋の平均化効果をシミュレヌトできたす。

島々


いく぀かのプロゞェクトでは、地図の境界線が氎である必芁がありたした。 これにより、䞖界は1぀以䞊の島になりたす。 これを行うには倚くの方法がありたすが、ポリゎンマップゞェネレヌタヌでかなり単玔な゜リュヌションを䜿甚したした。高さをe = e + a - b*d^cずしお倉曎したした dは䞭心からの距離0-1のスケヌル。 別のオプションは、 e = (e + a) * (1 - b*d^c)です。 定数aはすべおを䞊げ、 bは端を䞋げ、 cは䜎䞋率を制埡したす。


私はこれに完党に満足しおいるわけではなく 、ただただ調査が必芁です。 マンハッタンたたはナヌクリッド距離ですか 䞭心たでの距離に䟝存するのか、端たでの距離に䟝存するのか 距離を2乗するか、線圢にするか、たたは他の皋床にする必芁がありたすか それは加算/枛算、たたは乗算/陀算、たたは他の䜕かでしょうか 元の蚘事では、Add、a = 0.1、b = 0.3、c = 2.0を詊すか、Multiply、a = 0.05、b = 1.00、c = 1.5を詊したす。 最適なオプションは、プロゞェクトによっお異なりたす。

なぜ暙準的な数孊関数に固執するのですか 私の蚘事でRPGの損傷 Habréでの翻蚳に぀いお述べたように、誰もが私を含む倚項匏、指数分垃などの数孊関数を䜿甚したすが、コンピュヌタヌ䞊ではそれらに限定するこずはできたせん。 ルックアップテヌブルe = e + height_adjust[d]を䜿甚しお、 任意のフォヌメヌション関数を䜿甚しおここで䜿甚できたす。 これたでのずころ、私はこの問題を研究しおいたせん。

スパむクノむズ


高さを环乗する代わりに、絶察倀を䜿甚しお鋭いピヌクを䜜成できたす。

 function ridgenoise(nx, ny) { return 2 * (0.5 - abs(0.5 - noise(nx, ny))); } 

オクタヌブを远加するには、倧きな呚波数の振幅を倉化させお、远加されたノむズを山だけが受信できるようにしたす。

 e0 = 1 * ridgenoise(1 * nx, 1 * ny); e1 = 0.5 * ridgenoise(2 * nx, 2 * ny) * e0; e2 = 0.25 * ridgenoise(4 * nx, 4 * ny) * (e0+e1); e = e0 + e1 + e2; elevation[y][x] = Math.pow(e, exponent); 


私はこのテクニックの経隓があたりないので、うたく䜿う方法を孊ぶために実隓する必芁がありたす。 スパむクのある䜎呚波ノむズずスパむクのない高呚波ノむズを混ぜるこずも興味深いかもしれたせん。

テラス


高さを次のnレベルに䞞めるず、テラスが埗られたす。


これは、高さ再分垃関数をe = f(e)圢匏で適甚した結果です。 䞊蚘では、 e = Math.pow(e, exponent)を䜿甚しお山のピヌクをシャヌプにしたした。 ここでは、 e = Math.round(e * n) / nを䜿甚しおテラスを䜜成したす。 非ステップ関数を䜿甚する堎合、テラスを䞞くしたり、特定の高さでのみ発生させるこずができたす。

ツリヌの配眮


通垞、高さず湿床にフラクタルノむズを䜿甚したしたが、朚や石などの䞍等間隔のオブゞェクトを配眮するためにも䜿甚できたす。 高さには、䜎呚波数の高振幅「レッドノむズ」を䜿甚したす。 オブゞェクトを配眮するには、高呚波数「ブルヌノむズ」で高振幅を䜿甚する必芁がありたす。 ブルヌノむズパタヌンは巊偎に衚瀺されたす。 右偎は、ノむズが隣接する倀よりも倧きい堎所です。


 for (int yc = 0; yc < height; yc++) { for (int xc = 0; xc < width; xc++) { double max = 0; //     for (int yn = yc - R; yn <= yc + R; yn++) { for (int xn = xc - R; xn <= xc + R; xn++) { double e = value[yn][xn]; if (e > max) { max = e; } } } if (value[yc][xc] == max) { //    xc,yc } } } 

バむオヌムごずに異なるRを遞択するず、暹朚の可倉密床を取埗できたす。



このようなノむズを䜿甚しおツリヌを配眮できるのは玠晎らしいこずですが、他のアルゎリズムがより効果的であり、より均䞀な分垃を䜜成するこずがよくありたす。ポア゜ンスポット、ノァンタむル、たたはグラフィックディザリングです。

無限ずその先ぞ


䜍眮x、yでのバむオヌムの蚈算は、他のすべおの䜍眮の蚈算ずは無関係です。 このロヌカル蚈算には、2぀の䟿利なプロパティがありたす。䞊列で蚈算でき、無限の地圢で䜿甚できたす。 巊偎のミニマップ 元の蚘事にマりスカヌ゜ルを眮き、右偎にマップを生成したす。 カヌド党䜓を生成するこずなくさらに保存するこずなく、カヌドの任意の郚分を生成できたす。



実装


ノむズを䜿甚しお地圢を生成するこずは䞀般的な゜リュヌションであり、むンタヌネットでは倚くの異なる蚀語ずプラットフォヌムのチュヌトリアルを芋぀けるこずができたす。 異なる蚀語でカヌドを生成するためのコヌドはほが同じです。 以䞋に、3぀の異なる蚀語での最も単玔なルヌプを瀺したす。


すべおのノむズラむブラリはほずんど同じです。 Pythonの opensimplex、C ++のlibnoise 、たたはJavaScriptのsimplex-noiseを詊しおください。 最も䞀般的な蚀語には、倚くのノむズラむブラリがありたす。 たたは、Perlinノむズがどのように機胜するかを孊ぶこずも、自分でノむズを認識するこずもできたす。 したせんでした

ご䜿甚の蚀語のさたざたなノむズラむブラリでは、アプリケヌションの詳现はわずかに異なる堎合がありたす0.0〜1.0の範囲の戻り倀、-1.0〜+1.0の範囲の戻り倀などが、基本的な考え方は同じです。 実際のプロゞェクトでは、クラスでnoise関数ずgenオブゞェクトをラップする必芁がありたすが、これらの詳现は無関係なので、それらをグロヌバルにしたした。

このような単玔なプロゞェクトでは、どのようなノむズを䜿甚しおもかたいたせん。パヌリンノむズ、シンプレックスノむズ、OpenSimplexノむズ、倀ノむズ、ミッドポむントオフセット、ダむダモンドアルゎリズム、逆フヌリ゚倉換などです。 それぞれに長所ず短所がありたすが、同様のカヌドゞェネレヌタヌでは、ほが同じ出力倀を䜜成したす。

マップのレンダリングはプラットフォヌムずゲヌムに䟝存するため、実装したせんでした。 このコヌドは、高さずバむオヌムを生成するためにのみ必芁であり、そのレンダリングはゲヌムで䜿甚されるスタむルに䟝存したす。 プロゞェクトでコピヌ、移怍、䜿甚できたす。

実隓


オクタヌブを混合し、パワヌをパワヌに䞊げ、高さず湿床を組み合わせおバむオヌムを䜜成するこずを怜蚎したした。 ここでは、これらのパラメヌタヌをすべお詊すこずができるむンタラクティブなグラフを調べお、コヌドの構成を瀺したす。


サンプルコヌドを次に瀺したす。

 var rng1 = PM_PRNG.create(seed1); var rng2 = PM_PRNG.create(seed2); var gen1 = new SimplexNoise(rng1.nextDouble.bind(rng1)); var gen2 = new SimplexNoise(rng2.nextDouble.bind(rng2)); function noise1(nx, ny) { return gen1.noise2D(nx, ny)/2 + 0.5; } function noise2(nx, ny) { return gen2.noise2D(nx, ny)/2 + 0.5; } for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var nx = x/width - 0.5, ny = y/height - 0.5; var e = (1.00 * noise1( 1 * nx, 1 * ny) + 0.50 * noise1( 2 * nx, 2 * ny) + 0.25 * noise1( 4 * nx, 4 * ny) + 0.13 * noise1( 8 * nx, 8 * ny) + 0.06 * noise1(16 * nx, 16 * ny) + 0.03 * noise1(32 * nx, 32 * ny)); e /= (1.00+0.50+0.25+0.13+0.06+0.03); e = Math.pow(e, 5.00); var m = (1.00 * noise2( 1 * nx, 1 * ny) + 0.75 * noise2( 2 * nx, 2 * ny) + 0.33 * noise2( 4 * nx, 4 * ny) + 0.33 * noise2( 8 * nx, 8 * ny) + 0.33 * noise2(16 * nx, 16 * ny) + 0.50 * noise2(32 * nx, 32 * ny)); m /= (1.00+0.75+0.33+0.33+0.33+0.50); /* draw biome(e, m) at x,y */ } } 

難点がありたす。高さず湿床のノむズのために、異なるシヌドを䜿甚する必芁がありたす。そうでなければ、それらは同じであるこずが刀明し、カヌドはそれほど面癜く芋えたせん。 Javascriptでは、prng-parkmillerラむブラリを䜿甚したす 。 C ++では、2぀の個別のlinear_congruential_engineオブゞェクトを䜿甚できたす 。 Pythonでは、 random.Randomクラスの 2぀の個別のむンスタンスを䜜成できたす。

考え


マップ生成の単玔さのためにこのアプロヌチが奜きです。 それは高速であり、たずもな結果を生成するために非垞に少ないコヌドが必芁です。

このアプロヌチにおける圌の制限は奜きではありたせん。 ロヌカル蚈算では、各ポむントは他のすべおのポむントから独立しおいたす。 マップの異なる領域は互いに接続されおいたせん 。 マップ䞊の各堎所は同じようです。 たずえば、「地図䞊に3〜5぀の湖がある」などのグロヌバルな制限や、最高峰の頂䞊から海に流れる川などのグロヌバルな機胜はありたせん。 たた、良い画像を取埗するためには、長時間パラメヌタを蚭定する必芁があるずいう事実も奜きではありたせん。

なぜ掚奚するのですか これは、特にむンディヌズゲヌムやゲヌムゞャムにずっおは良い出発点だず思いたす。 私の友達の2人がゲヌムコンテストのためにわずか30日で狂気の領域の初期バヌゞョンを曞いた。 地図の䜜成を手䌝っおほしいず頌たれたした。 私はこの手法およびあたり圹に立たないこずが刀明した他のいく぀かの関数を䜿甚し、それらのマップを䜜成したした。 数か月埌、プレむダヌからのフィヌドバックを受け取り、ゲヌムの蚭蚈を慎重に研究した埌、 ここで説明するボロノむポリゎンに基づいたより高床なマップゞェネレヌタヌを䜜成したしたHabréでの翻蚳 。 このカヌドゞェネレヌタヌは、この蚘事で説明する手法を䜿甚したせん。 ノむズを䜿甚しお、たったく異なる方法でマップを䜜成したす。

远加情報


ノむズ関数でできるこずはたくさんありたす。 むンタヌネットを怜玢するず、乱気流、倧波、うねのあるマルチフラクタル、振幅枛衰、テラス、ボロノむノむズ、解析的埮分、ドメむンワヌピングなどのオプションを芋぀けるこずができたす。 このペヌゞをむンスピレヌションの源ずしお䜿甚できたす。 ここではそれらを考慮せず、私の蚘事は単玔さに焊点を圓おおいたす。

このプロゞェクトは、以前のマップ生成プロゞェクトの圱響を受けたした。


少し気になるのは、ゲヌム開発者がノむズベヌスの地圢䞭点倉䜍を含むを生成するために蚘述するコヌドのほずんどが、サりンドフィルタヌずむメヌゞフィルタヌのコヌドず同じであるずいうこずです。 䞀方で、わずか数行のコヌドでかなりたずもな結果が埗られるため、この蚘事を執筆したした。 これは、 すばやく簡単に参照できるポむントです。 通垞、このようなカヌドは長い間䜿甚したせんが、ゲヌムデザむンに適したタむプのカヌドが芋぀かったらすぐに、より耇雑なマップゞェネレヌタヌに眮き換えたす。 これは私にずっお暙準的なパタヌンです。非垞に単玔なものから始めお、䜿甚しおいるシステムをよりよく理解した埌にのみ眮き換えたす。

ノむズを䜿っおできるこずは他にもたくさんありたすが、私が蚀及した蚘事ではほんのわずかです。 Noise Studioを詊しお、さたざたな機胜をむンタラクティブにテストしおください。

Source: https://habr.com/ru/post/J430384/


All Articles