7

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.

Community
  • 1
  • 1
Tim Arnold
  • 8,359
  • 8
  • 44
  • 67
  • Well, the one thing you're obviously doing wrong is not posting your code so that someone could check it for any (more or less obvious) issues. ;) I can only tell you that this is the correct way to go about it, and that it works for me (grid graph). – CodeSmile Aug 20 '15 at 11:47
  • I'm having a similar problem, I've asked it here: http://stackoverflow.com/questions/32317628/subclass-of-gkgraphnode-costtonode-method-never-getting-called including the code that I am using – Marcus Aug 31 '15 at 18:30
  • @LearnCocos2D sample code posted! – Tim Arnold Aug 31 '15 at 19:35
  • Do you have any updates? I am interested in this too. – Weston Oct 20 '15 at 22:45
  • Sadly still have not heard back from my dev tech support ticket – Tim Arnold Oct 23 '15 at 20:55
  • 1
    @Weston check latest edit to question, this bug should be fixed in 9.2 beta 2 – Tim Arnold Nov 04 '15 at 15:01
  • Thanks for the update, I was in the process of rolling my own. If you can test in 9.2 and post an answer, I will close my question and mark it as duplicate. – Weston Nov 04 '15 at 18:53
  • @Weston an answer involves simply running the test suite in my [test project](https://github.com/timcamber/GKGraphNode2D-Subclass-Bug) on 9.2 beta 2. The tests pass, so I consider this resolved in that version. – Tim Arnold Nov 04 '15 at 20:13

1 Answers1

3

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.

Tim Arnold
  • 8,359
  • 8
  • 44
  • 67
  • I tested as well, this is confirmed https://www.dropbox.com/s/o5819r51x7yiiaa/MapTest1.mov?dl=0 – Weston Nov 05 '15 at 14:30