Как я могу заменить GameObject префабом и добавить в префаб все компоненты и настройки свойств компонентов в префаб? - PullRequest
0 голосов
/ 03 марта 2019

В сценарии EditorWindow сценария PrefabReplace сначала перед изменениями я проверил, нет ли в выбранном игровом объекте какого-либо компонента типа монобихев, а затем произвел замену.Но теперь я хочу иметь возможность заменять и дублировать выбранные объекты в режиме редактора, а также в режиме выполнения.Поэтому я изменил обе переменные newObject и компоненты и сделал их статическими.

Тогда я больше не проверяю, есть ли у выбранных gameobject / s какие-либо компоненты типа monobehaviour.

И создал новый классРасширение с методом, который должен сделать копию компонентов в новый сборный.Но он дает мне исключение и не уверен, будет ли он работать каким-либо образом.

Сценарий класса Extension:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;

public static class Extension
{
    public static T AddComponent1<T>(this GameObject game, T duplicate) where T : Component
    {
        T target = game.AddComponent<T>();
        foreach (PropertyInfo x in typeof(T).GetProperties())
            if (x.CanWrite)
                x.SetValue(target, x.GetValue(duplicate));
        return target;
    }

    public static void Init(GameObject go, Component comp)
    {
        go.AddComponent1(comp);
    }
}

Но я получаю исключение в классе Extension в строках:14 и 20:

x.SetValue(target, x.GetValue(duplicate));

И

go.AddComponent1(comp);

Исключение составляет:

TargetException: для нестатического метода требуется цель

Основнойцель состоит в том, чтобы заменить игровой объект префабом, и чтобы он имел те же компоненты и все настройки и значения компонентов, что и игровой объект, который был заменен.

Например, если у меня есть куб, который вращается или перемещается из стороны всторона, и я заменяю куб сферой, чтобы сфера вращалась или двигалась из стороны в сторону.

Нулевое исключение, которое я получаю:

