Flipped x-scale breaks collision handling (SpriteKit 7.1)

二次信任 提交于 2019-12-18 03:38:10

问题


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;
}


回答1:


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;
}



回答2:


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;



回答3:


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.



来源:https://stackoverflow.com/questions/22454220/flipped-x-scale-breaks-collision-handling-spritekit-7-1

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!