using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; using UnityEngine.Serialization; namespace Unity.Tutorials.Core.Editor { /// /// Criterion for checking that a specific amount of prefab instances are created. /// public class PrefabInstanceCountCriterion : Criterion { /// /// Different comparison modes. /// public enum InstanceCountComparison { /// /// At least X amount of instances required. /// AtLeast, /// /// Exactly X amount of instances required. /// Exactly, /// /// No more than X amount of instances required. /// NoMoreThan, } /// /// The prefab of instances we want to count. /// [FormerlySerializedAs("prefabParent")] public GameObject PrefabParent; /// /// The wanted comparison mode. /// [FormerlySerializedAs("comparisonMode")] public InstanceCountComparison ComparisonMode = InstanceCountComparison.AtLeast; /// /// The desired amount of instances. /// [Range(0, 100)] [FormerlySerializedAs("instanceCount")] public int InstanceCount = 1; [SerializeField, HideInInspector] FutureObjectReference m_FutureReference; /// /// Starts testing of the criterion. /// public override void StartTesting() { UpdateCompletion(); Selection.selectionChanged += UpdateCompletion; } /// /// Stops testing of the criterion. /// public override void StopTesting() { Selection.selectionChanged -= UpdateCompletion; } /// /// Evaluates if the criterion is completed. /// /// protected override bool EvaluateCompletion() { if (PrefabParent == null) return false; var matches = FindObjectsOfType().Where(go => PrefabUtilityShim.GetCorrespondingObjectFromSource(go) == PrefabParent); var count = matches.Count(); switch (ComparisonMode) { case InstanceCountComparison.AtLeast: return count >= InstanceCount; case InstanceCountComparison.Exactly: var complete = count == InstanceCount; if (complete && InstanceCount == 1 && m_FutureReference != null) m_FutureReference.SceneObjectReference.Update(matches.First()); return complete; case InstanceCountComparison.NoMoreThan: return count <= InstanceCount; default: return false; } } /// /// Returns FutureObjectReference for this Criterion. /// /// protected override IEnumerable GetFutureObjectReferences() { if (m_FutureReference == null) yield break; yield return m_FutureReference; } /// /// Destroys unused future reference assets and updates future references. /// protected override void OnValidate() { // Destroy unreferenced future reference assets base.OnValidate(); // Update future reference var needsUpdate = false; if (ComparisonMode == InstanceCountComparison.Exactly && InstanceCount == 1) { if (m_FutureReference == null) { m_FutureReference = CreateFutureObjectReference(); m_FutureReference.ReferenceName = "Prefab Instance"; needsUpdate = true; } } else DestroyImmediate(m_FutureReference, true); if (needsUpdate) UpdateFutureObjectReferenceNames(); } /// /// Auto-completes the criterion. /// /// True if the auto-completion succeeded. public override bool AutoComplete() { var prefabInstances = FindObjectsOfType().Where(go => PrefabUtilityShim.GetCorrespondingObjectFromSource(go) == PrefabParent); var actualInstanceCount = prefabInstances.Count(); var difference = actualInstanceCount - InstanceCount; if (difference == 0) return true; switch (ComparisonMode) { case InstanceCountComparison.AtLeast: difference = Math.Min(0, difference); break; case InstanceCountComparison.NoMoreThan: difference = Math.Max(0, difference); break; } if (difference < 0) { for (var i = 0; i < -difference; i++) PrefabUtility.InstantiatePrefab(PrefabParent); } else { foreach (var prefabInstance in prefabInstances.Take(difference)) DestroyImmediate(prefabInstance); } return true; } } }