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 specific Components are added to a GameObject. /// public class ComponentAddedCriterion : Criterion { [SerializeField, FormerlySerializedAs("targetGameObject")] ObjectReference m_TargetGameObject; [SerializeField, FormerlySerializedAs("requiredComponents")] SerializedTypeCollection m_RequiredComponents = new SerializedTypeCollection(); /// /// Returns the target GameObject. /// public GameObject TargetGameObject { get { if (m_TargetGameObject == null) return null; return m_TargetGameObject.SceneObjectReference.ReferencedObjectAsGameObject; } set { if (m_TargetGameObject == null) m_TargetGameObject = new ObjectReference(); m_TargetGameObject.SceneObjectReference.Update(value); } } /// /// Returns the required Components. /// public IList RequiredComponents { get { return m_RequiredComponents.Select(typeAndFutureReference => typeAndFutureReference.SerializedType.Type) .ToList(); } set { var items = value.Select(type => new TypeAndFutureReference(new SerializedType(type))).ToList(); m_RequiredComponents.SetItems(items); OnValidate(); } } /// /// Starts testing of the criterion. /// public override void StartTesting() { UpdateCompletion(); EditorApplication.update += UpdateCompletion; } /// /// Stops testing of the criterion. /// public override void StopTesting() { EditorApplication.update -= UpdateCompletion; } /// /// Destroys unused future reference assets and updates future references. /// protected override void OnValidate() { // Destroy unused future reference assets base.OnValidate(); // Update future references var requiredComponentsIndex = 0; foreach (var typeAndFutureReference in m_RequiredComponents) { requiredComponentsIndex++; var type = typeAndFutureReference.SerializedType.Type; if (type == null) continue; if (typeAndFutureReference.FutureReference == null) typeAndFutureReference.FutureReference = CreateFutureObjectReference(); typeAndFutureReference.FutureReference.ReferenceName = string.Format("{0}: {1}", requiredComponentsIndex, type.FullName); } if (requiredComponentsIndex != 0) UpdateFutureObjectReferenceNames(); } /// /// Evaluates if the criterion is completed. /// /// protected override bool EvaluateCompletion() { var gameObject = TargetGameObject; if (gameObject == null) return false; if (RequiredComponents.Count() == 0) return true; foreach (var type in RequiredComponents) { if (type == null) { Debug.LogWarning("Testing for a null component type will always fail."); return false; } if (gameObject.GetComponent(type) == null) return false; } // Update future references foreach (var requiredType in m_RequiredComponents) { var component = gameObject.GetComponent(requiredType.SerializedType.Type); requiredType.FutureReference.SceneObjectReference.Update(component); } return true; } /// /// Returns FutureObjectReference for this Criterion. /// /// protected override IEnumerable GetFutureObjectReferences() { return m_RequiredComponents .Where(c => c.SerializedType.Type != null && c.FutureReference != null) .Select(c => c.FutureReference); } /// /// Auto-completes the criterion. /// /// True if the auto-completion succeeded. public override bool AutoComplete() { var gameObject = TargetGameObject; if (gameObject == null) return false; foreach (var type in RequiredComponents) { var component = gameObject.AddComponent(type); if (component == null) return false; } return true; } /// /// Wrapper class for serialization purposes. /// [Serializable] public class SerializedTypeCollection : CollectionWrapper {} /// /// A SerializedType-FutureObjectReference pair. /// [Serializable] public class TypeAndFutureReference : ICloneable { /// /// The SerializedType. /// [SerializedTypeFilter(typeof(Component), false), FormerlySerializedAs("serializedType")] public SerializedType SerializedType; /// /// The FutureReference. /// [FormerlySerializedAs("futureReference")] public FutureObjectReference FutureReference; /// /// Constructs from a SerializedType. /// /// public TypeAndFutureReference(SerializedType serializedType) { this.SerializedType = serializedType; } /// /// Creates a clone of this instance. /// /// public object Clone() { return new TypeAndFutureReference(SerializedType); } } } }