Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SKPhysicsBody and [SKNode setScale:]

In my current project using SpriteKit, I have a bunch of sprites that need to be scaled up and down independently at various times. The problem is that when I scale the node, the physics body doesn't scale with it so it screws up the physics. Here's a small example I put together for the purpose of this question:

CGSize objectSize = CGSizeMake(100, 100);
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0, 0, self.size.width, self.size.height)];

SKSpriteNode *n1 = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:objectSize];
n1.position = CGPointMake(self.size.width/2, 2*self.size.height/3);
n1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:objectSize];
n1.physicsBody.affectedByGravity = YES;
[self addChild:n1];

SKSpriteNode *n2 = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:objectSize];
n2.position = CGPointMake(self.size.width/2, self.size.height/3);
n2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:objectSize];
n2.physicsBody.affectedByGravity = YES;
[self addChild:n2];

[n1 setScale:0.5];

enter image description here

Notice how the blue sprite (scaled down) sits on top of the red one but you can tell its physics body still has the dimension I told it, and it didn't scale.

So obviously, scaling down the node doesn't scale down the physicsBody. So my question is if I have to manually do it, how do I go about it?

I tried swapping the body with one of the right size when scaling, but then things get really convoluted if the old body had joints, etc... It'd be a lot simpler if I could just scale the existing body somehow.

like image 837
mprivat Avatar asked Oct 10 '13 20:10

mprivat


5 Answers

For anyone else reading this answer, I believe that now Physics Bodies scale with the SKNode. I am using Xcode 8.2.1 and Swift 3.0.2. I start a new Project, select Game, fill in the details. Change 2 files:

GameViewController.swift (add one line)

view.showsFPS = true
view.showsNodeCount = true

// add this to turn on Physics Edges    
view.showsPhysics = true

Game Scene.swift (replace the sceneDidLoad()) function with this:

override func sceneDidLoad() {

    var found = false
    self.enumerateChildNodes(withName: "testSprite") {
        node, stop in
            found = true
    }
    if !found {
        let testSprite = SKSpriteNode(color: UIColor.white, size: CGSize(width: 200.0, height: 200.0))
        testSprite.physicsBody = SKPhysicsBody(polygonFrom: CGPath(rect: CGRect(x: -100.0, y: -100.0, width: 200.0, height: 200.0), transform: nil))
        testSprite.physicsBody?.affectedByGravity = false
        testSprite.name = "testSprite"
        let scaleAction = SKAction.repeatForever(SKAction.sequence([
            SKAction.scale(to: 2.0, duration: 2.0),
            SKAction.scale(to: 0.5, duration: 2.0)
            ]))
        testSprite.run(scaleAction)
        self.addChild(testSprite)
    }

}

The physics boundary is scaling with the sprite node.

like image 55
Christian Cerri Avatar answered Nov 20 '22 02:11

Christian Cerri


Using either an SKAction or the Update loop, you can create a new SKPhysicsBody with the proper scale and apply it to the object. However, by doing so, you will lose the velocity. To fix this, you can do the following:

SKPhysicsBody *newBody = [SKPhysicsBody bodyWithRectangleOfSize:boxObject.size];
newBody.velocity = boxObject.physicsBody.velocity;

boxObject.physicsBody = newBody;
like image 30
Placus Brutus Avatar answered Nov 20 '22 02:11

Placus Brutus


Physics body shapes can't be scaled. Definitely not in Box2D which Sprite Kit uses internally.

Besides internal optimizations, scaling a physics body would have far reaching implications. For example scaling a body's shape up could get the body stuck in collisions. It would affect how joints interact. It would definitely change the body's density or mass and thus its behavior.

You could use a SKNode to which you add the sprite without a physics body, and then add additional SKNode with bodies of given sizes. You could then enable or disable the bodies when you start scaling the sprite. If you time this right the player won't notice that the collision shape simply went from full size to half size while the sprite animates that scaling transition.

You would have to calculate the body's density and perhaps other properties according to its size though.

like image 5
LearnCocos2D Avatar answered Nov 20 '22 02:11

LearnCocos2D


I had the exact same problem, I think its a bug in sprite kit.

Try using an action to scale, this works on whole scene.

[self runAction:[SKAction scaleTo:0.5 duration:0]];

My original question

Is this what you want to happen

- (void) anthoertest
{
    CGSize objectSize = CGSizeMake(100, 100);
    self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:CGRectMake(0, 0, self.size.width, self.size.height)];

    SKSpriteNode *n1 = [SKSpriteNode spriteNodeWithColor:[UIColor blueColor] size:objectSize];
    n1.position = CGPointMake(self.size.width/2, 2*self.size.height/3);
    n1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:n1.size];
    n1.physicsBody.affectedByGravity = YES;
    [self addChild:n1];

    SKSpriteNode *n2 = [SKSpriteNode spriteNodeWithColor:[UIColor redColor] size:objectSize];
    n2.position = CGPointMake(self.size.width/2, self.size.height/3);
    n2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:n2.size];
    n2.physicsBody.affectedByGravity = YES;
    [self addChild:n2];

    [self shrinkMe:n1];
}

- (void) shrinkMe:(SKSpriteNode *) s
{
    [s setScale:0.5];
    s.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:s.size];
}

enter image description here

like image 3
DogCoffee Avatar answered Nov 20 '22 02:11

DogCoffee


The physics body depends on the CGPath of the SKNode; you'll need to change that somehow. You might try scaling the CGPath with something like the following:

//scale up

CGFloat scaleFactor = 1.1;
CGAffineTransform scaleTransform = CGAffineTransformIdentity;
scaleTransform = CGAffineTransformScale(scaleTransform, scaleFactor, scaleFactor);
CGPathRef scaledPathRef = CGPathCreateCopyByTransformingPath(exampleNode.path, &scaleTransform);
exampleNode.path = scaledPathRef;

But keep in mind this won't update the appearance of nodes already in an SKScene. You might need to combine resizing the CGPath with an SKAction that scales the node.

like image 1
Goondaba Avatar answered Nov 20 '22 02:11

Goondaba