Почему при замене игрового объекта на префаб положение префаба немного назад, а не точно на позиции игрового объекта?

Это код для замены:

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;

public class SaveTransformsInfo : EditorWindow
{
    [SerializeField] private GameObject prefab;

    [MenuItem("Tools/Replace With Prefab")]
    static void CreateReplaceWithPrefab()
    {
        EditorWindow.GetWindow<SaveTransformsInfo>();
    }

    private void OnGUI()
    {
        prefab = (GameObject)EditorGUILayout.ObjectField("Prefab", prefab, typeof(GameObject), false);

        if (prefab == null || Selection.gameObjects.Length == 0)
        {
            GUI.enabled = false;
        }
        else
        {
            GUI.enabled = true;
        }
        if (GUILayout.Button("Replace"))
        {
            var selection = Selection.gameObjects;
            var parentGameobjects = CheckParents(selection);

#if UNITY_EDITOR
            UnityEditor.Selection.objects = parentGameobjects;
#endif

            for (var i = parentGameobjects.Length - 1; i >= 0; --i)
            {
                var selected = parentGameobjects[i];
                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.position = selected.transform.position;
                newObject.transform.rotation = selected.transform.rotation;
                newObject.transform.localScale = selected.transform.localScale;
                newObject.transform.SetSiblingIndex(selected.transform.GetSiblingIndex());
                DestroyObjectInPrefab(selected.transform);
            }
        }

        if(GUILayout.Button("Get Info"))
        {
            TransformSaver.SaveTransform(null, Selection.gameObjects[0]);
        }

        GUI.enabled = false;
        EditorGUILayout.LabelField("Selection count: " + Selection.gameObjects.Length);
    }

    private GameObject[] CheckParents(params GameObject[] objects)
    {
        List<GameObject> parents = new List<GameObject>(objects.Length);
        Transform[] transforms = objects.Select(go => go.transform).ToArray();
        for (int objectIndex = 0; objectIndex < transforms.Length; objectIndex++)
        {
            if (!IsChildOfAny(transforms[objectIndex], transforms))
                parents.Add(transforms[objectIndex].gameObject);
        }

        return parents.ToArray();
    }

    private bool IsChildOfAny(Transform potentialChild, params Transform[] potentialParents)
    {
        for (int index = 0; index < potentialParents.Length; index++)
        {
            if (IsParentOf(potentialParents[index], potentialChild))
                return true;
        }
        return false;
    }

    private bool IsParentOf(Transform potentialParent, Transform potentialChild)
    {
        if (potentialChild.parent == null)
            return false;

        if (potentialChild.parent == potentialParent)
            return true;

        return IsParentOf(potentialParent, potentialChild.parent);
    }

    private void DestroyObjectInPrefab(Transform transform)
    {
        if (PrefabUtility.IsPartOfPrefabInstance(transform))
        {
            //if a part of a prefab instance then get the instance handle
            Object prefabInstance = PrefabUtility.GetPrefabInstanceHandle(transform);
            //destroy the handle
            if (prefabInstance != null)
            {
                Undo.DestroyObjectImmediate(prefabInstance);
            }
        }
        //the usual destroy immediate to clean up scene objects
        //DestroyImmediate(transform.gameObject, true);
        Undo.DestroyObjectImmediate(transform.gameObject);
    }

    private void OnSelectionChange()
    {
        Repaint();
    }
}

Я выбираю эту дверь, дверь находится внутри космической станции, и дверь является дочерним элементом другого игрового объекта, а другой игровой объект также является дочерним:

Заменить дверь

Затем у меня есть сборный дом, который я хочу заменить дверью: сборный - это та же дверь, которую я только что изменил, две двери посередине сделал их красным, но сборный - это просто дверь, как на первом скриншоте. Масштабирование одинакового размера:

Сборный дом вместо двери

Затем при замене префаба он находится немного дальше от того места, где находилась дверь, но в инспекторе я вижу то же положение, что и дверь:

Замененный сборный

Теперь сборный дом расположен намного глубже внутри комнаты. Если я изменю положение и поворот в моем коде на локальные, то сборный объект будет перемещен намного вперед. Он никогда не встанет на место двери.

Я пробовал другие сценарии замены, которые дают одинаковый результат / с. Я ничего не менял в префабе (префаб - это дверь), только добавил эти цвета.


person Daniel Lip    schedule 12.10.2019    source источник


Ответы (2)


Это происходит потому, что центр префаба отличается от центра исходного объекта.

Вы можете повторно центрировать объект, поместив его в пустой объект и переместив в локальную позицию этого объекта.

Затем вы можете использовать этот объект в качестве префаба, который будет в сцене.

Надеюсь, это помогло

person Carlos Manuel    schedule 12.10.2019

Если вы просто пытаетесь изменить цвета, замените материалы

GameObject.GetComponent<MeshRenderer>().material = Material;
person Foxtrot games    schedule 03.01.2021