Reputation: 63
I have tried searching other answers and I cannot find one that applies to my scenario. I am writing a game in swift and want to create a stopwatch that determines how the long the player is playing. When the user touches down, the stopwatch will start and when a certain action happens, then the timer stops and resets. I'd like to use minutes, seconds, milliseconds (i.e. 00:00.00).
Currently, the time function kind of works. It doesn't start at 0, it starts at the current seconds in time (I know thats when I start it, but I don't know how to start it at 0). It also only updates when I touch the screen, I need it to count from 00:00.00 and update on its own until the cancel action is fired.
Thank you for your time.
Here is what I have so far:
class GameScene: SKScene {
var activeTimer = SKLabelNode()
//var bestTime = SKLabelNode()
var startTime = TimeInterval()
//var currentTime = TimeInterval()
//var countingTime = TimeInterval()
var updateTimerAction = SKAction()
override func didMove(to view: SKView) {
activeTimer = self.childNode(withName: "active_timer") as! SKLabelNode
//bestTime = self.childNode(withName: "best_time") as! SKLabelNode
startTime = TimeInterval(Calendar.current.component(.second, from:Date()))
updateTimerAction = SKAction.sequence([SKAction.run(updateTimer), SKAction.wait(forDuration: 1.0)])
}
func startGame() {
// Other code
startGameTimer()
}
func resetGame() {
// Other code
stopGameTimer()
}
func startGameTimer() {
run(SKAction.repeatForever(updateTimerAction), withKey: "timer")
}
func updateTimer() {
activeTimer.text = stringFromTimeInterval(interval: startTime) as String
}
func stringFromTimeInterval(interval: TimeInterval) -> NSString {
let ti = NSInteger(interval)
let ms = Int((interval.truncatingRemainder(dividingBy: 1)) * 1000)
let seconds = ti % 60
let minutes = (ti / 60) % 60
return NSString(format: "%0.2d:%0.2d.%0.2d",minutes,seconds,ms)
}
func stopGameTimer() {
removeAction(forKey: "timer")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
startGame()
for touch in touches {
// other code
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
// other code
}
}
override func update(_ currentTime: TimeInterval) {
updateTimer()
if <action> {
// stop timer and reset game
resetGame()
}
}
Upvotes: 2
Views: 10827
Reputation: 63
Here is what I ended up doing. While El Tomato's answer appears to work, given the setup of my code I went a little different route. After talking with some other friends, I chose to user a timer due to the design of my application. If I happen to run into any problems using a Timer object I will update this question.
The code below will start on a touch, update in the format: "00:00.00" and stop when the action is fired. This time will pause until another touch and the timer will then start from zero.
class GameScene: SKScene {
var seconds = 0
var timer = Timer()
var timeStarted = Bool()
var activeTimer = SKLabelNode()
//var bestTime = SKLabelNode()
override func didMove(to view: SKView) {
activeTimer = self.childNode(withName: "active_timer") as! SKLabelNode
//bestTime = self.childNode(withName: "best_time") as! SKLabelNode
timeStarted = false
}
func startGame() {
startGameTimer()
timeStarted = true
}
func resetGame() {
timeStarted = false
stopGameTimer()
seconds = 0
}
func startGameTimer() {
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: (#selector(updateTimer)), userInfo: nil, repeats: true)
}
func updateTimer() {
seconds += 1
activeTimer.text = timeString(time: TimeInterval(seconds))
}
func timeString(time:TimeInterval) -> String {
let hours = Int(time) / 3600
let minutes = Int(time) / 60 % 60
let seconds = Int(time) % 60
return String(format:"%02i:%02i.%02i", hours, minutes, seconds)
}
func stopGameTimer() {
timer.invalidate()
removeAction(forKey: "timer")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if !timeStarted {
startGame()
}
for touch in touches {
// other code
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
// other code
}
}
override func update(_ currentTime: TimeInterval) {
if timeStarted {
updateTimer()
}
if <action> {
// stop timer and reset game
resetGame()
}
}
}
Upvotes: 1
Reputation: 6707
The following lines of code come from an actual macOS game that I submitted to Mac App Store some 3 weeks ago. It counts down a number to 0. In your case, change
self.currentSeconds -= 1
to
self.currentSeconds += 1
timeBackNode
is an empty node that holds SKLabelNode objects timeNode0 and timeNode1. So create it when the game starts with didMove
.
var currentSeconds = Int() // currentSeconds
var timeNode0 = SKLabelNode() // timeNode0
var timeNode1 = SKLabelNode() // timeNode1
func setupTimeGoal() {
enumerateChildNodes(withName: "//TimerBack", using: { timeBackNode, _ in
let goal = self.timeDict["num"] as! Int
self.currentSeconds = goal // In your case, goal is 0 since you are going to count up.
self.timeNode0 = SKLabelNode(fontNamed: "Your font's font name")
self.timeNode0.text = self.makeTimeWithSeconds(secs: goal)
self.timeNode0.fontSize = 26
self.timeNode0.fontColor = SKColor.black
self.timeNode0.horizontalAlignmentMode = .center
self.timeNode0.position = CGPoint(x: 1, y: -22)
timeBackNode.addChild(self.timeNode0)
self.timeNode1 = SKLabelNode(fontNamed: "Your font's font name")
self.timeNode1.text = self.makeTimeWithSeconds(secs: goal) // this function translate a counting number into a time code like 00:00:00
self.timeNode1.fontSize = 26
self.timeNode1.fontColor = SKColor.cyan
self.timeNode1.horizontalAlignmentMode = .center
self.timeNode1.position = CGPoint(x: 0, y: -23)
timeBackNode.addChild(self.timeNode1)
})
}
func repeatTime() {
let waitAction = SKAction.wait(forDuration: 1.0)
let completionAction = SKAction.run {
if self.currentSeconds == 0 && self.gameState == .playing {
self.removeAction(forKey: "RepeatTime")
self.proceedLoss()
return
}
self.currentSeconds -= 1
self.timeNode0.text = self.makeTimeWithSeconds(secs: self.currentSeconds)
self.timeNode1.text = self.makeTimeWithSeconds(secs: self.currentSeconds)
}
let seqAction = SKAction.sequence([waitAction, completionAction])
let repeatAction = SKAction.repeatForever(seqAction)
self.run(repeatAction, withKey: "RepeatTime")
}
Upvotes: 2
Reputation: 745
Instead of using a TimeInterval object, it might be better to use a Timer object which calls a function that increments the value of a label (or an internal variable) from 0 onwards accordingly after every 1 second(or can be made lesser). And for stopping the clock just call the Timer object's invalidate function. Check the documentation for more info: https://developer.apple.com/documentation/foundation/timer
Upvotes: 2