가장자리가 감싸 진 세계에서 이동 방향 찾기


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)마다 랩이 하나씩 있습니다.

5
가로 및 세로 줄 바꿈이 모두있는 게임은 기본적으로 대각선 줄 바꿈이 있습니다.

각 좌표가 원에있는 것으로 생각하고 각 좌표에 대해 가능한 두 거리 중 더 짧은 거리를 개별적으로 파악하십시오.
Kerrek 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의 부호에 대한 작업이 필요합니다. 그 외에는 이것이 최상의 솔루션 IMHO입니다.
Scott Chamberlain

바로 그때 내가 고칠 것이다.

1
모든 것을 말하고 완료했을 때 dx 및 dy의 표시가 무엇인지에 대해 여전히 몇 가지 오류가 있습니다. 편집해도 괜찮습니까?
Scott Chamberlain

이것이 왜 받아 들여 질까요? 심지어 작동하지 않습니다 . MapX100, T.X90, S.X10 이라고 가정 dx합니다. 분명히 20이어야하지만이 알고리즘은 30을 반환합니다!
sam hocevar

이런 식으로 코드를 게시하기 전에 테스트 할 기회가 없을 때 발생합니다. 해결됩니다. 누군가이 이것으로 다른 오류를 발견하면 너무 많은 사람들이 오도하기 전에 아마 그것을 삭제할 것입니다.
Toomai

11

이러한 세계에는 S에서 T까지의 경로 수가 무한 합니다. T by (Tx, Ty)좌표, S 좌표 by (Sx, Sy)및 세계 크기를로 표시합니다 (Wx, Wy). T의 래핑 좌표는 (Tx + i * Wx, Ty + j * Wy)어디 i하고 j, 즉, 집합 요소의 정수이다 {..., -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
당신은 그것보다 더 잘 할 수 있습니다 : 그렇다면 abs(Tx-Sx) < Wx/2, i=0최적; 그렇지 않으면의 기호에 따라 최적의 선택은 i=-1또는 i=1입니다 Tx-Sx. 동일은 간다 Ty-Sy하고 j.
Ilmari Karonen

1
이 대답은 그러한 간단한 문제에 대해 엄청나게 복잡합니다. 최소값을 직접 계산할 수있는 경우 선형 검색을 사용할 필요가 없습니다.
sam hocevar

멋진 사진이지만 제안 된 알고리즘은이 답변이받은 공표를받을 자격이 없습니다.
RomanSt

5

가장 짧은 방향이 아닌 경우에도 가능한 한 방향 벡터를 계산 한 다음 X 좌표를 [-MapX/2,MapX/2]범위 내에 있고 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 TSdx 와 동일한 타일에 있으며 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 : 좌표 추상화

경로 찾기 알고리즘과 같이보다 공간적으로 "유체"적인 작업을 수행해야하는 경우 좌표를 추상화하면 경로 찾기 알고리즘이 세계가 반복 타일로 구성되어 있다는 사실조차 깨닫지 못합니다. 경로 찾기 알고리즘은 이론적으로 어떤 방향 으로든 무한히 갈 수 있습니다 (물론 숫자 제한으로 제한되지만 포인트를 얻습니다).

간단한 거리 계산을 위해이 작업을 귀찮게하지 마십시오.


sqrt를 취하기 전에 제곱 거리 값을 비교하는 현명한 아이디어!
Scott Chamberlain

아, 나는 감사 @Kol 좀 더 수학적인 설명과 함께 비슷한 대답을 가지고 볼이 저에게 일에 뭔가를 제공합니다

제곱 거리를 비교하는 것이 sqrt를 취하는 것보다 더 똑똑 할 수 있지만 맨해튼 거리를 사용하는 것은 곱셈을 전혀 필요로하지 않기 때문에 훨씬 더 똑똑합니다.
sam hocevar

0

"9 방향"으로 귀찮게하지 마십시오. 그 이유는 "직선 북쪽", "직선 서쪽", "직선 남쪽", "직선 동쪽"및 "동일한"9 가지 중에서 5 가지 퇴화 사례가 있기 때문이다. 예를 들어, 직선 북쪽은 북서쪽과 북동쪽이 합류하여 동일한 결과를 생성하는 경우를 나타 내기 때문에 축퇴합니다.

따라서 계산할 방향은 4 가지이며 최소값 만 선택할 수 있습니다.


나는 이것이 옳지 않다고 생각하거나 완전히 당신을 오해했습니다. 둘 중 하나.

-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보다 약간 낫지 만 작동하지 않습니다.
sam hocevar

1
또한 이러한 변경을 수행 해야하는 이유 를 이해해야 합니다. 좌표계 가 맨 위에서 시작하기 때문 이 아닙니다y . 재사용하려는 코드 가 각 경계에서 좌표를 미러링 하는 반면 원하는 동작은 월드 가장자리에서 좌표 를 래핑하는 것으로 추정되기 때문 입니다.
sam hocevar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.