如何在ScriptableObject中保存实例化预制体引用以复用
问题描述
我正在开发一个Unity EditorWindow,可动态创建场景并保存到资源文件夹,同时将场景名称添加到ScriptableObject的列表中,这部分功能运行正常。但尝试保存实例化的Prefab(GameObject)引用时,虽然列表元素数量显示正确,但重新打开项目或切换场景后,这些GameObject的引用会丢失。
以下是当前的核心代码:
创建网格并实例化预制体的代码
private void CreateGrid(GameObject objectInstance, GameObject parentObject) { int sceneNum = 0; for (int x = 0; x < tilesXAxis.value; x++) { for (int z = 0; z < tilesZAxis.value; z++, sceneNum++) { float rengeXMove = parentObject.transform.position.x + x * (gridTotalWidth.value / tilesXAxis.value); float rengeZMove = parentObject.transform.position.z + z * (gridTotalLength.value / tilesZAxis.value); GameObject _obj = PrefabUtility.InstantiatePrefab(objectInstance, parentObject.transform) as GameObject; TilesManager _tileCurrent = _obj.GetComponent<TilesManager>(); string _sceneName = CreateScenes((x).ToString() + "_" + z.ToString()); _tileCurrent.sceneName = _sceneName; _obj.transform.position = new Vector3(rengeXMove, 0, rengeZMove); AddElementsAtList(_sceneName, _obj, _tileCurrent); } } }
向ScriptableObject列表添加元素的代码
// Add elements to list :::Add Element At List void AddElementsAtList(string _addSceneNames, GameObject _addSceneGrid, TilesManager _addSceneGridBlocks) { serializedString_LandsScenesNames.InsertArrayElementAtIndex(serializedString_LandsScenesNames.arraySize); serializedObj_SceneGridObject.InsertArrayElementAtIndex(serializedObj_SceneGridObject.arraySize); serializedTiles_SceneGridBlocksTiles.InsertArrayElementAtIndex(serializedTiles_SceneGridBlocksTiles.arraySize); SerializedProperty addSceneNames = serializedString_LandsScenesNames.GetArrayElementAtIndex(serializedString_LandsScenesNames.arraySize - 1); SerializedProperty addScenesGrid = serializedObj_SceneGridObject.GetArrayElementAtIndex(serializedObj_SceneGridObject.arraySize - 1); SerializedProperty addSceneGridBlocks = serializedTiles_SceneGridBlocksTiles.GetArrayElementAtIndex(serializedTiles_SceneGridBlocksTiles.arraySize - 1); addSceneNames.stringValue = _addSceneNames; addScenesGrid.objectReferenceValue = _addSceneGrid as GameObject; addSceneGridBlocks.objectReferenceValue = _addSceneGridBlocks; serializedData_DataGrid.Update(); serializedData_DataGrid.ApplyModifiedProperties(); }
解决方案
引用丢失的核心原因是:你当前保存的是场景内的GameObject实例引用,而ScriptableObject作为资源文件,只能持久化保存对其他资源(如Prefab、纹理等)的引用,场景内对象在关闭场景或重启项目后会销毁,引用自然失效。
要解决这个问题,需要将实例化的GameObject保存为独立的Prefab资源,再将该Prefab的引用存入ScriptableObject:
1. 修改CreateGrid方法,添加预制体保存逻辑
在实例化GameObject后,将其保存到Assets目录下的指定路径,获取保存后的Prefab资源引用:
private void CreateGrid(GameObject objectInstance, GameObject parentObject) { int sceneNum = 0; // 定义预制体保存路径,必须在Assets目录下 string prefabSavePath = "Assets/GeneratedPrefabs/"; // 路径不存在则创建目录 if (!Directory.Exists(prefabSavePath)) { Directory.CreateDirectory(prefabSavePath); } for (int x = 0; x < tilesXAxis.value; x++) { for (int z = 0; z < tilesZAxis.value; z++, sceneNum++) { float rengeXMove = parentObject.transform.position.x + x * (gridTotalWidth.value / tilesXAxis.value); float rengeZMove = parentObject.transform.position.z + z * (gridTotalLength.value / tilesZAxis.value); GameObject _obj = PrefabUtility.InstantiatePrefab(objectInstance, parentObject.transform) as GameObject; TilesManager _tileCurrent = _obj.GetComponent<TilesManager>(); string _sceneName = CreateScenes((x).ToString() + "_" + z.ToString()); _tileCurrent.sceneName = _sceneName; _obj.transform.position = new Vector3(rengeXMove, 0, rengeZMove); // 将实例化对象保存为预制体资源 string prefabName = $"Tile_{x}_{z}.prefab"; GameObject savedPrefab = PrefabUtility.SaveAsPrefabAsset(_obj, prefabSavePath + prefabName); // 传入预制体资源引用而非场景实例 AddElementsAtList(_sceneName, savedPrefab, _tileCurrent); // 可选:如果不需要保留场景内实例,销毁清理 // DestroyImmediate(_obj); } } }
2. 验证ScriptableObject的字段类型
确保你的ScriptableObject类中,存储GameObject的字段类型为List<GameObject>,用于承载Prefab资源引用:
[CreateAssetMenu(fileName = "GridData", menuName = "Custom/Grid Data")] public class GridData : ScriptableObject { public List<string> landsScenesNames; public List<GameObject> sceneGridObjects; public List<TilesManager> sceneGridBlocksTiles; }
3. 关键注意事项
- 使用
PrefabUtility.SaveAsPrefabAsset(Unity 2018.3+)保存预制体,该方法返回的是资源文件引用,可直接存入ScriptableObject实现持久化。 - 若需要保持场景实例与Prefab的双向关联,可改用
PrefabUtility.SaveAsPrefabAssetAndConnect,但该方法会将场景实例标记为Prefab的实例,后续修改会同步到Prefab资源。
内容的提问来源于stack exchange,提问作者tiagob66
相关产品推荐
相关产品推荐

