gpt4 book ai didi

swift - 为什么 SceneKit 的 physicsWorld didBeginContact 会为一次碰撞触发多次?

转载 作者:搜寻专家 更新时间:2023-10-31 22:59:10 25 4
gpt4 key购买 nike

我正在使用 SceneKit 的 physicsBody 系统来检测对象之间的碰撞,并得到一些非常奇怪的结果。为了说明这一点,我有一个最小的示例,它生成两个具有运动学 physicsBodies 的球体并沿直线移动它们,以便它们短暂重叠。

我希望看到 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 错误还是这实际上是预期的行为?

最佳答案

SCNRenderer 在每一帧运行物理模拟,调用 renderer(_:didSimulatePhysicsAtTime:)其方法SCNSceneRendererDelegate .

在两个球体相交的过程中,会渲染几帧,每次运行物理模拟时,都会检测到碰撞。

这是预料之中的。由您来处理碰撞并将两个球体置于不再碰撞的状态。例如,在游戏中,一旦弹丸击中玩家,它就会消失。碰撞已处理,因此不再触发。


我在 XCode Version 8.0 beta 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
}
}

关于swift - 为什么 SceneKit 的 physicsWorld didBeginContact 会为一次碰撞触发多次?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39424122/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com