I'm creating a game where you have to tap as much circles as possible for 10 seconds. If the user taps a circle, counter+=0.15 milliseconds, but if he misses it, counter-=0.15
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(GameScene.updateTimer), userInfo: nil, repeats: true)
Now this is the timer function:
func updateTimer() {
counter-=1
timerLabel.text = "\(counter)"
if counter == 0 {
timerLabel.fontColor = UIColor.red
timerLabel.text = "TIME'S UP"
resetGameLabel.isHidden = false
Circle.removeFromParent()
timer.invalidate()
}
if counter < 0 {
timerLabel.fontColor = UIColor.red
timerLabel.text = "TIME'S UP"
resetGameLabel.isHidden = false
Circle.removeFromParent()
timer.invalidate()
}
}
Now what I want is to display milliseconds on that timerLabel.text, how can I do it?
Hope this helps:
func currentTimeInMilliSeconds()->Int64 {
return Int64(Date().timeIntervalSince1970 * 1000)
}
var counterInMilliSeconds = currentTimeInMilliSeconds()
If You Create a var timeToDisplay: Double and make it equals to the time, you can add or remove the milliseconds you want, and then just make the label Equals to var timeToDisplay instead of your counter.
Related
I have been trying to create a delay of one or two seconds on a self repeating timer. This is how I create the timer:
currentThread = Timer.scheduledTimer(timeInterval: 0.001, target: self, selector: #selector(Movement.updatePosition), userInfo: nil, repeats: true)
So the timer constantly runs the method updatePosition(). However, I have an if statement within that method where I would like to have the timer be delayed for a few seconds:
if distance <= respawnDistance * 0.1 {
// Delay timer for 1 second
}
And I was thinking that I could do this:
currentThread.invalidate()
And then just create another Timer that runs after 1 second, which leads to the reactivation of the previous timer. However, I think that would be inefficient if there is a way to sleep the current Timer?
NSTimer is not that accurate. Its maximum resolution is somewhere around 50 - 100ms. Anyhow, you can add a variable to control the firing of the timer:
var doNotUpdate = false
if distance <= respawnDistance * 0.1 {
doNotUpdate = true
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
doNotUpdate = false
}
}
func updatePosition() {
if doNotUpdate { return }
// update your position
}
The following function counts down a red to green light, then counts the reaction time for the user to hit a button after the green light is displayed.
func updateCounter() {
timerInt -= 1
if timerInt == 2{
light.image = UIImage(named: "r.png")
} else if timerInt == 1 {
light.image = UIImage(named: "yellow.png")
} else if timerInt == 0 {
light.image = UIImage(named: arc4random_uniform(2) == 0 ? "no.png" : "g.png")
timer.invalidate()
startStop.isEnabled = true
scoreTimer = Timer.scheduledTimer(timeInterval: 0.0001, target: self, selector: #selector(ViewController.updateScoreTime), userInfo: nil, repeats: true)
}
}
Where it states else if timerInt == 0, the user is given a random function. An image will either turn green or an "x" is displayed.
If the x is displayed, I would like the user to to have to hit a button that states game over to restart the red light sequence.
If a green light is displayed, I would like the user to have to test their reaction time.
This is how the function already runs now, except it does not change if the x is displayed. I guess I would like the function to run as follows:
if timeInt == 0 and green light is chosen
then run test reaction time
else if timeInt == 0 and x is chosen
then end reaction time and run game over button
How can I achieve this?
I am not exactly sure what you are trying to achieve, so I did my best. Try this:
func updateCounter() {
timerInt -= 1
if timerInt == 2{
light.image = UIImage(named: "r.png")
} else if timerInt == 1 {
light.image = UIImage(named: "yellow.png")
} else if timerInt == 0 {
let green = (arc4random_uniform(2) == 0)
light.image = UIImage(named: (green ? "g.png" : "no.png"))
if green {
// run test reaction time
} else {
//end reaction time and run game over button
}
// NOTE: Do you mean `scoreTimer.invalidate()`?
timer.invalidate()
startStop.isEnabled = true
// NOTE: This time interval seems WAY too small ↓↓↓↓↓↓: it is only a millisecond!
scoreTimer = Timer.scheduledTimer(timeInterval: 0.0001, target: self, selector: #selector(ViewController.updateScoreTime), userInfo: nil, repeats: true)
}
}
Replace the first two comments (aka lines beginning with //) with the appropriate code, and take note of the third and fourth comments.
So, I have this timer that is setup to run a specific function (which are both shown below) on a time interval variable called 'frequency' when I try and change the timeinterval variable frequency to a lower number based on the score number it doesn't seem to change the rate at which it fires it just seems to fire at the same time even if the frequency is changed to a lower number
override func didMove(to view: SKView) {
Timer.scheduledTimer(timeInterval: frequency, target: self, selector: #selector(GameScene.spawnFallingOjects), userInfo: nil, repeats: true)
}
func spawnFallingOjects() {
if (GameState.current == .playing || GameState.current == .blackstone) {
guard usingThirdEye == false else { return }
let scoreLabel = childNode(withName: "scoreLabel") as! Score
let lane = [-100, -50 , 0, 50, 100]
let duration = 3.0
switch scoreLabel.number {
case 0...50:
frequency = 6.0
print("frequency has changed: \(frequency)")
case 51...100:
frequency = 4.5
print("frequency has changed: \(frequency)")
case 101...200000:
frequency = 1.1
print("frequency has changed: \(frequency)")
default:
return
}
let randomX = lane[Int(arc4random_uniform(UInt32(lane.count)))]
let object:Object = Object()
object.createFallingObject()
object.position = CGPoint(x: CGFloat(randomX), y: self.size.height)
object.zPosition = 20000
addChild(object)
let action = SKAction.moveTo(y: -450, duration: duration)
object.run(SKAction.repeatForever(action))
}
}
How do I make the timer fire faster when the frequency number changes to a lower number? should I recreate the timer at the end of the function?
You should actually avoid using Timer, Sprite kit has its own time functionality, and Timer does not work well with it and is a real pain to manage.
Instead, use SKAction's to wait and fire:
let spawnNode = SKNode()
override func didMove(to view: SKView) {
let wait = SKAction.wait(forDuration:frequency)
let spawn = SKAction.run(spawnFallingObjects)
spawnNode.run(SKAction.repeatForever(SKAction.sequence([wait,spawn])))
addChild(spawnNode)
}
Then to make it faster, just do:
switch scoreLabel.number {
case 0...50:
spawnNode.speed = 1
print("speed has changed: \(spawnNode.speed)")
case 51...100:
spawnNode.speed = 1.5
print("speed has changed: \(spawnNode.speed)")
case 101...200000:
spawnNode.speed = 2
print("speed has changed: \(spawnNode.speed)")
default:
return
}
The timeInterval property of Timer is a readonly property. (And your code is not trying to write a new frequency into the property...)
should I recreate the timer at the end of the function?
Nearly yes. Just you have no need to do it at the end.
With changing your method header like this:
func spawnFallingOjects(_ timer: Timer) {
You can access the fired Timer through the parameter timer, so you may need to write something like this just after switch scoreLabel.number {...}:
if frequency != timer.timeInterval {
//Invalidate old Timer...
timer.invalidate()
//And then allocate new one
Timer.scheduledTimer(timeInterval: frequency, target: self, selector: #selector(GameScene.spawnFallingOjects), userInfo: nil, repeats: true)
}
You can modify the fireDate property of an existing Timer (in case which still isValid), but recreating a Timer instance is not a heavy operation (comparing to creating an SKSpriteNode instance), so recreating a new Timer seems to be a little bit easier.
I am making a game similar to pitfall in Swift and I am trying to make a boolean that shows whether the player is jumping or not. I want the boolean to become false after 3 seconds so that the player moves down again. I have tried using a delay function but it didn't work.
Thanks in advance.
Try this:
let delay = 3 // seconds to wait before firing
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(delay)) {
// set your var here
}
Replace DispatchQueue.main with whichever queue you're using.
The follow code snippet describes Player objects that have a isJumping property. When it's set to true (using didSet), it automatically starts a timer that after 3 seconds resets isJumping to false.
Please not that the snippet makes use of a NSTimer extensions for comfortably starting and handling the timer. Credits to https://gist.github.com/natecook1000/b0285b518576b22c4dc8
class Player {
private var resetJumpingTimer: NSTimer?
var isJumping: Bool = false {
didSet {
resetJumpingTimer?.invalidate() // Stops the timer in case it was already started
if isJumping {
self.resetJumpingTimer = NSTimer.schedule(3.0) { [weak self] _ in
self?.isJumping = false
}
}
}
}
}
extension NSTimer {
class func schedule(delay delay: NSTimeInterval, handler: NSTimer! -> Void) -> NSTimer {
let fireDate = delay + CFAbsoluteTimeGetCurrent()
let timer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault, fireDate, 0, 0, 0, handler)
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes)
return timer
}
}
After the player jumps, you create an NSTimer.
Declare global variables let timer = NSTimer() and var seconds = 3
Then after the player jumps you set the timer:
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: #selector(YOUR_CLASS_NAME.updateTimer()), userInfo: nil, repeats: true)
Then the method:
func updateTimer() {
seconds -= 1
if seconds == 0 {
// Do stuff
timer.invalidate() // Stop the timer
seconds == 3 // Reset # of seconds
}
Give it a try.
func update() {
seconds -= 1
if seconds <= 0 {
score = 5
}
}
You may want to make it <= if you don't invalidate the timer. That way it stays the variable, etc.
I have a timer that spawns another enemy every second. I want it so that based on the score that the user has the time between spawning new enemies decreases. Basically, the higher the score the more thee enemies. Here is my current timer code.
EnemyTimer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("enemies"), userInfo: nil, repeats: true)
All you need to do is make sure you use 2 or more decimal places eg: you want it to spawn faster than 1 second so..
let newSpeed = 0.75
EnemyTimer = NSTimer.scheduledTimerWithTimeInterval(newSpeed, target: self, selector: Selector("enemies"), userInfo: nil, repeats: true)
this will make them spawn every .75 seconds. Just make sure if you use whole number you do 1.0 not just 1 otherwise yes you will get the error Cannot convert value of type 'Int' to expected argument type 'NSTimeInterval' (aka 'Double')
Instead of a timer, you can use the update function of the SKScene to spawn enemies at variable intervals. For example
var previousTime : NSTimeInterval = -1
let score = 100
func spawnEnemy() {
print("Spawning Enemy")
}
override func update(currentTime: NSTimeInterval) {
var spawnTimeInterval : NSTimeInterval = 1 - (NSTimeInterval(score)/1000)
let minSpawnTime : NSTimeInterval = 0.1
if spawnTimeInterval < minSpawnTime {
spawnTimeInterval = minSpawnTime
}
if previousTime != -1 {
let elapsedTimeInterval = currentTime - previousTime
if elapsedTimeInterval > spawnTimeInterval {
spawnEnemy()
previousTime = currentTime
}
} else {
previousTime = currentTime;
}
}
In the above code, the each 100 points will reduce 0.1 seconds from the spawnTimeInterval. You can change this calculation as per your requirements.