Why is SceneKit#39;s physicsWorld didBeginContact firing multiple times for a single collision?(为什么场景工具包的PhysicsWorld didBeginContact会因为一次碰撞而多次开火?)
问题描述
我正在使用SceneKit的PhysiicsBody系统来检测对象之间的碰撞,并获得了一些非常奇怪的结果。为了说明这一点,我举了一个最小的例子,它生成了两个带有运动学物理的球体,并使它们沿直线移动,从而使它们短暂重叠。
我期望看到PhysicsWorld(:didBeginContact:)在球体第一次重叠时被调用一次,而PhysicsWorld(:didEndContact:)在它们停止重叠时调用一次。相反,我看到每个函数都被调用了25次!
以下是要重现的代码:在Xcode8.0中,使用"Game"模板创建一个全新的项目。将GameViewController.swft的内容替换为:import UIKit
import SceneKit
class GameViewController: UIViewController, SCNSceneRendererDelegate, SCNPhysicsContactDelegate {
    var scnScene: SCNScene!
    var scnView: SCNView!
    var cameraNode: SCNNode!
    var nodeA: SCNNode!
    var nodeB: SCNNode!
    var countBeginnings: Int = 0
    var countEndings: Int = 0
    override func viewDidLoad() {
        super.viewDidLoad()
        setupScene()
        setupNodes()
    }
    func setupScene() {
        // create a new SCNScene and feed it to the view
        scnView = self.view as! SCNView
        scnScene = SCNScene()
        scnView.scene = scnScene
        // assign self as SCNView delegate to get access to render loop
        scnView.delegate = self
        // assign self as contactDelegate to handle collisions
        scnScene.physicsWorld.contactDelegate = self
        // create the camera and position it at origin
        cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3Zero
        scnScene.rootNode.addChildNode(cameraNode)
        // tell scnView to update every frame
        scnView.isPlaying = true
    }
    func setupNodes() {
        // create two spheres with physicsBodies, one inside the other
        nodeA = SCNNode()
        nodeA.name = "Node A"
        nodeA.geometry = SCNSphere(radius: 1.0)
        nodeA.geometry!.firstMaterial?.diffuse.contents = UIColor.yellow.withAlphaComponent(0.6)
        // expected behavior
        // nodeA.position = SCNVector3(x: 0.0, y: -0.8, z: -10.0)
        // weird behavior
        nodeA.position = SCNVector3(x: 0.0, y: -0.9, z: -10.0)
        nodeA.physicsBody = SCNPhysicsBody(type: .kinematic, shape: SCNPhysicsShape(geometry: nodeA.geometry!, options: nil))
        scnScene.rootNode.addChildNode(nodeA)
        nodeB = SCNNode()
        nodeB.name = "Node B"
        nodeB.geometry = SCNSphere(radius: 0.5)
        nodeB.geometry!.firstMaterial?.diffuse.contents = UIColor.red
        nodeB.position = SCNVector3(x: -2.0, y: 0.0, z: -10.0)
        nodeB.physicsBody = SCNPhysicsBody(type: .kinematic, shape: SCNPhysicsShape(geometry: nodeB.geometry!, options: nil))
        scnScene.rootNode.addChildNode(nodeB)
        // node A can collide with node B but not the other way around
        nodeA.physicsBody!.categoryBitMask = 2
        nodeB.physicsBody!.categoryBitMask = 1
        nodeA.physicsBody!.contactTestBitMask = 1
    }
    func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
        countBeginnings += 1
        print("(" + String(countBeginnings) + ") " + contact.nodeA.name! + " began contact with " + contact.nodeB.name!)
    }
    func physicsWorld(_ world: SCNPhysicsWorld, didEnd contact: SCNPhysicsContact) {
        countEndings += 1
        print("(" + String(countEndings) + ") " + contact.nodeA.name! + " ended contact with " + contact.nodeB.name!)
    }
    var frameNumber = 0
    func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        nodeB.position.x += 0.01
        nodeB.position.y -= 0.01
    }
}
还有其他奇怪的事情正在上演。如果我稍微更改其中一个球体的初始位置,将y位置从-0.9移动到-0.8:
nodeA.position = SCNVector3(x: 0.0, y: -0.8, z: -10.0)
现在我得到了预期的行为,一个调用开始,一个调用结束!稍微不同的碰撞角度会导致完全不同的行为。
这可能是SceneKit错误,还是这实际上是预期行为?
推荐答案
SCN呈现器在每一帧运行物理模拟,调用其SCNSceneRendererDelegate的renderer(_:didSimulatePhysicsAtTime:)方法。
在两个球体交叉的过程中,将渲染多个帧,每次运行物理模拟时都会检测到碰撞。
这是意料之中的。这取决于您处理碰撞,并将两个球体置于不再碰撞的状态。例如,在游戏中,一旦投射物击中玩家,它就会消失。冲突已处理,因此不再激发。
在XCode8.0测试版5(8S193k)作为OS X应用程序运行时,我以以下方式使用了您的代码。我在控制台中看到以下跟踪。Begin和End方法只调用一次!
(1) Node A began contact with Node B
(1) Node A ended contact with Node B
import SceneKit
import QuartzCore
class GameViewController: NSViewController, SCNSceneRendererDelegate, SCNPhysicsContactDelegate {
    @IBOutlet weak var gameView: GameView!
    // MARK: Properties
    var cameraNode: SCNNode!
    var nodeA: SCNNode!
    var nodeB: SCNNode!
    var countBeginnings: Int = 0
    var countEndings: Int = 0
    // MARK: Initialization
    override func awakeFromNib(){
        super.awakeFromNib()
        // create a new scene
        let scene = SCNScene(named: "art.scnassets/scene.scn")!
        // create and add a camera to the scene
        let cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        scene.rootNode.addChildNode(cameraNode)
        // place the camera
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)
        // create and add a light to the scene
        let lightNode = SCNNode()
        lightNode.light = SCNLight()
        lightNode.light!.type = SCNLightTypeOmni
        lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
        scene.rootNode.addChildNode(lightNode)
        // create and add an ambient light to the scene
        let ambientLightNode = SCNNode()
        ambientLightNode.light = SCNLight()
        ambientLightNode.light!.type = SCNLightTypeAmbient
        ambientLightNode.light!.color = NSColor.darkGray
        scene.rootNode.addChildNode(ambientLightNode)
        // set the scene to the view
        self.gameView!.scene = scene
        // allows the user to manipulate the camera
        self.gameView!.allowsCameraControl = true
        // show statistics such as fps and timing information
        self.gameView!.showsStatistics = true
        // configure the view
        self.gameView!.backgroundColor = NSColor.black
        self.gameView!.delegate = self
        setupScene()
        setupNodes()
    }
    func setupScene() {
        // assign self as contactDelegate to handle collisions
        self.gameView!.scene?.physicsWorld.contactDelegate = self
        // create the camera and position it at origin
        cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3Zero
        self.gameView!.scene?.rootNode.addChildNode(cameraNode)
        // tell scnView to update every frame
        self.gameView.isPlaying = true
    }
    func setupNodes() {
        // create two spheres with physicsBodies, one inside the other
        nodeA = SCNNode()
        nodeA.name = "Node A"
        nodeA.geometry = SCNSphere(radius: 1.0)
        nodeA.geometry!.firstMaterial?.diffuse.contents = NSColor.yellow.withAlphaComponent(0.6)
        nodeA.position = SCNVector3(x: 0.0, y: -0.8, z: -10.0)
        nodeA.physicsBody = SCNPhysicsBody(type: .kinematic, shape: SCNPhysicsShape(geometry: nodeA.geometry!, options: nil))
        self.gameView!.scene?.rootNode.addChildNode(nodeA)
        nodeB = SCNNode()
        nodeB.name = "Node B"
        nodeB.geometry = SCNSphere(radius: 0.5)
        nodeB.geometry!.firstMaterial?.diffuse.contents = NSColor.red
        nodeB.position = SCNVector3(x: -2.0, y: 0.0, z: -10.0)
        nodeB.physicsBody = SCNPhysicsBody(type: .kinematic, shape: SCNPhysicsShape(geometry: nodeB.geometry!, options: nil))
        self.gameView!.scene?.rootNode.addChildNode(nodeB)
        // node A can collide with node B but not the other way around
        nodeA.physicsBody!.categoryBitMask = 2
        nodeB.physicsBody!.categoryBitMask = 1
        nodeA.physicsBody!.contactTestBitMask = 1
    }
    // MARK: SCNPhysicsContactDelegate
    func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
        countBeginnings += 1
        print("(" + String(countBeginnings) + ") " + contact.nodeA.name! + " began contact with " + contact.nodeB.name!)
    }
    func physicsWorld(_ world: SCNPhysicsWorld, didEnd contact: SCNPhysicsContact) {
        countEndings += 1
        print("(" + String(countEndings) + ") " + contact.nodeA.name! + " ended contact with " + contact.nodeB.name!)
    }
    // MARK: SCNSceneRendererDelegate
    var frameNumber = 0
    func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
        nodeB.position.x += 0.01
        nodeB.position.y -= 0.01
    }
}
                        这篇关于为什么场景工具包的PhysicsWorld didBeginContact会因为一次碰撞而多次开火?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:为什么场景工具包的PhysicsWorld didBeginContact会因为一次碰撞而多次开火?
				
        
 
            
        - 如何检查发送到 Android 应用程序的 Firebase 消息的传递状态? 2022-01-01
 - 在测试浓缩咖啡时,Android设备不会在屏幕上启动活动 2022-01-01
 - 用 Swift 实现 UITextFieldDelegate 2022-01-01
 - Android viewpager检测滑动超出范围 2022-01-01
 - android 4中的android RadioButton问题 2022-01-01
 - 想使用ViewPager,无法识别android.support.*? 2022-01-01
 - Android - 我如何找出用户有多少未读电子邮件? 2022-01-01
 - MalformedJsonException:在第1行第1列路径中使用JsonReader.setLenient(True)接受格式错误的JSON 2022-01-01
 - Android - 拆分 Drawable 2022-01-01
 - 使用自定义动画时在 iOS9 上忽略 edgesForExtendedLayout 2022-01-01
 
				
				
				
				