Search Unity

タイルマップと併用可能なプロシージャル生成パターン(前編)

, 5月 29, 2018

プロシージャル生成は、ゲームに多様性を与えるために多くのクリエイターによって使用されています。代表的な例としては Minecraft や、より最近のものでは Enter the GungeonDescenders などのゲームが挙げられます。本記事では、Unity 2017.2 で 2D 機能のひとつとして公開されたタイルマップ、および RuleTile と併用できる、いくつかのアルゴリズムについてご説明します。

プロシージャル生成でマップを作成すると、同じマップが二度出ないようにできます。様々な入力(時間、プレイヤーの現在のレベルなど)を使用して、ゲームのビルド後であってもコンテンツを動的に変更することができます。

本記事の内容

本記事では、プロシージャルなステージ生成手法でワールドを作成するためのメソッドでごく一般的なものをいくつかと、私の作成したカスタムバリエーションのいくつかをご紹介します。本記事をお読みになれば、例えば以下のようなマップを作成できるようになるでしょう。このマップは、タイルマップRuleTile を使用し、3 つのアルゴリズムを組み合わせて作成されています。

これらのアルゴリズムのどれかを使用してマップを生成する場合は、新しいデータをすべて含む整数の配列を 1 つ受け取ることになります。この受け取ったデータを、引き続き変更したり、タイルマップにレンダーしたりすることができます。

この先は、下記の事柄を念頭に置かれた上でお読みください。

  1. タイルとそれ以外の要素の識別はバイナリを使用して行われます。1 が ON、0 が OFF となります。
  2. すべてのマップは整数の 2 次元配列内に保存されます。この配列は(レンダー時以外は)各関数の最後でユーザーに戻されます。
  3. 配列を扱う関数である GetUpperBound() を使用して各マップの高さと幅を取得することで、各関数内に含まれる変数の数を減らし、コードをよりクリーンにします。
  4. 私はよく Mathf.FloorToInt() を使用します。なぜなら、タイルマップの座標系が左下から始まっており、Mathf.FloorToInt() を使用することで数字を四捨五入して整数にすることができるからです。
  5. 本記事内に掲載しているコードはすべて C# で記述されています。

GenerateArray

GenerateArray は、設定されたサイズの整数の配列を新しく 1 つ作成します。またその配列が full であるか empty(1 と 0)であるかも示すことができます。コードは以下のようになります。

RenderMap

この関数は、マップをタイルマップにレンダーするために使用されます。マップの幅と高さの分だけ周回して繰り返しチェックし、チェックしている位置で配列が 1 を持っている場合にのみタイルを配置します。

UpdateMap

この関数は、再レンダリングするのではなく、マップを更新するためだけに使用されます。この方法ならすべてのタイルとそのタイルデータを再描画しなくて済むため、使用するリソースの量を抑えることができます。

パーリンノイズ

パーリンノイズは様々な形で使用できます。そのひとつは、マップのトップレイヤーを作成するための使用です。これは単純に、現在の x 軸上の位置とシードを使用して新しいポイントを取得することで行われます。

単純な手法

この生成手法は、パーリンノイズのステージ生成内への実装を最も単純な形で行うものです。パーリンノイズ用の Unity 関数が利用可能なので複雑なプログラミングは不要です。また、関数 Mathf.FloorToInt() を使用することで、必ずタイルマップに適した整数を得られます。

タイルマップ上にレンダーされると以下のようになります。

平滑化

この関数を平滑化したものを使用することもできます。間隔を設定してパーリンノイズの高さを記録し、複数のポイント間で平滑化します。間隔内に収まる整数のリストが必要になるので、若干高度な関数となります。

この関数の冒頭部でまず間隔が 1 より大きいかどうか確認します。1 より大きい場合はノイズを生成し、これを間隔を設定して行うことで平滑化されたノイズを生成します。後続部は、実際に各ポイントの平滑化処理を行います。

