using System.Globalization;
using System.Reflection;
using UnityEngine;
using Object = UnityEngine.Object;
namespace X10D.Unity;
///
/// Extension methods for .
///
public static class ComponentExtensions
{
///
/// Copies the component to another game object.
///
/// The component to copy.
/// The game object to which the component will be copied.
/// The type of the component to copy.
///
/// is .
/// -or-
/// is .
///
///
/// already has a component of type .
///
///
/// This method will destroy the component on the source game object, creating a new instance on the target. Use with
/// caution.
///
public static void CopyTo(this T component, GameObject target)
where T : Component
{
if (component == null)
{
throw new ArgumentNullException(nameof(component));
}
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
if (target.TryGetComponent(out T targetComponent))
{
string message = ExceptionMessages.ComponentAlreadyExists;
message = string.Format(CultureInfo.CurrentCulture, message, target.name, typeof(T).Name);
throw new InvalidOperationException(message);
}
targetComponent = target.AddComponent();
var typeInfo = typeof(T).GetTypeInfo();
CopyFields(typeInfo, component, targetComponent);
CopyProperties(typeInfo, component, targetComponent);
}
///
/// Copies the component to another game object.
///
/// The component to copy.
/// The game object to which the component will be copied.
///
/// is .
/// -or-
/// is .
///
///
/// already has a component of the same type.
///
///
/// This method will destroy the component on the source game object, creating a new instance on the target. Use with
/// caution.
///
public static void CopyTo(this Component component, GameObject target)
{
if (component == null)
{
throw new ArgumentNullException(nameof(component));
}
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
var componentType = component.GetType();
if (target.TryGetComponent(componentType, out Component targetComponent))
{
string message = ExceptionMessages.ComponentAlreadyExists;
message = string.Format(CultureInfo.CurrentCulture, message, target.name, componentType.Name);
throw new InvalidOperationException(message);
}
targetComponent = target.AddComponent(componentType);
var typeInfo = componentType.GetTypeInfo();
CopyFields(typeInfo, component, targetComponent);
CopyProperties(typeInfo, component, targetComponent);
}
///
/// Returns an array of components of the specified type, excluding components that live on the object to which this
/// component is attached.
///
/// The component whose child components to retrieve.
/// The type of the components to retrieve.
/// An array representing the child components.
public static T[] GetComponentsInChildrenOnly(this Component component)
{
return component.gameObject.GetComponentsInChildrenOnly();
}
///
/// Moves the component to another game object.
///
/// The component to move.
/// The game object to which the component will be moved.
/// The type of the component to move.
///
/// is .
/// -or-
/// is .
///
///
/// already has a component of type .
///
///
/// This method will destroy the component on the source game object, creating a new instance on the target. Use with
/// caution.
///
public static void MoveTo(this T component, GameObject target)
where T : Component
{
if (component == null)
{
throw new ArgumentNullException(nameof(component));
}
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
component.CopyTo(target);
Object.Destroy(component);
}
///
/// Moves the component to another game object.
///
/// The component to move.
/// The game object to which the component will be moved.
///
/// is .
/// -or-
/// is .
///
///
/// already has a component of the same type.
///
///
/// This method will destroy the component on the source game object, creating a new instance on the target. Use with
/// caution.
///
public static void MoveTo(this Component component, GameObject target)
{
if (component == null)
{
throw new ArgumentNullException(nameof(component));
}
if (target == null)
{
throw new ArgumentNullException(nameof(target));
}
component.CopyTo(target);
Object.Destroy(component);
}
private static void CopyFields(TypeInfo typeInfo, T component, T targetComponent)
where T : Component
{
foreach (FieldInfo field in typeInfo.DeclaredFields)
{
if (field.IsStatic)
{
continue;
}
object fieldValue = GetNewReferences(component, targetComponent, field.GetValue(component));
field.SetValue(targetComponent, fieldValue);
}
}
private static void CopyProperties(TypeInfo typeInfo, T component, T targetComponent)
where T : Component
{
foreach (PropertyInfo property in typeInfo.DeclaredProperties)
{
if (!property.CanRead || !property.CanWrite)
{
continue;
}
MethodInfo getMethod = property.GetMethod;
MethodInfo setMethod = property.SetMethod;
if (getMethod.IsStatic || setMethod.IsStatic)
{
continue;
}
object propertyValue = GetNewReferences(component, targetComponent, property.GetValue(component));
property.SetValue(targetComponent, propertyValue);
}
}
private static object GetNewReferences(T component, T targetComponent, object value)
where T : Component
{
if (ReferenceEquals(value, component))
{
value = targetComponent;
}
else if (ReferenceEquals(value, component.gameObject))
{
value = targetComponent.gameObject;
}
return value;
}
}