Why are there multiple SKSpriteNode and SKAudioNode objects when I only created one of each?

I created an SKSpriteNode object, but multiple objects are created, appearing as strings of them. The sound coming from the SKAudioNode object is also duplicated many times to give a reverberating effect.


I suspect it's only a matter of setting the right property of one of the objects.


Here is the code below. I noticed it doing this when I added the code to set the pinned properties of the physicsBody objects to true in the didBegin callback method.


import UIKit
import SpriteKit

class GameScene: SKScene {

    let player = SKSpriteNode(imageNamed: "player")
    var moveRate: CGFloat!
    
    override func didMove(to view: SKView) {
        
        physicsWorld.contactDelegate = self
        
        if UIDevice.current.userInterfaceIdiom == .phone {
            moveRate = 3
        } else {
            moveRate = 15
        }
        
        player.physicsBody = SKPhysicsBody(rectangleOf: player.size)
        player.physicsBody?.isDynamic = true
        player.physicsBody?.affectedByGravity = false
        player.physicsBody?.categoryBitMask = 0b00001
//        player.physicsBody?.collisionBitMask = 0b00001
        player.physicsBody?.contactTestBitMask = 0b00001

        player.position = CGPoint(x: 20 + player.size.width/2, y: view.frame.height / 2)
        
        addChild(player)
        
        let carEngineStart = SKAudioNode(fileNamed: "car_engine_running")
        
        addChild(carEngineStart)
        
        run(SKAction.repeatForever(
            SKAction.sequence([
                SKAction.run(addCompetitor),
                SKAction.wait(forDuration: 4)
                ])
        ))

    }
    
    override func update(_ currentTime: TimeInterval) {
        
        let internalRollSign = TrialSpriteKit.sign(internalRoll)
        
        switch internalRollSign {
        case .zero:
            break
        case .positive:
            if player.position.y < self.size.height {
                player.position.y += moveRate
            }
        case .negative:
            if player.position.y > 0 {
                player.position.y -= moveRate
            }
        }
        
    }
    
    enum Car: String, CaseIterable {
        
        case blue = "blue"
        case green = "green"
        case orange = "orange"
        case purple = "purple"
        case utili = "utili"
        case white = "white"
        case yellow = "yellow"
        
        static func random<G: RandomNumberGenerator>(using generator: inout G) -> Car {
            return Car.allCases.randomElement(using: &generator)!
        }
        
        static func random() -> Car {
            var g = SystemRandomNumberGenerator()
            return Car.random(using: &g)
        }
        
    }

    func random() -> CGFloat {
        return CGFloat(Float(arc4random()) / /* 0xFFFFFFFF */ 4294967296)
    }
    
    func random(min: CGFloat, max: CGFloat) -> CGFloat {
        return random() * (max - min) + min
    }
    
    func addCompetitor() {
        
        // Create sprite
        let carString = Car.random().rawValue
        let car = SKSpriteNode(imageNamed: carString)
        
        car.physicsBody = SKPhysicsBody(rectangleOf: car.size) // 1
        car.physicsBody?.isDynamic = true
        car.physicsBody?.affectedByGravity = false
//        car.physicsBody?.categoryBitMask = 0b00001
        car.physicsBody?.collisionBitMask = 0b00001
        car.physicsBody?.contactTestBitMask = 0b00001

        // Determine where to spawn the car along the Y axis
        let actualY = random(min: car.size.height/2, max: size.height - car.size.height/2)
        
        // Position the car slightly off-screen along the right edge,
        // and along a random position along the Y axis as calculated above
        car.position = CGPoint(x: size.width + car.size.width/2, y: actualY)
        
        // Add the car to the scene
        addChild(car)
        
        // Determine speed of the car
        let actualDuration = random(min: CGFloat(2.0), max: CGFloat(4.0))
        
        // Create the actions
        let actionMove = SKAction.move(to: CGPoint(x: -car.size.width/2, y: actualY), duration: TimeInterval(actualDuration))
        let actionMoveDone = SKAction.removeFromParent()
        car.run(SKAction.sequence([actionMove, actionMoveDone]))
        
    }

}

extension GameScene: SKPhysicsContactDelegate {
    
    func didBegin(_ contact: SKPhysicsContact) {
        
        contact.bodyB.pinned = true
        player.physicsBody?.pinned = true
        
        let explosion = SKSpriteNode(imageNamed: "explosion")
        
        explosion.position = contact.contactPoint
        
        addChild(explosion)
        
        run(
            SKAction.sequence(
                [
                    SKAction.playSoundFileNamed("car_explosion", waitForCompletion: true),
                    SKAction.run({
                        explosion.removeFromParent()
                        contact.bodyB.node?.removeFromParent()
                    }),
                    SKAction.wait(forDuration: 1),
                    SKAction.run({
                        self.player.zRotation = 0
                        self.player.position = CGPoint(x: 20 + self.player.size.width/2, y: self.view!.frame.height / 2)
                        self.player.physicsBody?.pinned = false
                    })
                ]
            )
        )
        
     }
    
}