ios - 当我只创建一个时,为什么会有多个 SKSpriteNode 和 SKAudioNode 对象?

标签 ios swift sprite-kit skspritenode skaudionode

我创建了一个 SKSpriteNode 对象,但是创建了多个对象,显示为它们的字符串。来自 SKAudioNode 对象的声音也被复制多次以产生回响效果。

这是下面的代码。当我在 didBegin 回调方法中添加代码以将 physicsBody 对象的固定属性设置为 true 时,我注意到它这样做了。

这是截图。注意有多个爆炸 SKSpriteNode 对象。我只为爆炸创建了一个 SKSpriteNode 对象。

multiple car explosions

我怀疑我可以更改某处的设置以禁用此效果。

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 = 5
        } 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.bodyA.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
                    })
                ]
            )
        )

     }

}

最佳答案

您的问题中有多个节点的原因。

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.

根据 SpriteKit 文档,当您将 pinned 属性设置为 true 并且父节点具有物理实体时,这两个实体将被视为通过销接头连接。

因为两个物体是相连的,即使没有碰撞,也会重复接触,导致重复调用 didBegin 函数。每次调用 didBegin 都会创建一个新的 Sprite 节点。您的游戏调用 didBegin 的次数比您预期的要多,因此您最终得到的 Sprite 节点比您预期的要多。

一般的解决方案是只有在发生碰撞时才创建爆炸 Sprite 节点,并且每次碰撞只创建一次 Sprite 节点。

关于ios - 当我只创建一个时,为什么会有多个 SKSpriteNode 和 SKAudioNode 对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56089098/

相关文章:

ios - 如何在 swift spritekit 中创建重复片段

swift - 为什么 XCode Clean and Build 会修复 EXC_BAD_ACCESS?

ios - 在 Swift 中跟踪滑动运动

swift - 触摸开始时从 SKScene 中删除函数

xcode - 物理物体与另一个物理物体碰撞

iphone - iOS:推送通知并在登录时重新注册设备。

ios - 我们应该为一个单例类创建多个对象吗?这适合单例设计模式吗

ios - 如何提供TestFlight iOS beta应用程序的反馈?

ios - 返回 App SpriteKit 后按播放

ios - 加载非 http ://url scheme 后的操作