using UnityEngine;
using System.Collections.Generic;
using Lean.Common;
using FSA = UnityEngine.Serialization.FormerlySerializedAsAttribute;
namespace Lean.Touch
{
/// This component will draw a selection box.
[HelpURL(LeanTouch.PlusHelpUrlPrefix + "LeanSelectionBox")]
[AddComponentMenu(LeanTouch.ComponentPathPrefix + "Selection Box")]
public class LeanSelectionBox : MonoBehaviour
{
// This class will store an association between a Finger and a RectTransform instance
[System.Serializable]
public class FingerData : LeanFingerData
{
public RectTransform Box; // The RectTransform instance associated with this link
}
/// The camera this component will calculate using.
/// None/null = MainCamera.
public Camera Camera { set { _camera = value; } get { return _camera; } } [FSA("Camera")] [SerializeField] private Camera _camera;
/// Ignore fingers with StartedOverGui?
public bool IgnoreIfStartedOverGui { set { ignoreIfStartedOverGui = value; } get { return ignoreIfStartedOverGui; } } [FSA("IgnoreIfStartedOverGui")] [SerializeField] private bool ignoreIfStartedOverGui = true;
/// The selection box prefab.
public RectTransform Prefab { set { prefab = value; } get { return prefab; } } [FSA("Prefab")] [SerializeField] private RectTransform prefab;
/// The transform the prefabs will be spawned on.
/// NOTE: This RectTransform must fill the whole screen, like the main canvas.
public RectTransform Root { set { root = value; } get { return root; } } [FSA("Root")] [SerializeField] private RectTransform root;
/// The selected objects will be selected by this component.
public LeanSelectByFinger Select { set { select = value; } get { return select; } } [FSA("Select")] [SerializeField] private LeanSelectByFinger select;
// This stores all the links between Fingers and RectTransform instances
private List fingerDatas = new List();
// Temporary selectables inside box
private static List selectables = new List();
protected virtual void OnEnable()
{
LeanTouch.OnFingerDown += HandleFingerDown;
LeanTouch.OnFingerUpdate += HandleFingerSet;
LeanTouch.OnFingerUp += HandleFingerUp;
}
protected virtual void OnDisable()
{
LeanTouch.OnFingerDown -= HandleFingerDown;
LeanTouch.OnFingerUpdate -= HandleFingerSet;
LeanTouch.OnFingerUp -= HandleFingerUp;
}
private void HandleFingerDown(LeanFinger finger)
{
// Limit to one selection box
if (fingerDatas.Count > 0)
{
return;
}
// Only use fingers clear of the GUI
if (ignoreIfStartedOverGui == true && finger.StartedOverGui == true)
{
return;
}
// Make new link
var fingerData = LeanFingerData.FindOrCreate(ref fingerDatas, finger);
// Assign this finger to this link
fingerData.Finger = finger;
// Create LineRenderer instance for this link
fingerData.Box = Instantiate(prefab);
fingerData.Box.gameObject.SetActive(true);
// Move box to root
fingerData.Box.transform.SetParent(root, false);
}
private void HandleFingerSet(LeanFinger finger)
{
// Try and find the link for this finger
var fingerData = LeanFingerData.Find(fingerDatas, finger);
// Link exists?
if (fingerData != null && fingerData.Box != null)
{
WriteTransform(fingerData.Box, fingerData.Finger);
}
}
private void HandleFingerUp(LeanFinger finger)
{
// Try and find the link for this finger
var fingerData = LeanFingerData.Find(fingerDatas, finger);
// Link exists?
if (fingerData != null)
{
// Remove link from list
fingerDatas.Remove(fingerData);
// Destroy box GameObject
if (fingerData.Box != null)
{
Destroy(fingerData.Box.gameObject);
}
}
}
// Transform rect based on finger data
private void WriteTransform(RectTransform rect, LeanFinger finger)
{
// Make sure the camera exists
var camera = LeanHelper.GetCamera(_camera, gameObject);
if (camera != null)
{
var min = camera.ScreenToViewportPoint(finger.StartScreenPosition);
var max = camera.ScreenToViewportPoint(finger. ScreenPosition);
// Fix any inverted values
if (min.x > max.x)
{
var t = min.x;
min.x = max.x;
max.x = t;
}
if (min.y > max.y)
{
var t = min.y;
min.y = max.y;
max.y = t;
}
// Reset pivot in case you forgot
rect.pivot = Vector2.zero;
// Set anchors
rect.anchorMin = min;
rect.anchorMax = max;
// Make rect we check against
var viewportRect = new Rect();
viewportRect.min = min;
viewportRect.max = max;
// Rebuild list of all selectables within rect
selectables.Clear();
foreach (var selectable in LeanSelectableByFinger.Instances)
{
var viewportPoint = camera.WorldToViewportPoint(selectable.transform.position);
if (viewportRect.Contains(viewportPoint) == true)
{
selectables.Add(selectable);
}
}
// Select them
select.ReplaceSelection(selectables, finger);
}
else
{
Debug.LogError("Failed to find camera. Either tag your cameras MainCamera, or set one in this component.", this);
}
}
}
}
#if UNITY_EDITOR
namespace Lean.Touch.Editor
{
using TARGET = LeanSelectionBox;
[UnityEditor.CanEditMultipleObjects]
[UnityEditor.CustomEditor(typeof(TARGET))]
public class LeanSelectionBox_Editor : LeanEditor
{
protected override void OnInspector()
{
TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts);
Draw("_camera", "The camera the translation will be calculated using.\n\nNone/null = MainCamera.");
Draw("ignoreIfStartedOverGui", "Ignore fingers with StartedOverGui?");
BeginError(Any(tgts, t => t.Prefab == null));
Draw("prefab", "The selection box prefab.");
EndError();
BeginError(Any(tgts, t => t.Root == null));
Draw("root", "The transform the prefabs will be spawned on.\n\nNOTE: This RectTransform must fill the whole screen, like the main canvas.");
EndError();
BeginError(Any(tgts, t => t.Select == null));
Draw("select", "The selected objects will be selected by this component.");
EndError();
}
}
}
#endif