Tìm hướng đi trong một thế giới với các cạnh được bọc


20

Tôi cần tìm hướng khoảng cách ngắn nhất từ ​​một điểm trong thế giới 2D của tôi đến một điểm khác nơi các cạnh được bọc (như các tiểu hành tinh, v.v.). Tôi biết cách tìm khoảng cách ngắn nhất nhưng đang loay hoay tìm hướng đi.

Khoảng cách ngắn nhất được đưa ra bởi:

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));

Ví dụ về thế giới

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

Trong sơ đồ, các cạnh được hiển thị với: và -. Tôi cũng đã thể hiện sự lặp lại của thế giới ở phía trên bên phải. Tôi muốn tìm hướng theo độ từ S đến T. Vì vậy, khoảng cách ngắn nhất là lặp lại trên cùng bên phải của T. nhưng làm thế nào để tôi tính hướng theo hướng từ S đến T lặp lại ở trên cùng phải không?

Tôi biết vị trí của cả S và T nhưng tôi cho rằng tôi cần tìm vị trí của T lặp lại tuy nhiên có nhiều hơn 1.

Hệ tọa độ thế giới bắt đầu từ 0,0 ở phía trên bên trái và 0 độ cho hướng có thể bắt đầu ở phía Tây.

Có vẻ như điều này không quá khó nhưng tôi chưa thể tìm ra giải pháp. Tôi hy vọng ai đó có thể giúp đỡ? Bất kỳ trang web sẽ được đánh giá cao.


Các tọa độ cho T ở trên cùng bên phải là gì?

Tôi chưa bao giờ thấy một trò chơi với gói chéo. Thông thường bạn có một bọc cho mỗi hướng (N, E, S, W).

5
Bất kỳ trò chơi nào có cả gói ngang và dọc đều có gói chéo theo mặc định.

Hãy nghĩ về mỗi tọa độ như sống trên một vòng tròn và tìm ra khoảng cách ngắn hơn trong hai khoảng cách có thể cho từng tọa độ riêng lẻ.
Kerrek SB

1
@crazy: Tra cứu "hình xuyến" trên Wikipedia ...
Kerrek SB

Câu trả lời:


8

Bạn sẽ phải điều chỉnh thuật toán của mình một chút để tính góc - hiện tại bạn chỉ ghi lại sự khác biệt tuyệt đối về vị trí, nhưng bạn cần sự khác biệt tương đối (nghĩa là có thể dương hoặc âm tùy theo định vị).

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
Bạn cần một số công việc về các dấu hiệu của dx và dy vì mã sẽ bị phá vỡ nếu TX nhỏ hơn SX hoặc TY nhỏ hơn XY Ngoài ra đây là giải pháp tốt nhất IMHO.
Scott Chamberlain

Ngay sau đó tôi sẽ sửa nó.

1
Vẫn còn một vài lỗi về dấu hiệu của dx và dy sẽ là gì khi tất cả được nói và thực hiện, nếu tôi chỉnh sửa?
Scott Chamberlain

Tại sao đây là câu trả lời được chấp nhận ?? Nó thậm chí không hoạt động . Giả sử MapXlà 100, T.Xlà 90 và S.Xlà 10. dxnên rõ ràng là 20, nhưng thuật toán này sẽ trả về 30!
sam hocevar

Ugh đây là những gì xảy ra khi bạn không có cơ hội kiểm tra mã trước khi đăng nó. Sẽ sửa chữa. Nếu ai đó tìm thấy một lỗi khác với điều này, có lẽ tôi sẽ xóa nó trước khi có quá nhiều người hiểu lầm.
Toomai

11

Trong một thế giới như vậy, có số đường đi từ S đến T. Hãy biểu thị tọa độ của T theo (Tx, Ty), tọa độ của S theo (Sx, Sy)và kích thước của thế giới theo (Wx, Wy). Các tọa độ được bao bọc của T là (Tx + i * Wx, Ty + j * Wy), ở đâu ijlà các số nguyên, nghĩa là các phần tử của tập hợp {..., -2, -1, 0, 1, 2, ...}. Các vectơ nối S với T là (Dx, Dy) := (Tx + i * Wx - Sx, Ty + j * Wy - Sy). Đối với một (i, j)cặp nhất định , khoảng cách là chiều dài của vectơ sqrt(Dx * Dx + Dy * Dy)và hướng tính theo radian là atan(Dy / Dx). Đường dẫn ngắn nhất là một trong 9 đường dẫn, ở đâu ijđang ở {-1, 0, 1}: nhập mô tả hình ảnh ở đây

Các giá trị ijcho đường dẫn ngắn nhất có thể được xác định trực tiếp:

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;

Cảm ơn bạn, @IlmariKaronen, @SamHocevar và @romkyns vì sự giúp đỡ của bạn!


1
Bạn có thể làm tốt hơn thế: nếu abs(Tx-Sx) < Wx/2, thì i=0là tối ưu; mặt khác, sự lựa chọn tối ưu là i=-1hoặc i=1, tùy thuộc vào dấu hiệu của Tx-Sx. Cùng đi cho Ty-Syj.
Ilmari Karonen

