如何在不产生无数节点的情况下更新分数?
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;
如果您觉得不合适,您可以随时将其包装在另一种方法中。
这种方法有一个缺点。假设你的分数在一帧中更新了很多。这意味着您的文本将在每帧中设置多次。根据您拥有的游戏类型,这可能是也可能不是问题。仍然有一些方法可以保持这个基本设置并解决它。但对于绝大多数游戏来说,这都不是问题。
我玩过这个游戏,当精灵碰到边时,你就得一分。我想出了每次精灵接触到侧面时增加分数的代码。我通过 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;
如果您觉得不合适,您可以随时将其包装在另一种方法中。
这种方法有一个缺点。假设你的分数在一帧中更新了很多。这意味着您的文本将在每帧中设置多次。根据您拥有的游戏类型,这可能是也可能不是问题。仍然有一些方法可以保持这个基本设置并解决它。但对于绝大多数游戏来说,这都不是问题。