エッジがラップされた世界での移動方向の検索


20

2Dワールドのある点から、エッジがラップされている別の点(小惑星など)までの最短距離方向を見つける必要があります。私は最短距離を見つける方法を知っていますが、それがどの方向にあるのかを見つけるのに苦労しています。

最短距離は次のとおりです。

int rows = MapY;
int cols = MapX;

int d1 = abs(S.Y - T.Y);
int d2 = abs(S.X - T.X);
int dr = min(d1, rows-d1);
int dc = min(d2, cols-d2);

double dist = sqrt((double)(dr*dr + dc*dc));

世界の例

                   :         
                   :  T    
                   :         
    :--------------:---------
    :              :
    :           S  :
    :              :
    :              :
    :  T           :
    :              :
    :--------------:

図では、エッジは:および-で示されています。右上にも世界のラップされたリピートを示しました。SからTまでの度数で方向を見つけたいので、最短距離はTの右上の繰り返しまでですが、Sから右上の繰り返しTまでの度数で方向を計算するにはどうすればよいですか?

私はSとTの両方の位置を知っていますが、繰り返しTの位置を見つける必要があると思いますが、1以上あります。

ワールド座標系は左上の0,0から始まり、方向は0度から西から始まります。

これはそれほど難しくないはずですが、解決策を見つけることができませんでした。誰かが助けてくれるといいのですが?すべてのウェブサイトをいただければ幸いです。


右上のTの座標は何ですか?

対角線のラッピングを使用したゲームを見たことがありません。通常、各方向(N、E、S、W)に1つのラップがあります。

5
水平方向と垂直方向の両方の折り返しがあるゲームには、デフォルトで斜めの折り返しがあります。

各座標を円上に住んでいると考えて、各座標の2つの可能な距離のうち短い方を個別に計算します。
ケレックSB

1
@crazy:ウィキペディアの「トーラス」見上げて...
Kerrek SB

回答:


8

角度を計算するには、アルゴリズムを少し調整する必要があります。現在は絶対位置の差のみを記録しますが、相対的な差が必要です(つまり、位置によって正または負になります)。

int dx = T.X - S.X; // difference in position
int dy = T.Y - S.Y;

if (dx > MapX / 2) // if distance is bigger than half map width, then looping must be closer
    dx = (dx - MapX) * -1; // reduce distance by map width, reverse 
else if (dx < -MapX / 2) // handle the case that dx is negative
    dx = (dx + MapX) * -1;

//Do the same for dy
if (dy > MapY / 2)
    dy = (dy - MapY) * -1;
else if (dy < -MapY / 2)
    dy = (dy + MapY) * -1;

double dist = sqrt(dy*dy+dx*dx); // same as before
double angle = atan2(dy,dx) * 180 / PI; // provides angle in degrees

1
TXがSXより小さい場合、またはTYがXYより小さい場合、そのままのコードが壊れるので、dxとdyの兆候に関するいくつかの作業が必要です。
スコットチェンバレン

すぐにそれを修正します。

1
すべてを言って完了したときにdxとdyのサインがどうなるかについて、いくつかのエラーがまだあります。
スコットチェンバレン

なぜこれが受け入れられた答えですか?? うまくいきません。100、90 MapXT.XおよびS.X10であると仮定すると、dx明らかに20であるはずですが、このアルゴリズムは30を返します!
サムホセバー

これは、投稿する前にコードをテストする機会がないときに起こることです。修正します。誰かがこれで別のエラーを見つけた場合、多すぎる人が誤解を招く前に削除するでしょう。
Toomai

11

そのような世界に存在する無限 T.レッツ示すためにSからのパスの数によってTの座標(Tx, Ty)によってSの座標(Sx, Sy)とによって世界の大きさが(Wx, Wy)。Tのラップされた座標はです(Tx + i * Wx, Ty + j * Wy)。ここでiおよびjは整数、つまりsetの要素です{..., -2, -1, 0, 1, 2, ...}。SをTに接続するベクトルは(Dx, Dy) := (Tx + i * Wx - Sx, Ty + j * Wy - Sy)です。与えられた(i, j)ペアでは、距離はベクトルの長さsqrt(Dx * Dx + Dy * Dy)、ラジアン単位の方向はatan(Dy / Dx)です。最短経路は 9つの経路の一つであるijしています{-1, 0, 1}ここに画像の説明を入力してください

最短経路ij値は直接決定できます:

int i = Sx - Tx > Wx / 2 ? 1 : Sx - Tx < -Wx / 2 ? -1 : 0;
int j = Sy - Ty > Wy / 2 ? 1 : Sy - Ty < -Wy / 2 ? -1 : 0;

@ IlmariKaronen、@ SamHocevar、@ romkynsに感謝します!


1
それよりもうまくやることができます:if abs(Tx-Sx) < Wx/2、then i=0が最適です; そうでない場合、最適な選択はi=-1またi=1はの符号に応じてですTx-Sx。同じことがのために行くTy-Syj
イルマリカロネン

