1

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.

quickj
  • 25
  • 5

1 Answers1

0

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.

oneshot
  • 591
  • 1
  • 5
  • 27
  • Hey so I have tried this exact solution, however the only problem is that calling fireBullets() in the update function like you suggested is causing the bullets to be created much more rapidly than the 1 second intervals defined in my wait(for: duration) SKAction being ran in the sequence. I also tried using similar logic in the touches began stating if collisionDetected { fireBullets() } Sadly this only works if the screen is touched again after the collisionDetected becomes true. – quickj Jul 12 '23 at 19:03
  • I've edited the answer a few minutes ago, indeed I had skipped the fact that you were repeating the action forever. Check it out my edited answer now, it should be good. – oneshot Jul 12 '23 at 19:05
  • The edited solution works. – quickj Jul 12 '23 at 19:16
  • Another issue I'm seeing now, you're not calling super when overriding methods. I think you should do it, I'm gonna edit my answer once more. – oneshot Jul 12 '23 at 19:24
  • Could you explain a little more why you think I should call super here? Im not sure that it is necessary in this case when overriding the parent method. Thanks – quickj Jul 12 '23 at 21:22
  • It's not necessary but not "harmful" also for what you want to achieve in your project. And the best practice is to always call super, unless you need to disable functionalities in the base class. However, maybe me saying "issue" was misleading, not an issue just a good practice. I'm sorry but, as you can probably tell, english is not my native language. Hope you got the point now – oneshot Jul 12 '23 at 21:43
  • I will add the super calls if it doesn't cause any harm. Just for good practice – quickj Jul 12 '23 at 22:15
  • I ran into another problem a couple minutes ago, if you wouldn't mind taking a look when you aren't busy https://stackoverflow.com/questions/76674774/how-can-i-complete-the-action-of-an-skaction-move-where-the-object-being-moved-i – quickj Jul 12 '23 at 22:16
  • Now that I see that you are indeed using PhysicsContactDelegate, consider moving fireBullets() there, it’s better than in update(). – oneshot Jul 13 '23 at 03:57
  • Should I move it to didBeginContact? or didSimulatePhysics? – quickj Jul 13 '23 at 12:31