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

How to update the score without generating countless nodes?

我玩过这个游戏,当精灵碰到边时,你就得一分。我想出了每次精灵接触到侧面时增加分数的代码。我通过 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];
}

我该如何解决这个问题,以便在不添加那么多 sprite 的情况下更新分数?

对不起,如果这是一个非常愚蠢的问题,我是菜鸟。

你得到这么多节点的原因是你不断地向场景添加新标签。请注意,在您的 printScore 方法中,您正在创建新的 SKLabelNode 实例,然后将其添加到场景中。

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

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

您的 printScore 方法每次被调用时都会创建一个新的 SKLabelNode 并且您通过更新方法每秒调用它 60 次。你应该设置一个 BOOL 来通知你需要调用你的 printScoreMethod 只有当需要这样做时(因为分数已经改变)。

只添加一个 属性:

@property (nonatomic, strong) SKLabelNode *scoreLabel;

并且只在 viewDidLoad o 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];

然后仅当精灵接触到边时才调用 printScore 方法,而不是像您在 update 方法中所做的那样在每一帧上调用:

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

希望对您有所帮助。

创建全局布尔值并仅在第一次添加标签:

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];
}

有几种方法可以解决这个问题。我更喜欢隐藏尽可能多的底层实现的方法。理想情况下,我实际上会创建一个代表分数 HUD 的 class。从 "rest of the app code" 的角度来看,另一个游戏代码与 HUD 的合同是通过反映分数的分数 属性 实现的。

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

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

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;

如果您觉得不合适,您可以随时将其包装在另一种方法中。

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