如何在不生成无数节点的情况下更新分数?

时间:2015-02-04 17:08:29

标签: ios objective-c sprite-kit

我有这个游戏,当一个精灵触及两侧,你得到一个点。我想出了每次精灵触及侧面时增加分数的代码。我通过NSLog消息得到正确的输出。

但是,当我通过将代码从NSLog更改为SKLabelNode来尝试相同的代码时,我得到了超过一千个节点(我认为这将影响性能并减慢游戏速度)。此外,当分数增加时,它与旧分数重叠,而不仅仅是增加分数。我在框架中添加了更新中的代码。

下面是代码:

- (void)printScore {
    NSString *text = [NSString stringWithFormat:@"%ld",(long)userScore];
    SKLabelNode *score = [SKLabelNode labelNodeWithText:text];

    score.fontName = @"chalkduster";
    score.fontSize = 45;
    score.fontColor = [SKColor whiteColor];
    score.position = CGPointMake(CGRectGetMidX(self.frame) + 175 ,CGRectGetMidY(self.frame) + 350);
    [self addChild:score];
}

-(void)update:(CFTimeInterval)currentTime {
    [self printScore];
}

如何解决这个问题,以便在没有添加太多精灵的情况下更新分数?

对不起,如果这是一个非常愚蠢的问题,我就是诺布。

5 个答案:

答案 0 :(得分:2)

您获得如此多节点的原因是您不断向场景添加新标签。请注意,在printScore方法中,您要创建新的SKLabelNode个实例,然后将其添加到场景中。

为了纠正这个问题,您需要维护对单个SKLabelNode的引用,然后更新其文本。

此外,您可能希望将调用移至printScore仅在实际评分事件发生时调用,而不是为每个帧调用update。我当然假设你没有每一帧都有得分事件。

答案 1 :(得分:2)

只添加一个属性:

@property (nonatomic, strong) SKLabelNode *scoreLabel;

viewDidLoad init方法中只创建一次:

_scoreLabel = [SKLabelNode initWithFontNamed:@"chalkduster"];
_scoreLabel.fontSize = 45;
_scoreLabel.fontColor = [SKColor whiteColor];
_scoreLabel.position = CGPointMake(CGRectGetMidX(self.frame) + 175 ,CGRectGetMidY(self.frame) + 350);
[self addChild:_scoreLabel];

然后仅在精灵触及de侧时调用printScore方法,而不是像在update方法中那样在每个帧上调用:

- (void)printScore
{
     NSString *text = [NSString stringWithFormat:@"%ld",(long)userScore];
     self.scoreLabel.text = text;
}

我希望这会有所帮助。

答案 2 :(得分:0)

您的printScore方法每次调用时都会创建一个新的SKLabelNode,并且您通过更新方法每秒调用60次。您应该设置BOOL以通知您只有在需要时才会调用printScoreMethod(如分数已更改)。

答案 3 :(得分:0)

创建一个全局布尔值,并且只在第一次添加标签:

BOOL firsttime = YES;

- (void)printScore {
    NSString *text = [NSString stringWithFormat:@"%ld",(long)userScore];
    SKLabelNode *score = [SKLabelNode labelNodeWithText:text];

    if( firsttime )
    {
        score.fontName = @"chalkduster";
        score.fontSize = 45;
        score.fontColor = [SKColor whiteColor];
        score.position = CGPointMake(CGRectGetMidX(self.frame) + 175, CGRectGetMidY(self.frame) + 350);

        [self addChild:score];
        firsttime = NO;
    }
}

-(void)update:(CFTimeInterval)currentTime {
    [self printScore];
}

答案 4 :(得分:0)

有几种方法可以解决这个问题。我更喜欢一种隐藏了底层实现的方法。理想情况下,我实际上会创建一个代表HUD分数的类。从“应用程序代码的其余部分”的角度来看,其他游戏代码与HUD的合同是通过分数属性来反映分数。

有些人会说它是一个标签,看起来很麻烦。但是如果你改变了它的实现方式(例如,假设它变成了一个带有填充条的分数,或者你可能会有一个特殊的效果伴随着分数的变化),你会发现它使它变得非常容易处理。或者如果你突然有一个2人游戏并且两个玩家跟踪得分怎么办?您可以通过添加分数类的新实例轻松添加新分数。

这是我的意思的一个简单例子。

ScoreHUD.h

@interface ScoreHUD : NSObject

@property (nonatomic, assign) NSInteger score;

- (instancetype)initWithScene:(SKScene *)scene x:(CGFloat)x y:(CGFloat)y;

@end

ScoreHUD.m

@interface ScoreHUD()

@property (nonatomic, strong) SKLabelNode *scoreLabel;

@end

@implementation ScoreHUD

- (instancetype)initWithScene:(SKScene *)scene x:(CGFloat)x y:(CGFloat)y
{
    self = [super init];

    if (self) {
        _scoreLabel = [SKLabelNode initWithFontNamed:@"chalkduster"];
        _scoreLabel.fontSize = 45;
        _scoreLabel.fontColor = [SKColor whiteColor];
        _scoreLabel.position = CGPointMake(x, y);
        [scene addChild:_scoreLabel];

        // Setting initial label value to the default value of the score
        _scoreLabel.text = [@(_score) stringValue];
    }

    return self;
}

- (void)setScore:(NSInteger)score
{
    if (_score != score) {
        _score = score;

        self.scoreLabel.text = [@(score) stringValue];
    }
}

@end

请注意,我没有测试代码或检查它是否会编译。

所以要使用它,你可以在你的场景中创建一个这样的实例,如:

// Property in the scene
@property (nonatomic, strong) ScoreHUD *score;

// Initialize where you need to
self.score = [[ScoreHUD alloc] initWithScene:self x:CGRectGetMidX(self.frame) + 175 y:CGRectGetMidY(self.frame) + 350];

您可以在场景initWithSize中执行此操作。

当分数发生变化时,您只需执行以下操作:

self.score.score += 1;

如果它看起来很讨厌,你总是可以用另一种方法包装它。

这种方法有一个缺点。假设你的分数在一帧中得到了很多更新。这意味着您的文本将每帧设置多次。根据您拥有的游戏类型,这可能是也可能不是一个问题。有一些方法仍然可以使用这个基本设置并解决它。但对于绝大多数游戏来说,这不会是一个问题。