集羣尋路問題的一些思考和提煉

以下是不使用引擎內嵌的尋路,自己寫n個單位尋路時可能會用上的尋路手段

a星在大量單位尋路時消耗比較大,

Navmesh和大規模的單位進行尋路時,單位和單位之間的位置表現總是有一些問題

(表現爲太過規律或擠在一起,這篇博文介紹了一些可能有用的優化思路,讓單位移動看起來更舒服以及符合常理的移動表現)

 

問題1

停止一組單位的尋路.n個單位有同一個目的地時,容易出現單位重疊的問題,若用AABB,OBB等碰撞判定去處理性能可能會比較耗

問題1解決方案

可以在單位之間通過消息傳遞系統進行通知,第一個到達目的點的單位,標記爲到達,之後向這個位置移動的單位碰到了這個單位(用數據層的圓形觸發器,不用引擎自帶的的),則第一個單位會通知第二個單位,使第二個單位停止下來,之後依次的n個單位通知停止

 

問題2

單位1移動時被一羣單位擋住的問題

問題2解決方案

可以給每個單位賦予質量和力,單位間相互碰撞時相互施加力,然後這些力的施加可以在一個組內被傳播(消息通知)

效果爲大單位能衝散一羣小的單位,能提升效果表現,因爲只有一些簡單的力判定,猜想性能問題不大

 

問題3

一組單位聚在一個範圍內一起移動,但又不能太聚攏,同時需要動態的避開障礙物,避開障礙後表現上這個組又能合併在一起移動

問題3解決方案

計算這個組的平均中心位置

排除掉離散度最高(距離中心最遠)的一些單位(動態控制這個離散)

再次計算這個組的平均中心位置

控制離散度高的一些單位向這個組的中心靠攏(靠攏需要動態控制,否則可能會出現擠壓內部離散度低的單位的情況)

烘培阻擋信息,點擊地圖尋路時,在單位移動前先記錄下來這個移動方向可能會碰到的障礙物區域範圍(只需要一個大致的範圍,太過具體可能會耗費性能,需暴露參數,用於後續調整),然後根據移動策略繞開這個障礙區域

 

問題4

繞過障礙物

問題4解決方案

如果是每個單位向各個方向發n個射線,那m個單位就是n*m個射線,性能消耗會比較高,以下是2個可能的解決方法

1.預先烘培障礙信息,在障礙物邊緣做標記,從標記點向每個單位發射線,如果是在障礙物左右兩邊進行標記,則每個單位根這個障礙物會有2條射線組成一個有一定夾角的區域,移動時單位可能判定移動方向在這個夾角內,則向偏離這個角度的方向移動,這樣就能繞開障礙,同時在尋路時將要碰到的障礙物的大致範圍框定出一個射線區域。

這個方案有點射線追蹤的感覺

複雜度爲n個標記*m個單位

2.類似下面原文中的一張圖,根據一定的算法,將尋路分爲2段,以障礙物前方定點爲中心,單位當前位置到障礙物前方一定距離的某一點爲第一段,障礙物前方某一點到目的地爲第二段。羣組單位在尋路時,每個單位都向行進方向發一條射線,判斷這條射線是否指向這個夾角內,沒有指向夾角或者偏移過大則糾正方向。

複雜度爲n個單位*2段距離上檢查夾角偏移和糾正方向的消耗

 

---------------------------------------------

以下英文部分

轉自:http://gamasutra.com/blogs/AndrewErridge/20180522/318413/Group_Pathfinding__Movement_in_RTS_Style_Games.php

On and off for the last 5 years I’ve worked to improve grouped unit movement in an RTS-style game called The Maestros. As Dave Pottinger pointed out almost 20 years ago after his work on Age of Empires, the “pathfinding” part of movement gets all the attention, but making dozens of units follow a path intelligently is at least as important and quite difficult. I’d love to tell you about my... journey in this space.

What follows are by no means state of the art solutions. The industry’s had excellent minds on this problem for over two decades, and you and I have little hope of catching up over an afternoon coffee. So let’s focus on the nitty-gritty details of making basic pathfinding look and feel good for players under practical game constraints. A practical knowledge of 3D math is assumed, but a phD in AI is not recommended. I’d probably just upset you, honestly ;)

Goals

