namespace Rokid.UXR.Interaction { using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Assertions; /// /// InteractorGroup coordinates between a set of Interactors to /// determine which Interactor(s) should be enabled at a time. /// /// By default, Interactors are prioritized in list order (first = highest priority). /// Interactors can also be prioritized with an optional ICandidateComparer /// 交互器组合 在一组交互器之间进行协调,以确定一次应启用哪些交互器。 /// 默认情况下,交互器按列表顺序排列优先级(第一个 = 最高优先级)。 /// 还可以使用可选的 ICandidateComparer 对交互器进行优先级排序 /// public class InteractorGroup : MonoBehaviour, IInteractor { [SerializeField, Interface(typeof(IInteractor))] private List _interactors; protected List Interactors; public bool IsRootDriver { get; set; } = true; private IInteractor _candidateInteractor = null; private IInteractor _activeInteractor = null; [SerializeField, Interface(typeof(ICandidateComparer)), Optional] private MonoBehaviour _interactorComparer; [SerializeField, Optional] private UnityEngine.Object _data = null; public object Data { get; protected set; } = null; public int MaxIterationsPerFrame = 3; protected ICandidateComparer CandidateComparer = null; public event Action WhenStateChanged = delegate { }; public event Action WhenPreprocessed = delegate { }; public event Action WhenProcessed = delegate { }; public event Action WhenPostprocessed = delegate { }; protected virtual void Awake() { Interactors = _interactors.ConvertAll(mono => mono as IInteractor); CandidateComparer = _interactorComparer as ICandidateComparer; } protected virtual void Start() { foreach (IInteractor interactor in Interactors) { Assert.IsNotNull(interactor); } foreach (IInteractor interactor in Interactors) { interactor.IsRootDriver = false; } if (_interactorComparer != null) { Assert.IsNotNull(CandidateComparer); } if (Data == null) { _data = this; Data = _data; } } public void Preprocess() { foreach (IInteractor interactor in Interactors) { interactor.Preprocess(); } WhenPreprocessed(); } public void Process() { if (_activeInteractor != null) { _activeInteractor.Process(); } WhenProcessed(); } public void Postprocess() { foreach (IInteractor interactor in Interactors) { interactor.Postprocess(); } if (_activeInteractor != null && _activeInteractor.State == InteractorState.Disabled) { _activeInteractor = null; } WhenPostprocessed(); } public void ProcessCandidate() { _candidateInteractor = null; foreach (IInteractor interactor in Interactors) { interactor.ProcessCandidate(); if (interactor.HasCandidate) { if (_candidateInteractor == null || Compare(_candidateInteractor, interactor) > 0) { _candidateInteractor = interactor; } } } if (_candidateInteractor == null && Interactors.Count > 0) { _candidateInteractor = Interactors[Interactors.Count - 1]; } } public void Enable() { if (_activeInteractor == null) { return; } _activeInteractor.Enable(); } public void Disable() { foreach (IInteractor interactor in Interactors) { interactor.Disable(); } State = InteractorState.Disabled; } public void Hover() { if (State != InteractorState.Normal) { return; } _activeInteractor = _candidateInteractor; _activeInteractor.Hover(); State = InteractorState.Hover; } public void Unhover() { if (State != InteractorState.Hover) { return; } if (_activeInteractor != null) { _activeInteractor.Unhover(); } _activeInteractor = null; State = InteractorState.Normal; } public void Select() { if (State != InteractorState.Hover) { return; } _activeInteractor.Select(); State = InteractorState.Select; } public void Unselect() { if (State != InteractorState.Select) { return; } if (_activeInteractor != null) { _activeInteractor.Unselect(); } State = InteractorState.Hover; } public bool ShouldHover => _activeInteractor != null && _activeInteractor.ShouldHover; public bool ShouldUnhover => _activeInteractor == null || _activeInteractor.ShouldUnhover || _activeInteractor != _candidateInteractor; public bool ShouldSelect => _activeInteractor != null && _activeInteractor.ShouldSelect; public bool ShouldUnselect => _activeInteractor == null || _activeInteractor.ShouldUnselect; private void DisableAllInteractorsExcept(IInteractor enabledInteractor) { foreach (IInteractor interactor in Interactors) { if (interactor == enabledInteractor) continue; interactor.Disable(); } } public int Identifier => _activeInteractor != null ? _activeInteractor.Identifier : Interactors[Interactors.Count - 1].Identifier; public bool HasCandidate => _candidateInteractor != null && _candidateInteractor.HasCandidate; public object CandidateProperties => HasCandidate ? _candidateInteractor.CandidateProperties : null; public bool HasInteractable => _activeInteractor != null && _activeInteractor.HasInteractable; public bool HasSelectedInteractable => State == InteractorState.Select && _activeInteractor.HasSelectedInteractable; private InteractorState _state = InteractorState.Normal; public InteractorState State { get { return _state; } private set { if (_state == value) { return; } InteractorState previousState = _state; _state = value; WhenStateChanged(new InteractorStateChangeArgs( previousState, _state )); } } public virtual void AddInteractor(IInteractor interactor) { Interactors.Add(interactor); interactor.IsRootDriver = false; MonoBehaviour interactorMono = interactor as MonoBehaviour; if (interactorMono != null) { _interactors.Add(interactor as MonoBehaviour); } } public virtual void RemoveInteractor(IInteractor interactor) { if (!Interactors.Remove(interactor)) { return; } interactor.IsRootDriver = true; MonoBehaviour interactorMono = interactor as MonoBehaviour; if (interactorMono != null) { _interactors.Remove(interactor as MonoBehaviour); } } private int Compare(IInteractor a, IInteractor b) { if (!a.HasCandidate && !b.HasCandidate) { return -1; } if (a.HasCandidate && b.HasCandidate) { if (CandidateComparer == null) { return -1; } int result = CandidateComparer.Compare(a.CandidateProperties, b.CandidateProperties); return result > 0 ? 1 : -1; } return a.HasCandidate ? -1 : 1; } protected virtual void Update() { if (!IsRootDriver) { return; } Drive(); } public void Drive() { Preprocess(); InteractorState previousState = State; for (int i = 0; i < MaxIterationsPerFrame; i++) { if (State == InteractorState.Normal || (State == InteractorState.Hover && previousState != InteractorState.Normal)) { ProcessCandidate(); } previousState = State; Process(); if (State == InteractorState.Disabled) { break; } if (State == InteractorState.Normal) { if (_candidateInteractor != null && _activeInteractor != _candidateInteractor) { _activeInteractor = _candidateInteractor; Enable(); DisableAllInteractorsExcept(_activeInteractor); } if (ShouldHover) { Hover(); continue; } break; } if (State == InteractorState.Hover) { if (ShouldSelect) { Select(); continue; } if (ShouldUnhover) { Unhover(); continue; } break; } if (State == InteractorState.Select) { if (ShouldUnselect) { Unselect(); continue; } break; } } Postprocess(); } } }