简体   繁体   English

SpriteKit - 创建一个计时器

[英]SpriteKit - Creating a timer

How can I create a timer that fires every two seconds that will increment the score by one on a HUD I have on my screen? 我怎样才能创建一个每两秒触发一次的计时器,它会在我屏幕上的HUD上将分数增加一个? This is the code I have for the HUD: 这是我对HUD的代码:

    @implementation MyScene
{
    int counter;
    BOOL updateLabel;
    SKLabelNode *counterLabel;
}

-(id)initWithSize:(CGSize)size
{
    if (self = [super initWithSize:size])
    {
        counter = 0;

        updateLabel = false;

        counterLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
        counterLabel.name = @"myCounterLabel";
        counterLabel.text = @"0";
        counterLabel.fontSize = 20;
        counterLabel.fontColor = [SKColor yellowColor];
        counterLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeCenter;
        counterLabel.verticalAlignmentMode = SKLabelVerticalAlignmentModeBottom;
        counterLabel.position = CGPointMake(50,50); // change x,y to location you want
        counterLabel.zPosition = 900;
        [self addChild: counterLabel];
    }
}

In Sprite Kit do not use NSTimer , performSelector:afterDelay: or Grand Central Dispatch (GCD, ie any dispatch_... method) because these timing methods ignore a node's, scene's or the view's paused state. 在Sprite Kit中不要使用 NSTimerperformSelector:afterDelay:或Grand Central Dispatch(GCD,即任何dispatch_...方法),因为这些计时方法会忽略节点,场景或视图的paused状态。 Moreover you do not know at which point in the game loop they are executed which can cause a variety of issues depending on what your code actually does. 此外,您不知道它们在游戏循环中的哪个位置执行会导致各种问题,具体取决于您的代码实际执行的操作。

The only two sanctioned ways to perform something time-based in Sprite Kit is to either use the SKScene update: method and using the passed-in currentTime parameter to keep track of time. 在Sprite Kit中执行基于时间的内容的唯一两种方法是使用SKScene update:方法并使用传入的currentTime参数来跟踪时间。

Or more commonly you would just use an action sequence that starts with a wait action: 或者更常见的是,您只需使用以等待操作开头的动作序列:

id wait = [SKAction waitForDuration:2.5];
id run = [SKAction runBlock:^{
    // your code here ...
}];
[node runAction:[SKAction sequence:@[wait, run]]];

And to run the code repeatedly: 并重复运行代码:

[node runAction:[SKAction repeatActionForever:[SKAction sequence:@[wait, run]]]];

Alternatively you can also use performSelector:onTarget: instead of runBlock: or perhaps use a customActionWithDuration:actionBlock: if you need to mimick the SKScene update: method and don't know how to forward it to the node or where forwarding would be inconvenient. 或者你也可以使用performSelector:onTarget:而不是runBlock:或者使用customActionWithDuration:actionBlock:如果你需要模仿SKScene update:方法,并且不知道如何将它转发到节点或转发不方便的地方。

See SKAction reference for details. 有关详细信息,请参阅SKAction参考


UPDATE: Code examples using Swift 更新:使用Swift的代码示例

Swift 5 斯威夫特5

 run(SKAction.repeatForever(SKAction.sequence([
     SKAction.run( /*code block or a func name to call*/ ),
     SKAction.wait(forDuration: 2.5)
     ])))

Swift 3 斯威夫特3

let wait = SKAction.wait(forDuration:2.5)
let action = SKAction.run {
    // your code here ...
}
run(SKAction.sequence([wait,action]))

Swift 2 斯威夫特2

let wait = SKAction.waitForDuration(2.5)
let run = SKAction.runBlock {
    // your code here ...
}
runAction(SKAction.sequence([wait, run]))

And to run the code repeatedly: 并重复运行代码:

runAction(SKAction.repeatActionForever(SKAction.sequence([wait, run])))

In Swift usable: 在Swift中可用:

var timescore = Int()  
var actionwait = SKAction.waitForDuration(0.5)
            var timesecond = Int()
            var actionrun = SKAction.runBlock({
                    timescore++
                    timesecond++
                    if timesecond == 60 {timesecond = 0}
                    scoreLabel.text = "Score Time: \(timescore/60):\(timesecond)"
                })
            scoreLabel.runAction(SKAction.repeatActionForever(SKAction.sequence([actionwait,actionrun])))

