Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`-costToNode:` Not Sent to GKGraphNode2D Subclass in GameplayKit

I have what is perhaps a stupid question about Apple's new GameplayKit.

I am creating a 2D grid-based node layout for my game. I mostly love the functionality of the GKGraphNode2D, but would like to tweak it in one way. I'd like to conditionally add a penalty when traversing a certain kind of node pair. In other words, I want some nodes connected in a straight-forward way, and some nodes connected such that their traversal distance is modified by my app.

I thought subclassing GKGraphNode2D and overriding -costToNode: and -estimatedCostToNode: would work perfectly! I'd return the value supplied by super sometimes, and tweak it for my game at other times. These two methods are methods on GKGraphNode, the superclass of GKGraphdNode2D.

Unfortunately, when I try to do this, it appears that -costToNode: and -estimatedCostToNode: are never called on my GKGraphNode2D subclass. I would expect these methods to be invoked when I call -findPathFromNode:toNode: on my GKGraph object that contains a bunch of my GKGraphNode2D subclass objects.

What am I doing wrong?

Edit:

The following is my code.

Create two classes, CheapNode and ExpensiveNode, that are subclasses of GKGraphNode2D, as follows:

@import UIKit;
@import GameplayKit;

@interface CheapNode : GKGraphNode2D
@end

@implementation CheapNode

- (float)estimatedCostToNode:(GKGraphNode *)node {
    return 1;
}

- (float)costToNode:(GKGraphNode *)node {
    return 1;
}

@end

and

@import UIKit;
@import GameplayKit;

@interface ExpensiveNode : GKGraphNode2D
@end

@implementation ExpensiveNode

- (float)estimatedCostToNode:(GKGraphNode *)node {
    return 100;
}

- (float)costToNode:(GKGraphNode *)node {
    return 100;
}

@end

Then in a test, I've created some nodes, connected them, added them to a graph, and sent the findPathFromNode:toNode: message to the graph. Then, I check the path that the graph finds, and I don't find what I expect.

- (void)testNodeCostsUsed {
    /*
     This is the graph we are creating:

     A---B---C
     |       |
     F---E---D

     where all of the nodes are `CheapNode` objects, except 'B', which is an
     `ExpensiveNode` object.

     This test finds a path from node A to node C.

     If the cost methods in the `CheapNode` and `ExpensiveNode` subclasses
     are used, the route going from A to C around B (down, then right, then up)
     should be used, since the cost of going from B to C is 100, and the cost of
     going from any other node to any other node is 1 
     (see implementation of these classes).

     Instead, we see `GKGraph` choosing the "shortest" route in terms of number
     of nodes, from the top left immediately to the right to the top right.
     */

    CheapNode     *nodeA = [[CheapNode alloc]     initWithPoint:(vector_float2){0, 0}];
    ExpensiveNode *nodeB = [[ExpensiveNode alloc] initWithPoint:(vector_float2){1, 0}];
    CheapNode     *nodeC = [[CheapNode alloc]     initWithPoint:(vector_float2){2, 0}];
    CheapNode     *nodeD = [[CheapNode alloc]     initWithPoint:(vector_float2){2, 1}];
    CheapNode     *nodeE = [[CheapNode alloc]     initWithPoint:(vector_float2){1, 1}];
    CheapNode     *nodeF = [[CheapNode alloc]     initWithPoint:(vector_float2){0, 1}];

    [nodeA addConnectionsToNodes:@[ nodeB, nodeF ] bidirectional:YES];
    [nodeC addConnectionsToNodes:@[ nodeB, nodeD ] bidirectional:YES];
    [nodeE addConnectionsToNodes:@[ nodeF, nodeD ] bidirectional:YES];

    NSArray *allNodes = @[ nodeA, nodeB, nodeC, nodeD, nodeE, nodeF ];

    GKGraph *graph = [GKGraph graphWithNodes:allNodes];
    NSArray *nodes = [graph findPathFromNode:nodeA toNode:nodeC];

    NSArray *expectedPath   = @[ nodeA, nodeF, nodeE, nodeD, nodeC ];
    NSArray *prohibitedPath = @[ nodeA, nodeB, nodeC ];

    XCTAssert([nodes isEqualToArray:expectedPath], @"");
    XCTAssertFalse([nodes isEqualToArray:prohibitedPath], @"");
}

This test case fails. I also notice that estimatedCostToNode: and costToNode: are never sent to my GKGraphNode2D subclasses (as verified by log statements and breakpoints).

Here is a sample project demonstrating this issue. It should build and run on the latest developer beta of Xcode (as of 08/31/2015, Xcode beta 6).

Edit 2:

I have submitted the sample code and the description in this StackOverflow question as a bug (http://www.openradar.me/22524760) if anyone is interested in duping. If the bug persists after iOS 9 is released, I will use a developer support ticket to try to resolve this issue.

Edit 3:

Apple got back to me on my Developer Support Ticket. They admitted this is a bug, and it (as far as I can tell) seems to be fixed in iOS 9.2 beta 2 (7C46t). 🎉

Edit 4:

I have updated the sample project to illustrate another bug with this framework.

like image 223
Tim Arnold Avatar asked Aug 12 '15 20:08

Tim Arnold


1 Answers

Apple responded to my developer support ticket related to this issue and told me it was a known issue, and that there was a potential fix in the iOS SDK 9.2 beta 2, released November 3, 2015. I verified that the tests in my test project pass in 9.2b2, and so I expect that version to resolve this issue.

like image 162
Tim Arnold Avatar answered Sep 20 '22 17:09

Tim Arnold