Chỉnh sửa: Được rồi, tôi vừa đọc từ một trong những bình luận của bạn rằng bạn không muốn điều hướng tự động. Trong trường hợp đó, chỉ xem xét phần cuối cùng trong bài đăng này ("Đi bộ trên con đường đó") với trường hợp đơn giản là danh sách chỉ lưu trữ nơi chuột của bạn nhấp và tự xóa khi tìm thấy chướng ngại vật. Tôi cũng chỉ nhận thấy rằng bạn đã đề cập đến gạch một lần trong bài viết. Trong trường hợp đó, không cần biểu đồ hiển thị, bản thân lưới có thể được sử dụng để chạy sao A. Dù sao, tôi vẫn đăng bài này như một giải pháp tổng quát hơn cho vấn đề điều hướng nhấp chuột và điểm 2D.
Những gì bạn đang yêu cầu là làm thế nào để tìm đường trên môi trường 2D. Tôi đã viết một bài báo trước khi phác thảo một kỹ thuật có khả năng giải quyết vấn đề đó. Tôi sẽ bắt đầu bằng cách đặt một liên kết đến bài viết, và sau đó thêm một lời giải thích ngắn gọn về thuật toán.
http://www.david-gouveia.com/pathfinding-on-a-2d-poly Cross-map /
Tất nhiên, đây không phải là cách duy nhất để giải quyết nó. Tôi đang sử dụng một biểu đồ khả năng hiển thị. Bạn cũng có thể sử dụng lưới điều hướng. Hoặc một lưới. Biểu đồ khả năng hiển thị có một lợi thế đặc biệt là nó luôn trả về đường đi trực tiếp nhất giữa các điểm mà không cần thực hiện bất kỳ đường thẳng nào. Và xây dựng biểu đồ khả năng hiển thị trên đầu của một đa giác, bạn có thể chỉ định chính xác các vùng có thể đi bộ.
Ý tưởng
Ý tưởng chính ở đây là biểu diễn vùng có thể đi bộ của bạn dưới dạng đa giác và xây dựng biểu đồ khả năng hiển thị bằng các đỉnh lõm của đa giác . Nếu đa giác chứa các lỗ, bạn cũng sẽ sử dụng các đỉnh lồi của chúng .
Tạo một biểu đồ khả năng hiển thị có nghĩa là lấy mọi nút (hoặc đỉnh trong trường hợp này) của biểu đồ và kết nối nó với mọi đỉnh khác mà nó có thể "nhìn thấy". Bạn cần một số kiểm tra tầm nhìn để làm điều này. Cái tôi đã sử dụng được xây dựng dựa trên thử nghiệm giao cắt phân đoạn đơn giản, với một vài kiểm tra được thêm vào.
Sau đó, bất cứ khi nào bạn muốn tìm đường dẫn giữa hai vị trí, bạn tạm thời thêm chúng vào biểu đồ hiển thị và chỉ cần chạy thuật toán tìm đường dẫn A * cổ điển trên đó.
Đây là toàn bộ cấu trúc trông như thế nào:

