阅读 230

SCNNode 的 clone 与 flattenedClone 简单使用

说明

SCNNode对象有两个方法:clone 和 flattenedClone,同时还继承有 copy 方法,那它们有什么区别?

方法名 作用
copy 对象拷贝操作,只复制当前对象
clone 复制出一个新 node,并复制同样的树状结构(childNodes)
flattenedClone 复制出一个新 node,并将原来树状结构(childNodes)所有内容合并后,存放在当前对象下,可提高性能

clone 与 flattenedClone 的使用区别

我们以苹果官方示例程序中的小飞机模型来进行对比,3D 模型结构如下:

// 原始 node
let node = scene.rootNode.childNode(withName: "ship", recursively: true)
node?.simdPosition = simd_float3(0, 0, -0.5)
scene.rootNode.addChildNode(node!)

// clone 后的 node
let node2 = node?.clone()
node2?.simdPosition = simd_float3(0.5, 0, -0.5)
scene.rootNode.addChildNode(node2!)

// flattenedClone 后的 node
let node3 = node?.flattenedClone()
node3?.simdPosition = simd_float3(1, 0, -0.5)
scene.rootNode.addChildNode(node3!)
复制代码

看到的效果如下,我们得到了三个看上去一模一样的小飞机

使用时的注意事项

需要注意的是,原始的 node 和 clone 得到的 node2 其实是共用一个 geometry 对象的,同时也共用了所有的材质和贴图;flattenedClone 后得到的 node3 虽然生成了合并后的 geometry,但是仍然和前两个共用了材质和贴图,即它们的 geometry?.firstMaterial 是同一个对象。

这就导致了两个问题:

  • 当试图改变复制出来的 node 的材质或颜色时,三者会同时改变;
  • flattenedClone 时,内部结构复杂的 node(比如内部有多个子 node,子 node 有自己的 geometry),所有子 geometry 被合并,但材质贴图无法自动合并,会保留最外层或第一个子node 的材质;

比如下图这个模型:

因此,模型有多个部件,材质与贴图需要专门处理,使用 flattenedClone 应慎重。

但是第一个问题我们是可以通过简单的代码来处理的:

let node = scene.rootNode.childNode(withName: "ship", recursively: true)
node?.simdPosition = simd_float3(0, 0, -0.5)
scene.rootNode.addChildNode(node!)

let node2 = node?.clone()
node2?.simdPosition = simd_float3(0.5, 0, -0.5)
scene.rootNode.addChildNode(node2!)
// 找到真正有 geometry 的子 node
let shipMesh = node?.childNodes.first
// copy 这个 geometry 给自己对应的子 node 赋值
node2?.childNodes.first?.geometry = shipMesh?.geometry?.copy() as? SCNGeometry
// 新建一个 SCNMaterial 对象
node2?.childNodes.first?.geometry?.firstMaterial = SCNMaterial()
// 改变 geometry 的材质颜色
node2?.childNodes.first?.geometry?.firstMaterial?.diffuse.contents = UIColor.red

let node3 = node?.flattenedClone()
node3?.simdPosition = simd_float3(1, 0, -0.5)
scene.rootNode.addChildNode(node3!)
// geometry 已经不是同一个了,无需再 copy,但仍需创建新的 SCNMaterial 对象
node3?.geometry?.firstMaterial = SCNMaterial()
node3?.geometry?.firstMaterial?.diffuse.contents = UIColor.green
复制代码

只需要将 geometry 也 copy 一份,并赋值到对应的 clone 出的 node 下面,并新建一个SCNMaterial对象就可以改变材质和颜色了。