I've taken the swift example above and added in leading zeros for the clock. 我已经采取了上面的快速示例,并为时钟添加了前导零。

    func updateClock() {
    var leadingZero = ""
    var leadingZeroMin = ""
    var timeMin = Int()
    var actionwait = SKAction.waitForDuration(1.0)
    var timesecond = Int()
    var actionrun = SKAction.runBlock({
        timeMin++
        timesecond++
        if timesecond == 60 {timesecond = 0}
        if timeMin  / 60 <= 9 { leadingZeroMin = "0" } else { leadingZeroMin = "" }
        if timesecond <= 9 { leadingZero = "0" } else { leadingZero = "" }

        self.flyTimeText.text = "Flight Time [ \(leadingZeroMin)\(timeMin/60) : \(leadingZero)\(timesecond) ]"
    })
    self.flyTimeText.runAction(SKAction.repeatActionForever(SKAction.sequence([actionwait,actionrun])))
}

Here's the full code to build a timer for SpriteKit with Xcode 9.3 and Swift 4.1 这是使用Xcode 9.3和Swift 4.1为SpriteKit构建计时器的完整代码

在此输入图像描述

In our example the score label will be incrementd by 1 every 2 seconds. 在我们的示例中,分数标签将每2秒递增1。 Here's final result 这是最终结果

Good, let's start! 好,我们开始吧!

1) The score label 1)得分标签

First of all we need a label 首先,我们需要一个标签

class GameScene: SKScene {
    private let label = SKLabelNode(text: "Score: 0")
}

2) The score label goes into the scene 2)得分标签进入场景

class GameScene: SKScene {

    private let label = SKLabelNode(text: "Score: 0")

    override func didMove(to view: SKView) {
        self.label.fontSize = 60
        self.addChild(label)
    }
}

Now the label is at the center of the screen. 现在标签位于屏幕的中心。 Let's run the project to see it. 让我们运行项目来查看它。

在此输入图像描述

Please note that at this point the label is not being updated! 请注意,此时标签没有更新!

3) A counter 3)柜台

We also want to build a counter property which will hold the current value displayed by the label. 我们还想构建一个计数器属性,它将保存标签显示的当前值。 We also want the label to be updated as soon as the counter property is changed so... 我们还希望一旦计数器属性发生变化就更新标签......

class GameScene: SKScene {

    private let label = SKLabelNode(text: "Score: 0")
    private var counter = 0 {
        didSet {
            self.label.text = "Score: \(self.counter)"
        }
    }

    override func didMove(to view: SKView) {
        self.label.fontSize = 60
        self.addChild(label)

        // let's test it!
        self.counter = 123
    }
}

在此输入图像描述

4) The actions 4)行动

Finally we want to build an action that every 2 seconds will increment counter 最后,我们想要建立一个每2秒增加一个计数器的动作

class GameScene: SKScene {

    private let label = SKLabelNode(text: "Score: 0")
    private var counter = 0 {
        didSet {
            self.label.text = "Score: \(self.counter)"
        }
    }

    override func didMove(to view: SKView) {
        self.label.fontSize = 60
        self.addChild(label)
        // 1 wait action
        let wait2Seconds = SKAction.wait(forDuration: 2)
        // 2 increment action
        let incrementCounter = SKAction.run { [weak self] in
            self?.counter += 1
        }
        // 3. wait + increment
        let sequence = SKAction.sequence([wait2Seconds, incrementCounter])
        // 4. (wait + increment) forever
        let repeatForever = SKAction.repeatForever(sequence)

        // run it!
        self.run(repeatForever)
    }
}

The following code creates a new thread and waits 2 seconds before doing something on the main thread: 以下代码创建一个新线程,并在主线程上执行某些操作之前等待2秒:

BOOL continueIncrementingScore = YES;

dispatch_async(dispatch_queue_create("timer", NULL);, ^{
    while(continueIncrementingScore) {
        [NSThread sleepForTimeInterval:2];
        dispatch_async(dispatch_get_main_queue(), ^{
            // this is performed on the main thread - increment score here

        });
    }
});

Whenever you want to stop it - just set continueIncrementingScore to NO 无论何时你想停止它 - 只需将continueIncrementingScore设置为NO

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM