■實施上的注意事項php
Notes on Implementationnode
現在您瞭解了基本的方法,當你編寫本身的程序時,有一些額外的事情要考慮。下面給出我用C ++和Blitz Basic編寫的程序,用其餘語言也同樣有效。算法
Now that you understand the basic method, here are some additional things to think about when you are writing your own program. Some of the following materials reference the program I wrote in C++ and Blitz Basic, but the points are equally valid in other languages.安全
1.其餘單元(防止碰撞):若是你碰巧仔細看個人演示代碼,你會注意到它徹底忽略了屏幕上的其餘單元。該單元直接通過對方。根據遊戲,這多是能夠的,或者它可能是不能夠的。若是你想考慮其餘單元的路徑搜索算法,並讓它們彼此走動,另外一個,我建議你只考慮要麼在路徑的計算,處理它們的當前位置爲不能走時中止或相鄰的路徑搜索單元。對於正在相鄰的單元,防止碰撞能夠通過懲罰沿着各自的路徑節點,從而鼓勵單元路徑搜索找到一個替代路徑(在#2描述)。app
1. Other Units (collision avoidance): If you happen to look closely at my example code, you will notice that it completely ignores other units on the screen. The units pass right through each other. Depending on the game, this may be acceptable or it may not. If you want to consider other units in the pathfinding algorithm and have them move around one another, I suggest that you only consider units that are either stopped or adjacent to the pathfinding unit at the time the path is calculated, treating their current locations as unwalkable. For adjacent units that are moving, you can discourage collisions by penalizing nodes that lie along their respective paths, thereby encouraging the pathfinding unit to find an alternate route (described more under #2).less
若是你選擇認爲是移動和不相鄰的尋路單元其餘單元,你將須要開發一種方法來預測他們會在任何給定的時間點,使他們能夠獲得適當的迴避。否則,你最終可能會用異樣的路徑,其中單位鋸齒狀,以免其餘單元,不存在了。ide
If you choose to consider other units that are moving and not adjacent to the pathfinding unit, you will need to develop a method for predicting where they will be at any given point in time so that they can be dodged properly. Otherwise you will probably end up with strange paths where units zig-zag to avoid other units that aren't there anymore.oop
你還會,固然,須要開發一些碰撞檢測代碼,因爲再好的路徑是在它的計算方法時,事情可能隨時間而改變。當發生碰撞時的單位必須要麼計算新的路徑,或者若是其餘單元被移動,它不是一個迎面碰撞,等待其餘單元繼續與當前路徑以前到步驟閃開。佈局
You will also, of course, need to develop some collision detection code because no matter how good the path is at the time it is calculated, things can change over time. When a collision occurs a unit must either calculate a new path or, if the other unit is moving and it is not a head-on collision, wait for the other unit to step out of the way before proceeding with the current path.ui
這些技巧多是足以讓你開始。若是您想了解更多,這裏有一些你可能會發現有用的鏈接:
These tips are probably enough to get you started. If you want to learn more, here are some links that you might find helpful:
角色的轉向行爲:
http://www.red3d.com/cwr/steer/
克雷格雷諾的方向盤上的工做是從路徑搜索有點不一樣,但它能夠與路徑搜索集成,以作出更完整的移動和防撞系統。
Steering Behavior for Autonomous Characters: Craig Reynold's work on steering is a bit different from pathfinding, but it can be integrated with pathfinding to make a more complete movement and collision avoidance system.
計算機遊戲中的長短轉向:
http://ducati.doc.ntu.ac.uk/uksim/uksim%2704/Papers/Simon%20Tomlinson-%2004-20/paper04-20%20CR.pdf
在轉向和路徑搜索文學的一個有趣的調查。這是一個PDF文件。
The Long and Short of Steering in Computer Games: An interesting survey of the literature on steering and pathfinding. This is a pdf file.
協調機組運行:
http://www.gamasutra.com/features/game_design/19990122/movement_01.htm
由帝國時代的設計師戴夫·乍首先在兩部分組成的系列對造成和文章基於組的運動。
Coordinated Unit Movement: First in a two-part series of articles on formation and group-based movement by Age of Empires designer Dave Pottinger.
實現協調運動:
http://www.gamasutra.com/view/feature/3314/implementing_coordinated_movement.php
戴夫·乍的兩部分組成的系列第二。
Implementing Coordinated Movement: Second in Dave Pottinger's two-part series.
2.多樣的地造成本:在本教程中,個人程序隨行,地形僅僅是兩件事情之一 – 能走和不能走。可是,若是你有地形就是能走,但在更高的移動成本?沼澤,丘陵,地下城的樓梯,等等 - 這些都是地形是適合步行的全部例子,但在成本要比平坦,開闊地高。類似地,道路可能具備較低的運行成本比周圍的地形。
2. Variable Terrain Cost: In this tutorial and my accompanying program, terrain is just one of two things – walkable or unwalkable. But what if you have terrain that is walkable, but at a higher movement cost? Swamps, hills, stairs in a dungeon, etc. – these are all examples of terrain that is walkable, but at a higher cost than flat, open ground. Similarly, a road might have a lower movement cost than the surrounding terrain.
這個問題很容易加入,當你計算任何給定節點的G值在地造成本處理。只是獎金的成本添加到這樣的節點。在A *路徑搜索算法已經寫入尋找最低成本路徑,應該很容易地處理這個問題。在簡單的例子中,我描述的,當地勢只是能走和不能走,A *將查找最短,最直接的途徑。可是,在可變成本的地形環境,以最少的成本路徑可能行走了較長的距離 - 就像把周圍的沼澤道路,而不是直接通過它。
This problem is easily handled by adding the terrain cost in when you are calculating the G cost of any given node. Simply add a bonus cost to such nodes. The A* pathfinding algorithm is already written to find the lowest cost path and should handle this easily. In the simple example I described, when terrain is only walkable or unwalkable, A* will look for the shortest, most direct path. But in a variable-cost terrain environment, the least cost path might involve traveling a longer distance – like taking a road around a swamp rather than plowing straight through it.
一個有趣的附加考慮是什麼專業人士稱之爲「影響映射。」正如上述的可變成本的地形,你能夠創建一個額外的積分系統,並將其應用到AI的路徑。試想一下,你有個一堆通過山區保護的單位地圖。每當計算機通過通某人發送的路徑上,它被重擊。若是你願意,你能夠創建一個影響地圖,處罰節點,其中大量的屠殺正在發生。這會教電腦偏好安全的路徑,並幫助它避免愚蠢的狀況下,它不斷通過特定的路徑出兵,只是因爲它是短(但更危險)。
An interesting additional consideration is something the professionals call "influence mapping." Just as with the variable terrain costs described above, you could create an additional point system and apply it to paths for AI purposes. Imagine that you have a map with a bunch of units defending a pass through a mountain region. Every time the computer sends somebody on a path through that pass, it gets whacked. If you wanted, you could create an influence map that penalized nodes where lots of carnage is taking place. This would teach the computer to favor safer paths, and help it avoid dumb situations where it keeps sending troops through a particular path, just because it is shorter (but also more dangerous).
另外一種可能的用途是懲罰沿着附近的移動臺的路徑節點。之一的A *的缺點之一是,當一組的單元的全部嘗試找到的類似位置的路徑,一般有一個顯著程度的重疊,如一個或多個單元試圖利用相同或類似的路由到它們的目的地。添加一個點球已經被其餘單位聲稱「節點將有助於確保必定程度的分離,並減小衝突。不要把這樣的節點做爲不能走,可是因爲你還願意多臺設備才能夠擠過緊通道魚貫,若是有必要的。此外,你應該只處罰的單元,附近的路徑搜索單元,不是全部路徑的路徑,否則你會獲得奇怪的躲避行爲,避免單元的單元,都遠不及他們當時的路徑。此外,你應該只懲罰謊言沿着一條路徑,而不是已經訪問過並留下之前的路徑節點的當前和將來的部分道路節點。
Yet another possible use is penalizing nodes that lie along the paths of nearby moving units. One of the downsides of A* is that when a group of units all try to find paths to a similar location, there is usually a significant amount of overlap, as one or more units try to take the same or similar routes to their destinations. Adding a penalty to nodes already 'claimed' by other units will help ensure a degree of separation, and reduce collisions. Don't treat such nodes as unwalkable, however, because you still want multiple units to be able to squeeze through tight passageways in single file, if necessary. Also, you should only penalize the paths of units that are near the pathfinding unit, not all paths, or you will get strange dodging behavior as units avoid paths of units that are nowhere near them at the time. Also, you should only penalize path nodes that lie along the current and future portion of a path, not previous path nodes that have already been visited and left behind.
3.處理未探索區域:你曾經玩過一款PC遊戲的計算機總是準確的知道路該如何走,即便地圖沒有探索?根據不一樣的遊戲,那樣的路徑搜索太好能夠是不現實的。幸運的是,這是能夠很容易處理的問題。
3. Handling Unexplored Areas: Have you ever played a PC game where the computer always knows exactly what path to take, even though the map hasn't been explored yet? Depending upon the game, pathfinding that is too good can be unrealistic. Fortunately, this is a problem that is can be handled fairly easily.
答案是創建一個獨立的「已知的通過性」陣列的每一個玩家以及電腦對手的(每一個玩家,不是每個單元 - 那將須要更多的計算機內存)。每一個陣列將包含有關該玩家已探索區域的信息,與地圖的其餘部分假設爲適宜步行,直到證明並不是如此。使用這種方法,單位將漫步死角,使類似的錯誤選擇,直到他們發現周圍的路。一旦地圖探索,然而,路徑搜索會正常工做。
The answer is to create a separate "knownWalkability" array for each of the various players and computer opponents (each player, not each unit -- that would require a lot more computer memory). Each array would contain information about the areas that the player has explored, with the rest of the map assumed to be walkable until proven otherwise. Using this approach, units will wander down dead ends and make similar wrong choices until they have learned their way around. Once the map is explored, however, pathfinding would work normally.
4.更平滑的路徑:雖然A *會自動給出最短,成本最低的路徑,它不會自動給出看起來最平滑的路徑。看一看在我們的(圖7)計算的例子最終路徑。在該路徑中,第一個步驟是下面,並開始方格的右側。會不會我們的道路更順暢,若是第一步是正下方的起點,而不是方形的方格?
4. Smoother Paths: While A* will automatically give you the shortest, lowest cost path, it won't automatically give you the smoothest looking path. Take a look at the final path calculated in our example (in Figure 7). On that path, the very first step is below, and to the right of the starting square. Wouldn't our path be smoother if the first step was instead the square directly below the starting square?
有幾種方法來解決這個問題。當你正在計算路徑,你能夠處罰節點那裏有方向的變化,增長了處罰他們的G值扣分。或者,你能夠通過你的路徑運行的計算後,尋找在那裏選擇相鄰節點的地方會給你看起來更好的路徑。欲瞭解更多關於整個問題,請向更加逼真路徑搜索,一個(免費的,但須要註冊)在Gamasutra.com上Macro Pinter的文章。
There are several ways to address this problem. While you are calculating the path you could penalize nodes where there is a change of direction, adding a penalty to their G scores. Alternatively, you could run through your path after it is calculated, looking for places where choosing an adjacent node would give you a path that looks better. For more on the whole issue, check out Toward More Realistic Pathfinding, a (free, but registration required) article at Gamasutra.com by Marco Pinter.
5.非方格搜索區域:在我們的例子中,我們使用了一個簡單的二維方格佈局。你並不須要使用這種方法。你可使用不規則的形狀區域。想一想棋盤遊戲的風險,以及國家在那場比賽。你能夠設計一個路徑搜索方案進行一場比賽那樣。要作到這一點,你須要創建一個表,用於存儲毗鄰的國家,並與移動從一個國家到下一個相關的G值。你還須要拿出用於估計H.其餘一切會被處理一樣在上面的例子中的方法。而不是使用相鄰的方格,你會簡單地查找相鄰國家在表中增長新的項目到開啓列表時。
5. Non-square Search Areas: In our example, we used a simple 2D square layout. You don't need to use this approach. You could use irregularly shaped areas. Think of the board game Risk, and the countries in that game. You could devise a pathfinding scenario for a game like that. To do this, you would need to create a table for storing which countries are adjacent to which, and a G cost associated with moving from one country to the next. You would also need to come up with a method for estimating H. Everything else would be handled the same as in the above example. Instead of using adjacent squares, you would simply look up the adjacent countries in the table when adding new items to your open list.
同樣,你能夠創建一個固定的地形圖路徑的航點系統。航點一般走過的路徑上的點,也許在一個地牢道路或隧道的關鍵。做爲遊戲設計者,你能預先指定這些路點。兩個航點會被認爲是「相鄰」彼此是否有它們之間的直線路徑上沒有障礙。因爲在風險的例子,您將節省在某種類型的查找表這個鄰接信息,並用它生成新的開啓列表項目時。那麼你會(可能通過使用節點間的直線距離)和H成本(可能使用從節點到目標的直線距離)記錄相關的G值。一切將繼續如常。
Similarly, you could create a waypoint system for paths on a fixed terrain map. Waypoints are commonly traversed points on a path, perhaps on a road or key tunnel in a dungeon. As the game designer, you could pre-assign these waypoints. Two waypoints would be considered "adjacent" to one another if there were no obstacles on the direct line path between them. As in the Risk example, you would save this adjacency information in a lookup table of some kind and use it when generating your new open list items. You would then record the associated G costs (perhaps by using the direct line distance between the nodes) and H costs (perhaps using a direct line distance from the node to the goal). Everything else would proceed as usual.
阿米特Patel還寫了一個簡短的文章鑽研一些替代品。對於使用非方形的搜索區域上等距RPG地圖搜索的另外一個例子,看看個人文章兩個層次的A *路徑搜索。
Amit Patel has written a brief article delving into some alternatives. For another example of searching on an isometric RPG map using a non-square search area, check out my article Two-Tiered A* Pathfinding.
6.一些超速提示:當你開發本身的A *程序,或者改編我寫的,你最終會發現路徑搜索使用你的CPU時間大幅大塊,特別是若是你對路徑搜索的一臺像樣的數目板和一個至關大的地圖。若是你在網上讀過的東西了,你會發現,這是真實的,即便誰設計像星際爭霸或帝國時代遊戲的專業人士。若是你看到的東西開始放緩,因爲路徑搜索,這裏有一些想法,可能會加快速度:
6. Some Speed Tips: As you develop your own A* program, or adapt the one I wrote, you will eventually find that pathfinding is using a hefty chunk of your CPU time, particularly if you have a decent number of pathfinding units on the board and a reasonably large map. If you read the stuff on the net, you will find that this is true even for the professionals who design games like Starcraft or Age of Empires. If you see things start to slow down due to pathfinding, here are some ideas that may speed things up:
■考慮一個小地圖或更少的單位。
Consider a smaller map or fewer units.
永遠不要作路徑搜索以上幾個單元的時間。相反,把它們放在一個隊列,它們分佈在幾個遊戲循環。若是你的遊戲時,好比說,每秒40個週期運行,沒有人會注意到。但他們會發現,若是遊戲彷佛在一段時間放慢每一次當一束單位都計算路徑在同一時間。
Never do path finding for more than a few units at a time. Instead put them in a queue and spread them out over several game cycles. If your game is running at, say, 40 cycles per second, no one will ever notice. But they will notice if the game seems to slow down every once in a while when a bunch of units are all calculating paths at the same time.
請考慮使用更大的方格(或者任何你正在使用的形狀)爲您的地圖。這減小了搜索以找到的路徑的節點的總數。若是你有雄心,能夠設計出了用於在不一樣的狀況下,這取決於路徑的長度的兩個或更多個路徑搜索系統。這是專業人士作的,使用大面積的長路徑,而後切換到更精細的使用較小的方格/地區搜索,當你接近目標。若是你有興趣在這個概念,看看個人文章兩個層次的A *路徑搜索。
Consider using larger squares (or whatever shape you are using) for your map. This reduces the total number of nodes searched to find the path. If you are ambitious, you can devise two or more pathfinding systems that are used in different situations, depending upon the length of the path. This is what the professionals do, using large areas for long paths, and then switching to finer searches using smaller squares/areas when you get close to the target. If you are interested in this concept, check out my article Two-Tiered A* Pathfinding.
對於更長的路徑,考慮修訂是硬連接到遊戲預先計算好的路徑。
For longer paths, consider devising precalculated paths that are hardwired into the game.
考慮預處理地圖找出哪些領域是從地圖的其他部分沒法訪問。我把這些領域的「孤島」。在現實中,他們能夠是島嶼或者其餘任何地區,是另有圍牆關閉,沒法訪問。其中A *的缺點之一是,若是你告訴它來尋找路徑等方面,它會搜索整個地圖,停車,只有當每平方訪問/節點已通過打開和關閉名單處理。這會浪費大量的CPU時間。它能夠通過預先肯定哪些地區是不可訪問(通過洪水填充或類似的程序),記錄在某種類型的陣列信息,而後在開始路徑搜索前檢查它來預防。
Consider pre-processing your map to figure out what areas are inaccessible from the rest of the map. I call these areas "islands." In reality, they can be islands or any other area that is otherwise walled off and inaccessible. One of the downsides of A* is that if you tell it to look for paths to such areas, it will search the whole map, stopping only when every accessible square/node has been processed through the open and closed lists. That can waste a lot of CPU time. It can be prevented by predetermining which areas are inaccessible (via a flood-fill or similar routine), recording that information in an array of some kind, and then checking it before beginning a path search.
在擁擠的,迷宮似的環境中,考慮節點標記不隨地導致的死角。這些區域能夠手動預先指定的地圖編輯器,或者若是你有雄心的,你能夠開發一個算法,自動識別等領域。在給定的死衚衕區域節點的任何集合能夠賦予一個惟一的識別號碼。而後路徑搜索時,只停下來考慮一個死衚衕區域節點,若是起始位置或目的地剛好是在特定的死衚衕區問題,你能夠放心地忽略全部的死角。
In a crowded, maze-like environment, consider tagging nodes that don't lead anywhere as dead ends. Such areas can be manually pre-designated in your map editor or, if you are ambitious, you could develop an algorithm to identify such areas automatically. Any collection of nodes in a given dead end area could be given a unique identifying number. Then you could safely ignore all dead ends when pathfinding, pausing only to consider nodes in a dead end area if the starting location or destination happen to be in the particular dead end area in question.
7.維護開啓列表:這實際上是A *路徑搜索算法中最耗費時間的元素之一。您能夠訪問開啓列表時,都須要找到具備最小F值的方格。有幾種方法能夠作到這一點。根據須要,你能夠保存路徑項目,每次當你須要找到最小F值的方格時,簡單的遍歷整個列表。這是簡單的,但對於長路徑很慢。這能夠通過維護一個排序的列表,每次須要最小F-成本方形時間只需抓住了第一個項目從名單獲得改善。當我寫個人程序,這是我用第一種方法。
7. Maintaining the Open List: This is actually one of the most time consuming elements of the A* pathfinding algorithm. Every time you access the open list, you need to find the square that has the lowest F cost. There are several ways you could do this. You could save the path items as needed, and simply go through the whole list each time you need to find the lowest F cost square. This is simple, but really slow for long paths. This can be improved by maintaining a sorted list and simply grabbing the first item off the list every time you need the lowest F-cost square. When I wrote my program, this was the first method I used.
這將工做得至關好爲小地圖,但它不是最快答案。嚴重的A *程序員誰想要真正的速度使用一種叫作二進制堆,這是我在個人代碼中使用。在個人經驗,這種方法將是至少2-3倍的速度在大多數狀況下,而且在幾何形狀更快(快10+次)上較長的路徑。若是你主動去尋找更多關於二叉堆,看看個人文章,在A *路徑搜索使用二進制堆。
This will work reasonably well for small maps, but it isn't the fastest solution. Serious A* programmers who want real speed use something called a binary heap, and this is what I use in my code. In my experience, this approach will be at least 2-3 times as fast in most situations, and geometrically faster (10+ times as fast) on longer paths. If you are motivated to find out more about binary heaps, check out my article, Using Binary Heaps in A* Pathfinding.
另外一種可能的瓶頸是你的方式明確和維護路徑搜索調用之間的數據結構。我我的更喜歡存儲全部陣列。雖然節點能夠生成,記錄並保存在一個動態的,面向對象的方式,我發現,創建和刪除這些對象所需的時間量增長了額外的開銷,沒必要要的水平會減慢速度。若是你使用數組,不過,你須要調用之間幹淨的東西了。你會想在這種狀況下的最後一件事就是花零時間作完一切了在調用路徑搜索後,特別是若是你有一個大的地圖。
Another possible bottleneck is the way you clear and maintain your data structures between pathfinding calls. I personally prefer to store everything in arrays. While nodes can be generated, recorded and maintained in a dynamic, object-oriented manner, I find that the amount of time needed to create and delete such objects adds an extra, unnecessary level of overhead that slows things down. If you use arrays, however, you will need to clean things up between calls. The last thing you will want to do in such cases is spend time zero-ing everything out after a pathfinding call, especially if you have a large map.
我避免這種開銷通過創建一個二維數組稱爲whichList(X,Y),其指定在每一個節點上個人地圖做爲任一開啓列表或關閉列表上。路徑搜索的嘗試以後,我不歸零數組。相反,我在每個路徑搜索呼叫復位onClosedList和onOpenList的價值觀,每一個路徑嘗試尋找類似+5什麼都遞增。通過這種方式,算法能夠放心地忽略垃圾從之前的路徑搜索的嘗試遺留任何數據。我也喜歡存放F,G和H陣列的成本值。在這種狀況下,我只是寫在任何預先存在的價值和不打擾清除陣列時,我作的。
I avoid this overhead by creating a 2d array called whichList(x,y) that designates each node on my map as either on the open list or closed list. After pathfinding attempts, I do not zero out this array. Instead I reset the values of onClosedList and onOpenList in every pathfinding call, incrementing both by +5 or something similar on each path finding attempt. This way, the algorithm can safely ignore as garbage any data left over from previous pathfinding attempts. I also store values like F, G and H costs in arrays. In this case, I simply write over any pre-existing values and don't bother clearing the arrays when I'm done.
在多個陣列存儲數據佔用更多的內存,雖然如此,有一個權衡。最終,你應該使用什麼方法,你是最舒服的。
Storing data in multiple arrays consumes more memory, though, so there is a trade off. Ultimately, you should use whatever method you are most comfortable with.
8. Dijkstra的算法:當A *一般被認爲是最好的路徑搜索算法(見上面的小咆哮),存在至少一個其它的算法有其用途 - Dijkstra算法。 Dijkstra的是基本相同的A *,除了沒有啓發式(H始終爲0)。因爲它沒有啓發式,它通過在每個方向同樣擴大了搜索。正如你可能想象的,因爲這Dijkstra算法一般是結束了探索一個更大的區域以前目標被發現。這一般使得它比A *慢。
8. Dijkstra's Algorithm: While A* is generally considered to be the best pathfinding algorithm (see rant above), there is at least one other algorithm that has its uses - Dijkstra's algorithm. Dijkstra's is essentially the same as A*, except there is no heuristic (H is always 0). Because it has no heuristic, it searches by expanding out equally in every direction. As you might imagine, because of this Dijkstra's usually ends up exploring a much larger area before the target is found. This generally makes it slower than A*.
那麼,爲什麼使用它?有時候,我們不知道我們的目標位置是。假設你有一個須要去獲得某種資源的一些資源收集裝置。它可能知道幾個資源區域,但它但願去最近的一個。在這裏,Dijkstra的比A *更好,因爲我們不知道哪個是最接近的。我們惟一的選擇是重複使用A *查找到每個的距離,而後選擇這條道路。可能有無數類似的狀況,我們知道那種位置,我們可能會尋找的,想找到最近的一個,但不知道它在哪裏或哪個多是最接近的。
So why use it? Sometimes we don't know where our target destination is. Say you have a resource-gathering unit that needs to go get some resources of some kind. It may know where several resource areas are, but it wants to go to the closest one. Here, Dijkstra's is better than A* because we don't know which one is closest. Our only alternative is to repeatedly use A* to find the distance to each one, and then choose that path. There are probably countless similar situations where we know the kind of location we might be searching for, want to find the closest one, but not know where it is or which one might be closest.
(待續)