using UnityEngine; using System.Collections.Generic; using Lean.Common; using FSA = UnityEngine.Serialization.FormerlySerializedAsAttribute; namespace Lean.Touch { /// This component allows you to spawn a prefab at a point relative to a finger and the specified ScreenDepth. /// NOTE: To trigger the prefab spawn you must call the Spawn method on this component from somewhere. [HelpURL(LeanTouch.HelpUrlPrefix + "LeanSpawnWithFinger")] [AddComponentMenu(LeanTouch.ComponentPathPrefix + "Spawn With Finger")] public class LeanSpawnWithFinger : MonoBehaviour { public enum RotateType { ThisTransform, ScreenDepthNormal } [System.Serializable] public class FingerData : LeanFingerData { public Transform Clone; } /// The prefab that this component can spawn. public Transform Prefab { set { prefab = value; } get { return prefab; } } [FSA("Prefab")] [SerializeField] private Transform prefab; /// How should the spawned prefab be rotated? public RotateType RotateTo { set { rotateTo = value; } get { return rotateTo; } } [FSA("RotateTo")] [SerializeField] private RotateType rotateTo; /// Hold on to the spawned clone while the spawning finger is still being held? public bool DragAfterSpawn { set { dragAfterSpawn = value; } get { return dragAfterSpawn; } } [FSA("DragAfterSpawn")] [SerializeField] private bool dragAfterSpawn; /// If the specified prefab is selectable, select it when spawned? public bool SelectOnSpawn { set { selectOnSpawn = value; } get { return selectOnSpawn; } } [SerializeField] private bool selectOnSpawn; /// If you want the spawned component to be a selected with a specific select component, you can specify it here. /// None/null = It will be self selected. public LeanSelect SelectWith { set { selectWith = value; } get { return selectWith; } } [SerializeField] private LeanSelect selectWith; /// The conversion method used to find a world point from a screen point. public LeanScreenDepth ScreenDepth = new LeanScreenDepth(LeanScreenDepth.ConversionType.FixedDistance, Physics.DefaultRaycastLayers, 10.0f); /// This allows you to offset the finger position. public Vector2 PixelOffset { set { pixelOffset = value; } get { return pixelOffset; } } [FSA("PixelOffset")] [SerializeField] private Vector2 pixelOffset; /// If you want the pixels to scale based on device resolution, then specify the canvas whose scale you want to use here. public Canvas PixelScale { set { pixelScale = value; } get { return pixelScale; } } [FSA("PixelScale")] [SerializeField] private Canvas pixelScale; /// This allows you to offset the spawned object position. public Vector3 WorldOffset { set { worldOffset = value; } get { return worldOffset; } } [FSA("WorldOffset")] [SerializeField] private Vector3 worldOffset; /// This allows you transform the WorldOffset to be relative to the specified Transform. public Transform WorldRelativeTo { set { worldRelativeTo = value; } get { return worldRelativeTo; } } [FSA("WorldRelativeTo")] [SerializeField] private Transform worldRelativeTo; [SerializeField] private List fingerDatas; private static Stack fingerDataPool = new Stack(); /// This will spawn Prefab at the specified finger based on the ScreenDepth setting. public void Spawn(LeanFinger finger) { if (prefab != null && finger != null) { // Spawn and position var clone = Instantiate(prefab); UpdateSpawnedTransform(finger, clone); clone.gameObject.SetActive(true); if (dragAfterSpawn == true) { var fingerData = LeanFingerData.FindOrCreate(ref fingerDatas, finger); fingerData.Clone = clone; } // Select? if (selectOnSpawn == true) { var selectable = clone.GetComponent(); var selectableByFinger = selectable as LeanSelectableByFinger; var selectWithByFinger = selectWith as LeanSelectByFinger; if (selectableByFinger != null) { if (selectWithByFinger != null) { selectWithByFinger.Select(selectableByFinger, finger); } else if (selectWith != null) { selectWith.Select(selectableByFinger); } else { selectableByFinger.SelectSelf(finger); } } else if (selectable != null) { if (selectWithByFinger != null) { selectWithByFinger.Select(selectable, finger); } else if (selectWith != null) { selectWith.Select(selectable); } else { selectable.SelfSelected = true; } } } } } protected virtual void OnEnable() { LeanTouch.OnFingerUp += HandleFingerUp; } protected virtual void OnDisable() { LeanTouch.OnFingerUp -= HandleFingerUp; } protected virtual void Update() { for (var i = fingerDatas.Count - 1; i >= 0; i--) { var fingerData = fingerDatas[i]; if (fingerData.Clone != null) { UpdateSpawnedTransform(fingerData.Finger, fingerData.Clone); } } } private void UpdateSpawnedTransform(LeanFinger finger, Transform instance) { // Grab screen position of finger, and optionally offset it var screenPoint = finger.ScreenPosition; if (pixelScale != null) { screenPoint += pixelOffset * pixelScale.scaleFactor; } else { screenPoint += pixelOffset; } // Converted screen position to world position, and optionally offset it var worldPoint = ScreenDepth.Convert(screenPoint, gameObject, instance); if (worldRelativeTo != null) { worldPoint += worldRelativeTo.TransformPoint(worldOffset); } else { worldPoint += worldOffset; } // Write position instance.position = worldPoint; // Write rotation switch (rotateTo) { case RotateType.ThisTransform: { instance.rotation = transform.rotation; } break; case RotateType.ScreenDepthNormal: { instance.up = LeanScreenDepth.LastWorldNormal; } break; } } private void HandleFingerUp(LeanFinger finger) { LeanFingerData.Remove(fingerDatas, finger, fingerDataPool); } } } #if UNITY_EDITOR namespace Lean.Touch.Editor { using TARGET = LeanSpawnWithFinger; [UnityEditor.CanEditMultipleObjects] [UnityEditor.CustomEditor(typeof(TARGET), true)] public class LeanSpawnWithFinger_Editor : LeanEditor { protected override void OnInspector() { TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts); BeginError(Any(tgts, t => t.Prefab == null)); Draw("prefab"); EndError(); Draw("rotateTo", "How should the spawned prefab be rotated?"); Draw("dragAfterSpawn", "Hold on to the spawned clone while the spawning finger is still being held?"); Draw("selectOnSpawn", "If the specified prefab is selectable, select it when spawned?"); if (Any(tgts, t => t.SelectOnSpawn == true)) { BeginIndent(); Draw("selectWith", "If you want the spawned component to be a selected with a specific select component, you can specify it here.\n\nNone/null = It will be self selected."); EndIndent(); } Draw("ScreenDepth"); Separator(); Draw("pixelOffset", "This allows you to offset the finger position."); Draw("pixelScale", "If you want the pixels to scale based on device resolution, then specify the canvas whose scale you want to use here."); Separator(); Draw("worldOffset", "This allows you to offset the spawned object position."); Draw("worldRelativeTo", "This allows you transform the WorldOffset to be relative to the specified Transform."); } } } #endif