2016-09-10 9 views
3

저는 SceneKit의 physicsBody 시스템을 사용하여 객체들 간의 충돌을 감지하고 매우 이상한 결과를 얻습니다. 설명하기 위해, 저는 운동학 물리학을 사용하여 두 개의 구체를 생성하는 최소한의 예제를 얻었습니다. 그리고 그것들을 간단히 겹쳐 지도록 직선으로 움직였습니다.SceneKit의 physicsWorld가 왜 단일 충돌에 대해 여러 번 발사 접촉합니까?

구체가 처음으로 겹칠 때 physicsWorld (: didBeginContact :)가 한 번만 호출되고 physicsWorld (: didEndContact :)가 중복 호출을 중지 할 때 한 번 호출됩니다. 대신 각 함수가 25 번 호출됩니다.

다음은 코드를 재현하는 코드입니다. Xcode 8.0에서는 "게임"템플릿을 사용하여 새로운 프로젝트를 만듭니다. 이와 함께 GameViewController.swift의 내용을 바꾸기 :

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 버그일까요? 아니면 실제로 예상되는 동작입니까?

답변

1

SCNRenderer는 renderer(_:didSimulatePhysicsAtTime:) 메서드가 SCNSceneRendererDelegate 인 모든 프레임에서 물리 시뮬레이션을 실행합니다.

두 개의 구가 교차하는 동안 여러 개의 프레임이 렌더링되고 물리 시뮬레이션이 실행될 때마다 충돌이 감지됩니다.

예상됩니다. 충돌을 처리하고 두 구체를 더 이상 충돌하지 않는 상태로 두는 것은 당신에게 달려 있습니다. 예를 들어 게임에서 발사체가 플레이어를 때리면 사라집니다. 충돌이 처리되어 더 이상 발생하지 않습니다.


내가하는 OS X 응용 프로그램으로 엑스 코드 버전 8.0 베타 5 (8S193k) 실행 다음과 같은 방법으로 코드를 사용했다. 콘솔에서 다음 추적을 볼 수 있습니다. 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 
    } 
} 
+0

내가 실수라고 생각합니다. 왜 그랬을까요? 시작하고 충돌 했나요? 둘 다 충돌의 모든 프레임에 대해 부름 받았습니까? 그들 사이의 차이점은 무엇입니까? – Robert

+0

예를 들어 보셨습니까? didBegin과 didEnd는 충돌이있는 모든 프레임에서 호출되지 않으며 항상 같은 프레임에서 호출되는 것은 아닙니다. 또한 앞서 언급 한 초기 조건을 변경하면 겹치는 첫 번째 프레임에서 didBegin을 한 번만 호출하고 처음 프레임에서는 didEnd를 한 번만 호출하면 많은 프레임에서 해당 영역이 교차하는 것을 볼 수 있습니다. – Robert

+0

수정 된 답변 확인 –