5

I have created an SKSpriteNode called let's say Map that has an edge path that I have defined (some simple polygon shape).

What I am trying to figure out is how to add several other edge paths that would act as interior edges of the Map. As if the "map" as a whole did in fact have holes. Some sort of inner boundary shapes that could act with Map as a whole: one edge path (as shown below)

enter image description here ©

I understand that there is a method that allows for creating an SKPhysicsBody with bodies (some NSArray), like such

Map.physicsBody = [SKPhysicsBody bodyWithBodies:bodiesArray];

Does this method in fact generate what I have shown in the image? Assuming that the bodiesArray contains 3 SKSpriteNode's each with a defined path from using such method:

+ (SKPhysicsBody *)bodyWithEdgeChainFromPath:(CGPathRef)path

, with creating the path like such

        SKSpriteNode *innerNode1 = [SKSpriteNode spriteNodeWithImageNamed:@"map"];

        CGMutablePathRef innerNode1Path = CGPathCreateMutable();

        CGPathMoveToPoint(mapPath, NULL, 1110, 1110);
        CGPathAddLineToPoint(mapPath, NULL, <some x1>, <some y1>);
        CGPathAddLineToPoint(mapPath, NULL, <some x2>, <some y2>);
        CGPathAddLineToPoint(mapPath, NULL, <some x3>, <some y3>);
        . 
        .
        .
        CGPathCloseSubpath(mapPath);

        innerNode1.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:innerNode1Path];
        [bodiesArray addObject:innerNode1];

        // Repeat for other 2 nodes

I understand that an alternative would be to create 3 separate nodes with the location and shape of the intended "holes", but I am tying to avoid creating more nodes than I need. If anyone can confirm what I am trying to do is correct, or perhaps suggest an alternative that I am unaware of.

NOTE: IF what I am doing is correct but I am missing something, I would appreciate it if someone can show me the correct way to do what I am trying to do (even a simple example of a square with an inner smaller square would be great). Thanks!

EDIT 1: Below is the code snippet that I am using as an attempt to create the "inner boundaries". This issue here, is that while both the outer and inner rect's are drawn and shown, when I add the inner rect to the Map bodyWithBodies, it takes full control of the collision detection, removing all contact control from the outer rect shell. When I remove the bodyWithBodies it goes back to normal with showing both rects, the outer has collision detection (does not allow me to pass through), while the inner one has nothing... so close

// 1 Create large outer shell Map
CGRect mapWithRect = CGRectMake(map.frame.origin.x + offsetX, map.frame.origin.y + offsetY, map.frame.size.width * shrinkage, map.frame.size.height * shrinkage);

self.physicsWorld.gravity = CGVectorMake(0.0, 0.0);
self.physicsWorld.contactDelegate = self;

// 2 Create smaller inner boundary 
CGRect innerRect = CGRectMake(100, 100, 300, 300);
SKPhysicsBody *body = [SKPhysicsBody bodyWithEdgeLoopFromRect:innerRect];
body.categoryBitMask = wallCategory;
NSArray *bodyArray = [NSArray arrayWithObject:body];

// 3 Add bodies to main Map body
myWorld.physicsBody = [SKPhysicsBody bodyWithBodies:bodyArray]; 
myWorld.physicsBody.categoryBitMask = wallCategory;


if ( [[levelDict objectForKey:@"DebugBorder"] boolValue]  == YES) { 
    // This will draw the boundaries for visual reference during testing       
    [self debugPath:mapWithRect];
    [self debugPath:innerRect];
}

EDIT 2 This approach works..by just adding a new node with the same properties as the outer rect:

SKPhysicsBody *innerRectBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:innerRect];
innerRectBody.collisionBitMask = playerCategory;
innerRectBody.categoryBitMask = wallCategory;
SKNode *innerBoundary = [SKNode node];
innerBoundary.physicsBody = innerRectBody;
[myWorld addChild: innerBoundary];

...but I would very much like a cleaner solution that does not require additional nodes..thoughts?