1
この答えは、このような単純な問題に対しては非常に複雑です。最小値を直接計算できる場合、線形検索を使用する必要はありません。
サムホセバー

すばらしい写真ですが、提案されたアルゴリズムは、この回答が受け取った賛成に値するものではありません。
RomanSt

5

最短ではない場合でも、1つの可能な方向ベクトルを計算し、[-MapX/2,MapX/2]範囲内に収まるようにX座標をラップし、Yについても同じにします。

int DirX = (T.X - S.X + 3 * MapX / 2) % MapX) - MapX / 2;
int DirY = (T.Y - S.Y + 3 * MapY / 2) % MapY) - MapY / 2;

それでおしまい!また、さらに計算することなく距離を取得します。

double dist = sqrt((double)(DirX*DirX + DirY*DirY));

ありがとう!GLSLバージョン:vec2 toroidalNearestWay (vec2 from, vec2 to, vec2 mapSize) { return (mod((to - from + 3.0 * mapSize / 2.0), mapSize)) - mapSize / 2.0; }
1j01

0

これを行うにはいくつかの方法があると思います。ここに私が頭の外から考えることができる2があります:

#1:ケースを手動で処理する

発生する可能性のあるケースは正確に10個あります。

  • それはと同じタイルにあります S
  • 周囲の8つのタイルのいずれかにあります
  • まったく見つかりません。

ただし、周囲のタイルのそれぞれについて、XまたはY距離コンポーネントの異なる計算の順列です。ケースの数は有限であるため、ケースの計算方法をハードコーディングし、すべてのケース間の最短距離を見つけることができます。

を見つけるための2つのケースの例を示しdxます。ケース1 Tは、と同じタイルにありS、dxはjust S.x - T.xです。右側のタイルの場合、dxとして計算されTileWidth - S.x + T.xます。

               :         
               :  T    
               :         
:--------------:---------
:              :
:           S  :
:  |--------|--:--|
:dx=(S.x-T.x) dx=(TileWidth-S.x+T.x)
:  T           :
:              :
:--------------:

小さな最適化として、平方根を取る前に最小距離を見つけます。次に、最大7つのsqrtコールを保存します。

#2:座標を抽象化する

パス検索アルゴリズムのように、より空間的に「流体」を行う必要がある場合は、座標を抽象化するだけで、パス検索アルゴリズムは、世界がタイルの繰り返しで作られていることさえ認識しません。経路探索アルゴリズムは、理論的には任意の方向に無限に進む可能性があります(数値制限によって制限されますが、ポイントは得られます)。

単純な距離計算の場合、これを気にしないでください。


平方根を取る前に二乗距離の値を比較することについてのスマートなアイデア!
スコットチェンバレン

ああ、私は感謝、@Kolは、より多くの数学的な説明と同様の答えを持っている、参照これはと私に仕事に何かを与える

二乗距離を比較することは、sqrtを取るよりも賢いかもしれませんが、マンハッタン距離を使用することは、乗算をまったく必要としないため、さらに賢くなります。
サムホセバー

0

「9つの方向」に悩まないでください。その理由は、これらの9のうち5つの縮退したケースが存在するためです:「真北」、「真西」、「真南」、「真東」、「同一」。たとえば、北西と北東が結合して同じ結果を生成する場合を表すため、真っ直ぐな北は縮退しています。

したがって、計算する方向は4つあり、最小値を選択できます。


私はこれが正しいとは思わないか、あなたを完全に誤解しました。2つのうちの1つ。

-1

最終的にすべての回答をありがとう、Scott Chamberlainが編集したToomaiを使用しました。私の座標系は左上がyで始まり、下に移動すると増加するという事実のために、いくつかの変更も行いました(基本的にはyの通常のグラフ座標と比較して反転します)。

他の誰かがこのページを見つけ、同じ逆yシステムを持っている場合に備えて投稿しました。

  int dx = T.X - S.X; // difference in position
int dy = S.Y - T.Y;

if (dx > MapX / 2) // if distance is bigger than half map width, then looping must be closer
    dx = (dx - (MapX / 2)) * -1; // reduce distance by half map width, reverse 
else if (dx < -MapX / 2) // handle the case that dx is negative
    dx = (dx + (MapX / 2)) * -1;

//Do the same for dy
if (dy > MapY / 2)
    dy = (MapY - dy)) * -1;
else if (dy < -MapY / 2)
    dy = (dy + MapY);

double angle = atan2(dy,dx) * 180 / PI; // provides angle in degrees

angle = 180 - angle; //convert to 360 deg

このコードはToomaiのコードよりも若干優れていますが、機能しません。
サムホセバル

1
また、これらの変更を行う必要がある理由を理解する必要があります。それはだではない、あなたの座標系で始まるので、y一番上に。これは、目的の動作がワールドエッジで座標をラップすることになっているのに対し、再利用したコードは各境界で座標をミラーリングしたためです。
サムホセバル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.