1

私はしばらくの間これと戦ってきました、そして私の初心者の脳はそれを完全に解決することができません。私は標準のタイルマップを持っており、現在次のコードを使用して敵のスプライトをマップ内で移動させています

-(void) movePlayer:(ccTime)deltaTime {

    if (CGPointEqualToPoint(self.position, requestedPosition))
        return;


    float step = kPlayerSpeed * deltaTime;
    float dist = ccpDistance(self.position, requestedPosition);
    CGPoint vectorBetweenAB = ccpSub(self.position, requestedPosition);

    if (dist <= step) {
        self.position = requestedPosition;
        [self popPosition];
    } else {
        CGPoint normVectorBetweenAB = ccpNormalize(vectorBetweenAB);
        CGPoint movementVectorForThisFrame = ccpMult(normVectorBetweenAB, step);

        if (abs(vectorBetweenAB.x) > abs(vectorBetweenAB.y)) {
            if (vectorBetweenAB.x > 0) {
                [self runAnimation:walkLeft];
            } else {
                [self runAnimation:walkRight];
            }
        } else {
            if (vectorBetweenAB.y > 0) {
                [self runAnimation:walkDown];
            } else {
                [self runAnimation:walkUp];
            }
        }

        if (self.position.x > movementVectorForThisFrame.x) {
            movementVectorForThisFrame.x = -movementVectorForThisFrame.x;
        }
        if (self.position.y > movementVectorForThisFrame.y) {
            movementVectorForThisFrame.y = -movementVectorForThisFrame.y;
        }
        self.position = ccpAdd(self.position, movementVectorForThisFrame);
    }

}

movePlayer:は、クラスupdateWithDeltaTime:メソッドによって呼び出されます。ivar requestedPositionはupdateWithDeltaTimeメソッドでも設定され、基本的にキューから次のポイントを取得して移動します。これらのポイントはマップ上のどこにでも配置できるため、敵から対角線方向にある場合、敵のスプライトはそのポイントに直接移動します。しかし、上記のコードを変更して、移動を垂直および水平の移動のみに制限し、敵の移動がマンハッタン距離をとって斜めの経路に沿って「階段」するようにするにはどうすればよいですか(私はそれが呼ばれていると思います)。下の大まかな図に示されているように...Sは始点であり、Fは終点であり、数字はその経路に沿った各中間点であり、階段タイプの斜めの動きを作成します。最後に、この動作のオンとオフを切り替えることができるようにするつもりです。

 | | | | | | | | | |
 | | | | | | | | | |
 | |F| | | | | | | |
 | |5|4| | | | | | |
 | | |3|2| | | | | |
 | | | |1|S| | | | |
 | | | | | | | | | |
 | | | | | | | | | |
 | | | | | | | | | |
 | | | | | | | | | |
4

2 に答える 2

1

ですから、これを行うための本当に簡単な方法があるかどうかはわかりません。私がお勧めするのは、ゴール地点からどれだけ離れているかを計算することです。次に、yの差とxの差を取得します。次に、yの差がxより大きい場合は、スプライトを設定された量だけ上に移動します。xの差が大きい場合は、スプライトを設定された量だけ左に移動します。そうすれば、スプライトはフィニッシュポイントからの差をゆっくりと減少させながら、上下に移動し続けます。それが私がお勧めするロジックです。基本的に:

CGPoint difference = ccpSub(finishPoint, startPoint);
//Be aware that this if statement will not work for negative distances. Only for the example you gave!
if(difference.y>difference.x) {
     sprite.position = ccp(sprite.position.x, sprite.position.y+tileSize);
} else {
     sprite.position = ccp(sprite.position.x+tileSize, sprite.position.y);
}
于 2012-09-26T12:54:40.187 に答える
0

だから私が見つけた現在の解決策は次のとおりです...

