#if GRAPH_DESIGNER /// --------------------------------------------- /// Behavior Designer /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.BehaviorDesigner.Runtime.Tasks.Actions.GameObjectTasks { using Opsive.GraphDesigner.Runtime.Variables; using UnityEngine; [Opsive.Shared.Utility.Category("GameObject")] [Opsive.Shared.Utility.Description("Clones a GameObject with optional position offset, rotation offset, scale multiplier, and component copying.")] public class CloneGameObject : TargetGameObjectAction { [Tooltip("The position offset for the clone.")] [SerializeField] protected SharedVariable m_PositionOffset = Vector3.zero; [Tooltip("The rotation offset for the clone (Euler angles in degrees).")] [SerializeField] protected SharedVariable m_RotationOffset = Vector3.zero; [Tooltip("The scale multiplier for the clone.")] [SerializeField] protected SharedVariable m_ScaleMultiplier = Vector3.one; [Tooltip("The new parent for the clone. If null, no parent is assigned.")] [SerializeField] protected SharedVariable m_NewParent; [Tooltip("Should all components be copied?")] [SerializeField] protected SharedVariable m_CopyComponents = false; [Tooltip("The cloned GameObject.")] [SerializeField] [RequireShared] protected SharedVariable m_ClonedObject; /// /// Clones the GameObject. /// /// The status of the action. public override TaskStatus OnUpdate() { // Clone the GameObject. var cloned = UnityEngine.Object.Instantiate(m_ResolvedGameObject); cloned.transform.position = m_ResolvedTransform.TransformPoint(m_PositionOffset.Value); cloned.transform.rotation = m_ResolvedTransform.rotation * Quaternion.Euler(m_RotationOffset.Value); cloned.transform.localScale = Vector3.Scale(m_ResolvedTransform.localScale, m_ScaleMultiplier.Value); if (m_NewParent.Value != null) { cloned.transform.SetParent(m_NewParent.Value, true); } // Copy all components if requested. if (m_CopyComponents.Value) { var sourceComponents = m_ResolvedGameObject.GetComponents(); foreach (var sourceComponent in sourceComponents) { // Skip Transform as it's already handled. if (sourceComponent is Transform) { continue; } var componentType = sourceComponent.GetType(); var clonedComponent = cloned.GetComponent(componentType); if (clonedComponent == null) { clonedComponent = cloned.AddComponent(componentType); } // Copy component values using Unity's CopySerialized. #if UNITY_EDITOR UnityEditor.EditorUtility.CopySerialized(sourceComponent, clonedComponent); #else CopyComponentValues(sourceComponent, clonedComponent); #endif } } m_ClonedObject.Value = cloned; return TaskStatus.Success; } /// /// Copies component values from source to destination at runtime. /// /// The source component. /// The destination component. private void CopyComponentValues(Component source, Component destination) { if (source == null || destination == null) { return; } var sourceType = source.GetType(); var destinationType = destination.GetType(); if (sourceType != destinationType) { return; } // Copy all fields (public and private with SerializeField). var fields = sourceType.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); foreach (var field in fields) { // Skip constants and readonly fields. if (field.IsLiteral || field.IsInitOnly) { continue; } if (field.IsPublic || field.GetCustomAttributes(typeof(SerializeField), false).Length > 0) { field.SetValue(destination, field.GetValue(source)); } } } /// /// Resets the action values back to their default. /// public override void Reset() { base.Reset(); m_PositionOffset = Vector3.zero; m_RotationOffset = Vector3.zero; m_ScaleMultiplier = Vector3.one; m_NewParent = null; m_CopyComponents = false; m_ClonedObject = null; } } } #endif