[英]A* Pathfinding - how to modify G and H to include rough terrain movement cost?
我在2D游戲中實現了A *尋路,並且在帶有障礙物的普通地圖上效果很好。 現在,我試圖了解如何修改算法,因此它以2步而不是1步計算的是崎terrain的地形(丘陵,森林等)。
在移動成本為1的情況下,該算法在移動成本函數中使用整數10和14。 我對如果一個單元格的移動成本實際為2時如何修改這些值感興趣? 會是20:17嗎?
這是我當前的算法當前計算G和H的方式( 從Ray Wenderleich采納 ):
// Compute the H score from a position to another (from the current position to the final desired position
- (int)computeHScoreFromCoord:(CGPoint)fromCoord toCoord:(CGPoint)toCoord
{
// Here we use the Manhattan method, which calculates the total number of step moved horizontally and vertically to reach the
// final desired step from the current step, ignoring any obstacles that may be in the way
return abs(toCoord.x - fromCoord.x) + abs(toCoord.y - fromCoord.y);
}
// Compute the cost of moving from a step to an adjecent one
- (int)costToMoveFromStep:(ShortestPathStep *)fromStep toAdjacentStep:(ShortestPathStep *)toStep
{
return ((fromStep.position.x != toStep.position.x)
&& (fromStep.position.y != toStep.position.y))
? 14 : 10;
}
我想我明白了,在這行中,教程作者檢查了移動是否與當前正在考慮的移動相距1平方或2平方(對角線)。
return ((fromStep.position.x != toStep.position.x)
&& (fromStep.position.y != toStep.position.y))
? 14 : 10;
不幸的是,這是一個非常簡單的案例,並沒有真正解釋必須做的事情。 數字10用於簡化計算(10 = 1移動成本),而(14 = 1對角移動)是sqrt(10 * 10)的近似值。
我試圖在下面介紹地形成本,這需要額外的信息-我需要知道我要經過哪個單元格才能到達目的地。 事實證明,這確實很煩人,下面的代碼顯然不是我的最佳選擇,但是我試圖闡明每一步的情況。
如果我要進行對角線移動,我需要知道它的移動成本以及可用於到達該位置的2個正方形的移動成本。 然后,我可以選擇兩個正方形中最低的移動成本,並將其插入以下形式的等式中:
moveCost = (int)sqrt(lowestMoveCost*lowestMoveCost + (stepNode.moveCost*10) * (stepNode.moveCost*10));
這是檢查相鄰步驟並使用移動成本在其中創建新步驟的整個循環。 它會在我的地圖數組中找到圖塊,並返回其地形成本。
NSArray *adjSteps = [self walkableAdjacentTilesCoordForTileCoord:currentStep.position];
for (NSValue *v in adjSteps) {
ShortestPathStep *step = [[ShortestPathStep alloc] initWithPosition:[v CGPointValue]];
// Check if the step isn't already in the closed set
if ([self.spClosedSteps containsObject:step]) {
continue; // Ignore it
}
tileIndex = [MapOfTiles tileIndexForCoordinate:step.position];
DLog(@"point (x%.0f y%.0f):%i",step.position.x,step.position.y,tileIndex);
stepNode = [[MapOfTiles sharedInstance] mapTiles] [tileIndex];
// int moveCost = [self costToMoveFromStep:currentStep toAdjacentStep:step];
//in my case 0,0 is bottom left, y points up x points right
if((currentStep.position.x != step.position.x) && (currentStep.position.y != step.position.y))
{
//move one step away - easy, multiply move cost by 10
moveCost = stepNode.moveCost*10;
}else
{
possibleMove1 = 0;
possibleMove2 = 0;
//we are moving diagonally, figure out in which direction
if(step.position.y > currentStep.position.y)
{
//moving up
possibleMove1 = tileIndex + 1;
if(step.position.x > currentStep.position.x)
{
//moving right and up
possibleMove2 = tileIndex + tileCountTall;
}else
{
//moving left and up
possibleMove2 = tileIndex - tileCountTall;
}
}else
{
//moving down
possibleMove1 = tileIndex - 1;
if(step.position.x > currentStep.position.x)
{
//moving right and down
possibleMove2 = tileIndex + tileCountTall;
}else
{
//moving left and down
possibleMove2 = tileIndex - tileCountTall;
}
}
moveNode1 = nil;
moveNode2 = nil;
CGPoint coordinate1 = [MapOfTiles tileCoordForIndex:possibleMove1];
CGPoint coordinate2 = [MapOfTiles tileCoordForIndex:possibleMove2];
if([adjSteps containsObject:[NSValue valueWithCGPoint:coordinate1]])
{
//we know that possible move to reach destination has been deemed walkable, get it's move cost from the map
moveNode1 = [[MapOfTiles sharedInstance] mapTiles] [possibleMove1];
}
if([adjSteps containsObject:[NSValue valueWithCGPoint:coordinate2]])
{
//we know that the second possible move is walkable
moveNode2 = [[MapOfTiles sharedInstance] mapTiles] [possibleMove2];
}
#warning not sure about this one if the algorithm has to backtrack really far back
//find out which square has the lowest move cost
lowestMoveCost = fminf(moveNode1.moveCost, moveNode2.moveCost) * 10;
moveCost = (int)sqrt(lowestMoveCost*lowestMoveCost + (stepNode.moveCost*10) * (stepNode.moveCost*10));
}
// Compute the cost form the current step to that step
// Check if the step is already in the open list
NSUInteger index = [self.spOpenSteps indexOfObject:step];
if (index == NSNotFound) { // Not on the open list, so add it
// Set the current step as the parent
step.parent = currentStep;
// The G score is equal to the parent G score + the cost to move from the parent to it
step.gScore = currentStep.gScore + moveCost;
// Compute the H score which is the estimated movement cost to move from that step to the desired tile coordinate
step.hScore = [self computeHScoreFromCoord:step.position toCoord:toTileCoord];
// Adding it with the function which is preserving the list ordered by F score
[self insertInOpenSteps:step];
}
else { // Already in the open list
step = (self.spOpenSteps)[index]; // To retrieve the old one (which has its scores already computed ;-)
// Check to see if the G score for that step is lower if we use the current step to get there
if ((currentStep.gScore + moveCost) < step.gScore) {
// The G score is equal to the parent G score + the cost to move from the parent to it
step.gScore = currentStep.gScore + moveCost;
// Because the G Score has changed, the F score may have changed too
// So to keep the open list ordered we have to remove the step, and re-insert it with
// the insert function which is preserving the list ordered by F score
// Now we can removing it from the list without be afraid that it can be released
[self.spOpenSteps removeObjectAtIndex:index];
// Re-insert it with the function which is preserving the list ordered by F score
[self insertInOpenSteps:step];
}
}
}
這些類型的問題在芯片布線和游戲開發中很常見。
標准方法是擁有您的圖表(在C ++中,我會說您具有Boost“網格圖”或類似的結構)。 如果您負擔得起每個頂點有一個對象,那么解決方案就很容易。
您可以通過一條邊連接兩個頂點(相鄰或對角相鄰),除非它們之間沒有障礙。 您為該邊緣分配的權重等於邊緣長度(10或14)乘以地形成本。 有時人們不喜歡排除障礙物的邊緣,而是給它們分配極高的權重(這樣做的好處是,即使物體被卡在島上,使用這種方法也可以確保找到至少一條路徑)。
然后應用A *算法。 您的啟發式函數(H)可以是“悲觀的”(等於歐式距離乘以最大移動成本)或“樂觀的”(歐式距離乘以最小移動成本)或介於兩者之間的任何值。 不同的啟發式搜索將導致搜索的“個性”稍有不同,但通常無關緊要。
如果某些邊的移動成本為2,則只需將2加到父節點的G上,而不是1。
至於H:不需要更改。 產生的試探法仍然是可接受的/一致的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.