Will Von Ullrich
  • 2,129
  • 2
  • 15
  • 42
  • This may be what you're looking for https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKPhysicsBody_Ref/#//apple_ref/occ/clm/SKPhysicsBody/bodyWithTexture:alphaThreshold:size: – 0x141E Feb 04 '16 at 08:51
  • In my edit I have shown what I have done but (as explained) just not quite. I know I can create a separate `node` that can mimic the performance of the larger outer shell, but again, I'm just trying to avoid having more CPU stress. I figured something like this should be possible given Apples range of usability with SpriteKit.. :( – Will Von Ullrich Feb 04 '16 at 15:50
  • recheck your collision and contact masks – dragoneye Feb 04 '16 at 22:35
  • without changing any of the contact bit masks, when I use the rect path only for the outside, it works fine, but when i had the inner body through `bodyWithBodies` only that inner geometry detects collision – Will Von Ullrich Feb 05 '16 at 03:11

2 Answers2

1

you are doing nothing wrong here i come with an example where i created two edge rect bodies with two physics bodies

//adding bodies after some time using gcd

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self addBodyA];
    });

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self addBodyB];
    });
-(void)addBodyB
{
    SKSpriteNode *node=[SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(20, 20)];
    node.physicsBody=[SKPhysicsBody bodyWithRectangleOfSize:node.frame.size];
    node.position=CGPointMake(550, 420);
    node.physicsBody.restitution=1;
    [self addChild:node];

}
-(void)addBodyA
{
    SKSpriteNode *node=[SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(20, 20)];
    node.physicsBody=[SKPhysicsBody bodyWithRectangleOfSize:node.frame.size];
    node.position=CGPointMake(400, 420);
    node.physicsBody.restitution=1;
    [self addChild:node];

}
-(void)addEdgesBodies
{
    SKAction *r=[SKAction rotateByAngle:1.0/60 duration:1.0/60];

    SKSpriteNode *rect=[SKSpriteNode spriteNodeWithColor:[SKColor clearColor] size:CGSizeMake(300,300)];
    rect.physicsBody=[SKPhysicsBody bodyWithEdgeLoopFromRect:rect.frame];
    rect.position=CGPointMake(500, 400);
    [self addChild:rect];


    //
    SKSpriteNode *rect1=[SKSpriteNode spriteNodeWithColor:[SKColor clearColor] size:CGSizeMake(100,100)];
    rect1.physicsBody=[SKPhysicsBody bodyWithEdgeLoopFromRect:rect1.frame];
    rect1.position=CGPointMake(550, 450);
    [self addChild:rect1];


    [rect1 runAction:[SKAction repeatActionForever:r]];

}
 [self addEdgesBodies];

remember edge bodies comes with low cpu overhead so don't worry about performance untill your polygon don't have so many edges.

dragoneye
  • 703
  • 6
  • 14
  • thank you for the help - I appreciate it. so this approach will actually achieve inner interactive* boundary conditions (as the picture i showed)? Or will this just create this circumstance visually – Will Von Ullrich Feb 04 '16 at 15:22
  • I have created a large rectangle with a smaller inner rectangle but now it seems that the inner rect is the only area to take the boundary conditions, while I can pass through the outer rect now. This was done using bodyWithBodies (my code is above in an edit to my answer) – Will Von Ullrich Feb 04 '16 at 15:43
0

Your code for making a path then using it in a physics body looks like it would work. As well as your physics body from bodies. Unfortunately I do not know SKPhysicsBody's can really support holes because you can not flip the normals of the body. The way I read apples documentation is that it is meant to do things like take two circle shapes and make them into one body, rather then creating a complex shape like that. The problem being that having a hole inside of your bigger shape would mean ignoring collision in that area.

Here are some alternative options

One option is you could build your stages from multiple shapes. For example if you break your map into two pieces (with a line going through each shape) and make physics bodies for those. Then have them overlap and make them into one body then it might work out. I made a diagram showing this (pardon its terrible quality you should still be able to understand what it is doing (hopefully)). enter image description here

Another option would be to make it with a texture, this can hurt preformance a bit but if you can manage it then it probably would work nicely.

Map.physicsBody = SKPhysicsBody(texture: Map.texture, size: Map.size)
J.Doe
  • 1,502
  • 13
  • 47
  • Unfortunately I am in absolute need of independent bodies within the main boundary. I did consider this though as this would be a work around if I can not figure out a clean way of achieving what i need :) – Will Von Ullrich Feb 04 '16 at 15:33