1. ホーム
  2. javascript

[解決済み] マップタイリングアルゴリズム

2022-04-27 14:43:52

質問

地図

JavascriptでタイルベースのRPGを作っています。perlinのノイズのハイトマップを使い、ノイズの高さに応じてタイルの種類を割り当てています。

マップは最終的に以下のようになります(ミニマップ表示時)。

画像の各ピクセルから色値を抽出し、タイル辞書のタイルに対応する(0-255)間の位置に応じて整数(0-5)に変換する、かなり単純なアルゴリズムがある。この200x200の配列がクライアントに渡されます。

そして、エンジンは配列の値からタイルを決定し、キャンバスに描画します。そのため、山や海など、リアルな外観を持つ面白い世界ができあがります。

次にやりたいことは、タイルが隣のタイルにシームレスに溶け込むような、ある種のブレンドアルゴリズムを適用することでした。 もし のように、隣が同じタイプでない場合。上のマップの例は、プレイヤーがミニマップで見ているものです。画面上では、白い長方形でマークされた部分のレンダリングバージョンが表示されます。ここでは、タイルは単色のピクセルではなく、画像でレンダリングされています。

これは、ユーザーがマップ上で見ることのできる例です。 上のビューポートに表示されている位置とは異なります。

このビューで遷移させたいのです。

アルゴリズム

私は、ビューポート内のマップをトラバースして、各タイルの上に別の画像をレンダリングするシンプルなアルゴリズムを思いつきました(ただし、異なるタイプのタイルの隣にあることが条件)。(このアルゴリズムのアイデアは、現在のタイルの隣人をプロファイルすることでした。

これは、エンジンがレンダリングしなければならないシナリオの例で、現在のタイルはXでマークされたものです。

3x3の配列が作成され、その周りの値が読み込まれます。つまりこの例では、配列は次のようになります。

[
    [1,2,2]
    [1,2,2]
    [1,1,2]
];

そこで私が考えたのは、考えられるタイルの構成について、いくつかのケースを考えてみることでした。非常にシンプルなレベルです。

if(profile[0][1] != profile[1][1]){
     //draw a tile which is half sand and half transparent
     //Over the current tile -> profile[1][1]
     ...
}

すると、このような結果になります。

からの遷移として機能します。 [0][1] から [1][1] からではなく [1][1] から [2][1] というように、硬いエッジが残ります。そこで、そのような場合には、コーナータイルを使う必要があると考えました。3x3のスプライト・シートを2枚作り、必要なタイルの組み合わせがすべて収まるようにしました。そして、これをゲームにあるすべてのタイルに複製しました(白い部分は透明です)。この結果、各タイルは16枚になりました(各スプライトシートの中央のタイルは使用されていません)。

理想的な成果

この新しいタイルと正しいアルゴリズムで、例のセクションは次のようになります。

しかし、これまで試行錯誤してきた結果、いつもアルゴリズムに何らかの欠陥があり、パターンがおかしくなってしまうのです。すべてのケースを正しく処理することはできないようで、全体的に見ると、やり方が悪いように思います。

解決策?

そこで、どうすればこの効果を生み出せるのか、あるいはプロファイリングアルゴリズムをどのような方向で書けばいいのか、どなたか別の解決策を提示していただければ、とてもありがたいです

どのように解決するのですか?

このアルゴリズムの基本的な考え方は、前処理ステップですべてのエッジを見つけ、エッジの形状に応じて正しいスムージングタイルを選択することです。

まず、すべてのエッジを見つけることです。以下の例では エッジタイル X印のついたタイルは、8つの隣接するタイルのうち1つ以上がタンタイルである緑色のタイルです。異なる地形のタイプでは、この条件は、それが低い地形番号の隣人を持つ場合、タイルはエッジタイルであることに変換することができます。

すべてのエッジタイルが検出されたら、次に行うべきことは、各エッジタイルに対して正しいスムージングタイルを選択することです。以下は、私が作成したスムージングタイルの表現です。

実は、タイルの種類はそれほど多くはないことに注意してください。3x3マスのうち1つのマスからは外側の8枚のタイルが必要ですが、もう1つのマスからは角の4枚のタイルだけが必要です。つまり、全部で12種類のケースを区別しなければならない。

さて、1つのエッジタイルを見て、その最も近い4つの隣接タイルを見ることで、境界がどちらに曲がっているかを判断することができます。先ほどと同じようにエッジタイルに×印をつけると、次の6つのケースがあります。

これらのケースから対応するスムージングタイルを決定し、それに応じてスムージングタイルに番号を付けることができます。

それぞれのケースでaかbの選択が残っています。これは、草がどちらの側にあるかによって決まります。これを決定する一つの方法は、境界の向きを記録しておくことですが、おそらく最も簡単な方法は、端の隣のタイルを一つ選んで、それがどんな色をしているかを見ることでしょう。下の画像は5a)と5b)の2つのケースを表していますが、例えば右上のタイルの色を確認することで区別することができます。

この場合、最終的な列挙は次のようになる。

そして、対応するエッジタイルを選択すると、ボーダーは次のようになります。

最後に、境界がある程度規則的である限り、この方法は有効であると言えるかもしれません。より正確には、ちょうど2つのエッジタイルが隣接していないエッジタイルは別々に扱われなければならないでしょう。これはマップの端にある隣接するエッジタイルが1つである場合や、非常に狭い地形で隣接するエッジタイルの数が3つ、4つになる場合に起こります。