从基元创建自定义形状

时间:2015-07-27 15:02:46

标签: ios shape scenekit

我正在尝试使用原始形状组合创建自定义物理形状。目标是创建一个圆角的立方体。适当的方法似乎是 init(形状:变换:) ,我在这里找到https://developer.apple.com/library/prerelease/ios/documentation/SceneKit/Reference/SCNPhysicsShape_Class/index.html#//apple_ref/occ/clm/SCNPhysicsShape/shapeWithShapes:transforms

我认为这可以通过8个球体,12个圆柱体和中间的盒子来完成。任何人都可以提供这样做的例子吗?

1 个答案:

答案 0 :(得分:6)

是的,您可能已经注意到,从带有圆角的SCNBox创建物理实体会忽略倒角半径。实际上,几乎所有的基本几何形状(盒子,球体,圆柱体,金字塔,茶壶等)都会产生理想化形式的物理形状,而不是将它们的顶点网格直接转换为物理体。

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

init(shapes:transforms:)的{​​{1}}初始化程序是从这些理想化形状构建复杂形状的好方法。实际上,SCNShape初始值设定项也是如此:如果您为 init(node:options:) 参数传递[SCNPhysicsShapeKeepAsCompoundKey: true],则可以传递包含层次结构的options几何形状为基本形状的子节点,并且SceneKit会在创建物理形状之前将每个几何形状转换为理想化的物理形状,这是所有几何形状的结合。

我将展示每个例子。但首先,一些共享的背景:

SCNNode

以下是使用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(shapes:transforms:)

你在var compound: SCNPhysicsShape { let sphereShape = SCNPhysicsShape(geometry: SCNSphere(radius: radius), options: nil) let spheres = [SCNPhysicsShape](count: 8, repeatedValue: sphereShape) let sphereTransforms = [ SCNMatrix4MakeTranslation( radius, radius, radius), SCNMatrix4MakeTranslation(-radius, radius, radius), SCNMatrix4MakeTranslation(-radius, -radius, radius), SCNMatrix4MakeTranslation(-radius, -radius, -radius), SCNMatrix4MakeTranslation( radius, -radius, -radius), SCNMatrix4MakeTranslation( radius, radius, -radius), SCNMatrix4MakeTranslation(-radius, radius, -radius), SCNMatrix4MakeTranslation( radius, -radius, radius), ] let transforms = sphereTransforms.map { NSValue(SCNMatrix4: $0) } return SCNPhysicsShape(shapes: spheres, transforms: transforms) } cube.physicsBody = SCNPhysicsBody(type: .Dynamic, shape: compound) sphereTransforms看到的舞蹈是因为SceneKit期望每个参数都有一个ObjC transformsNSArray只能包含ObjC对象...变换是NSArray,它是一个结构,所以我们必须将它包装在SCNMatrix4中以将其存储在NSValue中。在Swift中,使用NSArray数组很方便,然后使用SCNMatrix4来获取包含每个元素的map数组。 (当我们将NSValue传递给SceneKit API时,Swift会自动连接到NSArray

这会创建一个只是立方体圆角的主体 - 它们之间有空的空间。根据您需要圆角立方体碰撞的情况,这可能就足够了。例如,如果你只是想在地板上制作圆角立方体骰子,那么角落碰撞是唯一重要的,因为地板不会在没有接触角落球体的情况下与模具中间碰撞。如果这就是你所需要的,那就去吧 - 如果你的物理形状尽可能简单,你就能获得最佳性能。

如果你想制作一个更精确的复合形状,边缘是圆柱体,面部是三个盒子或六个平面,你可以扩展上面的例子。只需为每种形状制作形状变换数组,并在转换为[NSValue]并传递给SceneKit之前连接数组。 (请注意,圆柱体需要旋转和平移变换,因此将[NSValue]SCNMatrix4MakeTranslation结合使用。)

然后,所有数学都变得难以想象。嵌套调用SCNMatrix4Rotate进行数学运算并不是那么有趣。所以你可以用节点代替它:

SCNMatrix4Whatever

将此节点放在场景中,您可以在定位球体(和圆柱体等)时可视化结果。请注意,此节点不必实际添加到场景中(除非您出于调试目的将其可视化)。一旦你得到它你想要它,用它来创建一个物理形状,并将该形状分配给你真正想在场景中绘制的其他节点:

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
}

顺便说一句,如果你在这里删除了保持复合选项,你将得到一个形状,它是你的八个角球的凸包网格(无论你是否也放入边和面,因为那些谎言在船体内)。也就是说,它可以让你得到一个圆角立方体的近似值......角半径将比理想化的几何体更不平滑,但是根据你需要这个碰撞体,你可能只需要它。