Search code examples
sprite-kitios7.1skphysicsbodyxscale

Flipped x-scale breaks collision handling (SpriteKit 7.1)


I use SKNode's xScale property to flip my sprites horizontally. Now, after updating iOS to version 7.1 horizontal flip causes my objects to sink inside the ground. (See animation below). The problem occurs only with xScale property. Vertical flips work fine.

// Init
{
    SKSpriteNode* ground = [SKSpriteNode spriteNodeWithColor:[UIColor blackColor] size:CGSizeMake(winSize.width, 150)];
    ground.position = CGPointMake(winSize.width/2, ground.size.height/2);
    ground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:ground.size center:CGPointZero];
    ground.physicsBody.dynamic = NO;
    ground.physicsBody.categoryBitMask = 0x01;
    ground.physicsBody.collisionBitMask = 0x02;
    ground.physicsBody.contactTestBitMask = 0x02;
    [self.world addChild:ground];

    SKSpriteNode* turtle = [SKSpriteNode spriteNodeWithImageNamed:@"turtle.png"];
    turtle.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:turtle.size.width/2];
    turtle.physicsBody.categoryBitMask = 0x02;
    turtle.physicsBody.collisionBitMask = 0x01;
    turtle.physicsBody.contactTestBitMask = 0x01;
    turtle.position = CGPointMake(winSize.width/2, winSize.height/2);
    [self.world addChild:turtle];
    self.turtle = turtle;
}

// Somewhere else
{
    self.turtle.xScale *= -1;
}

x-scale flip problem


Solution

  • I'm convinced this is a bug in SpriteKit.

    Anyway, here is one solution for the problem (Actually, this is more a workaround than a real solution but...): Wrap the sprite in a container node. Also, container node holds the physicsBody while the child node is merely a graphics node. This way you can safely flip the sprite using xScale without affecting the physics of the node.

    // Init
    {
        SKSpriteNode* turtleSprite = [SKSpriteNode spriteNodeWithImageNamed:@"turtle.png"];
        self.turtleSprite = turtleSprite;
    
        SKNode* turtleWrapper = [SKNode node]; 
        turtleWrapper.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:turtleSprite.size.width/2];
        turtleWrapper.physicsBody.categoryBitMask = 2;
        turtleWrapper.physicsBody.collisionBitMask = 1;
        turtleWrapper.physicsBody.contactTestBitMask = 1;
    
        [turtleWrapper addChild:turtleSprite];
        [self.world addChild:turtleWrapper];
    }
    
    // Elsewhere
    {
        self.turtleSprite.xScale *= -1;
    }