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:
我正在用SpriteKit编写一个iOS游戏,游戏中包含了很多标记的球。我正在构建这些“滚球”通过建立一个有两个孩子的家长SKSpriteNode:
a) an SKShapeNode (the actual circle shape)
a) SKShapeNode(实际的圆形)
b) and an SKLabelNode (the label)
b)和SKLabelNode(标签)
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;
4 个解决方案
#1
5
This seems to work
这似乎工作
-(void) didSimulatePhysics
{
[self enumerateChildNodesWithName:@"ball" usingBlock:^(SKNode *node, BOOL *stop) {
for (SKNode *n in node.children) {
n.zRotation = -node.zRotation;
}
}];
}
#2
5
Put all of the labels in a single container, add the label to the associated node's userData, then update label positions.
将所有标签放在一个容器中,将标签添加到关联节点的userData中,然后更新标签位置。
// 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
参见https://github.com/pmark/MartianRover/blob/master/hiSpeed/hiSpeed/Scenes/LimboScene.m
#3
2
One potential, super easy solution:
一个潜在的,超级简单的解决方案:
container.physicsBody.allowsRotation = NO
But of course this will prevent the entire sprite from rotating.
但这当然会阻止整个精灵旋转。
#4
0
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):
你可以使用两个物理实体并在它们之间添加一个针连接,这样它们就会同时更新。我的SKNode子类的示例代码(注意:需要将节点添加到场景/节点以添加连接):
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];
#1
5
This seems to work
这似乎工作
-(void) didSimulatePhysics
{
[self enumerateChildNodesWithName:@"ball" usingBlock:^(SKNode *node, BOOL *stop) {
for (SKNode *n in node.children) {
n.zRotation = -node.zRotation;
}
}];
}
#2
5
Put all of the labels in a single container, add the label to the associated node's userData, then update label positions.
将所有标签放在一个容器中,将标签添加到关联节点的userData中,然后更新标签位置。
// 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
参见https://github.com/pmark/MartianRover/blob/master/hiSpeed/hiSpeed/Scenes/LimboScene.m
#3
2
One potential, super easy solution:
一个潜在的,超级简单的解决方案:
container.physicsBody.allowsRotation = NO
But of course this will prevent the entire sprite from rotating.
但这当然会阻止整个精灵旋转。
#4
0
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):
你可以使用两个物理实体并在它们之间添加一个针连接,这样它们就会同时更新。我的SKNode子类的示例代码(注意:需要将节点添加到场景/节点以添加连接):
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];