66498


didBegin内に1つのNodeに対する接触は問題なく動作するのですが、didBegin内にもう一つ別のNodeの接触処理を書いたとこと、うまく作動しません。  
どうかよろしくお願い致します。

[code]

    func didBegin(_ contact: SKPhysicsContact) {
    
        var asteroid: SKPhysicsBody
        var target: SKPhysicsBody
        var itemBox: SKPhysicsBody
    
        if contact.bodyA.categoryBitMask == asteroidCategory {
            asteroid = contact.bodyA
            target = contact.bodyB
        } else {
            asteroid = contact.bodyB
            target = contact.bodyA
        }
    
        // ** 別の接触処置を記す場合、このコードでbodyAとbodyBを宣言してはダメなのでしょうか。
        if contact.bodyA.categoryBitMask == itemBoxCategory {
            itemBox = contact.bodyA
            target = contact.bodyB
        } else {
            itemBox = contact.bodyB
            target = contact.bodyA
        }
    
        guard let asteroidNode = asteroid.node as? AsteroidNode else { return }
        guard let tagetNode = target.node else { return }
        guard let itemBoxNode = itemBox.node else { return }
    
        // ** ここが作動しません。
        if(contact.bodyA.categoryBitMask == itemBoxCategory && contact.bodyB.categoryBitMask == spaceshipCategory)  || (contact.bodyB.categoryBitMask == itemBoxCategory && contact.bodyA.categoryBitMask == spaceshipCategory) {
            itemBoxNode.removeFromParent()
            addChild(item1)
        }
    
        guard let Bakuhatu = SKEmitterNode(fileNamed: "Bakuhatu") else { return }
        Bakuhatu.position = asteroidNode.position
        addChild(Bakuhatu)
    
        if target.categoryBitMask == missileCategory  {
            tagetNode.removeFromParent()
            asteroidNode.life -= missile.Pw
    
            if asteroidNode.life <= 0 {
                asteroidNode.removeFromParent()
            }
        }
    
        self.run(SKAction.wait(forDuration: 1.0)) {
            Bakuhatu.removeFromParent()
        }
    
        if target.categoryBitMask == spaceshipCategory {
            asteroidNode.removeFromParent()
            self.gameVC.lifeGauge.setProgress(gameVC.lifeGauge.progress - 0.2, animated: true)
            lifeLabel.text = "Life:\(Int(gameVC.lifeGauge.progress * 100))"
    
            if self.gameVC.lifeGauge.progress <= 0.0 {
                GameOver()
            }
        }
    
    }
    
[/code]

※補足  
接触に必要な  
categoryBitMask  
collisionBitMask  
contactTestBitMask  
は、下記の様に設定しております。

[code]

    let spaceshipCategory : UInt32 = 0b0001
    let missileCategory : UInt32 = 0b0010
    let asteroidCategory  : UInt32 = 0b0100
    let itemBoxCategory : UInt32 = 0b1000
    
    //spaceship
    self.spaceship.physicsBody?.categoryBitMask = spaceshipCategory
    self.spaceship.physicsBody?.collisionBitMask = spaceshipCategory | missileCategory | asteroidCategory | itemBoxCategory
    self.spaceship.physicsBody?.contactTestBitMask = asteroidCategory
    
    //itemBox
    itemBox.physicsBody?.categoryBitMask = itemBoxCategory
    itemBox.physicsBody?.collisionBitMask = spaceshipCategory | itemBoxCategory
    itemBox.physicsBody?.contactTestBitMask = spaceshipCategory
    
[/code]


CC BY-SA 4.0
2020-05-10T12:29:43.297
66490
2020-05-11T02:40:04.600
2020-05-11T00:20:10.237
3060
39881
question
swift xcode spritekit
Swift SpriteKit 接触処理（didBegin） が動作しない


まず大前提ですが、「spaceshipとitemBoxの接触を検出したい」と言う場合には、コメントに示したように、spaceship側とitemBox側の両方の`contactTestBitMask`に相手側を指定するようにしてください。片側だけだと、うまく検出してくれない場合があるようです。

[code]

    self.spaceship.physicsBody?.contactTestBitMask = asteroidCategory | itemBoxCategory
    
[/code]

* * *

さて、その部分の設定は正しくできているとして、あなたの現在の`didBegin(_:)`を見てみると、最も問題になるのはこの行だと言えます。

[code]

        guard let asteroidNode = asteroid.node as? AsteroidNode else { return }
    
[/code]

この行、「asteroid対何か」「何か対asteroid」の接触時の処理を書くためには必須のものですが、それ以外の接触でこの行にたどり着いた場合、変数`asteroid`には、その名前に反してasteroidを表す`SKPhysicsBody`は入っていませんから、`as?
AsteroidNode`の結果が必ず`nil`となり、else節内の`return`で処理を終了してしまいます。

