将精灵移动限制为垂直和水平

时间:2012-09-25 20:27:59

标签: ios vector cocos2d-iphone game-physics

我一直在与这个斗争一段时间,我的noob大脑无法解决这个问题。我有一个标准的瓷砖地图,目前使用以下代码在地图上移动我的敌人精灵

-(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| | | | |
 | | | | | | | | | |
 | | | | | | | | | |
 | | | | | | | | | |
 | | | | | | | | | |

2 个答案:

答案 0 :(得分: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);
}

答案 1 :(得分: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数组中的字符的tile坐标路径,例如......

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,从中获取下一个tile坐标,将其转换为像素位置,然后将其分配给我的requestedPosition ivar,然后运行movePlayer:方法(原始帖子中的代码)

为了自己的利益,我在pathfind方法中添加了注释,因此可能不那么全面,但它们可能有助于您理解我的想法。

最后这是我提出的最好的,它允许我确保我可以将我的角色移动到没有限制的像素位置,或者如果我想将它们移动到那里但是将它们限制为仅通过瓦片中心垂直或水平移动。因此,如果你能看到一种方法来优化和优化这些代码,那么任何建设性的批评都会受到赞赏。

编辑:FIXME:在那里突出显示了我还没有时间去看的潜在错误。如果我需要调查和修复,我会重新发布。