using System; using System.Collections.Generic; using UnityEngine.U2D.Animation; namespace UnityEngine.U2D.IK { internal class SpriteSkinVisibilityCullingStrategy : BaseCullingStrategy { /// /// SpriteSkin registry used to keep visibility state of a SpriteSkin and bone transforms. /// class SpriteSkinRegistry { public int[] boneIds; public bool isVisible; public SpriteSkinRegistry(int[] boneIds, bool isSkinVisible) { this.boneIds = boneIds; isVisible = isSkinVisible; } } /// /// Maps SpriteSkins to SpriteSkinRegistry. /// Dictionary m_SpriteSkinRegistries; /// /// Counts (value) how many visible Sprite Skins use a given bone (key). /// Dictionary m_BoneVisibilityCount; public override bool AreBonesVisible(IList boneTransformIds) { for (var i = 0; i < boneTransformIds.Count; i++) { var boneId = boneTransformIds[i]; if (m_BoneVisibilityCount.ContainsKey(boneId)) return m_BoneVisibilityCount[boneId] > 0; } return false; } protected override void OnInitialize() { m_SpriteSkinRegistries = new Dictionary(); m_BoneVisibilityCount = new Dictionary(); var spriteSkins = SpriteSkinContainer.instance.spriteSkins; for (var i = 0; i < spriteSkins.Count; i++) UpdateSpriteSkinVisibility(spriteSkins[i]); AddListeners(); } protected override void OnDisable() { m_SpriteSkinRegistries.Clear(); m_BoneVisibilityCount.Clear(); RemoveListeners(); } void AddListeners() { SpriteSkinContainer.onAddedSpriteSkin += UpdateSpriteSkinVisibility; SpriteSkinContainer.onRemovedSpriteSkin += UnregisterSpriteSkin; SpriteSkinContainer.onBoneTransformChanged += OnBoneTransformChanged; } void RemoveListeners() { SpriteSkinContainer.onAddedSpriteSkin -= UpdateSpriteSkinVisibility; SpriteSkinContainer.onRemovedSpriteSkin -= UnregisterSpriteSkin; SpriteSkinContainer.onBoneTransformChanged -= OnBoneTransformChanged; } protected override void OnUpdate() { foreach (var (spriteSkin, registry) in m_SpriteSkinRegistries) { var isVisible = spriteSkin.spriteRenderer.isVisible; if (registry.isVisible != isVisible) { registry.isVisible = isVisible; RecalculateVisibility(registry); } } } void OnBoneTransformChanged(SpriteSkin spriteSkin) { UnregisterSpriteSkinBonesMapping(spriteSkin); RegisterSpriteSkinBonesMapping(spriteSkin); UpdateSpriteSkinVisibility(spriteSkin); } bool IsSpriteSkinRegistered(SpriteSkin spriteSkin) => m_SpriteSkinRegistries.ContainsKey(spriteSkin); void UnregisterSpriteSkin(SpriteSkin spriteSkin) { UnregisterSpriteSkinBonesMapping(spriteSkin); } void UpdateSpriteSkinVisibility(SpriteSkin spriteSkin) { var visible = spriteSkin.spriteRenderer.isVisible; var registry = RegisterSpriteSkinBonesMapping(spriteSkin); if (registry.isVisible == visible) return; registry.isVisible = visible; RecalculateVisibility(registry); } SpriteSkinRegistry RegisterSpriteSkinBonesMapping(SpriteSkin spriteSkin) { if (IsSpriteSkinRegistered(spriteSkin)) return m_SpriteSkinRegistries[spriteSkin]; var bones = spriteSkin.boneTransforms ?? Array.Empty(); var records = new int[bones.Length]; var newRegistry = new SpriteSkinRegistry(records, false); for (var i = 0; i < bones.Length; i++) { var bone = bones[i]; if (bone == null) continue; var id = bone.GetInstanceID(); records[i] = id; } m_SpriteSkinRegistries[spriteSkin] = newRegistry; return newRegistry; } void UnregisterSpriteSkinBonesMapping(SpriteSkin spriteSkin) { if (!IsSpriteSkinRegistered(spriteSkin)) return; var registry = m_SpriteSkinRegistries[spriteSkin]; registry.isVisible = false; m_SpriteSkinRegistries.Remove(spriteSkin); RecalculateVisibility(registry); } void RecalculateVisibility(SpriteSkinRegistry registry) { var bones = registry.boneIds; var visible = registry.isVisible; var countOperation = visible ? 1 : -1; for (var i = 0; i < bones.Length; i++) { var bone = bones[i]; if (m_BoneVisibilityCount.ContainsKey(bone)) { var count = m_BoneVisibilityCount[bone] + countOperation; if (count <= 0) m_BoneVisibilityCount.Remove(bone); else m_BoneVisibilityCount[bone] = count; } else if (visible) m_BoneVisibilityCount[bone] = 1; } } } }