Reputation: 311
In my game, you tap anywhere on the screen and a bullet goes in that direction. The only problem is that you can shoot as fast as you can tap. Is there any way to add a delay after each shot. So I would like to shoot, wait 1 second then shoot. Here is my code in touchesEnded:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else {
return
}
let touchLocation = touch.location(in: self)
//Set up initial location of bullet and properties
let bullet = SKSpriteNode(imageNamed: "bullet")
bullet.name = "Bullet"
bullet.position = player.position
bullet.setScale(0.75)
bullet.zPosition = 1
bullet.physicsBody = SKPhysicsBody(circleOfRadius: bullet.size.width/2)
bullet.physicsBody?.isDynamic = true
bullet.physicsBody?.categoryBitMask = PhysicsCategory.Projectile
bullet.physicsBody?.contactTestBitMask = PhysicsCategory.Monster
bullet.physicsBody?.collisionBitMask = PhysicsCategory.None
bullet.physicsBody?.usesPreciseCollisionDetection = true
//Determine offset of location to bullet
let offset = touchLocation - bullet.position
//Stops Bullet from shooting backwards
if (offset.y < 0) { return }
addChild(bullet)
//Get the direction of where to shoot
let direction = offset.normalized()
//Make it shoot far enough to be guaranteed off screen
let shootAmount = direction * 1000
//Add the shoot amount to the current position
let realDest = shootAmount + bullet.position
//Create the actions
if currentGameState == gameState.inGame {
let actionMove = SKAction.move(to: realDest, duration: 1.0)
let actionMoveDone = SKAction.removeFromParent()
bullet.run(SKAction.sequence([actionMove, actionMoveDone]))
}
}
Thanks for any help.
Upvotes: 3
Views: 572
Reputation: 13675
You can do this using action keys. An action key is a string that makes an action identifiable.
How to use it in this case?
As I said already in comments, you will fire a bullet, then run an action with a key, on a specific node, which will last one second. A presence of this key/action means that weapon is locked. So every time you try to fire a bullet, you check if this key is present on a specific node. When action finishes, the key will be automatically removed as well. Here is the code:
import SpriteKit
let kLockWeaponActionKey = "kLockWeaponActionKey"
class GameScene: SKScene {
func shoot(atPoint targetLocation:CGPoint){
// 1 check if weapon is unlocked, or return
guard self.action(forKey: kLockWeaponActionKey) == nil else {
print("Weapon locked")
return
}
let bullet = SKSpriteNode(color: .purple, size: CGSize(width: 20, height: 20))
addChild(bullet)
let shoot = SKAction.move(to: targetLocation, duration: 3)
//2 shoot
bullet.run(shoot)
//3 lock weapon
self.run(SKAction.wait(forDuration: 1), withKey: kLockWeaponActionKey)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let targetLocation = touch.location(in: self)
self.shoot(atPoint:targetLocation)
}
}
}
If you try to spam bullets fast, you will see a log in the console which says : "weapon locked".
Upvotes: 0
Reputation: 423
This is a more simple approach, based on the use of Date:
var time = Date()
func shoot(after timeInterval: Double) {
guard Date() - timeInterval > time else {
print("WAIT")
return
}
print("SHOOT")
time = Date() // reset the timer
}
// CALL THIS INSIDE touchesEnded
shoot(after: 1)
Just modify for your needs :]
Upvotes: 1
Reputation: 2357
You could take a look at the Throttle
implementation of RxSwift for one possible solution. Throttle is used to limit the number of events created in a defined time interval:
let timeIntervalSinceLast: RxTimeInterval
if let lastSendingTime = _lastSentTime {
timeIntervalSinceLast = now.timeIntervalSince(lastSendingTime)
}
else {
timeIntervalSinceLast = _parent._dueTime
}
let couldSendNow = timeIntervalSinceLast >= _parent._dueTime
if couldSendNow {
self.sendNow(element: element)
return
}
Upvotes: 0