RealityKit中PhysicsRevoluteJoint无法固定实体铰接的问题
RealityKit中PhysicsRevoluteJoint约束失效导致螺旋桨漂移问题排查与修复
问题场景
基于visionOS的RealityKit沉浸式SwiftUI应用中,尝试通过PhysicsRevoluteJoint将动态的propellerEntity铰接至静态的stickyEntity,预期螺旋桨绕Y轴原地旋转且保持铰接点位置固定,但施加冲量后螺旋桨从铰接点漂移,关节约束未生效。
用户原实现代码:
主逻辑代码
let stickyEntity = ModelEntity(mesh: .generateSphere(radius: 0.01)) var stickyBody = PhysicsBodyComponent(shapes: [.generateSphere(radius: 0.01)], mass: 10.0, mode: .static) stickyBody.isAffectedByGravity = false stickyEntity.position = [0.0, 1.0, 0.0] stickyEntity.components.set(stickyBody) stickyEntity.components.set(CollisionComponent(shapes: [.generateSphere(radius: 0.01)])) let propellerEntity = await createPropeller() jointsSimulation.addChild(propellerEntity) propellerEntity.position = stickyEntity.position // Create hinge let hingeOrientation = simd_quatf(from: [1, 0, 0], to: [0, 1, 0]) let attachmentPin = stickyEntity.pins.set(named: "attachment_hinge", position: .zero, orientation: hingeOrientation) let propellerPin = propellerEntity.pins.set(named: "propellerPin", position: propellerEntity.position(relativeTo: stickyEntity), orientation: hingeOrientation) let revoluteJoint = PhysicsRevoluteJoint(pin0: attachmentPin, pin1: propellerPin) do { try revoluteJoint.addToSimulation() // Apply force (this doesn't keep the entity in place) let impulseAction = ImpulseAction( targetEntity: .sourceEntity, linearImpulse: [0.05, 0.0, 0.05] ) let impulseAnimation = try AnimationResource.makeActionAnimation(for: impulseAction) propellerEntity.playAnimation(impulseAnimation) } catch { print(error) }
螺旋桨创建函数
func createPropeller() async -> ModelEntity { let propeller = ModelEntity(mesh: .generateBox(size: [0.1, 0.01, 0.1])) var propellerPhysics = PhysicsBodyComponent( massProperties: .init(mass: 0.1), material: .generate(staticFriction: 0.0, dynamicFriction: 0.0, restitution: 0.0), mode: .dynamic ) propellerPhysics.angularDamping = 0.0 propellerPhysics.linearDamping = 1.0 propellerPhysics.isAffectedByGravity = false propeller.components.set(propellerPhysics) propeller.components.set(CollisionComponent( shapes: [ShapeResource.generateBox(size: [0.1, 0.01, 0.1])] )) return propeller }
问题原因
- 销钉位置配置错误:
propellerPin的位置使用了propellerEntity.position(relativeTo: stickyEntity),此时螺旋桨与stickyEntity位置重合,相对位置为.zero,但这个值是全局坐标系下的相对位置,并非螺旋桨本地坐标系的铰接点,导致关节约束的锚点错位。 - 冲量类型错误:使用了线性冲量而非角冲量,线性冲量会给螺旋桨施加平移力,直接突破关节约束的位置限制,引发漂移。
- 静态实体冗余配置:静态实体的
mass参数无效(.static模式下物理引擎会忽略质量),属于冗余设置。
修复方案
1. 修正销钉位置
将螺旋桨的销钉位置设置为本地坐标系原点(因为螺旋桨已经与stickyEntity位置重合,本地原点就是正确的铰接点):
let propellerPin = propellerEntity.pins.set(named: "propellerPin", position: .zero, orientation: hingeOrientation)
2. 改用角冲量驱动旋转
替换线性冲量为角冲量,针对Y轴施加旋转力,符合铰接关节的旋转轴要求:
// 替换原冲量代码 let angularImpulseAction = ImpulseAction( targetEntity: .sourceEntity, angularImpulse: [0.0, 5.0, 0.0] // 绕Y轴施加角冲量 ) let angularImpulseAnimation = try AnimationResource.makeActionAnimation(for: angularImpulseAction) propellerEntity.playAnimation(angularImpulseAnimation)
3. 优化物理体配置
移除静态实体的冗余mass参数,调整螺旋桨的阻尼参数以优化旋转流畅性:
// 静态实体物理体简化 var stickyBody = PhysicsBodyComponent(shapes: [.generateSphere(radius: 0.01)], mode: .static) stickyBody.isAffectedByGravity = false stickyEntity.components.set(stickyBody) // 螺旋桨物理体调整(可选,根据需求调整阻尼) propellerPhysics.angularDamping = 0.1 // 轻微角阻尼防止无限旋转 propellerPhysics.linearDamping = 0.0 // 线性阻尼设为0,让关节约束完全控制位置
修复后完整代码
主逻辑代码
let stickyEntity = ModelEntity(mesh: .generateSphere(radius: 0.01)) var stickyBody = PhysicsBodyComponent(shapes: [.generateSphere(radius: 0.01)], mode: .static) stickyBody.isAffectedByGravity = false stickyEntity.position = [0.0, 1.0, 0.0] stickyEntity.components.set(stickyBody) stickyEntity.components.set(CollisionComponent(shapes: [.generateSphere(radius: 0.01)])) let propellerEntity = await createPropeller() jointsSimulation.addChild(propellerEntity) propellerEntity.position = stickyEntity.position // Create hinge let hingeOrientation = simd_quatf(from: [1, 0, 0], to: [0, 1, 0]) let attachmentPin = stickyEntity.pins.set(named: "attachment_hinge", position: .zero, orientation: hingeOrientation) let propellerPin = propellerEntity.pins.set(named: "propellerPin", position: .zero, orientation: hingeOrientation) let revoluteJoint = PhysicsRevoluteJoint(pin0: attachmentPin, pin1: propellerPin) do { try revoluteJoint.addToSimulation() // 施加绕Y轴的角冲量 let angularImpulseAction = ImpulseAction( targetEntity: .sourceEntity, angularImpulse: [0.0, 5.0, 0.0] ) let angularImpulseAnimation = try AnimationResource.makeActionAnimation(for: angularImpulseAction) propellerEntity.playAnimation(angularImpulseAnimation) } catch { print(error) }
优化后的螺旋桨创建函数
func createPropeller() async -> ModelEntity { let propeller = ModelEntity(mesh: .generateBox(size: [0.1, 0.01, 0.1])) var propellerPhysics = PhysicsBodyComponent( massProperties: .init(mass: 0.1), material: .generate(staticFriction: 0.0, dynamicFriction: 0.0, restitution: 0.0), mode: .dynamic ) propellerPhysics.angularDamping = 0.1 propellerPhysics.linearDamping = 0.0 propellerPhysics.isAffectedByGravity = false propeller.components.set(propellerPhysics) propeller.components.set(CollisionComponent( shapes: [ShapeResource.generateBox(size: [0.1, 0.01, 0.1])] )) return propeller }
效果验证
修改后,螺旋桨会绕Y轴原地旋转,铰接点位置保持固定,不会再出现漂移现象,PhysicsRevoluteJoint的约束生效。
内容的提问来源于stack exchange,提问作者Magnus T
相关产品推荐
相关产品推荐

