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}");
}
}
}
}