Ở đâu:
- Các đường màu vàng là polgyon đại diện cho nơi bạn có thể đi bộ.
- Các vòng tròn màu trắng là các đỉnh đa giác tạo nên biểu đồ khả năng hiển thị (các nút)
- Các đường màu tím kết nối các đỉnh nằm trong tầm nhìn của nhau (các cạnh).
- Đường màu xanh nhạt là một ví dụ về việc tìm đường đi giữa hai vị trí (chấm xanh và chấm đỏ).
- Các đường màu lục nhạt là các cạnh tạm thời được thêm vào biểu đồ cùng với các nút bắt đầu và kết thúc trong đường dẫn (chấm xanh lục và chấm đỏ).
Thực hiện
1) Đại diện
Về việc thực hiện điều này, trước tiên bạn cần có một cách để thể hiện một đa giác cho sàn nhà của bạn. Các lớp sau đây là đủ:
public class Polygon
{
public class SimplePolygon
{
List<Vector2> Vertices;
}
List<SimplePolygon> Outlines;
List<SimplePolygon> Holes;
}
2) Chọn nút
Sau đó, bạn cần một cách để đi qua từng đỉnh của đa giác và quyết định xem chúng có phải là các nút trong biểu đồ khả năng hiển thị hay không. Các tiêu chí cho điều đó là các đỉnh lõm trong các phác thảo và các đỉnh lồi trong các lỗ. Tôi sử dụng một chức năng như thế này:
public static bool IsVertexConcave(IList<Vector2> vertices, int vertex)
{
Vector2 current = vertices[vertex];
Vector2 next = vertices[(vertex + 1) % vertices.Count];
Vector2 previous = vertices[vertex == 0 ? vertices.Count - 1 : vertex - 1];
Vector2 left = new Vector2(current.X - previous.X, current.Y - previous.Y);
Vector2 right = new Vector2(next.X - current.X, next.Y - current.Y);
float cross = (left.X * right.Y) - (left.Y * right.X);
return cross < 0;
}
3) Chọn các cạnh
Bây giờ bạn sẽ cần đi qua từng cặp của các đỉnh này và quyết định xem chúng có nằm trong tầm ngắm hay không. Tôi đã sử dụng phương pháp sau đây làm điểm bắt đầu cho kiểm tra đó:
static bool LineSegmentsCross(Vector2 a, Vector2 b, Vector2 c, Vector2 d)
{
float denominator = ((b.X - a.X) * (d.Y - c.Y)) - ((b.Y - a.Y) * (d.X - c.X));
if (denominator == 0)
{
return false;
}
float numerator1 = ((a.Y - c.Y) * (d.X - c.X)) - ((a.X - c.X) * (d.Y - c.Y));
float numerator2 = ((a.Y - c.Y) * (b.X - a.X)) - ((a.X - c.X) * (b.Y - a.Y));
if (numerator1 == 0 || numerator2 == 0)
{
return false;
}
float r = numerator1 / denominator;
float s = numerator2 / denominator;
return (r > 0 && r < 1) && (s > 0 && s < 1);
}
Nhưng phải sử dụng một vài cách hack khác để ổn định cho các trường hợp cạnh, vì vậy nó không phải là điều kiện tốt để đăng. Vẫn đang cố gắng tìm ra một giải pháp sạch và ổn định cho vấn đề này.
4) Xây dựng đồ thị và chạy A-star
Bạn sẽ cần tạo một biểu đồ khả năng hiển thị bằng cách sử dụng các đỉnh và cạnh đó và chạy A * trên đó. Để tìm hiểu cách xây dựng biểu đồ và áp dụng A * Tôi khuyên bạn nên nghiên cứu bài viết sau:
http://bloss.msdn.com/b/ericlippert/archive/2007/10/02/path-finding-USE-a-in-c-3-0.aspx
Sau đó, bạn có thể muốn gói gọn tất cả những thứ này trong một lớp duy nhất, để bạn có một giao diện dễ sử dụng, chẳng hạn như:
public class Floor
{
public Floor(Polygon polygon)
{
_polygon = polygon;
BuildGraph();
}
public IEnumerable<Vector> GetPath(Vector2 start, Vector2 end)
{
// Add start and end as temporary nodes and connect them to the graph
// Run A* on the graph
// Remove temporary nodes and edges
}
private Polygon _polygon;
private Graph _graph;
}
Bằng cách này, bạn chỉ cần tạo một thể hiện Tầng và gọi phương thức GetPath trên đó bất cứ khi nào bạn cần tìm đường dẫn giữa hai vị trí.
5) Đi trên con đường đó
Cuối cùng, bạn cần phải làm cho nhân vật của bạn đi trên con đường được tạo ra. Anh ta cần phải có một số loại bộ nhớ trong cho điều đó, nhưng không quá khó để thực hiện điều đó. Ví dụ:
- Thêm một Danh sách bên trong nhân vật để lưu trữ con đường anh ta hiện đang theo dõi.
- Trên mỗi chu kỳ Cập nhật, lấy giá trị đầu tiên từ danh sách và di chuyển nhân vật của bạn về phía đó.
- Nếu anh ta đến đủ gần đích, hãy xóa phần tử đầu tiên khỏi danh sách và lặp lại.
- Nếu danh sách trở nên trống rỗng, anh ta đã đến đích.