Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SpriteKit SKPhysicsBody with Inner Edges

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?

like image 710
Will Von Ullrich Avatar asked Nov 09 '22 20:11

Will Von Ullrich


1 Answers

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.

like image 104
dragoneye Avatar answered Nov 14 '22 23:11

dragoneye