簡體   English   中英

A *尋路-如何修改G和H以包括崎terrain的地形移動成本?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM