時差等角図:画面上の点の地図座標を計算します


12

これについてはすでに多くのリソースがあることはわかっていますが、自分の座標系に一致するリソースが見つからないため、これらのソリューションを自分のニーズに合わせるのに非常に苦労しています。私が学んだことは、これを行う最良の方法は変換行列を使用することです。それを実装することは問題ありませんが、座標空間をどのように変換する必要があるのか​​わかりません。

これが私の座標系を示す画像です:

ここに画像の説明を入力してください

画面上の点をこの座標系に変換するにはどうすればよいですか?


これをチェックしてください:jsfiddle.net/Sd4ZP/18
Markus von Broady

これがどのように役立つかはわかりません。私の言っていることがよくわからなかったと思います。
Chris

それは逆に変換を行いますので、あなたはそれを逆にする必要があります。
Markus von Broady

回答:


23

まず、これがコードです。説明は次のとおりです。

/*
 * tw, th contain the tile width and height.
 *
 * hitTest contains a single channel taken from a tile-shaped hit-test
 * image. Data was extracted with getImageData()
 */

worldToTilePos = function(x, y) {

    var eventilex = Math.floor(x%tw);
    var eventiley = Math.floor(y%th);

    if (hitTest[eventilex + eventiley * tw] !== 255) {
        /* On even tile */

        return {
            x: Math.floor((x + tw) / tw) - 1,
            y: 2 * (Math.floor((y + th) / th) - 1)
        };
    } else {
        /* On odd tile */

        return {
            x: Math.floor((x + tw / 2) / tw) - 1,
            y: 2 * (Math.floor((y + th / 2) / th)) - 1
        };
    }
};

このコードは、質問に示されているマップのボックスでは機能しません。これは、奇数のタイルが左にオフセットされているのに対し、奇数のタイルは通常、右にオフセットされているためです(タイルマップエディターの場合と同様)。奇数タイルの場合に返されるx値を微調整することで、これを簡単に修正できるはずです。

説明

これは、このタスクを達成するためのやや強引な方法のように思えるかもしれませんが、少なくともピクセルが完全であり、わずかに柔軟性があるという利点があります。

トリックは、マップを1つの千鳥格子としてではなく、2つのグリッドを重ねて表示することです。奇数行グリッドと偶数行グリッドがありますが、代わりにそれらを赤と緑と呼び、きれいな図を作成できるようにします...

赤と緑の2つのグリッド

その画像の右側にある点に紫色の点を付けたことに注意してください。これは、元のタイル空間で見つけようとするポイントの例です。

世界のどの点についても注意すべき点は、それが常に正確に2つの領域にあるということです-赤と緑の領域(エッジ上にある場合を除き、とにかくギザギザのエッジ境界内でクリッピングされる可能性があります)。それらの地域を見つけましょう...

2つの候補地域

次に、2つの領域のどちらが正しいかを選択します。常に1つの答えがあります。

ここから、いくつかのより簡単な計算を行い、サンプルポイントから2つの領域の各中心点までの距離の2乗を計算できます。どちらが最も近いかが私たちの答えになります。

ただし、別の方法があります。テスト領域ごとに、タイルの正確な形状と一致するビットマップをサンプリングします。その単一タイルのローカル座標に変換されたポイントでそれをサンプリングします。この例では、次のようになります。

ポイントサンプル

左側の1つは、緑の領域をチェックしてヒット(黒のピクセル)を取得します。右側では、赤い領域をテストして、ミス(白いピクセル)を取得しています。2番目のテストは、常にどちらか一方のみであり、両方ではないため、もちろん冗長です。

次に、奇数のタイルが1,1にヒットしたという結論に達します。この座標は、奇数行と偶数行に異なる変換を使用して、元のタイル座標に簡単にマッピングできます。

このメソッドでは、ピクセルテストビットマップでピクセルごとの単純なプロパティを使用することもできます。たとえば、白はオフタイル、黒はヒット、青は水、赤は固体です。


2
最高です³!
HumanCatfood 2013

