Flipped x-scale breaks collision handling (SpriteKit 7.1)
Asked Answered
C

3

9

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

Chao answered 17/3, 2014 at 12:25 Comment(4)
What do you use to take gif screenshots of your app?Covington
@JohnRiselvato I use LICEcap, it's great for creating animated gif's of gameplay.Lw
I have a very large game in development that worked perfectly on iOS 7. But on 7.1 I get this same problem, including collision callbacks not working, only when the xScale is negative. What's strange is that sometimes the physics do work a little then fail. This is a major bug and I'm surprised this made it through. Going to have to try that container workaround for now.Townie
Just passed one afternoon to understand that f**ing collision problem. I'm glad to know that's a SpriteKit bug.Reidreidar
C
13

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;
}
Chao answered 17/3, 2014 at 14:13 Comment(2)
A workaround is indeed a solution, just not the ideal solution.Lw
Here is a link where this is also confirmed. digitalleaves.com/blog/2014/03/…Put
O
6

I encountered this problem a couple of days ago. I wanted to invert the sprite based on it's movement (right or left) and found that setting xScale disables any collisions/contacts.

However, I used this line every time I set the xScale property and everything went back to normal.

node.xScale = -1.0
node.physicsBody = node.physicsBody;
Ondine answered 28/3, 2014 at 6:52 Comment(2)
Simple and effective solution. I don't understand how reassigning the same pointer this issue is resolved, but it works. I have been researching and I notice that the collision (after flipping the sprite) continues working, but it is always detected a few distance below from where it should be... It's like the physicsBody of flipped sprite becomes smaller...Defile
@Ondine interesting. Actually, I encountered a similar issue the other day, but in my case collisions work completely random when using scale. Sometimes they work as they should but other times they are completely or semi-broken. I realised that setting the scale on nodes is the issue but your suggestion doesn't seem to help in my case. I would appreciate if you could have a look: #37306151. Thanks!Minimize
I
0

Alternative Solution

In my case I generally subclass SKSpriteNode to represent nodes in my scene and then encapsulate their behaviour (animation and movement) in the subclass. The Wrap sprite in container node solution doesn't work in my case as I have actions that animate the sprites texture and also move the sprite in sequence.

For example:

class Player: SKSpriteNode{

    override init() {
        ...
        super.init(texture: texture, color: nil, size: texture.size())
        physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(70,75))
    }

    func attack(){
        let action = SKAction.sequence([
            SKAction.moveTo(enemyPosition, duration: 0),
            SKAction.animateWithTextures(frames, timePerFrame: 0.5)
            ])
        runAction(action)
    }
}

Workaround

In my case I solved this by adding these methods to my 'Player' class:

class Player: SKSpriteNode{
    private var flipped: Bool
    ...

    func flip(){
        flipped = !flipped
        position = CGPointMake(-position.x, position.y)
    }

    //MARK: Workaround for iOS7 -xScale physics bugs
    func willSimulatePhysics(){
        xScale = 1
    }

    func didSimulatePhysics(){
        xScale = flipped ? -1 : 1
    }
}

And then override SKScene methods:

class MyScene: SKScene{
    ...

    override func update(currentTime: CFTimeInterval) {
        super.update(currentTime)
        player.willSimulatePhysics()
    }

    override func didSimulatePhysics() {
        super.didSimulatePhysics()
        player.didSimulatePhysics()
    }
}

This works because the node flip is disabled just before physics are simulated for the scene. The node flip is then applied just before the frame is rendered.

Immature answered 17/3, 2015 at 11:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.