namespace Zinnia.Tracking.Follow { using System; using UnityEngine; using UnityEngine.Events; using Zinnia.Data.Collection.List; using Zinnia.Extension; using Zinnia.Process.Component; using Zinnia.Tracking.Follow.Modifier; /// /// Mirrors the properties of another based on the given . /// public class ObjectFollower : GameObjectSourceTargetProcessor { /// /// Holds data about a event. /// [Serializable] public class EventData { [Tooltip("The source utilize within the Modifier.FollowModifier.")] [SerializeField] private GameObject eventSource; /// /// The source utilize within the . /// public GameObject EventSource { get { return eventSource; } set { eventSource = value; } } [Tooltip("The target to apply the Modifier.FollowModifier on.")] [SerializeField] private GameObject eventTarget; /// /// The target to apply the on. /// public GameObject EventTarget { get { return eventTarget; } set { eventTarget = value; } } [Tooltip("The optional offset the target follow against the source.")] [SerializeField] private GameObject eventTargetOffset; /// /// The optional offset the target follow against the source. /// public GameObject EventTargetOffset { get { return eventTargetOffset; } set { eventTargetOffset = value; } } /// /// Clears . /// public virtual void ClearEventSource() { EventSource = default; } /// /// Clears . /// public virtual void ClearEventTarget() { EventTarget = default; } /// /// Clears . /// public virtual void ClearEventTargetOffset() { EventTargetOffset = default; } public EventData Set(EventData source) { return Set(source.EventSource, source.EventTarget, source.EventTargetOffset); } public EventData Set(GameObject source, GameObject target, GameObject targetOffset = null) { EventSource = source; EventTarget = target; EventTargetOffset = targetOffset; return this; } public void Clear() { Set(default, default, default); } } /// /// Defines the event with the . /// [Serializable] public class FollowEvent : UnityEvent { } [Tooltip("A GameObject collection of target offsets to offset the GameObjectSourceTargetProcessor.Targets against the source whilst following. The GameObject for the target offset must be a child of the corresponding target.")] [SerializeField] private GameObjectObservableList targetOffsets; /// /// A collection of target offsets to offset the against the source whilst following. The for the target offset must be a child of the corresponding target. /// public GameObjectObservableList TargetOffsets { get { return targetOffsets; } set { targetOffsets = value; } } [Header("Follow Settings")] [Tooltip("The Modifier.FollowModifier to apply.")] [SerializeField] private FollowModifier followModifier; /// /// The to apply. /// public FollowModifier FollowModifier { get { return followModifier; } set { followModifier = value; } } /// /// Emitted before any processing. /// public UnityEvent Preprocessed = new UnityEvent(); /// /// Emitted after all processing is complete. /// public UnityEvent Processed = new UnityEvent(); /// /// Clears . /// public virtual void ClearTargetOffsets() { if (!this.IsValidState()) { return; } TargetOffsets = default; } /// /// Clears . /// public virtual void ClearFollowModifier() { if (!this.IsValidState()) { return; } FollowModifier = default; } /// public override void Process() { if (!this.IsValidState()) { return; } Preprocessed?.Invoke(); base.Process(); Processed?.Invoke(); } /// /// Applies the follow modification of the given source to the given target. /// /// The source to take the follow data from. /// The target to apply the follow data to. protected override void ApplySourceToTarget(GameObject source, GameObject target) { GameObject followOffset = GetFollowOffset(); if (followOffset != null && !followOffset.transform.IsChildOf(Targets.NonSubscribableElements[Targets.CurrentIndex].transform)) { throw new ArgumentException($"The `TargetOffsets` at index [{Targets.CurrentIndex}] must be a child of the GameObject at `Targets` index [{Targets.CurrentIndex}]."); } FollowModifier.Modify(source, target, followOffset); } /// /// Gets the Follow Offset for the current target offset based on the current target index. /// /// protected virtual GameObject GetFollowOffset() { if (Targets == null || TargetOffsets == null || Targets.NonSubscribableElements.Count == 0 || TargetOffsets.NonSubscribableElements.Count == 0) { return null; } int currentIndexTargets = TargetOffsets.NonSubscribableElements.ClampIndex(Targets.CurrentIndex); return TargetOffsets.NonSubscribableElements[currentIndexTargets]; } } }