// Select on enable and add to the list. protected override void OnEnable() { //Check to avoid multiple OnEnable() calls for each selectable if (m_EnableCalled) { return; } base.OnEnable(); if (s_SelectableCount == s_Selectables.Length) { BaseSelectable[] temp = new BaseSelectable[s_Selectables.Length * 2]; Array.Copy(s_Selectables, temp, s_Selectables.Length); s_Selectables = temp; } m_CurrentIndex = s_SelectableCount; s_Selectables[m_CurrentIndex] = this; s_SelectableCount++; isPointerDown = false; DoStateTransition(currentSelectionState, true); m_EnableCalled = true; }
// Selection logic /// <summary> /// Finds the selectable object next to this one. /// </summary> /// <remarks> /// The direction is determined by a Vector3 variable. /// </remarks> /// <param name="dir">The direction in which to search for a neighbouring BaseSelectable object.</param> /// <returns>The neighbouring BaseSelectable object. Null if none found.</returns> /// <example> /// <code> /// using UnityEngine; /// using System.Collections; /// using UnityEngine.UI; // required when using UI elements in scripts /// /// public class ExampleClass : MonoBehaviour /// { /// //Sets the direction as "Up" (Y is in positive). /// public Vector3 direction = new Vector3(0, 1, 0); /// public Button btnMain; /// /// public void Start() /// { /// //Finds and assigns the selectable above the main button /// BaseSelectable newSelectable = btnMain.FindSelectable(direction); /// /// Debug.Log(newSelectable.name); /// } /// } /// </code> /// </example> public BaseSelectable FindSelectable(Vector3 dir) { dir = dir.normalized; Vector3 localDir = Quaternion.Inverse(transform.rotation) * dir; Vector3 pos = transform.TransformPoint(GetPointOnRectEdge(transform as RectTransform, localDir)); float maxScore = Mathf.NegativeInfinity; BaseSelectable bestPick = null; for (int i = 0; i < s_SelectableCount; ++i) { BaseSelectable sel = s_Selectables[i]; if (sel == this) { continue; } if (!sel.IsInteractable() || sel.navigation.mode == Navigation.Mode.None) { continue; } #if UNITY_EDITOR // Apart from runtime use, FindSelectable is used by custom editors to // draw arrows between different selectables. For scene view cameras, // only selectables in the same stage should be considered. if (Camera.current != null && !UnityEditor.SceneManagement.StageUtility.IsGameObjectRenderedByCamera(sel.gameObject, Camera.current)) { continue; } #endif var selRect = sel.transform as RectTransform; Vector3 selCenter = selRect != null ? (Vector3)selRect.rect.center : Vector3.zero; Vector3 myVector = sel.transform.TransformPoint(selCenter) - pos; // Value that is the distance out along the direction. float dot = Vector3.Dot(dir, myVector); // Skip elements that are in the wrong direction or which have zero distance. // This also ensures that the scoring formula below will not have a division by zero error. if (dot <= 0) { continue; } // This scoring function has two priorities: // - Score higher for positions that are closer. // - Score higher for positions that are located in the right direction. // This scoring function combines both of these criteria. // It can be seen as this: // Dot (dir, myVector.normalized) / myVector.magnitude // The first part equals 1 if the direction of myVector is the same as dir, and 0 if it's orthogonal. // The second part scores lower the greater the distance is by dividing by the distance. // The formula below is equivalent but more optimized. // // If a given score is chosen, the positions that evaluate to that score will form a circle // that touches pos and whose center is located along dir. A way to visualize the resulting functionality is this: // From the position pos, blow up a circular balloon so it grows in the direction of dir. // The first BaseSelectable whose center the circular balloon touches is the one that's chosen. float score = dot / myVector.sqrMagnitude; if (score > maxScore) { maxScore = score; bestPick = sel; } } return(bestPick); }