/*
* @author Valentin Simonov / http://va.lent.in/
*/
using System;
using TouchScript.Hit;
using TouchScript.Utils;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using TouchScript.Pointers;
namespace TouchScript.Layers
{
///
/// Base class for all pointer layers. Used to check if some object is hit by a pointer.
///
///
///
///
///
/// In TouchScript it's a layer's job to determine if a pointer on the screen hits anything in Unity's 3d/2d world.
/// keeps a sorted list of all layers in which it queries when a new pointer appears. It's a layer's job to return if this pointer hits an object. Layers can even be used to "hit" objects outside of Unity's 3d world, for example Scaleform integration is implemented this way.
/// Layers can be configured in a scene using or from code using API.
/// If you want to route pointers and manually control which objects they should "pointer" it's better to create a new layer extending .
///
[ExecuteInEditMode]
public abstract class TouchLayer : MonoBehaviour
{
#region Events
///
/// Occurs when layer determines that a pointer has hit something.
///
public event EventHandler PointerBegan
{
add { pointerPressInvoker += value; }
remove { pointerPressInvoker -= value; }
}
// Needed to overcome iOS AOT limitations
private EventHandler pointerPressInvoker;
#endregion
#region Public properties
///
/// Pointer layer's name.
///
public string Name;
///
/// Layers screen to world projection normal.
///
public virtual Vector3 WorldProjectionNormal
{
get { return transform.forward; }
}
///
/// Gets or sets an object implementing to be asked for layer specific actions.
///
/// The delegate.
public ILayerDelegate Delegate { get; set; }
#endregion
#region Private variables
///
/// The layer projection parameters.
///
protected ProjectionParams layerProjectionParams;
///
/// Layer manager.
///
protected ILayerManager manager;
#endregion
#region Temporary variables
private List tmpHitTestList = new List(10);
#endregion
#region Public methods
///
/// Gets the projection parameters of this layer which might depend on a specific pointer data.
///
/// Pointer to retrieve projection parameters for.
///
public virtual ProjectionParams GetProjectionParams(Pointer pointer)
{
return layerProjectionParams;
}
///
/// Checks if a point in screen coordinates hits something in this layer.
///
/// Pointer.
/// Hit result.
/// true, if an object is hit, ; false otherwise.
public virtual HitResult Hit(IPointer pointer, out HitData hit)
{
hit = default(HitData);
if (enabled == false || gameObject.activeInHierarchy == false) return HitResult.Miss;
if (Delegate != null)
{
if (Delegate.ShouldReceivePointer(this, pointer)) return HitResult.Hit;
return HitResult.Miss;
}
return HitResult.Hit;
}
#endregion
#region Unity methods
///
/// Unity Awake callback.
///
protected virtual void Awake()
{
setName();
if (!Application.isPlaying) return;
manager = LayerManager.Instance;
layerProjectionParams = createProjectionParams();
StartCoroutine(lateAwake());
}
private IEnumerator lateAwake()
{
yield return null;
// Add ourselves after TouchManager finished adding layers in order
manager.AddLayer(this, -1, false);
}
// To be able to turn layers off
private void Start() {}
///
/// Unity OnDestroy callback.
///
protected virtual void OnDestroy()
{
if (!Application.isPlaying || TouchManager.Instance == null) return;
StopAllCoroutines();
manager.RemoveLayer(this);
}
#endregion
#region Internal methods
internal void INTERNAL_AddPointer(Pointer pointer)
{
addPointer(pointer);
}
internal void INTERNAL_UpdatePointer(Pointer pointer)
{
updatePointer(pointer);
}
internal bool INTERNAL_PressPointer(Pointer pointer)
{
pressPointer(pointer);
if (pointerPressInvoker != null) pointerPressInvoker.InvokeHandleExceptions(this, new TouchLayerEventArgs(pointer));
return true;
}
internal void INTERNAL_ReleasePointer(Pointer pointer)
{
releasePointer(pointer);
}
internal void INTERNAL_RemovePointer(Pointer pointer)
{
removePointer(pointer);
}
internal void INTERNAL_CancelPointer(Pointer pointer)
{
cancelPointer(pointer);
}
#endregion
#region Protected functions
///
/// Checks the hit filters.
///
/// The pointer.
/// HitData for the pointer.
///
protected HitResult checkHitFilters(IPointer pointer, HitData hit)
{
hit.Target.GetComponents(tmpHitTestList);
var count = tmpHitTestList.Count;
if (count == 0) return HitResult.Hit;
var hitResult = HitResult.Hit;
for (var i = 0; i < count; i++)
{
var test = tmpHitTestList[i];
if (!test.enabled) continue;
hitResult = test.IsHit(pointer, hit);
if (hitResult != HitResult.Hit) break;
}
return hitResult;
}
///
/// Updates pointer layers's name.
///
protected virtual void setName()
{
if (string.IsNullOrEmpty(Name)) Name = "Layer";
}
///
/// Called when a pointer is added.
///
/// Pointer.
/// This method may also be used to update some internal state or resend this event somewhere.
protected virtual void addPointer(Pointer pointer) {}
///
/// Called when a layer is pressed over an object detected by this layer.
///
/// Pointer.
/// This method may also be used to update some internal state or resend this event somewhere.
protected virtual void pressPointer(Pointer pointer) {}
///
/// Called when a pointer is moved.
///
/// Pointer.
/// This method may also be used to update some internal state or resend this event somewhere.
protected virtual void updatePointer(Pointer pointer) {}
///
/// Called when a pointer is released.
///
/// Pointer.
/// This method may also be used to update some internal state or resend this event somewhere.
protected virtual void releasePointer(Pointer pointer) {}
///
/// Called when a pointer is removed.
///
/// Pointer.
/// This method may also be used to update some internal state or resend this event somewhere.
protected virtual void removePointer(Pointer pointer) {}
///
/// Called when a pointer is cancelled.
///
/// Pointer.
/// This method may also be used to update some internal state or resend this event somewhere.
protected virtual void cancelPointer(Pointer pointer) {}
///
/// Creates projection parameters.
///
/// Created instance.
protected virtual ProjectionParams createProjectionParams()
{
return new ProjectionParams();
}
#endregion
}
///
/// Arguments used with events.
///
public class TouchLayerEventArgs : EventArgs
{
///
/// Gets the pointer associated with the event.
///
public Pointer Pointer { get; private set; }
///
/// Initializes a new instance of the class.
///
/// The pointer associated with the event.
public TouchLayerEventArgs(Pointer pointer)
: base()
{
Pointer = pointer;
}
}
}