doublea
doublea

Reputation: 2576

How can I prevent the child of an SKSpriteNode from rotating with its parent?

I'm using SpriteKit to write an iOS game that involves a number of labeled balls. I'm constructing these 'rollingBalls' by building a parent SKSpriteNode with 2 children:

a) an SKShapeNode (the actual circle shape)

b) and an SKLabelNode (the label)

The balls will be moving all over the screen, interacting with each other and other objects, in 2 dimensions, and entirely dependent on the expected physics (think billiards). But if at all possible I'd like the label to NOT rotate with the parent, so that it's remains easily readable at all times.

What's the easiest way to do this?

Should the label not be a child of the container? Is there some other way to peg it to the ballShape? Or is there some property I can set on the label, etc.?

Here's what I've got now:

double ballDiameter = 40;
UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: 
                            CGRectMake(-ballDiameter / 2, -ballDiameter / 2, 
                                        ballDiameter, ballDiameter)];

SKSpriteNode *container = [[SKSpriteNode alloc]init];

container.name = @"rollingBall";

SKShapeNode *ballShape = [[SKShapeNode alloc] init];
ballShape.path = ovalPath.CGPath;

SKLabelNode *ballLabel = [SKLabelNode labelNodeWithFontNamed:@"Arial"];
ballLabel.text = @"some random string";
ballLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeCenter;
ballLabel.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter;
ballLabel.position = CGPointMake(0,0);

[container addChild:ballShape];
[container addChild:ballLabel];

container.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ballDiameter / 2];
container.physicsBody.usesPreciseCollisionDetection = YES;

Upvotes: 5

Views: 4316

Answers (4)

thomasguenzel
thomasguenzel

Reputation: 670

Although the other solutions work as well, i found that both nodes have a small gap between them when moving them using the physics simulation.

You can use 2 physics bodies and add a pin joint between them, this way they'll be updated simultaneously. My sample code for a subclass of SKNode (note: you need to have added the node to a scene/node to add a joint):

SKSpriteNode *overlay = [SKSpriteNode spriteNodeWithImageNamed:@"overlay"];
overlay.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10.0];
overlay.allowsRotation = NO;
[self addChild:overlay];

self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:26.0];

SKPhysicsJointPin *pinJoint = [SKPhysicsJointPin jointWithBodyA:overlay.physicsBody bodyB:self.physicsBody anchor:self.position];
[self.scene.physicsWorld addJoint:pinJoint];

Upvotes: 0

pmark
pmark

Reputation: 291

enter image description here

Put all of the labels in a single container, add the label to the associated node's userData, then update label positions.

// Add a container as a scene instance variable.
SKNode *labels;

- (void)addLabelContainer
{
    labels = [SKNode node];
    [self addChild:labels];
}

- (void)addLabelForNode:(SKNode*)node
{
    SKLabelNode *label = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
    node.userData = [NSMutableDictionary dictionaryWithObject:label forKey:@"label"];
    label.text = node.name;
    label.fontSize = 5;
    label.zPosition = node.zPosition + 1;
    [labels addChild:label];
}

- (void)removeLabelForNode:(SKNode*)node
{
    [[node.userData objectForKey:@"label"] removeFromParent];
    [node.userData removeObjectForKey:@"label"];
}

- (void)update:(NSTimeInterval)currentTime
{
    for (SKNode *node in self.children) {
        SKNode *label = (SKLabelNode*)[node.userData objectForKey:@"label"];
        if (label) {
            label.position = node.position;
        }
    }
}

See https://github.com/pmark/MartianRover/blob/master/hiSpeed/hiSpeed/Scenes/LimboScene.m

Upvotes: 6

DogCoffee
DogCoffee

Reputation: 19946

This seems to work

-(void) didSimulatePhysics
{
    [self enumerateChildNodesWithName:@"ball" usingBlock:^(SKNode *node, BOOL *stop) {

        for (SKNode *n in node.children) {
            n.zRotation = -node.zRotation;
        }

    }];
}

Upvotes: 5

doublea
doublea

Reputation: 2576

One potential, super easy solution:

container.physicsBody.allowsRotation = NO

But of course this will prevent the entire sprite from rotating.

Upvotes: 1

Related Questions