So I have a game with a sprite called enemy which has an invisible circle around it and a player sprite that can move freely 360 degrees around the enemy. When the player enters the frame of the enemy's circle however, this triggers the enemy to begin shooting bullets at the player. The player can still move. The problems I am having is with keeping the enemy continuously shooting the bullets as well as continuously firing them toward the position of the player even after the player has changed locations.
I will add some code to help you checkout the problem:
For fast reference we have this short piece here where i included only the minimum of what I thought was necessary for fast help. (I am not sure where to call the fireBullets() function... Inside the update function? Inside of did move? Inside of didBegin?).
func fireBullets() {
var fireBulletAction: SKAction
fireBulletAction = SKAction.run {
let bullet = self.createBullets()
var moveBullet = SKAction.move(to: self.player.position, duration: 1)
bullet.run(moveBullet)
}
var waitForDuration: SKAction
waitForDuration = SKAction.wait(forDuration: 1)
var bulletSequence = SKAction.sequence([fireBulletAction, waitForDuration])
var repeatBulletSequence = SKAction.repeatForever(bulletSequence)
enemy.run(repeatBulletSequence, withKey: "fireBullet")
}
Or to recreate the problem even faster and easier with all components(It's still only 96 lines of code), I made this simple GameScene.swift file which you can easily copy and paste from text into XCode.
EDIT: I had not considered that you were using SKAction.repeatForever
in your project, so I've edited my original answer which was partly wrong.
This is your problem:
override func didMove(to view: SKView) {
// side note: add this when overriding methods
super.didMove(to: view)
createPlayer(view: view)
createEnemy(view: view)
if collisionDetected {
fireBullets()
}
}
This method is called when the scene is presented. And in your sample project at that point collisionDetected
is false
, so fireBullets()
will never trigger.
You should move it inside the update()
function, which is called once every 1/60 seconds, assuming your fps setting is 60. Note: I'm also assuming you don't want to locate the player and the enemy such that the shooting is triggered as soon as the scene is presented:
override func update(_ currentTime: TimeInterval) {
super.update(currentTime)
if !collisionDetected {
detectCollisions()
} else if !isShooting {
fireBullets()
}
}
Note that I've added a isShooting
check, it's a new variable. The reason of it is that, since you're using SKAction.repeatForever
and update()
is called multiple times, you want to trigger the fireBullets()
method just once. A way to achieve this is to declare indeed another variable to keep track of it.
var isShooting = false
Then in fireBullets()
you set it to true as soon as it is fired for the first time, so that it will not be fired again:
isShooting = true
If you wanna read more about it, here is another similar example, from a question I answered.
This way in your sample project everything works as intended, at least in my simulator, as per what you have stated.
On a side node, you should remove the bullet nodes from the scene at some point, I suppose you actually take care of this in your actual project, otherwise the node count will skyrocket up untill a crash.
Also, for collision detection consider using SKPhysicsContactDelegate
. You could handle the fireBullets()
call there.