如何为PlayCanvas递归clone方法打猴子补丁并在递归结束后执行代码
解决PlayCanvas递归clone时自定义代码重复执行的问题
我太懂这个痛点了——PlayCanvas的clone()方法会递归遍历整个节点层级,直接打猴子补丁的话,自定义代码会跟着递归调用跑N次,完全不是我们想要的「克隆完整个树再统一处理」的效果。下面给你一个靠谱的重写方案,能精准控制自定义代码只在整个递归克隆流程结束后执行一次。
核心思路
我们需要区分「顶层clone调用」和「递归内部的clone调用」:只有当是用户主动发起的顶层调用完成后,才触发自定义逻辑。这里用一个临时标记来追踪当前是否处于递归克隆过程中,避免内部调用误触发。
具体实现代码
// 先保存原始的clone方法,别弄丢了 const originalClone = pc.Entity.prototype.clone; // 用Symbol做临时属性名,避免和其他代码的属性冲突 const isCloningSymbol = Symbol('isCloning'); // 重写Entity的clone方法 pc.Entity.prototype.clone = function(options) { // 判断是否是顶层调用:当前实体没有标记时,就是第一次调用 const isTopLevelCall = !this[isCloningSymbol]; // 顶层调用时,打上标记,告诉后续递归调用「别触发自定义逻辑」 if (isTopLevelCall) { this[isCloningSymbol] = true; } // 执行原始的克隆逻辑,拿到克隆后的实体 const clonedEntity = originalClone.call(this, options); // 只有顶层调用完成后,才执行自定义代码 if (isTopLevelCall) { // 移除临时标记,避免污染实体 delete this[isCloningSymbol]; // -------------------------- // 这里写你的自定义代码,只会执行一次! console.log("整个层级克隆完成,开始处理:", clonedEntity.name); // 示例:遍历克隆后的整个层级做统一处理 this.handleClonedHierarchy(clonedEntity); // -------------------------- } return clonedEntity; }; // 示例自定义处理函数,你可以改成自己的逻辑 function handleClonedHierarchy(rootEntity) { // 比如给所有克隆节点加个标识 rootEntity.findComponents('script').forEach(script => { script.cloned = true; }); // 或者调整层级、修改属性等等 }
为什么这个方案有效?
- 临时标记
isCloningSymbol是唯一的,不会和你的其他代码或PlayCanvas内部属性冲突; - 只有第一次调用
clone()(也就是你主动发起的顶层调用)会进入自定义代码分支,递归过程中内部的clone()调用会看到标记存在,跳过自定义逻辑; - 完全保留了PlayCanvas原始
clone()的所有功能和参数,不会影响原有克隆行为。
备选方案:计数器方式
如果你担心临时属性的问题,也可以用计数器追踪递归层级:
const originalClone = pc.Entity.prototype.clone; let cloneDepth = 0; pc.Entity.prototype.clone = function(options) { cloneDepth++; const clonedEntity = originalClone.call(this, options); cloneDepth--; // 当计数器回到0,说明整个克隆流程结束 if (cloneDepth === 0) { // 执行自定义代码 console.log("克隆完成,处理:", clonedEntity); } return clonedEntity; };
这个方案更简洁,但要注意如果同时有多个并行的克隆操作,计数器会互相干扰,所以第一种标记方式更安全。
内容的提问来源于stack exchange,提问作者mcmorry
相关产品推荐
相关产品推荐

