【问题标题】:How to update the score without generating countless nodes?如何在不生成无数节点的情况下更新分数?
【发布时间】:2015-02-04 17:08:29
【问题描述】:

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

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

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

【问题讨论】:

    标签: ios objective-c sprite-kit


    【解决方案1】:

    只添加一个属性:

    @property (nonatomic, strong) SKLabelNode *scoreLabel;
    

    然后在 viewDidLoadinit 方法中只创建一次:

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

    我希望这会有所帮助。

    【讨论】:

    • 您应该将属性名称更改为 scoreLabel,因为 score 将是存储实际分数的变量
    • 存储实际分数的变量称为userScore 。但是,我同意您将其命名为不同的名称以避免冲突。
    【解决方案2】:

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

    为了更正此问题,您需要保留对单个 SKLabelNode 的引用,然后更新其文本。

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

    【讨论】:

    • 如何创建和维护对 SKLabelNode 的单一引用并更新其文本?
    • 您的场景需要有一个SKLabelNode 类型的成员属性。因此,当您的场景出现时,您实际上可以为该属性实例化一个实例,并根据需要更新该属性的文本值。
    • 还是不太明白,能举个例子或者用更简单的方式解释一下吗?我是菜鸟,对不起
    • @RohanKalantri 请参阅上面 ThXou 的回答。但是,需要一个如何将属性添加到类的示例确实表明您可能需要阅读一些关于 iOS 开发(以及一般的 OO 开发?)的基本模式。
    【解决方案3】:

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

    【讨论】:

    • 那么重叠呢?
    • @RohanKalantri - 您可以在更新之前从父级中删除 SKLabelNode,也可以保留当前的 ​​SKLabelNode 并简单地更改文本属性以反映更新的数据。
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

      有几种方法可以解决这个问题。我更喜欢一种隐藏尽可能多的底层实现的方法。理想情况下,我实际上会创建一个代表分数 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
      

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

      因此,要使用它,您可以在场景中创建一个 this 的实例,例如:

      // 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;
      

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-11-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多