SpriteKit SKPhysicsBody bodyWithTexture is Upside Down
Asked Answered
F

3

8

Hi I'm trying to fix this bug with spritekit's physics shape appearing upside down.

[SKPhysicsBody bodyWithTexture:monsterTexture size:monsterTexture.size]

The first time the monster appears the phsyics body orientation is correct. But the second time and every time after that the monster appears it's physics body is inverted along the Y axis. See picture where skView.showsPhysics = true; so the physics shapes are displayed. The fact that it works correctly the first time makes me think maybe some property I'm not aware of is being modified or something.

I thought of putting 2 physics blocks together in the rough shape of the boot but that is not ideal as there are some other more complex shapes I want to use bodyWithTexture on that are experiencing the same bug.

I have also tried bodyWithTexture:monsterTexture, the original SKTexture object instead of bodyWithTexture:monster.texture, the Monster object's texture.

physics body upside down

Here is my add monster code. Let me know if you need more.

- (Monster *)monster:(NSDictionary *)settings
{
    NSDictionary * monsterDefaults = [self monsterDefaults];
    NSDictionary * monsterConfig   = [self monsterConfig:settings[TYPE]];
    SKTexture * monsterTexture     = monsterConfig[TEXTURE] ? monsterConfig[TEXTURE] : monsterDefaults[TEXTURE];
    Monster * monster              = [Monster spriteNodeWithTexture:monsterTexture];

    // Animation
    if (monsterConfig[ANIMATION]) {
        [monster runAction:monsterConfig[ANIMATION]];
    }

    // Moster Stats
    monster.name   = MONSTER_SPRITE;
    monster.type   = settings[TYPE];
    monster.points = monsterConfig[POINTS] ? [monsterConfig[POINTS] intValue] : [monsterDefaults[POINTS] intValue];
    monster.damage = monsterConfig[DAMAGE] ? [monsterConfig[DAMAGE] intValue] : [monsterDefaults[DAMAGE] intValue];
    monster.hp     = monsterConfig[HP]     ? [monsterConfig[HP] intValue] : [monsterDefaults[HP] intValue];
    monster.lethal = monsterConfig[LETHAL] ? [monsterConfig[LETHAL] boolValue] : [monsterDefaults[LETHAL] boolValue];

    // Monster Physics
    float physicsResize = monsterConfig[RESIZE] ? [monsterConfig[RESIZE] floatValue] : [monsterDefaults[RESIZE] floatValue];
    switch ([monsterConfig[SHAPE] intValue]) {
        case COMPLEX:
            NSLog(@"%@", monster.texture);
            NSLog(@"rotation: %f", monster.zRotation);
            NSLog(@"x scale: %f", monster.xScale);
            NSLog(@"y scale: %f", monster.yScale);
            monster.physicsBody = [SKPhysicsBody bodyWithTexture:monster.texture size:monster.texture.size];
            break;
        case RECTANGLE:
            monster.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(monster.size.width * physicsResize, monster.size.height * physicsResize)];
            break;
        default:
            monster.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:(monster.size.height * physicsResize) / 2];
            break;
    }

    monster.physicsBody.dynamic             = false;
    monster.physicsBody.affectedByGravity   = false;
    monster.physicsBody.categoryBitMask     = monsterCategory;
    monster.physicsBody.contactTestBitMask  = weaponCategory | heroCategory;
    monster.physicsBody.collisionBitMask    = defaultCategory;

    // Monster Flight Pattern
    SKAction * flightPattern = [self monsterFlightPattern:monster settings:settings];

    // Monster Rotation
    // Rotation disabled for physics text
    // [self monsterRotation:monster rotationConfig:monsterConfig[ROTATION]];

    // Move & Remove
    SKAction * remove = [Config removeAction:monster];
    [monster runAction:[SKAction sequence:@[flightPattern, remove]]];

    return monster;
}

I am caching the texture when the class loads

@property (nonatomic) SKTexture * monsterBootTexture;
...

- (id)initWithFrameSize:(CGSize)frameSize
{
    ...
    SKTextureAtlas * atlas = [SKTextureAtlas atlasNamed:monsterAtlas];
    self.monsterBootTexture = [atlas textureNamed:MONSTER_BOOT];
    ...
}

The NSLog reads as follows:

2015-01-02 12:03:20.619 Gadget Blaster[3301:665394] <SKTexture> 'boot.png' (97 x 100)
2015-01-02 12:03:20.623 Gadget Blaster[3301:665394] <SKTexture> 'boot.png' (97 x 100)

I have added the following logs per LearnCocos2D's comment:

2015-01-03 12:00:06.131 Gadget Blaster[3987:772046] rotation: 0.000000
2015-01-03 12:00:06.133 Gadget Blaster[3987:772046] x scale: 1.000000
2015-01-03 12:00:06.134 Gadget Blaster[3987:772046] y scale: 1.000000
2015-01-03 12:00:08.131 Gadget Blaster[3987:772046] rotation: 0.000000
2015-01-03 12:00:08.131 Gadget Blaster[3987:772046] x scale: 1.000000
2015-01-03 12:00:08.132 Gadget Blaster[3987:772046] y scale: 1.000000
2015-01-03 12:00:10.156 Gadget Blaster[3987:772046] rotation: 0.000000
2015-01-03 12:00:10.156 Gadget Blaster[3987:772046] x scale: 1.000000
2015-01-03 12:00:10.159 Gadget Blaster[3987:772046] y scale: 1.000000