平滑化は以下の手順で行われます。

  1. 現在の位置と最後の位置を取得する。
  2. 2 つの位置の間の差異を取得する(主に必要な情報は y 軸上の差異)。
  3. 次に、該当箇所をどの程度変更すべきか特定します。これは y の差異を変数 interval で割って求められます。
  4. この時点で位置の設定が開始できます。ゼロまで処理を行い続けます。
  5. y 軸で 0 に到達すると、高さの変更値を現在の高さに加え、次の x 軸の位置についてプロセスを繰り返します。
  6. 直前の位置と現在の位置の間の各位置の処理が完了したら、次のポイントに移ります。

間隔が 1 以下の場合は、上記のひとつ前の関数を使用して処理を行います。

レンダーされると以下のように表示されます。

ランダムウォーク

RandomWalkTop

このアルゴリズムは、ちょうどコインを投げて裏か表かでランダムに決定するのと同様の仕組みです。2 つのうちのどちらかの結果が出ます。結果が表であれば 1 ブロック上に進み、裏であれば 1 ブロック下に進みます。常に上または下に移動することで、ステージに高さが加わります。このアルゴリズムの唯一のデメリットは、見た目が非常にギザギザになることです。この仕組みを具体的に見てみましょう。

この生成手法だと、パーリンノイズ生成の場合よりも高さが平滑になります。


RandomWalkTopSmoothed

この生成手法はパーリンノイズ生成の場合よりも高さが平滑になります。

ランダムウォークのこのバリエーションは、上述のバージョンと比較して、結果が大幅に平滑になります。これは、関数に新しい変数を 2 つ加えることで行えます。

  • 最初の変数は、現在の高さが維持されていた長さを格納するために使用されます。これは整数で、高さを変更するとリセットされます。
  • 2 つ目の変数はこの関数の入力値で、同じ高さのセクション幅の最小値として使用されます。これは実際に関数を見て頂くと分かりやすいと思います。

これで、追加が必要な要素が把握できました。それでは関数を確認してみましょう。

以下の GIF 画像からお分かりの通り、ランダムウォークアルゴリズムの平滑化処理では、ステージ内に広い平らな部分を作り出すことができます。

まとめ

本記事が、皆様のプロジェクトに何らかのプロシージャル生成を使用するきっかけとなれば嬉しく思います。プロシージャル生成マップについてより詳しく学びたい場合は、Procedural Generation Wiki(英語)または Roguebasin.com(英語)が非常に役立つ資料となっています。

本シリーズの次の記事では、プロシージャル生成を使った洞窟システムの作成方法をご紹介します。

プロシージャル生成を使用して面白いものが作成できたら、ぜひ Twitter または下のコメント欄で私に教えてください!

Unite Berlin での 2 次元プロシージャル生成についての講演

より深く掘り下げて学べるライブデモ・セッションに参加したいですか? 6 月 20 日、Unite Berlin 会場のホールのミニシアターで、タイルマップと併用可能なプロシージャル生成パターンについてお話します。また講演後も会場におりますので、直接お話しましょう!(※訳注:Unite Berlin は 6 月 21 日、盛況のうちに閉幕しました)

コメント受付を終了しました。

  1. I’m probably being stupid but how do you implement this into your code i’m noob at programming i’m more of a designer.

  2. Alan Mattano

    7月 5, 2018 5:31 pm

    So useful. I wish more blogs posting code! A new Categorie?

  3. Sampsa Lehtonen

    6月 12, 2018 9:26 pm

    I’m sorry to be spoil the party but I don’t think the author has understood how Perlin noise works. If you sample just using integer values, you’re effectively generating white noise.

    Perlin noise has built in interpolation in it. You’re supposed to sample it with much longer wavelengths to generate more low frequency noise.

    Instead of this, the author the goes and implements choppy linear interpolation and decides it’s not good enough, and then uses random walk with longer segments to generate smoother noise.

    Just go to square one, and multiply the noise position by some value such as 0.11f to get nice smooth hills.

  4. Awesome, I wanna learn more about PCG and found this. Love it.

  5. great stuff!!
    Thank you very much for sharing great informative useful for everyone.vidmateapp

  6. Finally some awesome posts!

    Thanks, you can also revive your youtube channel with that kind of lessons.

  7. Isaac Surfraz

    5月 29, 2018 3:26 pm

    Best blog post in a while. Walks the perfect line between interesting and development-useful info oriented content :)

    The idea of looking over algorithms commonly used in various game types could be a great one to revisit for more blog posts :)