SpriteKit – Creando un temporizador

¿Cómo puedo crear un temporizador que se active cada dos segundos y que incremente el puntaje en uno en un HUD que tengo en mi pantalla? Este es el código que tengo para el 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]; } } 

En el kit de Sprite , no use NSTimer , performSelector:afterDelay: o Grand Central Dispatch (GCD, es decir, cualquier método de dispatch_... ) porque estos métodos de sincronización ignoran el estado de paused un nodo, escena o vista. Además, no sabe en qué punto del ciclo del juego se ejecutan, lo que puede causar una variedad de problemas dependiendo de lo que realmente haga su código.

Las dos únicas formas sancionadas de realizar algo basado en tiempo en Sprite Kit es utilizar el método SKScene update: y usar el parámetro currentTime pasado para realizar un seguimiento del tiempo.

O más comúnmente usaría una secuencia de acción que comienza con una acción de espera:

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

Y para ejecutar el código repetidamente:

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

Alternativamente, también puede usar performSelector:onTarget: lugar de runBlock: o quizás use una customActionWithDuration:actionBlock: si necesita imitar el método SKScene update: y no sabe cómo reenviarlo al nodo o dónde sería inconveniente el reenvío.

Vea la referencia SKAction para más detalles.


ACTUALIZACIÓN: ejemplos de código usando Swift

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

Aund para ejecutar el código repetidamente:

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

En Swift utilizable:

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

He tomado el rápido ejemplo anterior y lo he añadido en ceros a la izquierda para el reloj.

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

Aquí está el código completo para construir un temporizador para SpriteKit con Xcode 9.3 y Swift 4.1

enter image description here

En nuestro ejemplo, la etiqueta de puntuación se incrementará en 1 cada 2 segundos. Aquí está el resultado final

Bien, ¡empecemos!

1) La etiqueta de puntaje

Antes que nada, necesitamos una etiqueta

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

2) La etiqueta de puntuación entra en la escena

 class GameScene: SKScene { private let label = SKLabelNode(text: "Score: 0") override func didMove(to view: SKView) { self.label.fontSize = 60 self.addChild(label) } } 

Ahora la etiqueta está en el centro de la pantalla. Vamos a ejecutar el proyecto para verlo.

enter image description here

Tenga en cuenta que en este momento la etiqueta no se está actualizando.

3) Un contador

También queremos construir una propiedad de contador que contendrá el valor actual que muestra la etiqueta. También queremos que la etiqueta se actualice tan pronto como se cambie la propiedad del contador, así que …

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

enter image description here

4) Las acciones

Finalmente, queremos construir una acción que cada 2 segundos incremente el contador

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

El siguiente código crea un nuevo hilo y espera 2 segundos antes de hacer algo en el hilo principal:

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

Siempre que quiera detenerlo, simplemente configure continueIncrementingScore en NO