In RTS movement, some players want realistic, slow-rotating tanks and squads of infantry hustling together like Company of Heroes. Our game is about executing big plays in quick brawls so our priorities were “responsive over realistic,” and “direct-control over coordinated formations.” Think more Starcraft than Age of Empires.

Where We Started

UDK (Unreal Engine 3’s SDK) supports A* pathfinding in a navmesh-based space, and has pretty effective (if finicky) navmesh generation.  Unfortunately, pathfinding was implemented almost entirely in unreachable engine code which we could not modify in UDK. All in all, if I selected a single unit and right clicked a pathable location, I could expect it to get there eventually.   Both Unity and UE4 have about the same level of support today though tuned a bit better (though UE4 offers better low-level access).  I thought we were pretty well covered with that. Boy, was I wrong.

Problem #1 - Stopping the Group

The next step is moving a group. If I select a few of our Doughboy units and ask them to move to the exact same location, only one of them is going to actually make it there. At best, the others will be adjacent to that one who made it. So how do they know when they’re done moving? Two clicks and we’ve already hit our first issue!

What we came up with was a sort of message-passing system. The first guy who got there was set to have reached the destination, and anybody who touched him and was also trying to get to the same place would consider himself at his destination. Then those guys could pass that message on to anybody who bumped them. We called this “transitive bumping.” This felt pretty clever, and works well for clustered groups, but still has some silly degenerate cases (e.g. if units are in a line).

Problem #2 - Moving Through the Crowd

Another issue we ran into early on was one unit being blocked by another. While UDK’s pathfinding supported creating new obstacles in the navmesh, doing it for a couple hundred units who were constantly changing their location resulted in unplayable performance. Because of this, units were always trying to move through one another instead of around.

Our solve was to allow units to apply a force to one another under certain conditions. This also needed to propagate throughout the group like our stopping messages.

A more natural looking solution might be to tell the unit to move themself out of the way (a la Starcraft 2), and then to move themselves back. In either case, determining the exact states/conditions to “push” another unit was incredibly complex and error-prone. “You can push Allies but not enemies, idle units but not attacking units.” In our case, it took ~10 unique clauses with various levels of nesting to achieve. Yikes! I’d love to find a more generic solve here.

Problem #3 - Staying in Your Lane

After our first public demo of The Maestros at GDC in 2014, I received some feedback from a mentor of mine that the game felt “messy.” Plenty of things contributed to this at the time, but the problem that was most at fault was that even simple, straight-line movements had units jockeying for position along the same path. Nobody would expect a real-life crowd to do that, and certainly not a group of military-trained robots. All of our units were still acting completely independently. When they received a single, common destination from a player’s click and tried to get there on their own fastest route, they’d often choose the same route as the guy next to them. The result was about as graceful as all 8 lanes of the 405 freeway collapsing into one lane instantaneously.

The general solution to this isn’t terribly hard. Calculate a center point for the current group, take the difference of each unit’s position from that center point, and issue a bespoke move command for each unit with their offset from the destination.

For units A, B, & C, and a clicked location (red reticle), offset each destination

 

That worked great for the basic case of moving a unit cluster from one open area to another, but as you’ll begin to learn in this article - most of the “general” feeling solutions have conditions where they break down. The most obvious is if you try to move next to an obstacle. As you can see below, the center point is fine, but unit C would be inside a boulder (gray box).

Another issue was that if your units were spread out and you clicked near the center, you’d expect them to collapse inwards. Using a naive offset, however, they’d generally stay put. Offsetting the destination also fails to meet expectations if your units are too spread out. For example, you’ve all your units in one cluster, but your commander (unit A) was off solo farming 2 screens away. When you issue a move to a point near the center of the cluster, you’d expect all your units, including your commander, to end up generally underneath your cursor (red reticle). In fact, none of them end up under your cursor if you apply offsets naively.

Summarizing many issues in one sentence, “There are situations where some or all units should collapse together, not maintain their offset from the group’s center.” The idea of determining who is in a group or not can sound a bit daunting, and certainly there are some complex clustering algorithms that could be applied here. My solution to this problem ended up being much simpler and has been unexpectedly effective across a huge number of scenarios. Here’s the rundown:

Borrowing language from our code, I calculate a “SmartCenter” for the group

  1. Calculate the average position of all units in the group
  2. Remove any units that aren’t within 1 standard deviation of that average
  3. Recalculate the average position from that subset of the group

