我目前正在尝试为当地城镇创建视觉路线规划器界面。 GUI是缺少详细信息的地图,只是简单地显示了带有灰色线条的绿色背景。 代表车辆的另一个图片框是否有可能避免并且无法访问“地图图片框”的绿色区域?
如何使汽车图片框沿着地图图片框遵循某些路线,并且可以通过计算得出?
答案 0 :(得分:0)
我们将分两步进行此操作:
随着您的进步,我会丰富这个答案,以便与您保持联系。
首先,我们将处理数据。路线图基本上是由点(或节点)链接的点(或节点)网络。
这些点具有坐标(因此我们以后可以绘制它们),有时还有其他值,例如名称。它们是道路的每一个起点和终点,有时介于两者之间。为了简单起见,用户只能在点上开始和结束。
顶点有长度,有时还包括诸如“难度”,“最大速度”之类的东西。这些统计信息将确定哪条道路最适合用户的出行。我们还可以确保当用户不想这样做时,算法不会考虑顶点,或者如果我们选择这样做,也可以强迫他穿过特定的顶点。
您会发现我通过在地图的构造函数中进行设置来包含了一个样本地图。我包括了近似坐标和距离。您可以按自己喜欢的任何方式进行更改。这里的数据代表什么:
我在这里使用A *(读取A STAR
)算法。您早些时候提到了Dijkstra的算法,这很好,但是为此,我将坚持使用A *。
这两种方法之间存在差异。如果您对它们进行维基百科,那么您会发现这两张图片,它们确实很好地显示了两者之间的主要区别:
Dijkstra的算法:
A *算法:
如您所见,这两种算法都做得很好。
我评论了代码,但是您可能会对所有这些问题有很多疑问。我会闲逛回答他们。
要操纵此算法,您必须了解以下内容:
difficulty
值使该算法看起来更长。没必要(这就是为什么我到处都将其设置为1)的原因,但是如果您想将一条山路与一条高速公路或速度限制为30 km / h的道路与另一条可以以100 km / h的速度行驶的道路进行比较,您可以调整此值。TestMyMap()
子尝试一些寻路。自己计算以进行比较。更改其他地方的终点和起点。发狂!#Region " Mapping "
Public Class MapPoint
Private _vertexList As New List(Of MapVertex) 'one point can have as many "roads" as you want
Private _xCoord As Decimal 'for now the coordinates are meaningless, but at some point we might want to use them to actually paint the map
Private _yCoord As Decimal
Private _name As String 'just a useful label
Public Parent As MapVertex = Nothing 'useful for backtracking at the end (and get an itinary)
'f is the total "cost" of the the node being analyzed (f = g + h) while doing pathfinding
'g is the distance between the node being analyzed and the starting point while doing pathfinding
'those values serve no other purpose
'h is the estimated minimal distance between the two points while doing pathfinding
'in the Sub 'CalculateH' I'm using the points coordinates and simple trigonometry to estimate h
'technically you can skip the square root part, but I decided against it. There are no dealmaking advantages to my decision.
'it's better to underestimate the minimal distance, that's why a theoretical straight line is a good choice
Public f As Decimal = 0
Public g As Decimal = 0
Public h As Decimal = 0
Public ReadOnly Property VertexList As List(Of MapVertex)
Get
Return _vertexList
End Get
End Property
Public ReadOnly Property X As Decimal
Get
Return _xCoord
End Get
End Property
Public ReadOnly Property Y As Decimal
Get
Return _yCoord
End Get
End Property
Public ReadOnly Property Name As String
Get
Return _name
End Get
End Property
Public Sub New(name As String, xx As Decimal, yy As Decimal, ParamArray vertex() As MapVertex)
_name = name
_xCoord = xx
_yCoord = yy
For Each v As MapVertex In vertex
_vertexList.Add(v)
Next
End Sub
Public Sub AddVertex(vertex As MapVertex)
_vertexList.Add(vertex)
End Sub
Public Sub CalculateH(startingPoint As MapPoint, endPoint As MapPoint)
h = Convert.ToDecimal(Math.Sqrt(Math.Pow((startingPoint.X - endPoint.X), 2) + Math.Pow((startingPoint.Y - endPoint.Y), 2)))
End Sub
Public Sub UpdateFandG(currentNodeG As Decimal, distance As Decimal)
g = currentNodeG + distance
f = g + h
End Sub
Public Sub ResetPathfindingValues()
f = 0
g = 0
h = 0
Parent = Nothing
For Each v As MapVertex In _vertexList
v.ResetPathfindingValues()
Next
End Sub
End Class
Public Class MapVertex
Private _name As String 'just a useful label
Private _length As Decimal 'length of the road. If you have straight roads we can use simple math to get the distances, but curvy roads have their own length, and I'll assume that your roads will be anything
Private _difficulty As Decimal 'if you want to make some roads faster than others, we can use a variable like this one. It could be something else, like "maxSpeed", but for a prototype "difficulty" will do fine
'I suggest making the difficulty a multiplier. It'll be easier to use that way (so a road twice faster would be a '0.5' and a road twice harder to use would be a '2.0')
Public Parent As MapPoint = Nothing 'useful for backtracking at the end (and get an itinary)
'start and end has no meaning here, as both are end points for the line. The important part is that they refer to existing points
'a vertex must ALWAYS be linked to two points (or it makes no sense, you cannot have a line with only one end unless you go full moebius and we're not using enough dimensions for this to make sense
'if you feel like it, you can have a vertex looping from one point and going back to it, but it'll serve no purpose and will never be actually used except by the pathfinding algorithm (which will note it as a bad move every time)
Private _startPoint As MapPoint
Private _endPoint As MapPoint
Public ReadOnly Property Name As String
Get
Return _name
End Get
End Property
'you can play around with difficulty if you like, it'll change which path the algorithm thinks is the better
Public ReadOnly Property AdjustedLength As Decimal
Get
Return _length * _difficulty
End Get
End Property
Public Sub New(name As String, startPoint As MapPoint, endPoint As MapPoint, length As Decimal, difficulty As Decimal)
_name = name
_startPoint = startPoint
_endPoint = endPoint
_length = length
_difficulty = difficulty
'automatically adding this vertex to it's points on creation:
_startPoint.AddVertex(Me)
_endPoint.AddVertex(Me)
End Sub
'this function is so we can get the "other side" of a path from it's start
Public Function GetOtherNode(currentNode As MapPoint) As MapPoint
If _startPoint Is currentNode Then
Return _endPoint
Else
Return _startPoint
End If
End Function
Public Sub ResetPathfindingValues()
Parent = Nothing
End Sub
End Class
Public Class Map
'the Map object is a collection of points.
'those points are linked by vertex
'it could be different depending on what's your purpose
Private _points As New List(Of MapPoint)
Public Function GetPoint(name As String) As MapPoint
For Each p As MapPoint In _points
If p.Name = name Then
Return p
End If
Next
Return Nothing
End Function
Public Sub New()
'You can use this part to build a simple map with a handful of points. For the demonstration that's how we'll work, but later on you can design a mean to load maps from a specific source if you want
'Here's one I threw on the screen at random. If you have one you like better, send me a pastebin and I'll use it instead
'First I create the points:
Dim oldBarn As MapPoint = New MapPoint("Old Barn", 0, 0)
Dim home As MapPoint = New MapPoint("Home", 0, 12)
Dim forest As MapPoint = New MapPoint("Forest", 0, 21)
Dim creepyDeadEnd As MapPoint = New MapPoint("Creepy Dead End", 0, 26)
Dim groceries As MapPoint = New MapPoint("Groceries", 11, 8)
Dim girlfriendsPlace As MapPoint = New MapPoint("Girlfriend's Place", 20, 8)
Dim friendsHome As MapPoint = New MapPoint("Friend's Home", 5, 13)
Dim bar As MapPoint = New MapPoint("The Foo's Bar", 32, 14) 'hehehe
'Second I create the roads between those points (as long as 2 points exist I could create the road, I just decided I would be systematic)
Dim smallRoad As MapVertex = New MapVertex("Small Road", oldBarn, home, 12, 1)
Dim oakStreet As MapVertex = New MapVertex("Oak Street", home, forest, 9, 1)
Dim pathway As MapVertex = New MapVertex("Pathway", forest, creepyDeadEnd, 5, 1)
Dim oldRoad As MapVertex = New MapVertex("Old Road", oldBarn, friendsHome, 17, 1)
Dim arlingtonStreet As MapVertex = New MapVertex("Arlington Street", home, groceries, 7, 1)
Dim fourthStreet As MapVertex = New MapVertex("4th Street", home, girlfriendsPlace, 12, 1)
Dim placeLittlefinger As MapVertex = New MapVertex("Place Littlefinger", groceries, girlfriendsPlace, 9, 1)
Dim cedarCreek As MapVertex = New MapVertex("Cedar Creek", forest, bar, 19, 1)
Dim alley As MapVertex = New MapVertex("Alley", friendsHome, groceries, 7, 1)
Dim mainStreet As MapVertex = New MapVertex("Main Street", groceries, bar, 22, 1)
Dim durnhamRoad As MapVertex = New MapVertex("Durnham Road", friendsHome, bar, 27, 1)
Dim secretRoad As MapVertex = New MapVertex("Secret Road", oldBarn, bar, 61, 1)
'Adding the points to the Map
_points.AddRange({oldBarn, home, forest, creepyDeadEnd, groceries, girlfriendsPlace, friendsHome, bar})
End Sub
'This is an implementation of the A* algorithm, which is a very popular pathfinding algorithm for simple grids/node networks
'You could use Dijkstra's algorithm if you like it better or are a Sapkowsky's fan, but I think A* will do a great job here
Public Function FindTheShortestPath(startingPoint As MapPoint, endPoint As MapPoint) As String
Dim openList As New List(Of MapPoint) 'nodes that are going to be analyzed soon
Dim closedList As New List(Of MapPoint) 'nodes that have been analyzed
'we always start the analysis... from the starting point of course!
openList.Add(startingPoint)
startingPoint.CalculateH(startingPoint, endPoint)
'as long as we haven't found the path to the end point, the algorithm will continue looking
'there are 2 ways to exit this function:
' #1 is finding the endPoint
' #2 is having nowhere left to go and still not finding the end point (which means that it's impossible)
While (openList.Count > 0)
'look for the lowest f in the openList
'this is our current node for the analysis
Dim currentNode As MapPoint = Nothing
For Each p As MapPoint In openList
If currentNode Is Nothing OrElse p.f < currentNode.f Then
currentNode = p
End If
Next
'remove the currentNode from the open list (it's being analyzed) and add to the closedList (no need to come back)
openList.Remove(currentNode)
closedList.Add(currentNode)
'check if we've reached the end node
If currentNode Is endPoint Then
'yay!
'now let's backtrack to get an itinary:
Dim path As String = BackTrack(endPoint)
ResetAllPathfindingVariables()
Return path
End If
'finding which nodes are connected to the Current node
'then analyzing it
For Each vertex As MapVertex In currentNode.VertexList
Dim nextNode As MapPoint = vertex.GetOtherNode(currentNode)
'only work on a node if it's out of the closedList
If Not closedList.Contains(nextNode) Then
'I'm tracking parents to make an itinary when this is finished
nextNode.CalculateH(currentNode, endPoint)
nextNode.UpdateFandG(currentNode.g, vertex.AdjustedLength)
'if it's not yet on the openList, now's time to add it there!
'(I know that a negative check with a 'Else' clause is stupid, but it's easier to read in this context)
If Not openList.Contains(nextNode) Then
openList.Add(nextNode)
nextNode.UpdateFandG(currentNode.g, vertex.AdjustedLength)
nextNode.Parent = vertex
vertex.Parent = currentNode
Else
'if this is a known node but we found a faster path to reach it, update it's parent
If currentNode.g + vertex.AdjustedLength < nextNode.g Then
nextNode.UpdateFandG(currentNode.g, vertex.AdjustedLength)
nextNode.Parent = vertex
vertex.Parent = currentNode
End If
End If
End If
Next
End While
ResetAllPathfindingVariables()
Return "No path was found."
End Function
Private Sub ResetAllPathfindingVariables()
For Each p As MapPoint In _points
p.ResetPathfindingValues()
Next
End Sub
'recursive function to show the path found by the algorithm
Private Function BackTrack(location As Object, Optional path As String = "") As String
If path <> "" Then path = " => " & path
Select Case True
Case TypeOf location Is MapPoint
Dim currentPoint As MapPoint = DirectCast(location, MapPoint)
path = currentPoint.Name & path
If currentPoint.Parent Is Nothing Then
Return path
Else
Return BackTrack(currentPoint.Parent, path)
End If
Case TypeOf location Is MapVertex
Dim currentVertex As MapVertex = DirectCast(location, MapVertex)
path = currentVertex.Name & path
If currentVertex.Parent Is Nothing Then
Return path
Else
Return BackTrack(currentVertex.Parent, path)
End If
End Select
Return ""
End Function
End Class
Private Sub TestMyMap()
_map = New Map()
Dim path As String = _map.FindTheShortestPath(_map.GetPoint("Home"), _map.GetPoint("The Foo's Bar"))
Console.WriteLine(path)
End Sub
#End Region
当然,我会在那里帮助您了解这一部分的内容。很重,我知道。仍然很有趣,尤其是一旦完成,您就可以使用它!