Home > Mobile >  Unity paste <ALL> components recursively to parent and all sub-children
Unity paste <ALL> components recursively to parent and all sub-children

Time:01-28

I'm trying to edit an existing component copier for the sake of copying the components over to a new game object all with the same name.

Components are successfully recursively returned with

static Component[] copiedComponents;

static void Copy(){
   copiedComponents = UnityEditor.Selection.activeGameObject.GetComponentsInChildren<Component>();
}

pasting is done with

foreach(var targetGameObject in UnityEditor.Selection.gameObjects) {
  if (!targetGameObject)
    continue;

  Undo.RegisterCompleteObjectUndo(targetGameObject, targetGameObject.name   ": Paste All Components");

  foreach(var copiedComponent in copiedComponents) {
    if (!copiedComponent)
      continue;

    UnityEditorInternal.ComponentUtility.CopyComponent(copiedComponent);

    var targetComponent = targetGameObject.GetComponent(copiedComponent.GetType());

    if (targetComponent) // if gameObject already contains the component
    {
      if (UnityEditorInternal.ComponentUtility.PasteComponentValues(targetComponent)) {
        Debug.Log("Successfully pasted: "   copiedComponent.GetType());
      } else {
        Debug.LogError("Failed to copy: "   copiedComponent.GetType());
      }
    } else // if gameObject does not contain the component
    {
      if (UnityEditorInternal.ComponentUtility.PasteComponentAsNew(targetGameObject)) {
        Debug.Log("Successfully pasted: "   copiedComponent.GetType());
      } else {
        Debug.LogError("Failed to copy: "   copiedComponent.GetType());
      }
    }
  }
}

It correctly grabs every component of the parent and every single nested child, but when pasting it will only paste it on the selected parent. Do I need to store it in a dictionary with keyvalues of the gameObject name and a list of the components or something like that?

Any input would be appreciated

CodePudding user response:

Well you paste into

var targetComponent = targetGameObject.GetComponent(copiedComponent.GetType());

This only looks for components on this target object!

There is no bubbling down any hierarchy like GetComponentsInChildren does

You would basically have to check if the target object's hierarchy even matches at all and look for the correct child object.

For that you would need to additionally store the path to those child objects. You can use AnimationUtility.CalculateTransformPath like e.g.

using System.Linq;

...

private class ComponentInfo
{
    public Component Component { get; }
    public string Path { get; }

    public ComponentInfo(Component component string path)
    {
        Component = component;
        Path = path;
    }
}

static ComponentInfo[] copiedComponents;

static void Copy()
{
   var sourceRootObject = UnityEditor.Selection.activeGameObject;
   // added 'true' to also include inactive
   copiedComponents = sourceRootObject.GetComponentsInChildren<Component>(true)
      // for each component store along with the path
      .Select(c => new ComponentInfo(c, AnimationUtility.CalculateTransformPath(c.transform, sourceRootObject.transform)))
      .ToArray();
}

And then later now that you have the paths stored you can use Transform.Find to try and find that object on the receiving side

foreach(var targetGameObject in UnityEditor.Selection.gameObjects) 
{
    if (!targetGameObject) continue;

    Undo.RegisterCompleteObjectUndo(targetGameObject, targetGameObject.name   ": Paste All Components");

    foreach(var copiedComponent in copiedComponents) 
    {
        if (!copiedComponent.Component) continue;

        // Try to find target child object
        GameObject targetObject = null;
        if(string.IsNullOrWhiteSpace(copiedComponent.Path))
        {
            // No path -> root object itself
            targetObject = targetGameObject;
        }
        else
        {
            // Otherwise try and find child
            var child = targetGameObject.transform.Find(copiedComponent.Path); 
            if(!child) 
            { 
                Debug.LogError($"Couldn't find {copiedComponent.Path} within {targetGameObject}");
                continue;
            }

            // If you also want to actually create according children
            // I will leave this as a homework for you
            // basically you would need to split the path on '/' and for each of those check if
            // according child exists and if not create it ;)

            targetObject = child.gameObject;
        }

        // From now on below make sure to use 'targetObject' everywhere instead of 'targetGameObject'

        UnityEditorInternal.ComponentUtility.CopyComponent(copiedComponent.Component);

        var type = copiedComponent.Component.GetType();

        if(targetObject.TryGetComponent(type, out var targetComponent))
        {
            // if gameObject already contains the component
            if (UnityEditorInternal.ComponentUtility.PasteComponentValues(targetComponent)) 
            {
                Debug.Log($"Successfully pasted: {type}");
            } 
            else 
            {
                Debug.LogError($"Failed to copy: {type)}");
            }
        } 
        else 
        {
            // if gameObject does not contain the component
            if (UnityEditorInternal.ComponentUtility.PasteComponentAsNew(targetObject))
            {
                Debug.Log($"Successfully pasted: {type}");
            }
            else 
            {
                Debug.LogError($"Failed to copy: {type}");
            }
        }
    }
}
  • Related