SceneKit:两个节点之间接触后 UIView 加载缓慢
SceneKit: slow loading of UIView after contact between two nodes
我刚刚发现 Scene Kit 有一个非常奇怪的行为。我创建了一个干净的项目来查看是否会发生这种情况,它确实发生了。考虑以下 GameViewController
class:
import UIKit
import QuartzCore
import SceneKit
class GameViewController: UIViewController, SCNPhysicsContactDelegate {
func physicsWorld(world: SCNPhysicsWorld, didBeginContact contact: SCNPhysicsContact) {
let label = UILabel(frame: CGRectMake(0, 0, 100, 100))
label.textColor = .whiteColor()
label.text = "Test"
self.view.addSubview(label)
}
override func viewDidLoad() {
// Controller
super.viewDidLoad()
// Scene
let scene = SCNScene()
scene.physicsWorld.contactDelegate = self
// Scene View
let scnView = self.view as! SCNView
scnView.scene = scene
// Camera
let camera = SCNCamera()
camera.usesOrthographicProjection = true
camera.orthographicScale = 8
// Camera Node
let cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.position = SCNVector3(x: -3, y: 7.5, z: 10)
cameraNode.eulerAngles = SCNVector3(x: -0.5, y: -0.5, z: 0)
scene.rootNode.addChildNode(cameraNode)
// Light
let light = SCNLight()
light.type = SCNLightTypeDirectional
// Light Node
let lightNode = SCNNode()
lightNode.light = light
lightNode.eulerAngles = SCNVector3Make(-1, -0.5, 0)
scene.rootNode.addChildNode(lightNode)
// Box Shape
let geometry = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0)
geometry.materials.first?.diffuse.contents = UIColor(red: 255, green: 255, blue: 0, alpha: 1)
// Upper Box
let box = SCNNode(geometry: geometry)
box.physicsBody = SCNPhysicsBody(type: .Dynamic, shape: nil)
box.physicsBody?.categoryBitMask = 1
box.physicsBody?.contactTestBitMask = 2
scene.rootNode.addChildNode(box)
// Bottom Box
let box2 = SCNNode(geometry: geometry)
box2.position.y -= 5
box2.physicsBody = SCNPhysicsBody(type: .Static, shape: nil)
box2.physicsBody?.categoryBitMask = 2
box2.physicsBody?.contactTestBitMask = 1
box2.physicsBody?.affectedByGravity = false
scene.rootNode.addChildNode(box2)
}
}
这里发生的是,我们创建了一个 SCNScene
,然后我们添加了 camera/lightning 并创建了两个会碰撞的盒子。我们还将控制器设置为物理接触委托的委托。
但重要的代码在最上面,当两个对象发生接触时。我们创建一个 UILabel
并将其放置在屏幕的左上角。但问题是:标签显示大约需要七秒。而且似乎总是这个时间。这看起来很奇怪,但您可以自己尝试:只需创建一个 SceneKit 项目(使用 Swift),然后将控制器代码替换为我提供的代码。
补充说明:如果你在physicsWorld()
函数中添加print("a")
,它会立即运行,所以代码在正确的时间被运行,但是由于某种原因,UILabel
没有立即显示。
一开始我以为你的场景没有在接触后渲染;因此不显示标签,所以放入 scnView.playing = true
行。但是,这导致标签永远不会显示。
接下来的想法是,您正试图在线程上做一些您不应该做的事情。修改 UIKit 视图实际上应该只在主线程上完成,并且不清楚哪个线程调用 SCNPhysicsContactDelegate
函数。鉴于确保它在主线程上执行 运行 相对容易...
func physicsWorld(world: SCNPhysicsWorld, didBeginContact contact: SCNPhysicsContact) {
dispatch_async(dispatch_get_main_queue()) {
let label = UILabel(frame: CGRectMake(0, 0, 100, 100))
label.textColor = .whiteColor()
label.text = "Test"
self.view.addSubview(label)
}
}
这解决了我的延迟问题,也适用于 scnView.playing = true
。
我刚刚发现 Scene Kit 有一个非常奇怪的行为。我创建了一个干净的项目来查看是否会发生这种情况,它确实发生了。考虑以下 GameViewController
class:
import UIKit
import QuartzCore
import SceneKit
class GameViewController: UIViewController, SCNPhysicsContactDelegate {
func physicsWorld(world: SCNPhysicsWorld, didBeginContact contact: SCNPhysicsContact) {
let label = UILabel(frame: CGRectMake(0, 0, 100, 100))
label.textColor = .whiteColor()
label.text = "Test"
self.view.addSubview(label)
}
override func viewDidLoad() {
// Controller
super.viewDidLoad()
// Scene
let scene = SCNScene()
scene.physicsWorld.contactDelegate = self
// Scene View
let scnView = self.view as! SCNView
scnView.scene = scene
// Camera
let camera = SCNCamera()
camera.usesOrthographicProjection = true
camera.orthographicScale = 8
// Camera Node
let cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.position = SCNVector3(x: -3, y: 7.5, z: 10)
cameraNode.eulerAngles = SCNVector3(x: -0.5, y: -0.5, z: 0)
scene.rootNode.addChildNode(cameraNode)
// Light
let light = SCNLight()
light.type = SCNLightTypeDirectional
// Light Node
let lightNode = SCNNode()
lightNode.light = light
lightNode.eulerAngles = SCNVector3Make(-1, -0.5, 0)
scene.rootNode.addChildNode(lightNode)
// Box Shape
let geometry = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0)
geometry.materials.first?.diffuse.contents = UIColor(red: 255, green: 255, blue: 0, alpha: 1)
// Upper Box
let box = SCNNode(geometry: geometry)
box.physicsBody = SCNPhysicsBody(type: .Dynamic, shape: nil)
box.physicsBody?.categoryBitMask = 1
box.physicsBody?.contactTestBitMask = 2
scene.rootNode.addChildNode(box)
// Bottom Box
let box2 = SCNNode(geometry: geometry)
box2.position.y -= 5
box2.physicsBody = SCNPhysicsBody(type: .Static, shape: nil)
box2.physicsBody?.categoryBitMask = 2
box2.physicsBody?.contactTestBitMask = 1
box2.physicsBody?.affectedByGravity = false
scene.rootNode.addChildNode(box2)
}
}
这里发生的是,我们创建了一个 SCNScene
,然后我们添加了 camera/lightning 并创建了两个会碰撞的盒子。我们还将控制器设置为物理接触委托的委托。
但重要的代码在最上面,当两个对象发生接触时。我们创建一个 UILabel
并将其放置在屏幕的左上角。但问题是:标签显示大约需要七秒。而且似乎总是这个时间。这看起来很奇怪,但您可以自己尝试:只需创建一个 SceneKit 项目(使用 Swift),然后将控制器代码替换为我提供的代码。
补充说明:如果你在physicsWorld()
函数中添加print("a")
,它会立即运行,所以代码在正确的时间被运行,但是由于某种原因,UILabel
没有立即显示。
一开始我以为你的场景没有在接触后渲染;因此不显示标签,所以放入 scnView.playing = true
行。但是,这导致标签永远不会显示。
接下来的想法是,您正试图在线程上做一些您不应该做的事情。修改 UIKit 视图实际上应该只在主线程上完成,并且不清楚哪个线程调用 SCNPhysicsContactDelegate
函数。鉴于确保它在主线程上执行 运行 相对容易...
func physicsWorld(world: SCNPhysicsWorld, didBeginContact contact: SCNPhysicsContact) {
dispatch_async(dispatch_get_main_queue()) {
let label = UILabel(frame: CGRectMake(0, 0, 100, 100))
label.textColor = .whiteColor()
label.text = "Test"
self.view.addSubview(label)
}
}
这解决了我的延迟问题,也适用于 scnView.playing = true
。