我需要设置节点的物理体。当我有一个自定义枢轴但没有缩放或旋转时,我可以简单地通过枢轴的反转来变换形状,如下所示:
let boxGeo = SCNBox(width: 5, height: 5, length: 1, chamferRadius: 0)
let box = SCNNode(geometry: boxGeo)
rootNode.addChildNode(box)
// i have a custom pivot here:
box.pivot = SCNMatrix4MakeTranslation(1, 1, 1)
// i can simply transform the shape by inverse of pivot:
let boxPhysicsShape = SCNPhysicsShape(geometry: box.geometry!)
.transformed(by: SCNMatrix4Invert(box.pivot))
box.physicsBody = SCNPhysicsBody(type: .static, shape: boxPhysicsShape)
请注意,
transformed(by:)
是我在下面提供的辅助函数:
public extension SCNPhysicsShape {
func transformed(by transform: SCNMatrix4) -> SCNPhysicsShape {
return SCNPhysicsShape(
shapes: [self],
transforms: [NSValue(scnMatrix4: transform)])
}
func translated(by translation: SCNVector3) -> SCNPhysicsShape {
let transform = SCNMatrix4MakeTranslation(translation.x, translation.y, translation.z)
return transformed(by: transform)
}
func scaled(by scale: SCNVector3) -> SCNPhysicsShape {
let transform = SCNMatrix4MakeScale(scale.x, scale.y, scale.z)
return transformed(by: transform)
}
func rotated(by rotation: SCNVector4) -> SCNPhysicsShape {
let transform = SCNMatrix4MakeRotation(rotation.w, rotation.x, rotation.y, rotation.z)
return transformed(by: transform)
}
}
这个效果很好。但是,当节点缩放时,这将不再起作用:
// ...
box.scale = SCNVector3Make(1.5, 1.5, 1.5)
box.pivot = SCNMatrix4MakeTranslation(1, 1, 1)
// ...
let boxPhysicsShape = SCNPhysicsShape(geometry: box.geometry!)
.scaled(by: box.scale)
.transformed(by: SCNMatrix4Invert(box.pivot))
如果打开
showPhysicsShapes
调试选项,物理形状会错位。
与缩放类似,当涉及旋转时,我也无法对齐物理形状。
这是您可以使用的最小重现代码:
class My3DScene: SCNScene {
override init() {
super.init()
let camera = SCNCamera()
let cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 50)
rootNode.addChildNode(cameraNode)
let boxGeo = SCNBox(width: 5, height: 5, length: 1, chamferRadius: 0)
boxGeo.firstMaterial?.diffuse.contents = UIColor.red
let box = SCNNode(geometry: boxGeo)
box.scale = SCNVector3Make(1.5, 1.5, 1.5)
box.eulerAngles = SCNVector3Make(1, 2, 3)
box.pivot = SCNMatrix4MakeTranslation(1, 1, 1)
rootNode.addChildNode(box)
let boxPhysicsShape = SCNPhysicsShape(geometry: box.geometry!)
// no need to rotate, shape is rotated by node automatically
.scaled(by: box.scale)
.transformed(by: SCNMatrix4Invert(box.pivot))
box.physicsBody = SCNPhysicsBody(type: .static, shape: boxPhysicsShape)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scnView = SCNView(frame: self.view.frame)
scnView.debugOptions = .showPhysicsShapes
self.view.addSubview(scnView)
let scene = My3DScene()
scnView.present(scene, with: .fade(withDuration: 1), incomingPointOfView: nil)
}
}
public extension SCNPhysicsShape {
func transformed(by transform: SCNMatrix4) -> SCNPhysicsShape {
return SCNPhysicsShape(
shapes: [self],
transforms: [NSValue(scnMatrix4: transform)])
}
func translated(by translation: SCNVector3) -> SCNPhysicsShape {
let transform = SCNMatrix4MakeTranslation(translation.x, translation.y, translation.z)
return transformed(by: transform)
}
func scaled(by scale: SCNVector3) -> SCNPhysicsShape {
let transform = SCNMatrix4MakeScale(scale.x, scale.y, scale.z)
return transformed(by: transform)
}
func rotated(by rotation: SCNVector4) -> SCNPhysicsShape {
let transform = SCNMatrix4MakeRotation(rotation.w, rotation.x, rotation.y, rotation.z)
return transformed(by: transform)
}
}
您遇到一个问题,即缩放和旋转节点时物理形状未正确对齐。这是因为 SCNPhysicsShape 在创建物理体的形状时不会自动考虑节点的缩放、旋转和枢轴。您当前的方法适用于未变换的形状,但应用缩放或旋转后,这会导致未对齐。
为了解决这个问题,您需要将节点的完整变换(包括缩放、旋转和枢轴)应用到物理形状。您需要应用包括缩放、旋转和枢轴调整的完整变换矩阵,而不仅仅是缩放和平移物理形状。
计算节点的完整变换:这包括其缩放、旋转和枢轴。
使用此完整变换来变换物理形状。
检查下面更新的代码。
class My3DScene: SCNScene {
override init() {
super.init()
let camera = SCNCamera()
let cameraNode = SCNNode()
cameraNode.camera = camera
cameraNode.position = SCNVector3(x: 0, y: 0, z: 50)
rootNode.addChildNode(cameraNode)
let boxGeo = SCNBox(width: 5, height: 5, length: 1, chamferRadius: 0)
boxGeo.firstMaterial?.diffuse.contents = UIColor.red
let box = SCNNode(geometry: boxGeo)
// Set scale, rotation, and pivot
box.scale = SCNVector3Make(1.5, 1.5, 1.5)
box.eulerAngles = SCNVector3Make(1, 2, 3)
box.pivot = SCNMatrix4MakeTranslation(1, 1, 1)
rootNode.addChildNode(box)
// Create the physics shape and transform it with the node's full transformation
let boxPhysicsShape = SCNPhysicsShape(geometry: box.geometry!)
// Apply the full transform: scale, rotation, and pivot
let nodeTransformation = box.worldTransform
// Apply the full transformation to the physics shape
let transformedShape = boxPhysicsShape.transformed(by: nodeTransformation)
// Set the physics body
box.physicsBody = SCNPhysicsBody(type: .static, shape: transformedShape)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scnView = SCNView(frame: self.view.frame)
scnView.debugOptions = .showPhysicsShapes
self.view.addSubview(scnView)
let scene = My3DScene()
scnView.present(scene, with: .fade(withDuration: 1), incomingPointOfView: nil)
}
}