1
Câu trả lời này là vô cùng phức tạp cho một vấn đề đơn giản như vậy. Không cần sử dụng tìm kiếm tuyến tính khi giá trị tối thiểu có thể được tính trực tiếp.
sam hocevar

Hình ảnh đẹp, nhưng thuật toán được đề xuất không xứng đáng với bất kỳ sự ủng hộ nào mà câu trả lời này đã nhận được.
RomanSt

5

Tính toán một vectơ chỉ hướng có thể, ngay cả khi nó không phải là ngắn nhất, sau đó bọc tọa độ X của nó sao cho nó nằm trong [-MapX/2,MapX/2]phạm vi và tương tự cho 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;

Đó là nó! Bạn cũng có được khoảng cách mà không cần tính toán thêm:

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

Cảm ơn! Phiên bản GLSL:vec2 toroidalNearestWay (vec2 from, vec2 to, vec2 mapSize) { return (mod((to - from + 3.0 * mapSize / 2.0), mapSize)) - mapSize / 2.0; }
1j01

0

Tôi đoán có một số cách để làm điều này. Đây là 2 tôi có thể nghĩ ra khỏi đỉnh đầu của tôi:

# 1: Xử lý các trường hợp thủ công

Có chính xác 10 trường hợp có thể xảy ra:

  • Nó nằm trong cùng một ô với S
  • Nó nằm trong bất kỳ 8 ô xung quanh
  • Nó không được tìm thấy ở tất cả.

Đối với mỗi ô xung quanh, chúng là hoán vị của các phép tính khác nhau cho thành phần khoảng cách X hoặc Y. Vì đó là số lượng trường hợp hữu hạn, bạn chỉ có thể mã hóa cách tính toán chúng và tìm khoảng cách ngắn nhất giữa tất cả các trường hợp.

Đây là một minh họa cho 2 trường hợp để tìm kiếm dx. Trường hợp 1, Ttrong cùng một ô như S, dx chỉ S.x - T.x. Đối với gạch bên phải, dxsẽ được tính như TileWidth - S.x + T.x.

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

Là một tối ưu hóa nhỏ, tìm khoảng cách tối thiểu trước khi bạn lấy căn bậc hai. Sau đó, bạn tiết kiệm cho mình tới 7 sqrtcuộc gọi.

# 2: Tóm tắt tọa độ

Nếu bạn cần làm một cái gì đó "lỏng" hơn, như thuật toán tìm đường, chỉ cần trừu tượng tọa độ để thuật toán tìm đường của bạn thậm chí không nhận ra thế giới được tạo ra từ các ô lặp lại. Thuật toán tìm đường có thể đi vô tận theo bất kỳ hướng nào về mặt lý thuyết (cũng tốt thôi, bạn sẽ bị giới hạn bởi các giới hạn số, nhưng bạn có được điểm).

Để tính toán khoảng cách đơn giản, đừng bận tâm làm điều này.


Ý tưởng thông minh về việc so sánh giá trị khoảng cách bình phương trước khi lấy sqrt!
Scott Chamberlain

À tôi hiểu rồi, @Kol có một câu trả lời tương tự với lời giải thích toán học hơn, cảm ơn vì điều này mang lại cho tôi thứ gì đó để làm việc

So sánh khoảng cách bình phương có thể thông minh hơn so với lấy sqrt, nhưng sử dụng khoảng cách Manhattan thậm chí còn thông minh hơn vì nó không yêu cầu nhân lên chút nào.
sam hocevar

0

Đừng bận tâm với "9 hướng". Lý do là có 5 trường hợp thoái hóa trong số 9 trường hợp: "hướng bắc thẳng", "hướng tây", "hướng nam thẳng", "hướng đông" và "giống hệt". Ví dụ, hướng bắc thẳng là suy biến vì nó đại diện cho trường hợp phía tây bắc và đông bắc tham gia và tạo ra kết quả tương tự.

Như vậy, bạn có 4 hướng để tính toán, và chỉ có thể chọn mức tối thiểu.


Tôi không nghĩ rằng điều này là đúng, hoặc tôi đã hoàn toàn hiểu lầm bạn. Một trong hai.

-1

Cảm ơn tất cả các câu trả lời cuối cùng tôi đã sử dụng Toomai do Scott Chamberlain chỉnh sửa. Tôi cũng đã thực hiện một vài thay đổi do hệ thống tọa độ của tôi bắt đầu bằng y ở phía trên bên trái và tăng khi bạn di chuyển xuống (về cơ bản là đảo ngược so với tọa độ biểu đồ bình thường cho y).

Tôi đã đăng trong trường hợp bất kỳ ai khác tìm thấy trang này và có cùng hệ thống 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

Mã này tốt hơn một chút so với Toomai nhưng cũng không hoạt động.
sam hocevar

1
Ngoài ra, bạn cần hiểu lý do tại sao bạn phải thực hiện những thay đổi này. Không phải vì hệ thống tọa độ của bạn bắt đầu yở trên cùng. Đó là vì hành vi mong muốn được cho là bao bọc tọa độ ở rìa thế giới, trong khi mã bạn sử dụng lại nhân đôi tọa độ ở mỗi ranh giới.
sam hocevar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.