Unity随机平台关卡生成器:销毁实例化Prefab Transform报错求助
问题解决:关卡销毁后出现MissingReferenceException错误
问题背景
开发随机平台关卡生成器,使用预制件生成关卡的功能正常,但玩家死亡时调用DestroyAllLevelParts()销毁旧关卡,再调用CreateLevel()生成新关卡时,触发MissingReferenceException错误,提示已销毁的Transform仍被访问。因计划用ML Agent训练AI,不想重新加载场景,需实现销毁+重置关卡的逻辑。
错误原因
- 未清空引用列表:调用
DestroyAllLevelParts()销毁对象后,all_LevelParts列表中仍保留着已销毁对象的Transform引用,后续操作可能误访问这些无效引用。 - 使用
DestroyImmediate不当:DestroyImmediate是同步销毁方法,运行时使用可能导致帧内引用异常,更适合编辑模式下使用。 - 残留引用未重置:生成新关卡时,若未彻底清除旧关卡的状态引用,可能引发无效访问。
解决方案
1. 销毁后清空引用列表
销毁所有关卡部件后,立即清空all_LevelParts,避免后续代码访问无效引用。
2. 替换DestroyImmediate为Destroy
运行时使用异步销毁方法Destroy,让Unity在帧末处理销毁逻辑,避免同步销毁导致的引用异常。
3. 生成新关卡前重置状态
在CreateLevel方法开头重置起始位置,并添加双重保险确保关卡部件列表为空。
修改后的完整代码
public class LevelGenerator : MonoBehaviour { [SerializeField] private Transform levelPart_Start; [SerializeField] private List<Transform> levelPartList; [SerializeField] private Transform levelPart_Finish; [SerializeField] private int platforms; private List<Transform> all_LevelParts = new List<Transform>(); private Vector3 lastEndPosition; public void CreateLevel() { // 重置起始位置,确保不受旧关卡影响 lastEndPosition = levelPart_Start.Find("EndPosition").position; // 双重保险:确保列表为空(防止之前销毁逻辑未执行完全) if(all_LevelParts.Count > 0) { DestroyAllLevelParts(); } for(int i = 0; i < platforms; i++) { SpawnLevelPart(); } SpawnLevelPart(levelPart_Finish, lastEndPosition); } public void SpawnLevelPart() { Transform chosenLevelPart = levelPartList[Random.Range(0, levelPartList.Count)]; Transform lastLevelPartTransform = SpawnLevelPart(chosenLevelPart, lastEndPosition); lastEndPosition = lastLevelPartTransform.Find("EndPosition").position; } public Transform SpawnLevelPart(Transform levelPart, Vector3 spawnPosition) { Transform levelPartTransform = Instantiate(levelPart, spawnPosition, Quaternion.identity); all_LevelParts.Add(levelPartTransform); return levelPartTransform; } public void DestroyAllLevelParts() { foreach (Transform currentLevel in all_LevelParts) { // 运行时使用Destroy替代DestroyImmediate Destroy(currentLevel.gameObject); } // 清空列表,移除无效引用 all_LevelParts.Clear(); } }
额外注意事项
- 调用顺序必须是:先执行
DestroyAllLevelParts(),再调用CreateLevel()。Unity的异步销毁逻辑不会影响新对象的生成,无需额外等待帧。 - 确保玩家死亡逻辑所在的脚本,调用
LevelGenerator方法时,LevelGenerator对象未被销毁。
内容的提问来源于stack exchange,提问作者Hai Dinh
相关产品推荐
相关产品推荐

