using System;
using UnityEngine;
namespace LMWidgets
{
public enum LeapPhysicsState
{
Interacting, // Responsible for moving the widgets with the fingers
Reflecting, // Responsible for reflecting widget information and simulating the physics
Disabled // State in which the widget is disabled
}
///
/// Base class for physics.
/// Handles state changes between Interacting and Reflecting.
///
public abstract class LeapPhysicsBase : MonoBehaviour
{
protected event EventHandler> StateChangeHandler;
private LeapPhysicsState m_state = LeapPhysicsState.Reflecting; // Don't set this directly. Use accessor.
protected GameObject m_target = null;
protected Vector3 m_pivot = Vector3.zero;
protected Vector3 m_targetPivot = Vector3.zero;
///
/// Represents the current Physics state.
///
///
/// Use this property to set the state rather than the m_state field so that proper events and functions are called on changes.
/// Chaning the property will also call onInteractionEnabled and onInteractionDisabled as appropritate.
///
protected LeapPhysicsState State {
get {
return m_state;
}
set {
// Call enabled and disabled functions as appropriate.
if ( m_state != LeapPhysicsState.Disabled && value == LeapPhysicsState.Disabled ) {
onInteractionDisabled();
}
else if ( m_state == LeapPhysicsState.Disabled && value != LeapPhysicsState.Disabled ) {
onInteractionEnabled();
}
m_state = value; // Update underlying value
// Fire changed event
EventHandler> handler = StateChangeHandler;
if ( handler != null ) { handler(this, new EventArg(State)); }
}
}
///
/// Represents whether the widget is enabled or disabled.
///
public bool Interactable {
get {
return !(State == LeapPhysicsState.Disabled);
}
set {
if (State == LeapPhysicsState.Disabled && value == true ) {
State = LeapPhysicsState.Reflecting;
}
else if (State != LeapPhysicsState.Disabled && value == false) {
State = LeapPhysicsState.Disabled;
}
}
}
// Apply the physics interactions when the hand is no longer interacting with the object
protected abstract void ApplyPhysics();
// Apply interactions with the objects
protected abstract void ApplyInteractions();
// Apply constraints for the object (e.g. Constrain movements along a specific axis)
protected abstract void ApplyConstraints();
///
/// Called when widget becomes interactable.
///
///
/// Implement this function to handle changes to the widget when interaction is enabled (ie. starting an enable animation)
///
protected virtual void onInteractionEnabled() {}
///
/// Called when widget becomes non-interactable.
///
///
/// Implement this function to handle changes to the widget when interaction is disabled (ie. starting a disable animation)
///
protected virtual void onInteractionDisabled() {}
///
/// Resets the pivots
///
protected virtual void ResetPivots()
{
m_pivot = transform.localPosition;
if (m_target != null)
m_targetPivot = transform.parent.InverseTransformPoint(m_target.transform.position);
}
///
/// Returns true or false by checking if "HandModel" exits in the parent of the collider
///
///
///
private bool IsHand(Collider collider)
{
return collider.transform.parent && collider.transform.parent.parent && collider.transform.parent.parent.GetComponent();
}
///
/// Change the state of the physics to "Interacting" if no other hands were interacting and if the collider is a hand
///
///
protected virtual void OnTriggerEnter(Collider collider)
{
if (m_target == null && IsHand(collider) && State != LeapPhysicsState.Disabled)
{
State = LeapPhysicsState.Interacting;
m_target = collider.gameObject;
ResetPivots();
}
}
///
/// Change the state of the physics to "Reflecting" if the object exiting is the hand
///
///
protected virtual void OnTriggerExit(Collider collider)
{
// TODO: Use interpolation to determine if the hand should still continue interacting with the widget to solve low-FPS
// TODO(cont): It should solve low-FPS or fast hand movement problems
if (collider.gameObject == m_target)
{
State = LeapPhysicsState.Reflecting;
m_target = null;
}
}
protected virtual void Awake()
{
if (GetComponent() == null)
{
Debug.LogWarning("This Widget lacks a collider. Will not function as expected.");
}
}
protected virtual void FixedUpdate()
{
if (m_target == null && State == LeapPhysicsState.Interacting)
{
State = LeapPhysicsState.Reflecting;
}
switch (State)
{
case LeapPhysicsState.Interacting:
ApplyInteractions();
break;
case LeapPhysicsState.Reflecting:
ApplyPhysics();
break;
case LeapPhysicsState.Disabled:
break;
default:
break;
}
ApplyConstraints();
}
}
}