如何在Swift中深度复制RealityKit Material以实现对象选中状态切换?
解决选中对象切换材质后无法恢复原材质的问题
问题描述
想要实现的功能:
- 用户选中对象
- 对象切换材质以标识已选中状态
- 对象取消选中后恢复原材质
当前实现流程:
// 存储对象原材质 activeObjectMaterials = detectedTaggedEntity.model.model!.materials[0] // 切换为高亮材质,此步骤正常工作 detectedTaggedEntity.model.model?.materials = [highlightMaterial] // 尝试恢复原材质,但失败:因为材质按引用存储,切换高亮时原材质引用已被改变,activeObjectMaterials不再是原始值 detectedTaggedEntity.model.model?.materials = [activeObjectMaterials]
注:所有材质均为运行时程序化生成
原因分析
材质对象是引用类型,你直接赋值activeObjectMaterials = detectedTaggedEntity.model.model!.materials[0]时,只是保存了该材质对象的内存引用。当后续替换材质数组时,如果原材质对象被修改(或者材质数组的引用指向了新对象),之前保存的activeObjectMaterials会同步变化,导致无法恢复到最初的原始材质状态。
解决方案
方案1:创建原材质的深拷贝
既然材质是运行时生成的,在选中对象时,对原材质进行深拷贝,保存一份独立的副本,而不是引用。取消选中时,直接使用这个副本恢复。
示例代码(伪代码,根据你的引擎/框架调整深拷贝逻辑):
// 选中时:深拷贝原材质 function deepCloneMaterial(material) { // 根据材质类型实现深拷贝,比如复制所有属性、着色器参数等 const cloned = new material.constructor(); Object.assign(cloned, material); // 如果有嵌套属性(比如纹理、 uniforms),需要递归拷贝 if (material.uniforms) { cloned.uniforms = {...material.uniforms}; } return cloned; } // 存储拷贝后的原材质 activeObjectMaterials = deepCloneMaterial(detectedTaggedEntity.model.model!.materials[0]) // 切换高亮材质 detectedTaggedEntity.model.model?.materials = [highlightMaterial] // 恢复时使用拷贝的材质 detectedTaggedEntity.model.model?.materials = [activeObjectMaterials]
方案2:维护材质状态映射表
给每个可选中对象维护一个状态映射,存储它的原始材质和高亮材质,避免直接操作原材质引用。
示例:
// 全局或局部维护对象材质状态 const objectMaterialStates = new Map(); // 选中对象时 const entity = detectedTaggedEntity; const originalMat = entity.model.model!.materials[0]; objectMaterialStates.set(entity, { original: originalMat, highlight: highlightMaterial }); entity.model.model?.materials = [highlightMaterial]; // 取消选中时 const state = objectMaterialStates.get(detectedTaggedEntity); if (state) { detectedTaggedEntity.model.model?.materials = [state.original]; // 可选:如果不再需要,移除映射 objectMaterialStates.delete(detectedTaggedEntity); }
注:如果材质可能动态更新,这种方式需要确保映射表中的原始材质是未被修改的初始版本,可能结合方案1的拷贝来使用。
方案3:切换材质属性而非替换材质数组
不直接替换整个材质数组,而是修改材质的属性来实现高亮效果,取消时再还原属性。比如修改材质的颜色、自发光值等:
示例:
// 选中时:保存原材质属性,修改为高亮属性 const material = detectedTaggedEntity.model.model!.materials[0]; // 保存原始属性 objectMaterialStates.set(material, { originalColor: material.color.clone(), originalEmissive: material.emissive.clone() }); // 设置高亮 material.color.set(0xff0000); material.emissive.set(0x330000); // 取消选中时:恢复原属性 const state = objectMaterialStates.get(material); if (state) { material.color.copy(state.originalColor); material.emissive.copy(state.originalEmissive); objectMaterialStates.delete(material); }
这种方式适合不需要完全替换材质,只是调整视觉参数的场景,彻底避免了材质引用的问题。
内容的提问来源于stack exchange,提问作者ElliotD
相关产品推荐
相关产品推荐