Additionally I'm experiencing some unexpected issues with collisions when using complex physics bodies. SKPhysicsBody bodyWithCircleOfRadius seems to perform much better and I'm considering just making all monsters circle physics shapes.

Fishgig answered 2/1, 2015 at 20:17 Comment(4)
check if the sprite or its parent is either scaled to -1 or rotatedFreed
@LearnCocos2D Looks correct. I have added 3 NSLogs to the method above and added the log output of the first 3 monsters below.Fishgig
I'm having the same problem, only when the scene is reloaded.Fulk
Is this happening again for you in iOS 8.2 Xcode 6.2?Indeclinable
F
0

The solution was to hold a strong reference to the atlas instead of the textures themselves. This also simplified my code sine I'm already preloading all my atlases with [SKTextureAtlas preloadTextureAtlases:textureAtlases withCompletionHandler:^{ ... }]; at the beginning of my scene. This seems to use the same amount of memory (if not less), and it does not produce the upside-down physics body bug anymore. The comments on this question (should I cache textures in properties in sprite kit?) helped me discover this as I was refactoring my code.

 // In Game Scene

 @property (nonatomic, strong) SKTextureAtlas * monsterAtlas;

 ...

 - (id)initWithSize:(CGSize)size
 {
      self.monsterAtlas = [SKTextureAtlas atlasNamed:monsterAtlasName];
      NSArray * textureAtlases = @[self.monsterAtlas];
      [SKTextureAtlas preloadTextureAtlases:textureAtlases withCompletionHandler:^{ ... }];
 }

 // In Monster Class

 - (Monster *)monster:(NSDictionary *)settings
 {
      ...

      SKTextureAtlas * atlas = [SKTextureAtlas atlasNamed:monsterAtlasName];
      NSString * textureName = monsterConfig[TEXTURE] ? monsterConfig[TEXTURE] : monsterDefaults[TEXTURE];
      Monster * monster      = [Monster spriteNodeWithTexture:[atlas textureNamed:textureName]];

      ...
 }
Fishgig answered 23/1, 2015 at 3:58 Comment(1)
This fixed it until xcode 6.2, and now it is broken again.Indeclinable
F
1

I think 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.

Use bodyWithBodies instead. Bummer, I really like this feature.

Fulk answered 9/1, 2015 at 17:43 Comment(2)
seems like most of the new features that came out with iOS8 are fairly wonky still =/Amethyst
Yeah. I was worried about that. Couldn't find any evidence on SO or the web though. Thanks for the answer. I also ran into some issues with removeFromParent when transitioning to iOS8. Maybe a patch will come out.Fishgig
F
0

The solution was to hold a strong reference to the atlas instead of the textures themselves. This also simplified my code sine I'm already preloading all my atlases with [SKTextureAtlas preloadTextureAtlases:textureAtlases withCompletionHandler:^{ ... }]; at the beginning of my scene. This seems to use the same amount of memory (if not less), and it does not produce the upside-down physics body bug anymore. The comments on this question (should I cache textures in properties in sprite kit?) helped me discover this as I was refactoring my code.

 // In Game Scene

 @property (nonatomic, strong) SKTextureAtlas * monsterAtlas;

 ...

 - (id)initWithSize:(CGSize)size
 {
      self.monsterAtlas = [SKTextureAtlas atlasNamed:monsterAtlasName];
      NSArray * textureAtlases = @[self.monsterAtlas];
      [SKTextureAtlas preloadTextureAtlases:textureAtlases withCompletionHandler:^{ ... }];
 }

 // In Monster Class

 - (Monster *)monster:(NSDictionary *)settings
 {
      ...

      SKTextureAtlas * atlas = [SKTextureAtlas atlasNamed:monsterAtlasName];
      NSString * textureName = monsterConfig[TEXTURE] ? monsterConfig[TEXTURE] : monsterDefaults[TEXTURE];
      Monster * monster      = [Monster spriteNodeWithTexture:[atlas textureNamed:textureName]];

      ...
 }
Fishgig answered 23/1, 2015 at 3:58 Comment(1)
This fixed it until xcode 6.2, and now it is broken again.Indeclinable
T
0

I was able to solve this problem in my code by creating an SKPhysicsBody array in my didMoveToView array from the available textures I needed. Instead of recreating the SKPhysicsBody when it needed changed for my given SKSpriteNode, I was able to reference the already created SKPhysicsBody in the array. This kept the texture from being flipped upside down. Hope this approach can help others stuck on this one.

// variable declared in SKScene
var myPhysicsBodies: [SKPhysicsBody]()

// put in didMoveToView
for var i = 0; i <= 6; i++ {        
    self.myPhysicsBodies.append(SKPhysicsBody(texture: self.myTextureFrames[i], size: self.myObject.size))
}

// used when changing physicsBody
self.myObject.physicsBody = self.myPhysicsBodies[self.frameIndex]

UPDATE: This was working all well and good until iOS 9 SDK. I found that my 0th and 6th physics bodies would work but the others would not appear. I was able to extract the texture.plist from device and found that textures 1 through 5 were rotated (ie, textureRotated = YES in the plist). I assume the rotation is used to decrease the overall image size for the atlas. It appears that since the images in the texture atlas are being rotated, this somehow affects the ability to generate a physics body from the texture.

Titmouse answered 30/5, 2015 at 22:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.