Search code examples
iosobjective-csprite-kitgame-physicsgameplay-kit

GameplayKit GKPolygonObstacle Not Working With GKGoal


Similarly to a question I have posted here, I am now realizing that the problem is more trivial that I anticipated, as this works for some elements of GameplayKit but not others..

I have an obstacle, an SKNode, that I am trying to define as a GKPolygonObstacle that can be used by an agent, a GKAgent2D, as an obstacle to avoid when moving in an SKScene I have set up.

I have looked into to Apple's AgentsCatalog to see how they use GKObstacle with an agent in the GameplayKit method:

goalToAvoidObstacles:(nonnull NSArray<GKObstacle *> *) maxPredictionTime:(NSTimeInterval)

When I use the following code in my own project to create GKCircleObstacle objects, I find that the agent quite nicely navigates around and avoids these circular obstacles quite nicely depending on the weight I give it (importance level).

Here is the code that I am using:

NSArray<GKObstacle *> *obstacles2 = @[
    [self addObstacleAtPoint:CGPointMake(CGRectGetMidX(self.frame),
                             CGRectGetMidY(self.frame) + 150)],
    [self addObstacleAtPoint:CGPointMake(CGRectGetMidX(self.frame) - 200,
                             CGRectGetMidY(self.frame) - 150)],
    [self addObstacleAtPoint:CGPointMake(CGRectGetMidX(self.frame) + 200,
                             CGRectGetMidY(self.frame) - 150)], ];
  
enemy.avoidGoal = [GKGoal goalToAvoidObstacles:obstacles2 maxPredictionTime:1];
[enemy.agent.behavior setWeight:100 forGoal:enemy.avoidGoal];

With the following method that creates and adds these obstacles: (this is directly pulled from Apple's source code for the AgentsCatalog)

- (GKObstacle *)addObstacleAtPoint:(CGPoint)point {
    SKShapeNode *circleShape = [SKShapeNode shapeNodeWithCircleOfRadius:50];
    circleShape.lineWidth = 2.5;
    circleShape.fillColor = [SKColor grayColor];
    circleShape.strokeColor = [SKColor redColor];
    circleShape.zPosition = 1;
    circleShape.position = point;
    [self addChild:circleShape];

    GKCircleObstacle *obstacle = [GKCircleObstacle obstacleWithRadius:50];
    obstacle.position = (vector_float2){point.x, point.y};

    return obstacle;
}

This works great as the enemy avoids these circles as it tries to move to a certain changing position in the scene.

ISSUE

When I attempt to recreate this GKGoal behavior by using GKPolygonObstacle objects in place of the GKCircleObstacle objects, the enemy agent cannot seem to identify the polygon obstacles as obstacles to avoid for the behavioral goal. Here is how I am attempting to add these obstacles:

NSArray<GKObstacle *> *obstacles = [SKNode obstaclesFromNodePhysicsBodies:innerMapArray];

// Take this array of GKPolygonObstacle objects and add it 
// to the GKGoal of the enemy as obstacles to avoid

enemy.avoidGoal = [GKGoal goalToAvoidObstacles:obstacles maxPredictionTime:1];
[enemy.agent.behavior setWeight:100 forGoal:enemy.avoidGoal];

What is most frustrating about this, is that I know that the array is correctly creating an NSArray of GKPolygonObstacle objects as I have used this approach for pathfinding as well (before I decided to painfully implement GameplayKit and it's seek, avoid, and wander goals). Here is how I was using this innerMapArray:

- (NSArray *)findPathWithNode:(SKNode *)nodeToFindPath {
    
    NSArray *obstacles = [SKNode obstaclesFromNodePhysicsBodies:innerMapArray];
    GKObstacleGraph *graph = [GKObstacleGraph graphWithObstacles:obstacles bufferRadius:35.0f];


    // Set up enemy and target
    GKGraphNode2D *target = [GKGraphNode2D nodeWithPoint:vector2((float)character.position.x, (float)character.position.y)];
    GKGraphNode2D *enemy = [GKGraphNode2D nodeWithPoint:vector2((float)nodeToFindPath.position.x, (float)nodeToFindPath.position.y)];
    
    [graph connectNodeUsingObstacles:enemy];
    [graph connectNodeUsingObstacles:target];
    
    /// Create tracking path
    NSArray *pathPointsFound = [graph findPathFromNode:enemy toNode:target];

    
    return pathPointsFound;
}

This method quite nicely returns the points at which the most efficient path should include to go around obstacles I am telling the enemy to avoid when trying to reach a location.

  • So the real question becomes: why does the GKGoal accept GKCircleObstacle objects but not GKPolygonObstacle objects?

If someone can help me figure out how to convert these SKNode objects into acceptable obstacles that register with the GKGoal I would be extremely appreciative. Thank you.


Solution

  • This is a very strange / simple answer, and I'm afraid I now have the obstacles being avoided (more than not at least..) by the enemy.

    • Giving the GKGoal for avoiding obstacles a very low maxPredictionTime (t < 10) seems to make the agent ignore the obstacles completely. Any prediction time of 10 or greater + a weight of 100+ seems to cause correct behavior.