-(void)pathfind {
    //Get a reference to the tile map
    CCTMXTiledMap *tileMap = (CCTMXTiledMap *)[[[[CCDirector sharedDirector] runningScene] getChildByTag:kStartLayer] getChildByTag:kMapNode];
    NSAssert(tileMap != nil, @"No Tile map");

    //Convert self.position to tile coordinates and set to both vars. stepTileCoord so that we can add a tile in direction of travel.
    CGPoint selfTileCoord = [tileMap tileCoordFromLocation:self.position];
    CGPoint stepTileCoord = [tileMap tileCoordFromLocation:self.position];

    //Init this var to keep track of the last tile worked out so the next one can be worked out from it. We start at self.position.
    CGPoint lastTileCoord = selfTileCoord;

    //For loop, iterates over each orginal position in the requestedPositionQueue.
    for (int i=0; i < [requestedPositionQueue count]; i++) {

        //Get the current original path tile coordinate.
        CGPoint requestedTileCoord = [[requestedPositionQueue objectAtIndex:i] CGPointValue];
        NSMutableArray *tempPoints = [NSMutableArray array];

        //While loop interates until it has worked out every intermediate step to the original path tile.
        while (!CGPointEqualToPoint(requestedTileCoord, stepTileCoord)) {

            //FIXME: what happens if path takes us back over the orignal start point?
            if (CGPointEqualToPoint(selfTileCoord, requestedTileCoord)) {
                //Not sure if this will cause an issue.
                break;
            }

            CGPoint vectorToDest = ccpSub(lastTileCoord, requestedTileCoord);

            if (abs(vectorToDest.x) > abs(vectorToDest.y)) {
                if (vectorToDest.x > 0) {
                    stepTileCoord.x -= 1;
                } else {
                    stepTileCoord.x += 1;
                }
            } else {
                if (vectorToDest.y > 0) {
                    stepTileCoord.y -= 1;
                } else {
                    stepTileCoord.y += 1;

                }
            }

            //If the tile worked out is the requestedTileCoord then break, no need to add it to tempPoints as it will already be in the requestedPosition Queue.
            if (CGPointEqualToPoint(requestedTileCoord, stepTileCoord)) {
                break;
//                CCLOG(@"%@", [requestedPositionQueue description]);
            } else {
                //Save this tile to tempPoints arrway. And save it to lastTileCoord.
                [tempPoints addObject:[NSValue valueWithCGPoint:stepTileCoord]];
                lastTileCoord = stepTileCoord;
//                CCLOG(@"\n{%f, %f}", lastTileCoord.x, lastTileCoord.y);
            }
        }
        //As we have now got an array, tempPoints, with all the intermediate step points to current point out of requestedPositionQueue we shall add all objects in tempPoints to requestedPositionQueue. NSIndexSet will ensure its added at the correct location in array.
        NSIndexSet *indexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(i, [tempPoints count])];
        [requestedPositionQueue insertObjects:tempPoints atIndexes:indexes];
//        CCLOG(@"%@", [requestedPositionQueue description]);
        //i is updated so the next point pulled out of the array isn't one of the worked out intermediate steps.
        i += [tempPoints count];
    }
}

まず、ivar配列に保存されている文字のタイル座標のパスがあります...

requestedPositionQueue = [NSMutableArray arrayWithObjects:
                                      [NSValue valueWithCGPoint:CGPointMake(7, 18)],
                                      [NSValue valueWithCGPoint:CGPointMake(11, 18)],
                                      [NSValue valueWithCGPoint:CGPointMake(7, 22)],
                                      [NSValue valueWithCGPoint:CGPointMake(7, 18)],
                                      [NSValue valueWithCGPoint:CGPointMake(11, 22)],
                                      nil];

ご覧のとおり、これには斜めの動きの形を必要とするいくつかのポイントがあります。次に、pathfindメソッドを呼び出します。これにより、requestedPositionQueueが繰り返され、すべての中間タイル座標が追加されます。これにより、パス全体が完了するまで、現在のタイルの横にあるタイルが常に垂直または水平に選択されます。

次に、update:メソッドでrequestedPositionArrayを反復処理し、そこから次のタイル座標を取得してピクセル位置に変換し、それをrequestedPosition ivarに割り当ててから、movePlayer:メソッド(元の投稿のコード)を実行します。

私自身の利益のためにpathfindメソッドにコメントを入れたので、それほど包括的ではないかもしれませんが、それらは私の考えの理解に役立つかもしれません。

最後に、これは私が思いついた最高のものであり、制限なしでキャラクターをピクセル位置に移動できるか、そこに移動したいがタイルの中心を介して垂直または水平にのみ移動するように制限することができます。したがって、このコードを改良および最適化する方法を見つけることができる場合は、実行してください。そうしないと、建設的な批判が常に歓迎されます。

編集:FIXME:そこには、私がまだ見る時間がなかった潜在的なバグが浮き彫りになっています。調査し、必要に応じて修正したら、再投稿します。

于 2012-09-27T03:35:00.267 に答える