SpriteKit bodyWithTexture misaligned

I am trying to implement bodyWithTexture on some of my sprites and it works great for the most part. My issue is that for some reason the physics bodies that are created don't align with the the actual shape of my sprite. I have noticed that it has something to do with the texture atlas Xcode generates, which rotates the sprites in question in order to fit. Here is a image of theissue.

How might I resolve this issue? Thanks in advance!

Note: I have tried the contents of thispostwith no luck. Note 2: I would any code examples in Objective-C.

Update: Here is all the code used to set-up the physics body. As for the textures I'm letting SpriteKit handle that behind the scenes.

@implementation AsteroidNode

+(instancetype)astroidOfType:(AstoridType)type {

      AsteroidNode *astroid;

    if (type == AstoridTypeA) {
        astroid = [self spriteNodeWithImageNamed:@"rock_a"];
        astroid.type = AstoridTypeA;
    } else if (type == AstoridTypeB) {
        astroid = [self spriteNodeWithImageNamed:@"rock_b"];
        astroid.type = AstoridTypeB;
    } else {
        astroid = [self spriteNodeWithImageNamed:@"rock_c"];
        astroid.type = AstoridTypeC;
    }

    astroid.name = @"astroid";
    astroid.zPosition = 1;

    //changes asteroid size for smaller screen sizes
    if ( IS_IPHONE_4_OR_LESS | IS_IPHONE_5) {
        astroid.size = [Utils setNodeSize:astroid.size];
    }
    [astroid setUpPhysicsBody];

    return astroid;
}

-(void)setUpPhysicsBody {   
    self.physicsBody = [SKPhysicsBody bodyWithTexture:self.texture size:self.size];
    self.physicsBody.friction = 0;
    self.physicsBody.linearDamping = 0;
    self.physicsBody.categoryBitMask = CollisionCatAstroid;
    self.physicsBody.collisionBitMask = 0;
    self.physicsBody.contactTestBitMask = CollisionCatShip | CollisionCatBottomEdge;
}
@end

Using bodyWithPolygonFromPath works great on the 6 and 6+ but not on the 5 and 4S.

CGFloat offsetX = self.size.width / 2;
CGFloat offsetY = self.size.height / 2;

if (self.type == AstoridTypeA) {
    CGMutablePathRef path = CGPathCreateMutable();

    CGPathMoveToPoint(path, NULL, 20 - offsetX, 87 - offsetY);
    CGPathAddLineToPoint(path, NULL, 77 - offsetX, 86 - offsetY);
    CGPathAddLineToPoint(path, NULL, 103 - offsetX, 48 - offsetY);
    CGPathAddLineToPoint(path, NULL, 88 - offsetX, 13 - offsetY);
    CGPathAddLineToPoint(path, NULL, 63 - offsetX, 16 - offsetY);
    CGPathAddLineToPoint(path, NULL, 33 - offsetX, 5 - offsetY);
    CGPathAddLineToPoint(path, NULL, 3 - offsetX, 36 - offsetY);

    CGPathCloseSubpath(path);

    self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path];
} else if (self.type == AstoridTypeB) {
    CGMutablePathRef path = CGPathCreateMutable();

    CGPathMoveToPoint(path, NULL, 43 - offsetX, 91 - offsetY);
    CGPathAddLineToPoint(path, NULL, 81 - offsetX, 80 - offsetY);
    CGPathAddLineToPoint(path, NULL, 97 - offsetX, 50 - offsetY);
    CGPathAddLineToPoint(path, NULL, 75 - offsetX, 10 - offsetY);
    CGPathAddLineToPoint(path, NULL, 25 - offsetX, 18 - offsetY);
    CGPathAddLineToPoint(path, NULL, 12 - offsetX, 36 - offsetY);
    CGPathAddLineToPoint(path, NULL, 10 - offsetX, 71 - offsetY);

    CGPathCloseSubpath(path);

    self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path];
} else {
    CGMutablePathRef path = CGPathCreateMutable();

    CGPathMoveToPoint(path, NULL, 41 - offsetX, 48 - offsetY);
    CGPathAddLineToPoint(path, NULL, 55 - offsetX, 32 - offsetY);
    CGPathAddLineToPoint(path, NULL, 40 - offsetX, 10 - offsetY);
    CGPathAddLineToPoint(path, NULL, 24 - offsetX, 12 - offsetY);
    CGPathAddLineToPoint(path, NULL, 11 - offsetX, 23 - offsetY);
    CGPathAddLineToPoint(path, NULL, 17 - offsetX, 43 - offsetY);

    CGPathCloseSubpath(path);
    self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path];
}

