using System.Collections.Generic; using System.Linq; using Unity.Profiling; using UnityEngine.Scripting.APIUpdating; using UnityEngine.U2D.Animation; using UnityEngine.U2D.Common; namespace UnityEngine.U2D.IK { /// /// Component responsible for managing and updating 2D IK Solvers. /// [DefaultExecutionOrder(UpdateOrder.ikUpdateOrder)] [MovedFrom("UnityEngine.Experimental.U2D.IK")] [IconAttribute(IconUtility.IconPath + "Animation.IKManager.png")] [ExecuteInEditMode] public partial class IKManager2D : MonoBehaviour, IPreviewable { #if UNITY_EDITOR internal static event System.Action onEnabledEditor; internal static event System.Action onDisabledEditor; #endif [SerializeField] List m_Solvers = new List(); [SerializeField] [Range(0f, 1f)] float m_Weight = 1f; [SerializeField] bool m_AlwaysUpdate = true; bool m_CullingEnabled; BaseCullingStrategy m_CullingStrategy; internal BaseCullingStrategy GetCullingStrategy() => m_CullingStrategy; /// /// Get and set the weight for solvers. /// public float weight { get => m_Weight; set => m_Weight = Mathf.Clamp01(value); } /// /// Get the Solvers that are managed by this manager. /// public List solvers => m_Solvers; int[] m_TransformIdCache; /// /// Solvers are always updated even if the underlying Sprite Skins are not visible. /// public bool alwaysUpdate { get => m_AlwaysUpdate; set { m_AlwaysUpdate = value; ToggleCulling(!m_AlwaysUpdate); } } void OnEnable() { ToggleCulling(!m_AlwaysUpdate); #if UNITY_EDITOR onEnabledEditor?.Invoke(this); #endif } void OnDisable() { ToggleCulling(false); #if UNITY_EDITOR onDisabledEditor?.Invoke(this); #endif } void ToggleCulling(bool enableCulling) { if(m_CullingStrategy != null && m_CullingEnabled == enableCulling) return; m_CullingEnabled = enableCulling; m_CullingStrategy?.RemoveRequestingObject(this); if (m_CullingEnabled) m_CullingStrategy = CullingManager.instance.GetCullingStrategy(); else m_CullingStrategy = CullingManager.instance.GetCullingStrategy(); m_CullingStrategy.AddRequestingObject(this); } void OnValidate() { m_Weight = Mathf.Clamp01(m_Weight); OnEditorDataValidate(); } void Reset() { FindChildSolvers(); OnEditorDataValidate(); } void FindChildSolvers() { m_Solvers.Clear(); var solvers = new List(); transform.GetComponentsInChildren(true, solvers); foreach (var solver in solvers) { if (solver.GetComponentInParent() == this) AddSolver(solver); } } /// /// Add Solver to the manager. /// /// Solver to add. public void AddSolver(Solver2D solver) { if (!m_Solvers.Contains(solver)) { m_Solvers.Add(solver); AddSolverEditorData(); } } /// /// Remove Solver from the manager. /// /// Solver to remove. public void RemoveSolver(Solver2D solver) { RemoveSolverEditorData(solver); m_Solvers.Remove(solver); } /// /// Updates the Solvers in this manager. /// public void UpdateManager() { if(m_Solvers.Count == 0) return; var profilerMarker = new ProfilerMarker("IKManager2D.UpdateManager"); profilerMarker.Begin(); ToggleCulling(!m_AlwaysUpdate); var solverInitialized = false; for (var i = 0; i < m_Solvers.Count; i++) { var solver = m_Solvers[i]; if (solver == null || !solver.isActiveAndEnabled) continue; if (!solver.isValid) { solver.Initialize(); solverInitialized = true; } if(!m_CullingEnabled) solver.UpdateIK(m_Weight); } if (m_CullingEnabled) { if (solverInitialized || m_TransformIdCache == null) CacheSolversTransformIds(); var canUpdate = m_AlwaysUpdate || m_CullingStrategy.AreBonesVisible(m_TransformIdCache); if (canUpdate) { for (var i = 0; i < m_Solvers.Count; i++) { var solver = m_Solvers[i]; if (solver == null || !solver.isActiveAndEnabled) continue; solver.UpdateIK(weight); } } } profilerMarker.End(); } void CacheSolversTransformIds() { var transformCache = new HashSet(); for (var s = 0; s < solvers.Count; s++) { var solver = solvers[s]; for (var c = 0; c < solver.chainCount; c++) { var chain = solver.GetChain(c); for (var b = 0; b < chain.transformCount; b++) { var boneTransform = chain.transforms[b]; if(boneTransform != null) transformCache.Add(boneTransform.GetInstanceID()); } } } m_TransformIdCache = transformCache.ToArray(); } /// /// Used by the animation clip preview window. Recommended to not use outside of this purpose. /// public void OnPreviewUpdate() { #if UNITY_EDITOR if (IsInGUIUpdateLoop()) UpdateManager(); #endif } static bool IsInGUIUpdateLoop() => Event.current != null; void LateUpdate() { UpdateManager(); } #if UNITY_EDITOR internal static Events.UnityEvent onDrawGizmos = new Events.UnityEvent(); void OnDrawGizmos() { onDrawGizmos.Invoke(); } #endif } }