其他
维护现有 SceneKit 3D 场景时:配置…
适用于维护、扩展现有 Apple SceneKit 项目(SCNView、SCNScene、SCNNode、材质、灯光、相机、SCNAction、物理、粒子、.scn/.dae/.abc 资产、SwiftUI SceneView),不用于新项目或 USD/USDZ 管线。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 35/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 39/100 ×0.15
适用场景
维护现有 SceneKit 3D 场景时特定
解决思路
- 仅维护现有 SceneKit 场景;新项目、重大现代化、USD/USDZ 管线迁移到 RealityKit。
- 在 UIKit 中创建 SCNView:用 `SCNView(frame: view.bounds)`,设置 `scene = SCNScene()`、`allowsCameraControl = true`、`autoenablesDefaultLighting = true`、`backgroundColor = .black`,再 `view.addSubview(sceneView)`。
- 生产环境需要自定义相机控制时,通常禁用 `allowsCameraControl`。
- 创建空场景用 `SCNScene()`;加载捆绑 `.scn` 用 `SCNScene(named: "art.scnassets/ship.scn")`;从 URL 加载 `.dae` 用 `try SCNScene(url: url, options: [.checkConsistency: true])`。
- 每个场景有 `rootNode`;所有内容作为后代节点存在;用 `scene.rootNode.addChildNode(parentNode)` 挂载节点。
- 节点用 `position`、`eulerAngles`、`scale` 定义在父节点坐标系中的位置/朝向/缩放;SceneKit 使用右手坐标系:+X 右、+Y 上、+Z 朝相机。
- 设置节点变换:`node.position = SCNVector3(...)`、`node.eulerAngles = SCNVector3(x: 0, y: .pi / 4, z: 0)`、`node.scale = SCNVector3(2, 2, 2)`;优先用 `node.simdPosition = SIMD3<Float>(...)` 提升性能。
- 使用内置几何:`SCNBox`、`SCNSphere`、`SCNCylinder`、`SCNCone`、`SCNTorus`、`SCNCapsule`、`SCNTube`、`SCNPlane`、`SCNFloor`、`SCNText`、`SCNShape`(拉伸 Bezier 路径);如 `SCNNode(geometry: SCNSphere(radius: 0.5))`。
- 查找节点:`scene.rootNode.childNode(withName: "Max", recursively: true)`;或 `scene.rootNode.childNodes { node, _ in node.name?.hasPrefix("enemy") == true }`。
- 材质用 `SCNMaterial`;单材质几何用 `firstMaterial`,多材质用 `materials` 数组。
- 设置颜色/纹理:`material.diffuse.contents = UIColor.systemBlue`、`UIImage(named: "brick")`、`material.normal.contents = UIImage(named: "brick_normal")`、`sphere.firstMaterial = material`。
- 配置 PBR:`lightingModel = .physicallyBased`;`diffuse.contents` 设 albedo,`metalness.contents` 设 0.8 或纹理,`roughness.contents` 设 0.2 或纹理,`normal.contents` 设法线,`ambientOcclusion.contents` 设 AO。
- 选择光照模型:`.physicallyBased`(metalness/roughness)、`.blinn`(默认)、`.phong`、`.lambert`(仅 diffuse)、`.constant`(无光照)、`.shadowOnly`。
- 每个材质属性是 `SCNMaterialProperty`,可接受 `UIColor`、`UIImage`、`CGFloat` 标量、`SKTexture`、`CALayer` 或 `AVPlayer`。
- 透明度:设置 `material.transparency = 0.5`、`material.transparencyMode = .dualLayer`、`material.isDoubleSided = true`。
- 灯光:把 `SCNLight` 挂到节点;灯光方向沿节点负 Z 轴。
- 创建环境光:`SCNLight`,`type = .ambient`,设置 `color`,无方向。
- 创建方向光:`type = .directional`,设 `castsShadow = true` 模拟平行光/阳光。
- 创建点光源:`type = .omni`,设置 `attenuationEndDistance = 20`。
- 创建聚光灯:`type = .spot`,设置 `spotInnerAngle = 20`、`spotOuterAngle = 60`。
- 挂载灯光:创建 `SCNNode`,设置 `lightNode.light = directional`、`eulerAngles = SCNVector3(-Float.pi / 3, 0, 0)`、`position = SCNVector3(0, 10, 10)`,加入 `scene.rootNode`。
- 阴影:`light.castsShadow = true`,`shadowMapSize = CGSize(width: 2048, height: 2048)`,`shadowSampleCount = 8`,`shadowRadius = 3.0`,`shadowColor = UIColor(white: 0, alpha: 0.5)`。
- 类别位掩码:`light.categoryBitMask = 1 << 1`;`node.categoryBitMask = 1 << 1`,使节点仅受第 2 类灯光照亮。
- 每个节点最多渲染 8 盏灯;对点光/聚光灯设置 `attenuationEndDistance`,让 SceneKit 跳过远处节点的灯。
- 相机:创建 `SCNNode`,赋 `SCNCamera()`,设置 `position`,用 `cameraNode.look(at: SCNVector3Zero)`,加入 `scene.rootNode`,再设 `sceneView.pointOfView = cameraNode`。
- 相机配置:`fieldOfView = 60`(度)、`zNear = 0.1`、`zFar = 500`、`automaticallyAdjustsZRange = true`。
- 正交相机:`usesOrthographicProjection = true`、`orthographicScale = 10`。
- 景深和 HDR:在 `SCNCamera` 上配置 `wantsDepthOfField`、`focusDistance`、`fStop`、`wantsHDR`、`bloomIntensity`、`bloomThreshold`、`screenSpaceAmbientOcclusionIntensity`。
- 动画有三种方式:`SCNAction`(声明式、面向游戏)、`SCNTransaction`(隐式动画)、显式 Core Animation。
- 用 `SCNAction` 创建可复用可组合动画对象并挂到节点:`SCNAction.move(by:duration:)`、`SCNAction.rotateBy(x:y:z:duration:)`,用 `node.runAction(.group([move, rotate]))`。
- 顺序动画:`node.runAction(.sequence([.fadeOut(duration: 0.3), .removeFromParentNode()]))`。
- 无限循环:用 `.repeatForever(pulse)`,其中 `pulse` 是 `.sequence([.scale(to: 1.2, duration: 0.5), .scale(to: 1.0, duration: 0.5)])`。
- 用 `SCNTransaction` 隐式动画:`SCNTransaction.begin()`、设 `animationDuration = 1.0`、修改 `node.position`/`node.opacity`、设 `completionBlock`、`SCNTransaction.commit()`。
- 用显式 Core Animation:创建 `CABasicAnimation(keyPath: "rotation")`,`toValue = NSValue(scnVector4: SCNVector4(0, 1, 0, Float.pi * 2))`,设 `duration = 2`、`repeatCount = .infinity`,用 `node.addAnimation(animation, forKey: "spin")`。
- 物理刚体:用 `SCNPhysicsBody(type:shape:)`,动态 `.dynamic`(受力和碰撞)、静态 `.static`(不可移动)、运动学 `.kinematic`(代码驱动)。
- 当 `shape` 为 `nil` 时,SceneKit 从几何体推导形状;为性能应使用简化形状。
- 简化物理形状:`SCNPhysicsShape(geometry: SCNBox(width: 1, height: 2, length: 1, chamferRadius: 0), options: nil)`,赋给 `SCNPhysicsBody(type: .dynamic, shape: shape)`,设 `mass = 2.0`、`restitution = 0.3`。
- 施加力:`applyForce(SCNVector3(0, 10, 0), asImpulse: false)` 持续力;`applyForce(SCNVector3(0, 5, 0), asImpulse: true)` 瞬时冲量;`applyTorque(SCNVector4(0, 1, 0, 2), asImpulse: true)`。
- 碰撞检测类别:定义 `PhysicsCategory` 位掩码(如 `player = 1 << 0`、`enemy = 1 << 1`、`ground = 1 << 2`)。
- 设置 `categoryBitMask`、`collisionBitMask`、`contactTestBitMask`:如 `playerNode.physicsBody?.categoryBitMask = PhysicsCategory.player`、`collisionBitMask = ground | enemy`、`contactTestBitMask = enemy`。
- 设置接触代理:`scene.physicsWorld.contactDelegate = self`;实现 `func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact)` 处理碰撞。
- 重力:`scene.physicsWorld.gravity = SCNVector3(0, -9.8, 0)`;用 `node.physicsBody?.isAffectedByGravity = false` 取消单个物体重力。
- 粒子系统:创建 `SCNParticleSystem`,设置 `birthRate = 100`、`particleLifeSpan = 2`、`particleSize = 0.1`、`particleColor = .orange`、`emitterShape = SCNSphere(radius: 0.5)`、`particleVelocity = 2`、`isAffectedByGravity = true`、`blendMode = .additive`。
- 挂载粒子:创建 `SCNNode`,`emitterNode.addParticleSystem(particles)`,加入 `scene.rootNode`。
- 从 Xcode 粒子编辑器加载粒子:`SCNParticleSystem(named: "fire.scnp", inDirectory: nil)`;可用 `colliderNodes` 让粒子与几何体碰撞。
- 模型加载:SceneKit 文档支持的场景源格式是 `.scn`、`.dae`、`.abc`。
- 捆绑资产:把场景文件放入 `.scnassets` 文件夹,纹理图片放入 asset catalogs,让 Xcode 为目标设备优化。
- USD/USDZ 是 RealityKit 迁移路径,不是默认 SceneKit 加载路径;新项目、重大更新、SCN 到 USD 资产转换交给 RealityKit。
- 加载并校验场景:用 `Bundle.main.url(forResource: "model", withExtension: "dae")` 获取 URL,`try SCNScene(url: url, options: [.checkConsistency: true])`,再用 `scene.rootNode.childNode(withName: "mesh", recursively: true)` 检查必需节点;失败则抛错/停止。
- 把一致性校验和必需节点校验作为 authoring/import 关卡:失败时修复源资产或导入选项,然后重复同一检查。
- 对生成的 `.scn` 文件,加载 Scene Serialization,要求导出成功且提交前通过校验重载。
- 大模型用 `SCNReferenceNode` 配合 `.onDemand` 加载策略实现懒加载。
- 导入时单位转换用 `SCNSceneSource.LoadingOption`:`SCNSceneSource(url: url, options: nil)`,`try source.scene(options: [.convertUnitsToMeters: 1.0])`。
- 不要使用 `SCNScene.Attribute.unit` 或 `UnitMetersPerUnit`;`SCNScene.Attribute` 仅是元数据:`.startTime`、`.endTime`、`.frameRate`、`.upAxis`。
- SwiftUI 集成:用 `SceneView(scene: scene, options: [.allowsCameraControl, .autoenablesDefaultLighting])` 嵌入 SceneKit。
- `SceneView` 可用选项:`.allowsCameraControl`、`.autoenablesDefaultLighting`、`.jitteringEnabled`、`.temporalAntialiasingEnabled`。
- 需要控制渲染循环时,把 `SCNView` 包进 `UIViewRepresentable`,用 `SCNSceneRendererDelegate` coordinator。
- 避免“未添加相机或灯光”:不要只设 `sceneView.scene = scene` 导致空白/黑屏;应添加相机节点、灯光,或使用 `autoenablesDefaultLighting`。
- 避免“物理形状使用精确几何体”:不要用复杂 mesh 作为 `SCNPhysicsShape`;应使用简化 primitive(如 `SCNBox`)作为物理形状。
- 避免“修改动态刚体变换”:不要直接设 `dynamicNode.position = ...` 重置物理模拟;应使用 `applyForce`/冲量。
- 审查清单:场景至少有一个相机节点并设为 `pointOfView`。
- 审查清单:场景有合适灯光,或原型阶段用 `autoenablesDefaultLighting`。
- 审查清单:物理形状使用简化几何体,而非完整网格细节。
- 审查清单:需要碰撞回调的刚体设置 `contactTestBitMask`。
- 审查清单:把 `SCNPhysicsContactDelegate` 赋给 `scene.physicsWorld.contactDelegate`。
- 审查清单:动态刚体变换通过力/冲量改变,而非直接改 position。
- 审查清单:每个节点灯光限制在 8 盏;点光/聚光灯设置 `attenuationEndDistance`。
- 审查清单:材质使用 `.physicallyBased` 光照模型以获得真实渲染。
- 审查清单:SceneKit 资产使用文档支持的 `.scn`、`.dae` 或 `.abc` 场景源格式。
- 审查清单:导入和导出资产在提交前通过一致性校验和必需节点检查。
- 审查清单:捆绑的 SceneKit 纹理/图片使用 asset catalogs 或 Xcode 优化资源。
- 审查清单:场景元数据/导入选项使用文档 API;不要杜撰 `SCNScene.Attribute.unit`。
- 审查清单:新的 USD/USDZ 管线或重大更新转到 RealityKit。
- 审查清单:Game Center 认证、排行榜、成就、多人游戏交给 GameKit。
- 审查清单:大模型使用 `SCNReferenceNode` 实现懒加载。
- 审查清单:平衡粒子的 `birthRate` 和 `particleLifeSpan` 以控制粒子数量。
- 审查清单:使用 `categoryBitMask` 将灯光和相机限定到相关节点。
- 审查清单:SwiftUI 场景使用 `SceneView` 或 `UIViewRepresentable` 包装的 `SCNView`。
- 审查清单:承认废弃状态;新项目评估 RealityKit。
- 进一步参考 `references/scenekit-patterns.md` 了解自定义几何、shader modifiers、约束、morph targets、hit testing、scene serialization、render loop delegates、性能、SpriteKit overlay、LOD、Metal shaders。
- 参考 SceneKit 文档、SCNSceneSource、SCNView、SceneView、SCNPhysicsShape、SCNShadable,以及 WWDC 2025 session 288: Bring your SceneKit project to RealityKit。
适用边界 · 注意事项
- 新项目、重大现代化或需要 USD/USDZ 管线时,不应继续使用 SceneKit,应转向 RealityKit。
- 需要 Game Center 认证、排行榜、成就或多人游戏能力时,应交给 GameKit,而不是用此技能处理。
- 非 Apple 平台或无 SceneKit/Xcode 工具链的项目不适用。