1
votes

I've written this code for the game over scene I have for a game:

#import "GameOverScene.h"
#import "SharedInfo.h"
@implementation GameOverScene

-(void)didMoveToView:(SKView *)view {
    /* Setup your scene here */

    [self setupView];

}

-(void)showGameEndingWithGameInformation:(NSDictionary *)gameEndingInformation{

}
-(void)setupView{
    SKLabelNode *GOTitle = [SKLabelNode labelNodeWithFontNamed:@"Generica Bold"];
    GOTitle.fontSize = 40.f;
    NSString* text = [NSString stringWithFormat:@"GAME OVER"];
    [GOTitle setText:text];
    GOTitle.position = CGPointMake(CGRectGetMidX(self.frame), self.frame.size.height- GOTitle.frame.size.height*1.5);
    [GOTitle setFontColor:[[SharedInfo sharedManager]colorFromHexString:@"#2EB187"]];
    [self addChild: GOTitle];


    SKLabelNode *replayButton = [SKLabelNode labelNodeWithFontNamed:@"Quicksand-Bold"];
    replayButton.fontSize = 25.f;
    NSString* replayText = [NSString stringWithFormat:@"Play Again"];
    [replayButton setText:replayText];
    replayButton.name = kGOSceneReplayButton;
    replayButton.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame)- self.frame.size.height/5);
    [replayButton setFontColor:[SKColor whiteColor]];
    SKShapeNode *bgNode = [SKShapeNode shapeNodeWithRectOfSize:replayButton.frame.size];
    [bgNode setFillColor:[UIColor redColor]];

    [replayButton addChild:bgNode];
    [self addChild:replayButton];

    NSLog(@"replay dimensions: %@",NSStringFromCGRect(replayButton.frame));
    SKLabelNode *returnButton = [SKLabelNode labelNodeWithFontNamed:@"Quicksand-Bold"];
    returnButton.fontSize = 25.f;

    NSString* returnText = [NSString stringWithFormat:@"Return To Main Menu"];
    [returnButton setText:returnText];
    returnButton.name = kGOSceneReturnToMainButton;
    returnButton.position = CGPointMake(CGRectGetMidX(self.frame), replayButton.position.y -self.frame.size.height/7  );
    [returnButton setFontColor:[SKColor whiteColor]];

    [self addChild:returnButton];

}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInNode:self];
    SKNode *sprite = [self nodeAtPoint:location];

    NSLog(@"sprite name: %@",sprite.name);

    if ([sprite.name isEqualToString:kGOSceneReturnToMainButton]||[sprite.name isEqualToString:kGOSceneReturnToMainButton]) {
        //return to main menu or retry

        [self.gameEndingSceneDelegate goToScene:sprite.name withOptions:nil]; //Sort out the options later on. 

    }

}
@end

When I run it though, I get this:

enter image description here

There are two issues I'm really confused about. Firstly, why do I have 8 nodes in the scene, where I should really have 4? I think something is doubling the nodes, but that's just a guess.

The more confusing issue is the red SKShapeNode positioning. I've read that scaling the parent node can cause problems to the child SKShapeNode, but I'm not scaling anything. Also, why does it place my red rectangle at a random position (it's not the middle of the parent, or corresponding with the bottom).

Thanks a lot for all the help in advance.

UPDATE 1: So following the suggestion, I checked if my method is being called twice, and thus creating the duplicates. No luck there, as it is only called once. The mystery still going strong!

As for the positioning shenanigans, I changed the code slightly to set the position of the red rectangle to match its parent node:

SKLabelNode *replayButton = [SKLabelNode labelNodeWithFontNamed:@"Quicksand-Bold"];
replayButton.fontSize = 25.f;
NSString* replayText = [NSString stringWithFormat:@"Play Again"];
[replayButton setText:replayText];
replayButton.name = kGOSceneReplayButton;
replayButton.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame)- self.frame.size.height/5);
[replayButton setFontColor:[SKColor whiteColor]];
SKShapeNode *bgNode = [SKShapeNode shapeNodeWithRectOfSize:replayButton.frame.size];
[bgNode setFillColor:[UIColor redColor]];


[self addChild:replayButton];
bgNode.position = replayButton.position;
[replayButton addChild:bgNode];

But after updating, I got this:

enter image description here

In case it helps, this is what I do to present the scene:

SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = YES;
scene = [GameOverScene sceneWithSize:self.view.frame.size];
[(GameOverScene*)scene setGameEndingSceneDelegate:self];
[(GameOverScene*)scene setScaleMode: SKSceneScaleModeAspectFill];
[(GameOverScene*)scene showGameEndingWithGameInformation:self.gameEndingInfo];

// Present the scene.
[skView presentScene:scene transition:sceneTransition];

Also, this is the output of my NSLog:

replay dimensions: {{221, 91}, {127, 25}

I've got a feeling that because I set my scene's setScaleMode, it gets strange, but nothing else is out of ordinary, so not sure what to do. I'm thinking maybe just create an image for my label and change the SKLabelNode to SKSpriteNode and set the image, so I skip adding the red rectangle as background for the label node. The reason I wanted to add the rectangle is actually to provide bigger hit target for when the 'Button' is tapped, so if anyone knows an easier, more straightforward way, I'd really appreciate it.

UPDATE 3:

I also tried setting the position of the rectangle to match that of parent label node:

bgNode.position = [replayButton convertPoint:CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame)- self.frame.size.height/5) fromNode:self];

the rectangle ends up at the same place as the last update (all the way to the right of the screen)

1
@originaluser2 I tried both, the setupView is only called once, and with the updated code, weirder stuff happens! I've updated the original post with the code update. This is quite strange!Septronic
@originaluser2 When SKLabelNode is added, in iOS 9 simulator , the nodes count is incremented by two. So, it's not OP's mistake.Whirlwind
@Whirlwind The thing is I'm running it on device.Septronic
@Septronic Obviously there is no difference, I am just saying that it's not code logic issue.Whirlwind
@Whirlwind Oh, Cool. Thanks for pointing that out. Any suggestions for me to try?Septronic

1 Answers

1
votes

There are few issues with your code:

  1. lineWidth property and it's default value of 1.0. It should be 0.0f

  2. verticalAlignmentMode property and it's default baseline alignment. It should be SKLabelVerticalAlignmentModeCenter.

  3. Wrong positioning of a shape node. It should be (0,0)

To fix it, change label's vertical alignment:

replayButton.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter;

set shapenode's lineWidth property to 0.0f:

bgNode.lineWidth = 0.0f;

and remove this line:

//bgNode.position should be CGPointZero which is (0,0)
bgNode.position = replayButton.position; 

Still, I would stay away of this approach. SKShapeNode is not needed in this situation. You can do the same with SKSpriteNode. What is important is that both SKShapeNode and SKLabelNode can't be drawn in batches, which means, can't be drawn in a single draw pass when rendered like SKSpriteNode. Take a look at this. Your example is too simple to make performance issues, but in general you should keep all this in mind.

If your button's text never change during the game, you should consider using SKSpriteNode initialized with texture. If interested in a pre made buttons for SpriteKit, take a look at SKAButton.

Hope this helps!