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