我有一个简单的 3d 区域,其中包含 4 面墙,每面都是一个 SCNNode,具有简单的 SCNBox 几何形状、矩形形状,并附有匹配的 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 联系人代理来检测实体何时到达边界墙之一。然后我从那堵墙上计算出它在另一个方向上的随机轨迹,清除它的 Action ,并应用一个新的移动 SCNAction。
这就是有趣的地方......
当这个“世界”的比例为 1:1 时。在标准 SCNScene 和使用 ARKit 投影的场景中,接触都被检测为正常。可见接触,即实体方向的可见变化似乎与预期的一样接近边界。当我检查每个联系人的contact.penetrationDistance时,它们的值是例如0.00294602662324905.
但是,当我在 ARKit 中将这个“世界”的比例更改为更小,比如相当于 10 厘米宽时,模拟就会崩溃。
实体和边界节点之间的接触在检测到接触时,它们之间存在较大的可见间隙。然而,contact.penetrationDistance 的大小与以前相同。
我打开了 ARSCNView 调试选项以在渲染中显示物理形状,它们看起来都是正确的比例,与它们节点的边界框相匹配。
从上面的代码示例中可以看出,边界节点是在我缩放关卡后生成的,在我的 AR 用户设置过程中。它们被添加到场景的根节点,而不是作为关卡节点的子节点。相同的代码被用于生成实体。
之前我曾尝试在物理体上使用 resetTransform() 函数,但是在我缩放关卡后,这并没有产生可靠的物理体缩放,所以我决定在之后生成边界和实体的节点水平已被缩放。
在 Apple 的文档中,确实声明如果 SCNPhysicsBody 不是自定义形状,它将采用应用于它的节点几何图形的比例。我不会受此影响,因为在将缩放应用到关卡之后,我正在生成几何图形及其各自的节点。
目前的一个假设是物理模拟在如此小的范围内分崩离析。但我不是依靠模拟力来移动 body ......
有没有更合适的方式来扩展物理世界?
或者,我是否正在盯着 SCNPhysicsWorld 中的错误,目前这是我无法控制的。
我确实考虑过的一个解决方案是以 1:1 的比例运行整个模拟但隐藏起来,然后将这些运动应用于较小的实体。可以想象,那会影响整个场景的表现……
Best Answer-推荐答案 strong>
第一次接触的穿透距离为负值,表示存在间隙。当您按比例缩小模拟大小时,此差距似乎不会按比例缩小。
作为解决上述过量问题的一种方法,我在 Contact Delegate 中对联系人进行了额外检查,以不获取为特定类别检测到的第一个联系人,而是确保穿透距离值为正,因此确保存在是两个对象之间的重叠,然后触发与边界连接的实体的方向变化。
关于iOS ARKit + SceneKit 物理接触检测缩放问题,我们在Stack Overflow上找到一个类似的问题:
https://stackoverflow.com/questions/49771646/
|