Search code examples
iosobjective-csprite-kitpath-findingskphysicsbody

Pathfinding With SKNode's Not Working


I have been developing a tracking algorithm that would have work quite well if sprite kit didn't hit me with this...

I am trying to determine whether or not a CGPoint lies within the SKPhysicsBody of a given SKNode in my scene. The problem I am having is I can not seem to get SpriteKit to accurately tell me if the physicsbody does in fact contain that point.

Here is a sample of one of the bodies that may contain the point, and how I am testing to see if it does:

        SKSpriteNode *innerMap1 = [SKSpriteNode spriteNodeWithImageNamed:@"test"];
        // FYI - "test" is exactly 363x452. Did that to make sure scaling was not an issue
        innerMap1.physicsBody = [SKPhysicsBody bodyWithTexture:[SKTexture textureWithImageNamed:@"test"] size:CGSizeMake(363, 452)];
        innerMap1.position = CGPointMake(200, 100);
        innerMap1.physicsBody.dynamic = NO;
        innerMap1.name = @"testNode";

I then tried 2 different ways to see if the node contains the following point:

Method 1

    CGPoint controlPoint = CGPointMake(<xf>, <yf>);
    if ([innerMap1 containsPoint:controlPoint]) . . .

Method 2

    SKPhysicsBody *body = [self.physicsWorld bodyAtPoint:controlPoint];
    if ([body.node.name isEqualTo:@"testNode"]) . . .

For whatever reason, both methods are not helpful. Method 1 will just tell me if the point is within the frame box of the node, which defeats the purpose of creating a unique node shape from a texture.... and Method 2 for some strange reason tends to be extremely glitchy and inaccurate. I read there is roughly a 15.5px overscale, but the results I am getting are no where near this minimal.

I could also just manually create a CGMutablePathRef and use the following:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint touchLocation = [touch locationInNode:self.scene];

    if (CGPathContainsPoint(pathRef, nil, touchLocation, NO)) {
        // If we are here it means user did touche physic body.
    }
}

but again, I want custom shapes that dont require individual path boundary building.

SO, my question, is what am I doing wrong? I was quite certain that Method 2 would have been the best solution, or at least work, but I can't seem to understand why it acts so strangely. Perhaps I am missing something with one of the 2 approaches that would fix my issues. I wish there was a method that could return a BOOL for points that lay within an SKTexture, but of course there are none. That is why using the physics body should have been the key - as it represents the exact image dimensions and points. Any suggestions will be greatly appreciated. Thanks!


Solution

  • Many time I've found strange issues with CGPathContainsPoint output. So, I've decide to write this workaround:

    P.S. (this solution working on Swift 2.x)

    let path: UIBezierPath = UIBezierPath.init(CGPath: myPath)
    let copyPath = CGPathCreateCopyByStrokingPath(myPath, nil, path.lineWidth, path.lineCapStyle, path.lineJoinStyle, path.miterLimit )
    if CGPathContainsPoint(copyPath, nil, myPoint, false) { 
       // ok path contain this point
    }