You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 16:25:32