c# - 有没有办法销毁/替换 Prefab 实例内部或一部分的 GameObject?
问题描述
我在编辑器中遇到异常:
InvalidOperationException:不允许在 Prefab 实例中销毁 GameObject。
然后我必须手动打开预制件并从那里移除游戏对象。但是有没有办法通过脚本自动来做到这一点?
这是我用预制件替换游戏对象的脚本。我对预制件所做的唯一更改是采用 gmeobject 并向其添加一些颜色和纹理,所有脚本和其他东西都是相同的。
我正在尝试用预制件替换场景中的所有门,该预制件也是一扇只有一些颜色和纹理的门。
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class PrefabReplace : EditorWindow
{
[SerializeField] private GameObject prefab;
private bool selectionChanged;
private string objectsToSearch = "";
private List<GameObject> foundObjects = new List<GameObject>();
private List<GameObject> duplicatedObjects = new List<GameObject>();
private bool searched = false;
private int count = 0;
private int countChilds = 0;
private bool countChildren = false;
private GUIStyle guiStyle = new GUIStyle(); //create a new variable
private Texture timage;
[MenuItem("Tools/Prefab Replace")]
static void CreateReplaceWithPrefab()
{
int width = 340;
int height = 300;
int x = (Screen.currentResolution.width - width) / 2;
int y = (Screen.currentResolution.height - height) / 2;
GetWindow<PrefabReplace>().position = new Rect(x, y, width, height);
}
private void OnGUI()
{
Texture oo = null;
Texture texture = (Texture)oo;
//EditorGUI.DrawTextureTransparent(new Rect(10, 10, 20, 20), timage);
guiStyle.fontSize = 20; //change the font size
Searching();
GUILayout.Space(50);
Replacing();
}
private void Searching()
{
GUI.Label(new Rect(10, 20, 150, 20), "Search by name", guiStyle);
objectsToSearch = GUI.TextField(new Rect(90, 60, 150, 20), objectsToSearch, 25);
if (objectsToSearch != "")
{
GUI.enabled = true;
}
else
{
GUI.enabled = false;
}
GUILayout.Space(40);
if (GUILayout.Button("Search"))
{
foundObjects = new List<GameObject>();
duplicatedObjects = new List<GameObject>();
countChildren = true;
countChilds = 0;
count = 0;
foreach (GameObject gameObj in GameObject.FindObjectsOfType<GameObject>())
{
if (gameObj.name == objectsToSearch)
{
count += 1;
foundObjects.Add(gameObj);
Transform[] childs = gameObj.GetComponentsInChildren<Transform>();
foreach (Transform go in childs)
{
foundObjects.Add(go.gameObject);
}
}
}
if (foundObjects.Count > 0)
{
searched = true;
}
else
{
searched = false;
}
}
GUI.enabled = true;
if (count > 0)
GUI.TextField(new Rect(90, 85, 60, 15), count.ToString(), 25);
if (foundObjects.Count > 0 && countChildren == true)
{
for (int i = 0; i < foundObjects.Count; i++)
{
if (foundObjects[i].transform.childCount > 0)
{
countChilds += foundObjects[i].transform.childCount;
}
}
countChildren = false;
}
GUI.enabled = true;
if (countChilds > 0)
GUI.TextField(new Rect(90, 105, 60, 15), countChilds.ToString(), 25);
GUILayout.Space(100);
if (foundObjects.Count > 0)
EditorGUILayout.LabelField("Test");
}
private void Replacing()
{
GUILayout.Space(20);
GUILayout.BeginVertical(GUI.skin.box);
GUILayout.Label("Replacing");
GUILayout.Space(20);
prefab = (GameObject)EditorGUILayout.ObjectField("Prefab", prefab, typeof(GameObject), false);
var selection = Selection.objects.OfType<GameObject>().ToList();
if (selectionChanged)
{
if (selection.Count == 0)
GUI.enabled = false;
for (var i = selection.Count - 1; i >= 0; --i)
{
var selectedObject = selection[i];
if (prefab != null && selection.Count > 0 &&
selectedObject.scene.name != null
&& prefab != PrefabUtility
.GetCorrespondingObjectFromSource(selectedObject))
{
GUI.enabled = true;
}
else
{
GUI.enabled = false;
}
}
}
else
{
GUI.enabled = false;
}
if (GUILayout.Button("Replace"))
{
InstantiatePrefab(selection);
selectionChanged = false;
}
GUILayout.Space(10);
GUI.enabled = true;
EditorGUILayout.LabelField("Selection count: " + Selection.objects.OfType<GameObject>().Count());
GUILayout.EndVertical();
}
private void OnInspectorUpdate()
{
Repaint();
}
private void OnSelectionChange()
{
selectionChanged = true;
}
private void InstantiatePrefab(List<GameObject> selection)
{
if (prefab != null && selection.Count > 0)
{
for (var i = selection.Count - 1; i >= 0; --i)
{
var selected = selection[i];
Component[] components = selected.GetComponents(typeof(MonoBehaviour));
if (components.Length == 0)
{
SceneManager.SetActiveScene(SceneManager.GetSceneByName(selected.scene.name));
var prefabType = PrefabUtility.GetPrefabType(prefab);
GameObject newObject;
if (prefabType == PrefabType.Prefab)
{
newObject = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
}
else
{
newObject = Instantiate(prefab);
newObject.name = prefab.name;
}
if (newObject == null)
{
Debug.LogError("Error instantiating prefab");
break;
}
Undo.RegisterCreatedObjectUndo(newObject, "Replace With Prefabs");
newObject.transform.parent = selected.transform.parent;
newObject.transform.localPosition = selected.transform.localPosition;
newObject.transform.localRotation = selected.transform.localRotation;
newObject.transform.localScale = selected.transform.localScale;
newObject.transform.SetSiblingIndex(selected.transform.GetSiblingIndex());
Undo.DestroyObjectImmediate(selected);
}
}
}
}
}
所有的门都被很好地替换了,但是截图中的门是唯一一个属于预制实例的门。
解决方案
PrefabUtility.LoadPrefabContents()
此方法执行以下操作:
将给定路径上的 Prefab Asset 加载到隔离场景中,并返回 Prefab 的根 GameObject。
您可以使用它来获取 Prefab 的内容并直接修改它,而不是通过 Prefab 的实例。这对于批处理操作很有用。
这将允许您以您想要的方式修改预制件,但是:
一旦你修改了预制件,你必须使用它写回它
SaveAsPrefabAsset
,然后调用UnloadPrefabContents
以从内存中释放预制件和隔离场景。
您必须再次将其保存回原始预制件并覆盖您的更改。
推荐阅读
- react-native - 如何检测被包裹的单词
React Native 中的组件 - ios - 指南针应用程序是否需要从用户那里获得位置许可?
- android - 如何正确访问 firestore 中的文档名称以填充 RecyclerView?
- c# - 提供多线程或任务创建
- javascript - JS:在循环中的push()之后更新最新添加元素的属性未分配正确的值
- c++ - 从 std::vector 中的每个对象调用重载函数
- reactjs - 调用了 React 上下文操作,但存储未更新(无 Redux / React Context / React Hooks)
- vba - 如何使用 vba 创建查询并根据用户在表单中键入的内容执行查询?
- java - E/AndroidRuntime:致命异常:AsyncTask #3
- c++ - 如果 C++ 中有多个继承模板,则非法调用非静态成员函数