0

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 the issue.

How might I resolve this issue? Thanks in advance!

Note: I have tried the contents of this post with 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;
    }
Community
  • 1
  • 1
MillerApps
  • 152
  • 1
  • 13

2 Answers2

1

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 as this 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
Community
  • 1
  • 1
Patrick Collins
  • 4,046
  • 3
  • 26
  • 29
1

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 like Texture Packer. One of Texture Packer's features is to allow/disallow image rotation.

For your reference, this is what Apple's docs as 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):

enter image description here

sangony
  • 11,636
  • 4
  • 39
  • 55
  • No luck with moving the call to setup the physics body. – MillerApps Apr 24 '15 at 12:41
  • @TylerMiller47 - This is probably a desperation move but try making your asteroid method an instance method instead of a class method. – sangony Apr 24 '15 at 12:56
  • @TylerMiller47 - Then I suggest you give Texture Packer a try. – sangony Apr 24 '15 at 13:41
  • I'm not too sure how to integrate the outputted files into my project. I tired and only get the placeholder images showing. Maybe I would be better off using bodyWithPolygonFromPath? – MillerApps Apr 24 '15 at 13:43
  • @TylerMiller47 - Whatever you are most comfortable with and works best for you. – sangony Apr 24 '15 at 13:49
  • I got the bodyWithPolygonFromPath working great on both the 6 and 6+, just not on the 5 and 4S. Any ideas? The code is above. And Thank you for all your help so far! – MillerApps Apr 24 '15 at 13:51