using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Assertions;
namespace Rokid.UXR.Interaction {
///
/// Interactable provides a base template for any kind of interactable object.
/// An Interactable can have Hover and HandleSelected Interactor(s) acting on it.
/// Concrete Interactables can define whether they have a One-to-One or
/// One-to-Many relationship with their associated concrete Interactors.
/// Interactable 为任何类型的可交互对象提供了一个基本模板。
/// Interactable 可以有 Hover 和 HandleSelected Interactor(s) 作用于它。
/// 具体的可交互对象可以定义它们是否具有一对一或
/// 与其关联的具体交互器的一对多关系。
///
public abstract class Interactable : MonoBehaviour, IInteractable
where TInteractor : Interactor
where TInteractable : Interactable
{
[SerializeField, Interface(typeof(IGameObjectFilter)), Optional]
private List _interactorFilters = new List();
private List InteractorFilters = null;
///
/// The max Interactors and max selecting Interactors that this Interactable can
/// have acting on it.
/// -1 signifies NO limit (can have any number of Interactors)
///
[SerializeField]
private int _maxInteractors = -1;
[SerializeField]
private int _maxSelectingInteractors = -1;
[SerializeField, Optional]
private UnityEngine.Object _data = null;
public object Data { get; protected set; } = null;
#region Properties
public int MaxInteractors
{
get
{
return _maxInteractors;
}
set
{
_maxInteractors = value;
}
}
public int MaxSelectingInteractors
{
get
{
return _maxSelectingInteractors;
}
set
{
_maxSelectingInteractors = value;
}
}
#endregion
public IEnumerable InteractorViews => _interactors.Cast();
public IEnumerable SelectingInteractorViews => _selectingInteractors.Cast();
private HashSet _interactors = new HashSet();
private HashSet _selectingInteractors = new HashSet();
[SerializeField]
private InteractableState _state = InteractableState.Normal;
public event Action WhenStateChanged = delegate { };
public event Action WhenInteractorViewAdded = delegate { };
public event Action WhenInteractorViewRemoved = delegate { };
public event Action WhenSelectingInteractorViewAdded = delegate { };
public event Action WhenSelectingInteractorViewRemoved = delegate { };
private MultiAction _whenInteractorAdded = new MultiAction();
private MultiAction _whenInteractorRemoved = new MultiAction();
private MultiAction _whenSelectingInteractorAdded = new MultiAction();
private MultiAction _whenSelectingInteractorRemoved = new MultiAction();
public MAction WhenInteractorAdded => _whenInteractorAdded;
public MAction WhenInteractorRemoved => _whenInteractorRemoved;
public MAction WhenSelectingInteractorAdded => _whenSelectingInteractorAdded;
public MAction WhenSelectingInteractorRemoved => _whenSelectingInteractorRemoved;
public InteractableState State
{
get
{
return _state;
}
private set
{
if (_state == value) return;
InteractableState previousState = _state;
_state = value;
WhenStateChanged(new InteractableStateChangeArgs(previousState, _state));
}
}
private static InteractableRegistry _registry =
new InteractableRegistry();
public static InteractableRegistry Registry => _registry;
protected virtual void InteractorAdded(TInteractor interactor)
{
WhenInteractorViewAdded(interactor);
_whenInteractorAdded.Invoke(interactor);
}
protected virtual void InteractorRemoved(TInteractor interactor)
{
WhenInteractorViewRemoved(interactor);
_whenInteractorRemoved.Invoke(interactor);
}
protected virtual void SelectingInteractorAdded(TInteractor interactor)
{
WhenSelectingInteractorViewAdded(interactor);
_whenSelectingInteractorAdded.Invoke(interactor);
}
protected virtual void SelectingInteractorRemoved(TInteractor interactor)
{
WhenSelectingInteractorViewRemoved(interactor);
_whenSelectingInteractorRemoved.Invoke(interactor);
}
public ICollection Interactors => _interactors;
public ICollection SelectingInteractors => _selectingInteractors;
public void AddInteractor(TInteractor interactor)
{
_interactors.Add(interactor);
InteractorAdded(interactor);
UpdateInteractableState();
}
public void RemoveInteractor(TInteractor interactor)
{
if (!_interactors.Remove(interactor))
{
return;
}
interactor.InteractableChangesUpdate();
InteractorRemoved(interactor);
UpdateInteractableState();
}
public void AddSelectingInteractor(TInteractor interactor)
{
_selectingInteractors.Add(interactor);
SelectingInteractorAdded(interactor);
UpdateInteractableState();
}
public void RemoveSelectingInteractor(TInteractor interactor)
{
if (!_selectingInteractors.Remove(interactor))
{
return;
}
interactor.InteractableChangesUpdate();
SelectingInteractorRemoved(interactor);
UpdateInteractableState();
}
private void UpdateInteractableState()
{
if (_selectingInteractors.Count > 0)
{
State = InteractableState.Select;
}
else if (_interactors.Count > 0)
{
State = InteractableState.Hover;
}
else
{
State = InteractableState.Normal;
}
}
public bool CanBeSelectedBy(TInteractor interactor)
{
if (MaxSelectingInteractors >= 0 &&
_selectingInteractors.Count == MaxSelectingInteractors)
{
return false;
}
if (MaxInteractors >= 0 &&
_interactors.Count == MaxInteractors &&
!_interactors.Contains(interactor))
{
return false;
}
if (InteractorFilters == null)
{
return true;
}
foreach (IGameObjectFilter interactorFilter in InteractorFilters)
{
if (!interactorFilter.Filter(interactor.gameObject))
{
return false;
}
}
return true;
}
public bool HasInteractor(TInteractor interactor)
{
return _interactors.Contains(interactor);
}
public bool HasSelectingInteractor(TInteractor interactor)
{
return _selectingInteractors.Contains(interactor);
}
public void Enable()
{
// RKLog.Info($"====Interaction==== {this.gameObject.name}, register interactable");
_registry.Register((TInteractable)this);
State = InteractableState.Normal;
}
public void Disable()
{
List selectingInteractorsCopy = new List(_selectingInteractors);
foreach (TInteractor selectingInteractor in selectingInteractorsCopy)
{
RemoveSelectingInteractor(selectingInteractor);
}
List interactorsCopy = new List(_interactors);
foreach (TInteractor interactor in interactorsCopy)
{
RemoveInteractor(interactor);
}
RKLog.Info($"====Interaction==== {this.gameObject.name}, unregister interactable");
_registry.Unregister((TInteractable)this);
}
public void RemoveInteractorByIdentifier(int id)
{
TInteractor foundInteractor = null;
foreach (TInteractor selectingInteractor in _selectingInteractors)
{
if (selectingInteractor.Identifier == id)
{
foundInteractor = selectingInteractor;
break;
}
}
if (foundInteractor != null)
{
RemoveSelectingInteractor(foundInteractor);
}
foundInteractor = null;
foreach (TInteractor interactor in _interactors)
{
if (interactor.Identifier == id)
{
foundInteractor = interactor;
break;
}
}
if (foundInteractor == null)
{
return;
}
RemoveInteractor(foundInteractor);
}
protected virtual void Awake()
{
InteractorFilters = _interactorFilters.ConvertAll(mono => mono as IGameObjectFilter);
}
protected virtual void Start()
{
foreach (IGameObjectFilter filter in InteractorFilters)
{
Assert.IsNotNull(filter);
}
if (Data == null)
{
_data = this;
Data = _data;
}
}
protected virtual void OnEnable()
{
Enable();
}
protected virtual void OnDisable()
{
Disable();
}
protected virtual void SetRegistry(InteractableRegistry registry)
{
if (registry == _registry) return;
var interactables = _registry.List();
foreach (TInteractable interactable in interactables)
{
registry.Register(interactable);
_registry.Unregister(interactable);
}
_registry = registry;
}
}
}