素晴らしくてよく説明された答え-コードにそれを実装する必要があるだけです。元のポスターには、その背後にあるコードやJavascriptのタグ以外の言語に関する情報は何も記載されていなかったので、A *の回答
Tom 'Blue' Piddock

1

あなたが抱えている問題はあなたの座標空間にあると思います。タイルに指定した座標は、実際には等角投影ではありません。xAxisを右下斜めに、yAxisを左下斜めに(またはその変形)と考える必要があります。

現在、図の座標軸に沿って移動すると、「タイルスペース」で対角線方向に移動するため、正方形は最終的にひし形になります(すべてがより複雑になります)。

あなたが探している変換行列は、それらのx軸とy軸から構築されたものです。実質的には以下の計算と同じです。

screenOffsetXY = screenPointXY - tileOriginXY;
tileX = dot(screenOffsetXY, xAxis) / tileWidth;
tileY = dot(screenOffsetXY, yAxis) / tileWidth;

編集:私はちょうど同じような質問に遭遇しました(しかし、私が話していた座標系について)、誰かがここではるかに完全な答えを出しました:

マウス座標を等尺性インデックスに変換する方法は?


0

基本的に、イベントリスナーを使用してウィンドウ内のマウスの位置を取得するには、ウィンドウ内のキャンバス位置のオフセットをマウス位置から削除する必要があります。これにより、マウス位置はキャンバスに相対的になります。

function mouseTwoGridPosition(e){

var mousex = e.pageX; //mouse position x
var mouseY = e.pageY;  //mouse position y
var canvas_width = 1000; //pixels
var offset_left = 100; //offset of canvas to window in pixels
var offset_top = 150; //offset of canvas to window in pixels

var isotile = 64; //if iso tile is 64 by 64

//get mouse position relative to canvas rather than window
var x = mousex - canvas_width/2 - offset_left;
var y = mousey - offset_top;


//convert to isometric grid
var tx = Math.round( (x + y * 2) / isotile) - 1;
var ty = Math.round((y * 2 - x) / isotile) - 1;

 //because your grid starts at 0/0 not 1/1 we subtract 1 
 // this is optional based on what grid number you want to start at



   return [tx,ty];
}

マウスムーブのキャンバスでイベントリスニングを行う方法を知っていると想定します。それ以外の場合は、アイソメトリックゲームデザインを検討する前にJSについてさらに学習することをお勧めします。D


これはちょっとシンプルすぎませんか?isoタイルの形と完全に一致させたいです。私がこれを正しく理解できれば、あなたは長方形の領域をチェックしているだけです。
Chris

アイソタイルの「フォーム」とはどういう意味ですか。たとえば、マウスが0:1の菱形の境界内にあるとすると、マウスがその境界を離れるまで、戻り値は0:1になります。タイルのサイズが異なる場合-私の方法は機能しません。提供される機能は私が使用するものであり、私にとってはうまく機能します。
Dave

他のすべてのソリューションははるかに複雑なので、ただ疑問に思います。タイルはもちろん同じサイズなのでチェックしてみます。
Chris

それをいじくり回して、キャンバスの左と上の正しいオフセットを取得していることを確認してください。そして、数学を実行すると、キャンバスの幅(キャンバスサイズの幅も)が正常に機能します。
Dave

私と同じ座標系を使用していますか?それはまだ完全にオフです。
Chris

0

座標空間を変えることで解決しました。これは最初の行にオフセットなしで開始され、そのために少し調整できる実際の例を見つけました。

    hWidth = this.tileset.tileSize.width / 2;
    hHeight = this.tileset.tileSize.height / 2;

    pX = point.x - halfWidth;
    pY = point.y - halfHeight;

    x = Math.floor((pX + (pY - hHeight) * 2) / this.tileset.tileSize.width);
    y = Math.floor((pY - (pX - hWidth) * 0.5) / this.tileset.tileSize.height);

    tx = Math.floor((x - y) / 2) + 1 + this.camera.x;
    ty = y + x + 2 + this.camera.y;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.