Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
9b89c12bbThis file is pinned to the selected release. Download the original to open it in your design tool.
using Leap.Unity.Attributes;
using Leap.Unity.Interaction;
using Leap.Unity.Query;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Leap.Unity.PhysicalInterfaces {
using IntObj = InteractionBehaviour;
public class HandleGroup : MonoBehaviour, IHandle {
[Header("Handles (Must Implement IHandle!)")]
[SerializeField]
private GameObject[] _handles = default;
public IIndexable<IHandle> handles {
// TODO: Create something like an "ElementsImplementInterface" attribute
get { return new GameObjectArrayComponentWrapper<IHandle>(_handles); }
}
#region hopes and dreams
// Something like this might also work, but it's not very graceful, and not generic
// either :(
//[System.Serializable]
//public struct ImplementsIHandleWrapper {
// [SerializeField]
// [ImplementsInterface(typeof(IHandle))]
// private MonoBehaviour _handleObj;
// public IHandle handle { get { return _handleObj as IHandle; } }
//}
//public ImplementsIHandleWrapper[] serializedHandleArray;
// THIS would work, but if we're going to go THAT far, might as well just make a
// collection struct that implements all sorts of useful interfaces and has events
// for listening to membership changes, and is drawn more nicely...
//[System.Serializable]
//public struct ImplementsInterfaceWrapper<T> where T : class {
// [SerializeField]
// [ImplementsInterfaceViaTypeProperty("getTypeProperty")]
// private MonoBehaviour _handleObj;
// public T handle { get { return _handleObj as T; } }
// public Type getTypeProperty() {
// return typeof(T);
// }
//}
//public ImplementsInterfaceWrapper<IHandle>[] GENERIC_serializedHandleArray;
#endregion
protected HashSet<IHandle> _heldHandles = new HashSet<IHandle>();
public virtual bool isHeld {
get {
return _heldHandles.Count > 0;
}
}
public virtual bool isMoving {
get {
return handles.Query().Any(handle => handle.isMoving);
}
}
/// <summary>
/// This property is ignored currently when set, and always returns false.
///
/// TODO: It would be interesting to allow a HandleGroup to have its OWN
/// InteractionObject associated with it that can be directly grabbed, to manipulate
/// a more complicated graph of handle-handle connections.
/// </summary>
public virtual bool isKinematic {
get {
return false;
}
set {
return;
}
}
protected virtual void Start() {
foreach (var handle in handles.GetEnumerator()) {
handle.OnHandleHoldBegin += onHandleGraspBegin;
handle.OnHandleHoldEnd += onHandleGraspEnd;
}
}
protected virtual void onHandleGraspBegin(IHandle handle) {
_heldHandles.Add(handle);
foreach (var otherHandle in handles.Query()
.Where(h => h != handle && !h.isHeld)) {
otherHandle.isKinematic = false;
}
if (_heldHandles.Count == 1) {
OnHoldBegin();
OnHandleHoldBegin(this);
}
}
protected virtual void onHandleGraspEnd(IHandle handle) {
// The last-held-handle should remain kinematic, allowing the final pose of the
// system to relax around that last-placed handle.
if (_heldHandles.Count == 1) {
handle.isKinematic = true;
}
_heldHandles.Remove(handle);
if (_heldHandles.Count == 0) {
OnHoldEnd();
OnHandleHoldEnd(this);
}
}
public event Action OnHoldBegin = () => { };
public event Action<IHandle> OnHandleHoldBegin = (thisHandle) => { };
public event Action OnHoldEnd = () => { };
public event Action<IHandle> OnHandleHoldEnd = (thisHandle) => { };
}
public struct GameObjectArrayComponentWrapper<GetComponentType>
: IIndexable<GetComponentType> {
GameObject[] _arr;
public GameObjectArrayComponentWrapper(GameObject[] arr) {
_arr = arr;
}
public GetComponentType this[int idx] {
get { return _arr[idx].GetComponent<GetComponentType>(); }
}
public int Count { get { return _arr.Length; } }
}
}