我有一个简单的3D区域,包含4面墙,每一面墙都是一个带有简单SCNBox几何体的SCNNode,呈矩形形状,并附加匹配的SCNPhysicsBody。SCNPhysicsBody使用SCNPhysicsShape.ShapeType.boundingBox,并设置为静态类型。以下是代码片段:
let size = (self.levelNode.boundingBox.max - self.levelNode.boundingBox.min) * self.levelNode.scale
//x //z
let geometryA = SCNBox(width: CGFloat(size.x), height: CGFloat(1 * self.levelNode.scale.x), length: 0.01, chamferRadius: 0)
let geometryB = SCNBox(width: CGFloat(size.z), height: CGFloat(1 * self.levelNode.scale.x), length: 0.01, chamferRadius: 0)
geometryA.firstMaterial?.diffuse.contents = UIColor(red: 0.0, green: 0.2, blue: 1.0, alpha: 0.65)
geometryB.firstMaterial?.diffuse.contents = UIColor(red: 0.0, green: 0.2, blue: 1.0, alpha: 0.65)
let nodeA = SCNNode(geometry: geometryA)
nodeA.position += self.levelNode.position
nodeA.position += SCNVector3(0, 0.25 * self.levelNode.scale.y, -size.z/2)
nodeA.name = "Boundary-01"
let nodeB = SCNNode(geometry: geometryA)
nodeB.position += self.levelNode.position
nodeB.position += SCNVector3(0, 0.25 * self.levelNode.scale.y, size.z/2)
nodeB.name = "Boundary-03"
let nodeC = SCNNode(geometry: geometryB)
nodeC.position += self.levelNode.position
nodeC.position += SCNVector3(-size.x/2, 0.25 * self.levelNode.scale.y, 0)
nodeC.eulerAngles = SCNVector3(0, -Float.pi/2, 0)
nodeC.name = "Boundary-02"
let nodeD = SCNNode(geometry: geometryB)
nodeD.position += self.levelNode.position
nodeD.position += SCNVector3(size.x/2, 0.25 * self.levelNode.scale.y, 0)
nodeD.eulerAngles = SCNVector3(0, Float.pi/2, 0)
nodeD.name = "Boundary-04"
let nodes = [nodeA, nodeB, nodeC, nodeD]
for node in nodes {
//
let shape = SCNPhysicsShape(geometry: node.geometry!, options: [
SCNPhysicsShape.Option.type : SCNPhysicsShape.ShapeType.boundingBox])
let body = SCNPhysicsBody(type: .static, shape: shape)
node.physicsBody = body
node.physicsBody?.isAffectedByGravity = false
node.physicsBody?.categoryBitMask = Bitmask.boundary.rawValue
node.physicsBody?.contactTestBitMask = Bitmask.edge.rawValue
node.physicsBody?.collisionBitMask = 0
scene.rootNode.addChildNode(node)
node.physicsBody?.resetTransform()
}
在这个区域内,我按照固定的时间间隔生成实体。每个实体都有一个SCNBox几何体,这次是一个立方体形状,比墙壁小,并且具有与上述相同的物理体参数。
为了简化实体在游戏区域内的行为,我计算它们要移动的路径,然后对相关节点应用SCNAction来移动它们。SCNAction会同时移动节点和附加到其上的物理体。
我使用SCNPhysicsWorld接触代理来检测实体何时到达边界墙之一。然后,我从那面墙计算出一个随机轨迹,清除其动作,并应用新的move SCNAction。
这就是事情变得有趣的地方......
当此“世界”处于1:1比例时,在标准SCNScene和使用ARKit投影的场景中都可以正常检测到接触。即预期的实体方向可见接触点非常靠近边界。当我检查每个接触的contact.penetrationDistance值时,它们的值为例如0.00294602662324905。
但是当我将此“世界”的比例更改为更小的尺寸,例如在ARKit中相当于10cm宽度时,模拟就会崩溃。
实体与边界节点之间的接触在检测到时具有相对巨大的可见间隙。然而,contact.penetrationDistance的数量级与之前相同。
我打开了ARSCNView的调试选项以在渲染中显示物理形状,它们看起来都是正确的比例,与其节点的边界框相匹配。
从上面的代码示例中可以看出,在我缩放级别期间,边界节点是在我的AR用户设置之后生成的。它们添加到场景的根节点中,而不是级别节点的子节点。使用相同的代码来生成实体。
以前,我尝试在物理体上使用resetTransform()函数,但这并没有产生可靠的物理体缩放效果,因此我决定在缩放级别之后生成边界和实体的节点。
在苹果的文档中,确实指出如果SCNPhysicsBody不是自定义形状,则它将采用应用于它的节点几何体的比例。我不受此影响,因为我是在缩放应用于级别之后生成几何体及其各自的节点。
目前的假设之一是物理模拟在如此小的比例下崩溃。但是,我不依赖于力的模拟来移动物体......
是否有更适合缩放物理世界的方法?
或者,我正在面对SCNPhysicsWorld中的一个错误,这是目前无法控制的Bug。
我曾经想过的一个解决方案是以1:1比例运行整个模拟,但隐藏它,然后将那些动作应用到较小的实体上。正如您可以想象的那样,这将影响整个场景的性能...