Ios 从基本体创建自定义形状

Ios 从基本体创建自定义形状,ios,shape,scenekit,Ios,Shape,Scenekit,我正在尝试创建一个自定义的物理形状,并结合原始形状。目标是创建一个圆形立方体。合适的方法似乎是init(shapes:transforms:),我在这里找到了它: 我想这可以用8个球体,12个圆柱体和一个中间的盒子来完成。有人能提供这样做的例子吗?是的,正如您可能已经注意到的,从带有圆角的SCNBox创建物理实体会忽略倒角半径。实际上,几乎所有的基本几何体(长方体、球体、圆柱体、棱锥体、茶壶等)都会生成理想化形式的物理形状,而不是将其顶点网格直接转换为物理实体 总的来说,这是一件好事。在理想化球

我正在尝试创建一个自定义的物理形状,并结合原始形状。目标是创建一个圆形立方体。合适的方法似乎是init(shapes:transforms:),我在这里找到了它:


我想这可以用8个球体,12个圆柱体和一个中间的盒子来完成。有人能提供这样做的例子吗?

是的,正如您可能已经注意到的,从带有圆角的
SCNBox
创建物理实体会忽略倒角半径。实际上,几乎所有的基本几何体(长方体、球体、圆柱体、棱锥体、茶壶等)都会生成理想化形式的物理形状,而不是将其顶点网格直接转换为物理实体

总的来说,这是一件好事。在理想化球体上执行碰撞检测比在接近球体的数百个三角形网格上执行碰撞检测要快得多(要测试的点是否在球体中心的半径距离内?)。理想化长方体同上(将点转换为长方体的局部坐标系,文本表示边界内的x/y/z)

sinit(shapes:transforms:)
SCNShape的初始值设定项是从这些理想化的形状构建复杂形状的好方法。实际上,
init(node:options:)
初始值设定项也是如此:如果为
options
参数传递
[scnpysishapekeepascompoundkey:true]
,则可以传递一个
SCNNode
,其中包含几何图形为基本图形的子节点层次结构,SceneKit将把这些几何图形转换成理想的物理图形,然后再创建一个物理图形,这个物理图形是所有几何图形的结合

我将展示每种方法的一个示例。但首先,一些共同的背景:

let side: CGFloat = 1 // one side of the cube
let radius: CGFloat = side / 4 // the corner radius
// the visual (but not physical) cube
let cube = SCNNode(geometry: SCNBox(width: side, height: side, length: side, chamferRadius: radius))
下面是使用
init(形状:转换:)
:

您在那里看到的使用
sphereTransforms
transforms
的舞蹈是因为SceneKit希望其每个参数都有一个ObjC
NSArray
,而
NSArray
s只能包含ObjC对象。。。转换是一个
SCNMatrix4
,它是一个结构,因此我们必须将其包装在
NSValue
中以存储在
NSArray
中。在Swift中,可以方便地使用
SCNMatrix4
数组,然后使用
map
获得一个包含每个元素的
NSValue
数组。(当我们将
[NSValue]
传递到SceneKit API时,Swift会自动桥接到引擎盖下的
NSArray
。)

这将创建一个实体,它只是立方体的圆角-它们之间有空白。根据需要圆角立方体碰撞的情况,这可能就足够了。例如,如果您只想使圆形立方体骰子在地板上滚动,则角碰撞是唯一重要的碰撞,因为如果不接触角球体,地板不会与骰子的中间碰撞。如果这就是你所需要的,那就去做吧-如果你的物理形状尽可能简单,你就会获得最好的性能。

如果希望生成更精确的复合形状(边为圆柱体,面为三个长方体或六个平面),可以扩展上述示例。只需对每种形状进行形状数组转换,并在转换为
[NSValue]
并传递给SceneKit之前连接这些数组。(请注意,圆柱体将同时需要旋转和平移变换,因此将
SCNMATRIX4MAKETRANSACTION
SCNMatrix4Rotate
结合使用)

再一次,所有的数学都变得难以想象。而嵌套调用
scnmatrix4无论什么
来进行计算都不是那么有趣。因此,您可以使用节点来执行此操作:

var nodeCompound: SCNNode  {
    // a node to hold the compound geometry
    let parent = SCNNode()

    // one node with a sphere
    let sphere = SCNNode(geometry: SCNSphere(radius: radius))
    // inner func to clone the sphere to a specific position
    func corner(x x: CGFloat, y: CGFloat, z: CGFloat) -> SCNNode {
        let node = sphere.clone()
        node.position = SCNVector3(x: x, y: y, z: z)
        return node
    }

    // clone the sphere to each corner as child nodes
    parent.addChildNode(corner(x:  radius, y:  radius, z:  radius))
    parent.addChildNode(corner(x: -radius, y:  radius, z:  radius))
    parent.addChildNode(corner(x: -radius, y: -radius, z:  radius))
    parent.addChildNode(corner(x: -radius, y: -radius, z: -radius))
    parent.addChildNode(corner(x:  radius, y: -radius, z: -radius))
    parent.addChildNode(corner(x:  radius, y:  radius, z: -radius))
    parent.addChildNode(corner(x: -radius, y:  radius, z: -radius))
    parent.addChildNode(corner(x:  radius, y: -radius, z:  radius))

    return parent
}
将此节点放置在场景中,可以在定位球体(和圆柱体等)时可视化结果。但是,请注意,该节点实际上不必添加到场景中(除非为了调试目的将其可视化)。获得所需形状后,使用它创建物理形状,并将该形状指定给场景中实际要绘制的其他节点:

cube.physicsBody = SCNPhysicsBody(type: .Dynamic, 
    shape: SCNPhysicsShape(node: nodeCompound, 
        options: [SCNPhysicsShapeKeepAsCompoundKey: true]))

顺便说一句,如果您在此处删除“保持为复合”选项,您将得到一个形状,该形状是由八个角球体组成的凸面外壳网格(无论您是否也将边和面放入,因为它们位于外壳内)。也就是说,它得到了一个圆形立方体的近似值。。。角半径将不如理想几何体平滑,但取决于您需要碰撞体的目的,它可能就是您所需要的一切。

谢谢!这真的很有帮助。关于可视化数学,在场景编辑器中的“物理检查器”选项卡下,有一个选项“自定义节点”,可用于放置节点。首先,我确实试图直接在场景编辑器中从节点创建自定义形状,但每当我将节点放置到位时,XCode就会中断。你是否也有同样的行为?
cube.physicsBody = SCNPhysicsBody(type: .Dynamic, 
    shape: SCNPhysicsShape(node: nodeCompound, 
        options: [SCNPhysicsShapeKeepAsCompoundKey: true]))