* * *

今までは「asteroid対何か」「何か対asteroid」でしか`didBegin(_:)`が呼ばれなかったところに「itemBox対spaceship」「spaceship対itemBox」の処理を割り込ませたために、どの部分がどんな場合に実行されるのかと言ったことがごっちゃになってしまっているのが、ある場合には必要な行が別の場合に邪魔をする、と言う原因になっています。

混乱を避けるためには、接触時の処理の種類ごとにメソッドを分けてやると良いでしょう。

[code]

    //「asteroid対何か」「何か対asteroid」
        func hit(asteroid: SKPhysicsBody, target: SKPhysicsBody) {
    
            guard let asteroidNode = asteroid.node as? AsteroidNode else { return }
            guard let tagetNode = target.node else { return }
    
            guard let bakuhatu = SKEmitterNode(fileNamed: "Bakuhatu") else { return }
            bakuhatu.position = asteroidNode.position
            addChild(bakuhatu)
    
            if target.categoryBitMask == missileCategory  {
                tagetNode.removeFromParent()
                asteroidNode.life -= missile.Pw
    
                if asteroidNode.life <= 0 {
                    asteroidNode.removeFromParent()
                }
            }
    
            self.run(SKAction.wait(forDuration: 1.0)) {
                bakuhatu.removeFromParent()
            }
    
            if target.categoryBitMask == spaceshipCategory {
                asteroidNode.removeFromParent()
                self.gameVC.lifeGauge.setProgress(gameVC.lifeGauge.progress - 0.2, animated: true)
                lifeLabel.text = "Life:\(Int(gameVC.lifeGauge.progress * 100))"
    
                if self.gameVC.lifeGauge.progress <= 0.0 {
                    GameOver()
                }
            }
    
        }
    
        //「itemBox対spaceship」「spaceship対itemBox」
        func hit(itemBox: SKPhysicsBody, target: SKPhysicsBody) {
    
            guard let itemBoxNode = itemBox.node else { return }
    
            itemBoxNode.removeFromParent()
            addChild(item1)
    
            //...
        }
    
[/code]

(`hit(asteroid:target:)`中に書かれた処理の一部は、`hit(itemBox:target:)`にも必要なのかもしれません。その場合は、`//...`のところの必要な処理を追加していってください。もちろん両者に共通な処理は、さらに別のメソッドを作って、そこにまとめてしまっても構いません。)

* * *

上記のようなメソッドを用意した上で、`didBegin(_:)`の中では、接触の種類に応じて、上記のメソッドを呼び出してやるだけ、と言った具合にします。

ただし、

[code]

        if contact.bodyA.categoryBitMask == asteroidCategory {
            //...
        } else {
            //...
        }
    
[/code]

と言った簡易判定は、接触判定の種類が「asteroid対何か」「何か対asteroid」しかなかったからうまくいっていたものです。接触判定の種類が増えた場合、簡易判定を2箇所に別個に書いても正しい判定はできません。

現在は、処理を行いたい接触は「asteroid対何か」「何か対asteroid」「itemBox対spaceship」「spaceship対itemBox」の4通りですから、それらをきちんと判定してやらないといけません。

[code]

        func didBegin(_ contact: SKPhysicsContact) {
    
            if contact.bodyA.categoryBitMask == asteroidCategory {
                //「asteroid対何か」
                hit(asteroid: contact.bodyA, target: contact.bodyB)
            } else if contact.bodyB.categoryBitMask == asteroidCategory {
                //「何か対asteroid」
                hit(asteroid: contact.bodyB, target: contact.bodyA)
            } else if contact.bodyA.categoryBitMask == itemBoxCategory && contact.bodyB.categoryBitMask == spaceshipCategory {
                //「itemBox対spaceship」
                hit(itemBox: contact.bodyA, target: contact.bodyB)
            } else if contact.bodyB.categoryBitMask == itemBoxCategory && contact.bodyA.categoryBitMask == spaceshipCategory {
                //「spaceship対itemBox」
                hit(itemBox: contact.bodyB, target: contact.bodyA)
            }
        }
    
[/code]

後は、接触判定の種類がもっと増えても、同じように「メソッドを増やす」「`didBegin(_:)`中の判定を増やす」で対応できるのはおわかりいただけると思います。

細かいところで修正が必要になるかもしれませんが、まずはお試しください。


CC BY-SA 4.0
2020-05-11T02:40:04.600
66498
2020-05-11T02:40:04.600
13972
66490
answer