为何已销毁的GameObject仍被FindGameObjectByTag()重复找到?
问题分析与解决方案
核心问题
- 赋值而非条件判断:
if(saveNum = true)是赋值操作,不是逻辑判断,这会导致代码始终进入第一个分支,无法切换查找Tile/Block标签的逻辑。 - 单次标签查找的局限性:
GameObject.FindGameObjectWithTag只会返回场景中第一个匹配标签的对象,即使销毁了第一个,循环内重复调用也无法保证正确获取下一个目标,且原逻辑中saveNum仅在循环结束后取反,两次循环都会查找同标签对象。 - 索引计算错误:存储位置需要3个元素(x/y/z),但原代码中
j +=2,会导致后续位置数据覆盖或数组越界;存储旋转需要4个元素(x/y/z/w),但k +=3,同样会造成旋转数据混乱。 - 低效的对象查找方式:循环内重复调用查找方法性能差,且容易出现对象引用错误。
修正后的代码
public void SaveLevel() { // 一次性获取所有需要处理的Tile和Block对象 List<GameObject> allTargetTiles = new List<GameObject>(GameObject.FindGameObjectsWithTag("Tile")); allTargetTiles.AddRange(GameObject.FindGameObjectsWithTag("Block")); // 重置索引计数器 int posIndex = 0; int rotIndex = 0; for (int i = 0; i < allTargetTiles.Count; i++) { GameObject currentTile = allTargetTiles[i]; if (currentTile == null) continue; // 根据名称标记Tile类型 if (currentTile.name.Contains("Hall")) { intTileList_forFile[i] = 0; } else if (currentTile.name.Contains("Corner")) { intTileList_forFile[i] = 1; } else if (currentTile.name.Contains("Edge")) { intTileList_forFile[i] = 2; } else if (currentTile.name.Contains("End")) { intTileList_forFile[i] = 3; } else if (currentTile.name.Contains("Middle")) { intTileList_forFile[i] = 4; } // 存储位置(x/y/z,每次占用3个索引位) intTilePlacement_forFile[posIndex] = currentTile.transform.position.x; intTilePlacement_forFile[posIndex + 1] = currentTile.transform.position.y; intTilePlacement_forFile[posIndex + 2] = currentTile.transform.position.z; posIndex += 3; // 存储旋转(x/y/z/w,每次占用4个索引位) intTileRotation_forFile[rotIndex] = currentTile.transform.rotation.x; intTileRotation_forFile[rotIndex + 1] = currentTile.transform.rotation.y; intTileRotation_forFile[rotIndex + 2] = currentTile.transform.rotation.z; intTileRotation_forFile[rotIndex + 3] = currentTile.transform.rotation.w; rotIndex += 4; // 销毁当前Tile Destroy(currentTile); } saveNum = !saveNum; Debug.Log("Complete"); }
关键改动说明
- 批量获取对象:用
FindGameObjectsWithTag(复数形式)一次性获取所有匹配标签的对象,避免循环内重复查找,确保遍历所有需要处理的Tile/Block。 - 修复索引逻辑:位置存储后索引加3,旋转存储后索引加4,保证数组索引不越界、数据不覆盖。
- 简化对象引用:直接用
currentTile引用当前处理对象,避免依赖单元素列表TileList[0]的不稳定引用。 - 修正条件逻辑:移除原代码中错误的赋值操作,改为批量处理所有目标对象(若需区分Tile和Block,可在遍历中添加标签判断)。
额外优化建议
- 替换名称判断逻辑:给不同类型Tile添加枚举组件、专属标签或Layer,比判断对象名称更可靠且性能更好。
- 改用List替代数组:
intTileList_forFile、intTilePlacement_forFile等建议用List<float>/List<int>,避免固定数组长度导致的越界问题。 - 优化销毁逻辑:若Tile数量较多,可考虑异步销毁或延迟销毁,避免卡顿。
内容的提问来源于stack exchange,提问作者NotJohn321
相关产品推荐
相关产品推荐