NullReferenceException: Object reference not set to an instance of an object
Extension.GetCopyOf[T] (UnityEngine.Component comp, T other) (at Assets/Scripts/Extension.cs:22)
Extension.AddComponent[T] (UnityEngine.GameObject go, T toAdd) (at Assets/Scripts/Extension.cs:12)
Extension.Init[T] (UnityEngine.GameObject go, T comp) (at Assets/Scripts/Extension.cs:17)
PrefabReplace.InstantiatePrefab (System.Collections.Generic.IReadOnlyList`1[T] selection) (at Assets/Editor/PrefabReplace.cs:222)
PrefabReplace.Replacing () (at Assets/Editor/PrefabReplace.cs:144)
PrefabReplace.OnGUI () (at Assets/Editor/PrefabReplace.cs:44)
System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
Rethrow as TargetInvocationException: Exception has been thrown by the target of an invocation.
System.Reflection.MonoMethod.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
System.Reflection.MethodBase.Invoke (System.Object obj, System.Object[] parameters) (at <d7ac571ca2d04b2f981d0d886fa067cf>:0)
UnityEditor.HostView.Invoke (System.String methodName, System.Object obj) (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:342)
UnityEditor.HostView.Invoke (System.String methodName) (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:336)
UnityEditor.HostView.InvokeOnGUI (UnityEngine.Rect onGUIPosition, UnityEngine.Rect viewRect) (at C:/buildslave/unity/build/Editor/Mono/HostView.cs:310)
UnityEditor.DockArea.DrawView (UnityEngine.Rect viewRect, UnityEngine.Rect dockAreaRect, System.Boolean customBorder, System.Boolean floatingWindow, System.Boolean isBottomTab) (at C:/buildslave/unity/build/Editor/Mono/GUI/DockArea.cs:361)
UnityEditor.DockArea.OldOnGUI () (at C:/buildslave/unity/build/Editor/Mono/GUI/DockArea.cs:320)
UnityEngine.Experimental.UIElements.IMGUIContainer.DoOnGUI (UnityEngine.Event evt, UnityEngine.Matrix4x4 worldTransform, UnityEngine.Rect clippingRect, System.Boolean isComputingLayout) (at C:/buildslave/unity/build/Modules/UIElements/IMGUIContainer.cs:266)
UnityEngine.Experimental.UIElements.IMGUIContainer.HandleIMGUIEvent (UnityEngine.Event e, UnityEngine.Matrix4x4 worldTransform, UnityEngine.Rect clippingRect) (at C:/buildslave/unity/build/Modules/UIElements/IMGUIContainer.cs:438)
UnityEngine.Experimental.UIElements.IMGUIContainer.HandleIMGUIEvent (UnityEngine.Event e) (at C:/buildslave/unity/build/Modules/UIElements/IMGUIContainer.cs:421)
UnityEngine.Experimental.UIElements.IMGUIContainer.HandleEvent (UnityEngine.Experimental.UIElements.EventBase evt) (at C:/buildslave/unity/build/Modules/UIElements/IMGUIContainer.cs:401)
UnityEngine.Experimental.UIElements.EventDispatcher.ProcessEvent (UnityEngine.Experimental.UIElements.EventBase evt, UnityEngine.Experimental.UIElements.IPanel panel) (at C:/buildslave/unity/build/Modules/UIElements/EventDispatcher.cs:511)
UnityEngine.Experimental.UIElements.EventDispatcher.Dispatch (UnityEngine.Experimental.UIElements.EventBase evt, UnityEngine.Experimental.UIElements.IPanel panel, UnityEngine.Experimental.UIElements.DispatchMode dispatchMode) (at C:/buildslave/unity/build/Modules/UIElements/EventDispatcher.cs:307)
UnityEngine.Experimental.UIElements.BaseVisualElementPanel.SendEvent (UnityEngine.Experimental.UIElements.EventBase e, UnityEngine.Experimental.UIElements.DispatchMode dispatchMode) (at C:/buildslave/unity/build/Modules/UIElements/Panel.cs:176)
UnityEngine.Experimental.UIElements.UIElementsUtility.DoDispatch (UnityEngine.Experimental.UIElements.BaseVisualElementPanel panel) (at C:/buildslave/unity/build/Modules/UIElements/UIElementsUtility.cs:245)
UnityEngine.Experimental.UIElements.UIElementsUtility.ProcessEvent (System.Int32 instanceID, System.IntPtr nativeEventPtr) (at C:/buildslave/unity/build/Modules/UIElements/UIElementsUtility.cs:68)
UnityEngine.GUIUtility.ProcessEvent (System.Int32 instanceID, System.IntPtr nativeEventPtr) (at C:/buildslave/unity/build/Modules/IMGUI/GUIUtility.cs:179)

1 Ответ

0 голосов
/ 04 марта 2019

Я использовал это вместо этого, и теперь оно работает ( source ):

public static class Extension
{

    public static T AddComponent<T>(this GameObject go, T toAdd) where T : Component
    {
        return go.AddComponent<T>().GetCopyOf(toAdd);
    }

    public static void Init<T>(this GameObject go, T comp) where T : Component
    {
        go.AddComponent(comp);
    }

    public static T GetCopyOf<T>(this Component comp, T other) where T : Component
    {
        var type = comp.GetType();
        if (type != other.GetType()) return null; // type mis-match

        const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default | BindingFlags.DeclaredOnly;

        var pinfos = type.GetProperties(flags);
        foreach (var pinfo in pinfos.Where(pinfo => pinfo.CanWrite))
        {
            try
            {
                pinfo.SetValue(comp, pinfo.GetValue(other, null), null);
            }
            catch { } // In case of NotImplementedException being thrown. For some reason specifying that exception didn't seem to catch it, so I didn't catch anything specific.
        }

        var finfos = type.GetFields(flags);
        foreach (var finfo in finfos)
        {
            finfo.SetValue(comp, finfo.GetValue(other));
        }

        return comp as T;
    }
}

Не уверен на 100%, но я думаю, что разница заключается в flagsпараметр для ограничения, какие свойства копируются.Очевидно, вы пытались скопировать также некоторые статические значения.


Однако, похоже, есть еще одна проблема с вашим selection .., он не обновляется корректно, поэтому, если я не выберу другой GameObject в иерархиии чем первый снова я получаю null для selection[i] в InstantiatePrefab.


Почему вы делаете

components = selected.GetComponents(typeof(MonoBehaviour));

, а не

components = selected.GetComponents(typeof(Component));

Обновление

Вот как я его использовал (немного переработал ваш скрипт)

public class PrefabReplace : EditorWindow
{
    [SerializeField] private GameObject _prefab;

    private bool _selectionChanged;
    private string _objectsToSearch = "";
    private List<GameObject> _foundObjects = new List<GameObject>();
    private readonly GUIStyle _guiStyle = new GUIStyle(); //create a new variable
    private int _count;
    private bool _addFoundObjects;
    private bool _keepNames = true;
    private bool _keepPlaceInHierarchy = true;
    private static GameObject _newObject;
    private static Component[] _components;

    [MenuItem("Tools/Prefab Replace")]
    private static void CreateReplaceWithPrefab()
    {
        const int width = 340;
        const int height = 600;

        var x = (Screen.currentResolution.width - width) / 2;
        var y = (Screen.currentResolution.height - height) / 2;

        GetWindow<PrefabReplace>().position = new Rect(x, y, width, height);
    }

    private void OnGUI()
    {
        _guiStyle.fontSize = 15; //change the font size
        Searching();
        GUILayout.Space(10);
        Replacing();
        GUILayout.Space(50);
        Settings();
    }

    private void Searching()
    {
        //GUI.Label(new Rect(10, 15, 150, 20), "Search by name", guiStyle);
        _objectsToSearch = GUI.TextField(new Rect(90, 35, 150, 20), _objectsToSearch, 25);

        if (_objectsToSearch != "")
        {
            GUI.enabled = true;
        }
        else
        {
            GUI.enabled = false;
            _count = 0;
        }
        GUILayout.Space(15);
        if (GUILayout.Button("Search"))
        {
            _foundObjects = new List<GameObject>();
            _count = 0;

            foreach (var gameObj in FindObjectsOfType<GameObject>().Where(gameObj => gameObj.name == _objectsToSearch))
            {
                _count += 1;
                _foundObjects.Add(gameObj);
                foreach (Transform child in gameObj.transform)
                {
                    _count += 1;
                    _foundObjects.Add(child.gameObject);
                }
            }

            if (_foundObjects.Count == 0)
            {
                _count = 0;
            }
        }

        GUI.enabled = true;
        EditorGUI.LabelField(new Rect(90, 65, 210, 15), "Number of found objects and childs");
        GUI.TextField(new Rect(90, 80, 60, 15), _count.ToString(), 25);

        GUILayout.Space(100);
        GUI.enabled = _count > 0;
        if (GUILayout.Button("Replace found objects"))
        {
            if (_prefab != null)
            {
                InstantiatePrefab(_foundObjects);
            }
        }

        GUI.enabled = true;
    }

    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 Settings()
    {
        _keepPlaceInHierarchy = GUILayout.Toggle(_keepPlaceInHierarchy, "Keep order place in hierarchy");
        _keepNames = GUILayout.Toggle(_keepNames, "Keep names");
    }

    private void OnInspectorUpdate()
    {
        Repaint();
    }

    private void OnSelectionChange()
    {
        _selectionChanged = true;
    }

    private void InstantiatePrefab(IReadOnlyList<GameObject> selection)
    {
        if (_prefab == null || selection.Count <= 0) return;

        for (var i = selection.Count - 1; i >= 0; --i)
        {
            var selected = selection[i];
            _components = selected.GetComponents(typeof(MonoBehaviour));

            //if (components.Length == 0)
            //{
            SceneManager.SetActiveScene(SceneManager.GetSceneByName(selected.scene.name));

            var prefabType = PrefabUtility.GetPrefabAssetType(_prefab);
            //GameObject newObject;

            if (prefabType == PrefabAssetType.Regular)
            {
                _newObject = (GameObject)PrefabUtility.InstantiatePrefab(_prefab);
            }
            else
            {
                _newObject = Instantiate(_prefab);

                if (_keepNames == false)
                {
                    _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;
            if (_keepPlaceInHierarchy)
            {
                _newObject.transform.SetSiblingIndex(selected.transform.GetSiblingIndex());
            }
            if (_keepNames)
            {
                _newObject.name = selected.name;
            }

            foreach (var comp in _components)
            {
                _newObject.Init(comp);
            }

            Undo.DestroyObjectImmediate(selected);
            //}
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...