【问题标题】:Why waitForDuration: method only works once in the update method in sprite kit?为什么 waitForDuration: 方法在 sprite kit 的 update 方法中只工作一次?
【发布时间】:2016-02-23 07:54:31
【问题描述】:

在我的代码中,我正在尝试更新标签 (SKSpriteNode),同时使用 sprite 工具包倒计时,但我不想使用 NSTimer,所以我找到了这个解决方案,但问题是 waitForDuration 方法仅适用第一次运行更新方法。我想使用 sprite kit 每秒运行一段代码(更新标签和变量)。任何帮助表示赞赏。

-(void)update:(CFTimeInterval)currentTime {

    id wait = [SKAction waitForDuration:1.0];
    id run = [SKAction runBlock:^{
        remainingCounts = remainingCounts - 1;
        [progressBar setProgress:remainingCounts/10];
        timerLabelNode.text =[NSString stringWithFormat:@"Time: %d", remainingCounts];

        if (--remainingCounts == 0) {
            _AreYouLost=YES;
            if (_score>[savedBestScore intValue]) {
               [self updateBestScore:[NSMutableString  stringWithFormat:@"%d",_score]];
            }
             [self updateBestScore:[NSMutableString  stringWithFormat:@"%d",_score]];
             SKScene *GameovrScn = [GameOverScene sceneWithSize:self.view.bounds.size];
            GameovrScn.scaleMode = SKSceneScaleModeAspectFill;
            [self.view presentScene:GameovrScn transition:[SKTransition doorwayWithDuration:0.4]];
             NSLog(@"Time is up!!!");
         }
      }];
}

【问题讨论】:

  • 您使用操作使这变得非常复杂。 update 方法是直接跟踪时间。您是否考虑过只使用更新方法中的时间进行倒计时?
  • 我想说只是将你的倒计时代码移动到一个单独的函数中。您不需要在update 中运行它。
  • 好的,但是我应该在哪里调用它以便每秒调用一次? @ChrisSlowik 我应该在更新方法中调用它吗?
  • 看看下面的答案
  • @Reza.Ab 我现在看到的代码实际上什么也没做——你在每个更新调用中创建了两个动作,但你从不运行它们。 SKAction runBlock:方法,实际上创建一个执行块的动作,而不是在调用时直接执行该块。您的代码的另一个问题是您试图每秒运行代码块,但您是在每秒运行 60 次的方法内部执行此操作的......这真的没有意义。您应该使用 trojanfoe 指向的内容,或者您​​可以使用 SKActions...

标签: ios objective-c sprite-kit


【解决方案1】:

我使用类似的技术来限制诸如射速之类的东西,它适用于任何支持帧更新的游戏引擎,您可以获得增量时间(即这一帧和最后一帧之间的时间) )。这几乎是我见过的所有游戏引擎除了 SpriteKit,它会为您提供当前时间(我认为是游戏开始后的时间,但没关系),因此您需要计算自己增加时间。

您需要使用一个实例变量来保存当前计时器值并从那个时间开始减少 delta 时间,当它是 <= 0.0 时,就该触发它了。如果您希望它重复,请重置倒计时。

例如:

#define TIMER_VALUE 1.0

@interface MyClass()
{
    CFTimeInterval _lastFrameTime;
    CFTimeInterval _timer;
}
@end

@implementation MyClass()

- (instancetype)init
{
    self = [super init];
    if (self) {
        _lastFrameTime = 0.0;
        _timer = TIMER_VALUE;
    }
    return self;
}

-(void)update:(CFTimeInterval)currentTime
{
    if (_lastFrameTime == 0.0)           // Might need < DBL_EPSILON here?
        _lastFrameTime = currentTime;    // First frame
    CFTimeInterval deltaTime = currentTime - _lastFrameTime;

    _timer -= deltaTime;
    if (_timer <= 0.0) {
        [self doThing];
        _timer = TIMER_VALUE;
    }

    _lastFrameTime = currentTime;
}
@end

【讨论】:

  • 非常感谢,你能告诉我我应该在哪里调用我想要的方法以每秒调用一次吗?对不起:(@trojenfoe
  • 这是对[self doThing];的调用。
  • 好吧,我有点糊涂了,我以为[self doThing];是时候到了?!那会在哪里呢?我的意思是我在哪里可以做到这一点---->> If(timer==0){ do whatever} 如果我想在用户获得分数时添加第二个怎么办?我的意思是我可以用我想要的任何方法做 _timer++ 吗? @trojanfoe 非常感谢。
  • 好的,所以我给_timer = 5.0; 提供了初始值,以便在initwithSize: 方法中倒计时到零,但它仍然从0 开始倒计时。@trojanfoe
  • @Reza.Ab 如果它不能解决您的问题,您应该不接受答案。
【解决方案2】:

这是使用 SKActions 的方法:

#import "GameScene.h"

@interface GameScene ()

@property (nonatomic, strong) SKLabelNode *label;
@property (nonatomic, assign) NSUInteger timeLeft;
@end

@implementation GameScene

-(instancetype)initWithCoder:(NSCoder *)aDecoder{

    if (self = [super initWithCoder:aDecoder]) {

        _timeLeft = 90;
        _label = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
    }

    return self;
}

//Override timeLeft's setter in order to update label's text property after each assignment to property is made.
-(void)setTimeLeft:(NSUInteger)timeLeft {

    _timeLeft = timeLeft;
    _label.text = [NSString stringWithFormat:@"Current score %lu", (unsigned long)_timeLeft];

}


-(void)didMoveToView:(SKView *)view {

    //How much to wait
    SKAction *wait = [SKAction waitForDuration:1];

    __weak typeof(self) weakSelf =  self;

    //What to do
    SKAction *block = [SKAction runBlock:^{
        weakSelf.timeLeft -= 1;
    }];

    //Sequence to repeat - look at this like it represents one step (wait and update what needed)
    SKAction *sequence = [SKAction sequence:@[wait, block]];

    self.label.text = [NSString stringWithFormat:@"Current score %lu", (unsigned long)self.timeLeft];
    self.label.fontSize = 45;
    self.label.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
    [self addChild:self.label];

    //Repeat the step above forever
    [self runAction:[SKAction repeatActionForever:sequence] withKey:@"countdown"];


}

@end

代码非常简单(并且有很多注释),但让我再次解释一下它是如何工作的:

  • 一个动作序列被创建一次,并被多次重复使用。
  • 在作为该序列一部分的块内部,您可以更新timeLeft 变量。这会自动(通过重写的设置器)更新标签的文本。
  • 序列永远重复。

要停止此操作,您可以通过密钥访问它,然后将其删除,如下所示:

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

    if ([self actionForKey:@"countdown"]){
        [self removeActionForKey:@"countdown"];
    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多