Unity中使用代码将预制加载到场景

Unity中使用代码将预制加载到场景

大家知道, 在日常修改预制的时候很方便, 我们将预制从资源文件夹往场景上"一拖", 然后就可以进行修改, 然后应用保存即可.

但是如果某些需求下, 我们想要使用代码来完成这个操作就比较麻烦.

比如需求为: 查找某个文件夹下所有预制, 如果其中有使用了RectMask2D组件的, 需要全部替换成Mask, 并且在替换完成后需要人工确定是否正确.

如果不需要人工确定, 其实很简单, 配合AssetDataBase相关的接口将预制加载出来, 然后替换组件后保存预制即可.

但是最后需要人工确定, 有问题还需要手动修改一部分, 那么就需要将预制加载到场景, 然后自动选中发生修改的节点来确定修改的正确.

按照正常的逻辑, 我可以像正常使用预制, 实例化对象的线路去思考, 但是实践下来发现, 加载到场景的是实例化之后的对象, 修改后无法应用保存, 和手动拖预制的结果不一致.

这时我们就需要使用另一个接口来达到目的了:

using UnityEditor;

// 将给定场景中的给定预制件实例化
// 第一个参数target是预制资源
// 第二个参数destinationScene是目标场景, 如果不传就默认是当前场景
public static Object PrefabUtility.InstantiatePrefab (Object target , SceneManagement.Scene destinationScene);

首先需要遍历选中目录, 查找预制并加载:

// Selection.activeObject是当前选中的目录
var dir = AssetDatabase.GetAssetPath(Selection.activeObject);
if (!Directory.Exists(dir))
{
    Debug.LogError("选中的不是目录!" + dir);
    return;
}

var allPrefabLst = AssetDatabase.FindAssets("t:Prefab", new []{dir});
foreach (var guid in allPrefabLst)
{
    var path = AssetDatabase.GUIDToAssetPath(guid);
    var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
    var find = go.GetComponentInChildren<RectMask2D>(true);
    if (!find) continue;
 	
    // ...
}

预制实例化并加到场景后, 将返回的对象转换类型后就可以设置其父节点:

var obj = PrefabUtility.InstantiatePrefab(prefab) as GameObject;

// 注意第二个参数, 如果不为false, 会导致加载出来的对象transform发生变化
obj.transform.SetParent(rootTransform, false);

我们还需要找到当前场景中指定的节点作为其统一挂载的父节点:

// 比如: 场景中的节点层级为: rootNode/xxx/parent
// 通过调用GetCurSceneObject("rootNode", "xxx/parent")完成需求
static GameObject GetCurSceneObject(string rootName, string selectPath)
{
    var curScene = SceneManager.GetActiveScene();
    foreach (var rootGameObject in curScene.GetRootGameObjects())
    {
        if (!rootName.Equals(rootGameObject.name)) continue;

        var transform = rootGameObject.transform.Find(selectPath);
        if (transform) return transform.gameObject;
    }

    return null;
}

然后是替换组件:

private static void ReplaceRectMask2D(GameObject go)
{
    var children = rootGo.GetComponentsInChildren<RectMask2D>(true);
    foreach (var rectMask2D in children)
    {
        var go = rectMask2D.gameObject;
        
        // 非运行模式下需要使用这个接口
        Object.DestroyImmediate(rectMask2D);
        go.AddComponent<Mask>();
    }
}

最后选中对象可以使用这里的介绍.

下面是完整的代码:

[MenuItem("Assets/PrintRectMask2DReferences", priority = 301)]
private static void PrintRectMask2DReferences()
{
    var dir = AssetDatabase.GetAssetPath(Selection.activeObject);
    if (!Directory.Exists(dir))
    {
        Debug.LogError("选中的不是目录!" + dir);
        return;
    }

    var allPrefabLst = AssetDatabase.FindAssets("t:Prefab", new []{dir});
    var rootNode = GetCurSceneObject("rootNode", "xxx/parent");
    if (!rootNode)
    {
        Debug.LogError("当前场景没有找到挂载节点: " + "rootNode/xxx/parent");
        return;
    }

    var rootTransform = rootNode.transform;
    var findLst = new List<GameObject>();
    foreach (var guid in allPrefabLst)
    {
        var path = AssetDatabase.GUIDToAssetPath(guid);
        var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
        var find = prefab.GetComponentInChildren<RectMask2D>(true);
        if (!find) continue;

        Debug.Log("find =>: " + path);

        var go = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
        if (!go) continue;

        findLst.Add(go);
        go.transform.SetParent(rootTransform, false);
        ReplaceRectMask2D(go);
    }

    Debug.LogError("一共找到预制: " + findLst.Count);
}

好了, 以上就是今天的内容, 希望对大家有所帮助.

猜你喜欢

转载自blog.csdn.net/woodengm/article/details/125329278
今日推荐