[解決済み] マップタイリングアルゴリズム
質問
地図
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つになる場合に起こります。
関連
-
元のイベントが実行されなかった後に要素を追加するためのjQueryソリューション
-
VUEグローバルフィルターの概念と留意点、基本的な使い方
-
[解決済み】GETできない / Nodejsエラー
-
フロントエンド非同期(アシンク)ソリューション(全ソリューション)
-
フロントエンド null のプロパティ 'disabled' を読み取れない 問題が解決された
-
[解決済み] ゲーム「2048」の最適なアルゴリズムとは?
-
[解決済み] オブジェクトの配列から、プロパティの値を配列として取り出す。
-
[解決済み] 配列から最初のN個の要素を取得する方法
-
[解決済み] JavaScriptでオブジェクトの配列から明確な値を取得する方法は?
-
[解決済み】画像処理。コカ・コーラ缶」認識のためのアルゴリズム改良
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
vue for 登録ページ効果 vue for sms 認証コードログイン
-
jQueryのコピーオブジェクトの説明
-
Javascript Bootstrapのグリッドシステム、ナビゲーションバー、ローテーションの説明
-
[解決済み] テスト
-
[解決済み] 期待される代入または関数呼び出し: 未使用式なし ReactJS
-
[解決済み】JavaScript TypeError: null のプロパティ 'style' を読み取ることができない
-
[解決済み】JavaScriptエラー(Uncaught SyntaxError: Unexpected end of input)
-
[解決済み】React-Redux: アクションはプレーンオブジェクトでなければならない。非同期アクションにはカスタムミドルウェアを使用する
-
[解決済み】ReactJSでエラー発生 Uncaught TypeError: Super expression は null か関数でなければならず、undefined ではありません。
-
JSクリックイベント - Uncaught TypeError: プロパティ 'onclick' に null を設定できません。