If the point we are trying to reach is within a standard deviation of the center point, I use naive independent movement. This guarantees that units will gather shoulder-to-shoulder in a tight cluster, and gives players the kind of direct control of the group shape we’re looking for in The Maestros.

If I don’t have a meaningful “Primary Cluster,” then my units are probably spread out all over the map. In this situation, I just want them to regroup as best they can. Another win for naive independent pathfinding. I detect this situation when the standard deviation for the group is larger than a particular maximum. Ideally, that maximum is relative to the area occupied by the group so I used the sum of all unit’s radii. That’s been reasonably effective.

If I have a “Primary Cluster,” but 1 or more units are more than 1 standard deviation from the group’s center, I collapse them in by giving them a destination in the direction (i.e. normal) of their offset, but only a standard deviation’s length (i.e. magnitude) away from the group’s central destination. This has the effect of “collapsing back in” and feels much more natural.

Problem #4 - Sticking Together

Overall applying relative offsets to each unit’s destination was a huge win for the “cleanliness” of movement within our game when moving in a straight line. Pathing around obstacles was still abysmal though. First, units will take their own shortest path around an obstacle, and don’t always stick together with their group. Second, our 8-lane to 1-lane traffic jam happens all over again at each intermediate point before we reach our destination (see second image).

Not pathing together

Traffic jam on intermediate points

 

I sat on this problem for an embarrassingly long time without a good answer. On day one, I thought to pick 1 unit’s path, and apply the offsets to each intermediate point. This breaks down quickly when you consider that often the reason you’re pathfinding in the first place is that your going tightly around an obstacle. Applying the offsets will leave 50% of your units trying to path into a rock, and naive independent pathfinding will cause a permanent gridlock before you even get near your final destination.

My conceptual answer to this setback wasn’t terribly clever either (depicted below). I’d move the path away from the corner, about one radius width. Determining this mathematically on the other hand proved incredibly elusive to me. How do I determine whether my path is cornering close to an obstacle or far away? If I am close, is the obstacle on my left or my right? On what axis is my left or my right for a given point in my path?

At some point I was going to have to do a raycast to locate obstacle volumes. Perhaps I could try raycasting radially around each point (pictured below)? Unfortunately it was prone to missing the obstacle entirely. The accuracy of this solution scaled directly with the number of raycasts I did per point on the path, and that felt terribly inefficient.

What I really needed was the left-right axis for a given turn. The hypothesis is that the angle of the turn is telling you about where you obstacle likely is. Most of my obstacles where going to be directly inside the “elbow” of my vectors, and occasionally outside it. I hit a breakthrough when I found the axis through the following operations:

  1. Generate the vectors for relative movement between points - For each pathfinding point B, subtract its predecessor, A, to get the vector from A -> B

  2. For each pair of subsequent vectors, A & B, add them to get a vector C that goes from the beginning of A to the end of B
  3. Cross C with an up/down vector to get a vector P, that bisects the area between A & B.

The vector P is the right/left axis for my turn! I check for obstacles on either side and shift my pathfinding point away from the obstacle by a little more than 1 standard deviation. The result goes from a path (green) directly on top of my obstacle, to one comfortably offset from it.

Before

After

Now, I can apply my offsets at the updated points along my path so my group can stick together as they path, and they won’t traffic jam. It doesn’t cover every situation, but in ~90% of cases we can get by without traffic-jamming. The improvement is enormous. Here’s a before & after of going around just one corner.

Before

Before

After

 

Learnings

My biggest learning from doing this is that “generalized” pathfinding algorithms like A* are unlikely to be the whole movement story for your game, especially if you’re trying to coordinate a group’s movement. The second thing I learned is that complexity is truly the enemy here. Pathfinding isn’t hard because the pathfinding algorithms are complex. A tight A* implementation is easily less than a hundred lines of pretty readable code, and is perfectly serviceable for most games. Pathfinding is hard because moving multiple units in real-time and space with one another produces an incredibly large volume of scenarios, and humans have pretty specific expectations of what should happen in many of those scenarios.

擴展參考:

1、http://www.red3d.com/cwr/steer/gdc99/

2、http://onemanmmo.com/index.php?cmd=newsitem&comment=news.1.179.0

3、https://www.researchgate.net/publication/224705309_The_Corridor_Map_Method_Real-Time_High-Quality_Path_Planning

4、https://pdfs.semanticscholar.org/a921/86a365a2e5c407f98efdbd95cf412f91a6e5.pdf

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章