Подтвердить что ты не робот

SpriteKit - создание таймера

Как создать таймер, который срабатывает каждые две секунды, чтобы увеличить счет на один на 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];
    }
}
4b9b3361

Ответ 1

В Sprite Kit не используйте NSTimer, performSelector:afterDelay: или Grand Central Dispatch (GCD, то есть любой метод dispatch_...), поскольку эти методы синхронизации игнорируют состояние узла, сцены или paused просмотра. Более того, вы не знаете, в какой момент игрового цикла они выполняются, что может вызвать множество проблем в зависимости от того, что на самом деле делает ваш код.

Единственный два санкционированных способа выполнения чего-либо, основанного на времени, в Sprite Kit - это использовать метод SKScene update: method и использовать переданный параметр currentTime для отслеживания времени.

Или чаще вы просто используете последовательность действий, которая начинается с действия ожидания:

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

И запустить код повторно:

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

В качестве альтернативы вы также можете использовать performSelector:onTarget: вместо runBlock: или, возможно, использовать customActionWithDuration:actionBlock: если вам нужно имитировать метод update: SKScene и не знаете, как переслать его на узел или где пересылка будет неудобной.

См. Ссылку SKAction для деталей.


ОБНОВЛЕНИЕ: примеры кода с использованием Swift

Swift 5

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

Свифт 3

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

Swift 2

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

И запустить код повторно:

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

Ответ 2

В 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])))

Ответ 3

Я взял приведенный выше пример и добавил в начало нулей для часов.

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

Ответ 4

Здесь полный код для создания таймера для SpriteKit с Xcode 9.3 и Swift 4.1

введите описание изображения здесь

В нашем примере метка счета будет увеличиваться на 1 раз в 2 секунды. Здесь конечный результат

Хорошо, пускай!

1) Метка метки

Прежде всего нам нужна метка

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

2) Метка метки попадает в сцену

class GameScene: SKScene {

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

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

Теперь метка находится в центре экрана. Позвольте запустить проект, чтобы увидеть его.

введите описание изображения здесь

Обратите внимание, что в этот момент метка не обновляется!

3) Счетчик

Мы также хотим создать свойство counter, которое будет удерживать текущее значение, отображаемое меткой. Мы также хотим, чтобы метка обновлялась сразу после изменения свойства счетчика, поэтому...

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 test it!
        self.counter = 123
    }
}

введите описание изображения здесь

4) Действия

Наконец, мы хотим создать действие, которое каждые 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)
    }
}

Ответ 5

Следующий код создает новый поток и ждет 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

        });
    }
});

Всякий раз, когда вы хотите его остановить, просто установите continueIncrementingScore на NO