I ended up with this, probably an easier way but It works. If anyone has any suggestions let me know.

-(void)setUpPhysicsBody {
        CGFloat offsetX = self.size.width / 2;
        CGFloat offsetY = self.size.height / 2;

        if (self.type == AstoridTypeA) {
            if (IS_IPHONE_4_OR_LESS | IS_IPHONE_5) {
                CGMutablePathRef path = CGPathCreateMutable();

                CGPathMoveToPoint(path, NULL, 17 - offsetX, 72 - offsetY);
                CGPathAddLineToPoint(path, NULL, 64 - offsetX, 71 - offsetY);
                CGPathAddLineToPoint(path, NULL, 85 - offsetX, 40 - offsetY);
                CGPathAddLineToPoint(path, NULL, 73 - offsetX, 10 - offsetY);
                CGPathAddLineToPoint(path, NULL, 52 - offsetX, 13 - offsetY);
                CGPathAddLineToPoint(path, NULL, 27 - offsetX, 4 - offsetY);
                CGPathAddLineToPoint(path, NULL, 2 - offsetX, 30 - offsetY);

                CGPathCloseSubpath(path);

                self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path];
            } else {
                CGMutablePathRef path = CGPathCreateMutable();

                CGPathMoveToPoint(path, NULL, 20 - offsetX, 87 - offsetY);
                CGPathAddLineToPoint(path, NULL, 77 - offsetX, 86 - offsetY);
                CGPathAddLineToPoint(path, NULL, 103 - offsetX, 48 - offsetY);
                CGPathAddLineToPoint(path, NULL, 88 - offsetX, 13 - offsetY);
                CGPathAddLineToPoint(path, NULL, 63 - offsetX, 16 - offsetY);
                CGPathAddLineToPoint(path, NULL, 33 - offsetX, 5 - offsetY);
                CGPathAddLineToPoint(path, NULL, 3 - offsetX, 36 - offsetY);

                CGPathCloseSubpath(path);

                self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path];
            }
          } else if (self.type == AstoridTypeB) {
            if (IS_IPHONE_4_OR_LESS | IS_IPHONE_5) {
                CGMutablePathRef path = CGPathCreateMutable();

                CGPathMoveToPoint(path, NULL, 35 - offsetX, 75 - offsetY);
                CGPathAddLineToPoint(path, NULL, 67 - offsetX, 66 - offsetY);
                CGPathAddLineToPoint(path, NULL, 80 - offsetX, 41 - offsetY);
                CGPathAddLineToPoint(path, NULL, 62 - offsetX, 8 - offsetY);
                CGPathAddLineToPoint(path, NULL, 20 - offsetX, 15 - offsetY);
                CGPathAddLineToPoint(path, NULL, 10 - offsetX, 30 - offsetY);
                CGPathAddLineToPoint(path, NULL, 8 - offsetX, 59 - offsetY);

                CGPathCloseSubpath(path);
                self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path];
            } else {
                CGMutablePathRef path = CGPathCreateMutable();

                CGPathMoveToPoint(path, NULL, 43 - offsetX, 91 - offsetY);
                CGPathAddLineToPoint(path, NULL, 81 - offsetX, 80 - offsetY);
                CGPathAddLineToPoint(path, NULL, 97 - offsetX, 50 - offsetY);
                CGPathAddLineToPoint(path, NULL, 75 - offsetX, 10 - offsetY);
                CGPathAddLineToPoint(path, NULL, 25 - offsetX, 18 - offsetY);
                CGPathAddLineToPoint(path, NULL, 12 - offsetX, 36 - offsetY);
                CGPathAddLineToPoint(path, NULL, 10 - offsetX, 71 - offsetY);

                CGPathCloseSubpath(path);

                self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path];
            }
        } else {

            if (IS_IPHONE_4_OR_LESS | IS_IPHONE_5) {
                CGMutablePathRef path = CGPathCreateMutable();

                CGPathMoveToPoint(path, NULL, 34 - offsetX, 40 - offsetY);
                CGPathAddLineToPoint(path, NULL, 45 - offsetX, 26 - offsetY);
                CGPathAddLineToPoint(path, NULL, 33 - offsetX, 8 - offsetY);
                CGPathAddLineToPoint(path, NULL, 20 - offsetX, 10 - offsetY);
                CGPathAddLineToPoint(path, NULL, 9 - offsetX, 19 - offsetY);
                CGPathAddLineToPoint(path, NULL, 14 - offsetX, 35 - offsetY);

                CGPathCloseSubpath(path);

                self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path]; 
            } else {
                CGMutablePathRef path = CGPathCreateMutable();

                CGPathMoveToPoint(path, NULL, 41 - offsetX, 48 - offsetY);
                CGPathAddLineToPoint(path, NULL, 55 - offsetX, 32 - offsetY);
                CGPathAddLineToPoint(path, NULL, 40 - offsetX, 10 - offsetY);
                CGPathAddLineToPoint(path, NULL, 24 - offsetX, 12 - offsetY);
                CGPathAddLineToPoint(path, NULL, 11 - offsetX, 23 - offsetY);
                CGPathAddLineToPoint(path, NULL, 17 - offsetX, 43 - offsetY);

                CGPathCloseSubpath(path);

                self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path];
            }
        }

        self.physicsBody.friction = 0;
        self.physicsBody.linearDamping = 0;
        self.physicsBody.categoryBitMask = CollisionCatAstroid;
        self.physicsBody.collisionBitMask = 0;
        self.physicsBody.contactTestBitMask = CollisionCatShip | CollisionCatBottomEdge;
    }

You've encountered a sprite-kit bug with the bodyWithTexture method.

The physicsbody is actually upside down, not misaligned or rotated. Therefore it is exactly the same asthis issue.

It is a bug in Spritekit on iOS8. My suspicion is that the bug occurs when the texture for the physicsbody is retrieved from the internal cache. It might be incorrectly flipping the image to try to correct co-ordinate systems from CoreGraphics.

In your case given the shape of your asteroids I recommend this instead:

init!(polygonFromPath path: CGPath!) -> SKPhysicsBody

According to your posted code, you are using an image and not a texture for your sprite.

As for the rotated sprite, SK does optimize needed space to create a texture atlas. This includes image rotation and trimming. I suspect you are adding a physics body before SK sets the rotation back to what it should be. Try adding a physics body after you have returned your asteroid.

If that does not resolve your issue, I recommend creating your own texture atlases using an app likeTexture Packer. One of Texture Packer's features is to allow/disallow image rotation.

For your reference, this is what Apple'sdocsas to say about how SK implements texture atlases:

After the app is built, new folders are created with a .atlasc suffix and placed in your app bundle’s Resource folder.Those new images are automatically rotated and trimmed to fit the maximum number of images into a single file, one whose images and orientation are tracked by a property list (.plist) associated with the folder. You do not need to change your code in order to use the texture atlas feature.

This is the illustration used by Apple (note the rotation):

SpriteKit bodyWithTexture misaligned


What Others Are Reading