public void BuildNavigation(SelectableGroup group) { Vector3 position; Vector3 direction; Vector3 localDir; float maxScore; foreach (SelectableOptionBase option in group.options) { Transform transform = option.GetTransform(); for (int i = 0; i < option.navigationArray.Length; i++) { maxScore = Mathf.NegativeInfinity; direction = SelectableNavigationUtils.GetDirectionVector((SelectableNavigationDirection)i); localDir = Quaternion.Inverse(transform.rotation) * direction; position = transform.TransformPoint(SelectableNavigationUtils.GetPointOnRectEdge(transform as RectTransform, localDir)); SelectableOptionBase bestPick = null; foreach (SelectableOptionBase other in group.options) { SelectableOptionBase sel = other; if (sel == option || sel == null) { continue; } var selRect = sel.GetTransform() as RectTransform; Vector3 selCenter = selRect != null ? (Vector3)selRect.rect.center : Vector3.zero; Vector3 myVector = sel.GetTransform().TransformPoint(selCenter) - position; // Value that is the distance out along the direction. float dot = Vector3.Dot(direction, myVector); // Skip elements that are in the wrong direction or which have zero or too much 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. float score = dot / myVector.sqrMagnitude; if (score > maxScore) { maxScore = score; bestPick = sel; } } option.navigationArray[i] = bestPick; } } }
private static void DrawNavigationArrow(Vector2 direction, SelectableOptionBase fromObj, SelectableOptionBase toObj) { if (fromObj == null || toObj == null) { return; } Transform fromTransform = fromObj.transform; Transform toTransform = toObj.transform; Vector2 sideDir = new Vector2(direction.y, -direction.x); Vector3 fromPoint = fromTransform.TransformPoint(SelectableNavigationUtils.GetPointOnRectEdge(fromTransform as RectTransform, direction)); Vector3 toPoint = toTransform.TransformPoint(SelectableNavigationUtils.GetPointOnRectEdge(toTransform as RectTransform, -direction)); float fromSize = HandleUtility.GetHandleSize(fromPoint) * 0.05f; float toSize = HandleUtility.GetHandleSize(toPoint) * 0.05f; fromPoint += fromTransform.TransformDirection(sideDir) * fromSize; toPoint += toTransform.TransformDirection(sideDir) * toSize; float length = Vector3.Distance(fromPoint, toPoint); Vector3 fromTangent = fromTransform.rotation * direction * length * 0.3f; Vector3 toTangent = toTransform.rotation * -direction * length * 0.3f; Handles.DrawBezier(fromPoint, toPoint, fromPoint + fromTangent, toPoint + toTangent, Handles.color, null, kArrowThickness); Handles.DrawAAPolyLine(kArrowThickness, toPoint, toPoint + toTransform.rotation * (-direction - sideDir) * toSize * kArrowHeadSize); Handles.DrawAAPolyLine(kArrowThickness, toPoint, toPoint + toTransform.rotation * (-direction + sideDir) * toSize * kArrowHeadSize); }
private static void BuildSmartNavigation(List <SelectableOptionBase> options, float[] maxSearchDistances) { List <BoxCollider2D> cachedColliders = new List <BoxCollider2D>(); List <RectTransform> cachedTransforms = new List <RectTransform>(); List <int> cachedLayerIndices = new List <int>(); //Attach BoxCollider2Ds to every option and set to to default layer to allow Raycasts to work foreach (SelectableOptionBase option in options) { BoxCollider2D collider = option.gameObject.AddComponent <BoxCollider2D>(); RectTransform optionRectTransform = option.GetComponent <RectTransform>(); collider.size = optionRectTransform.sizeDelta; cachedColliders.Add(collider); cachedTransforms.Add(optionRectTransform); cachedLayerIndices.Add(option.gameObject.layer); option.gameObject.layer = 0; } //Raycast from each control border to a direction for the given distance for the direction for (int i = 0; i < options.Count; i++) { for (int j = 0; j < maxSearchDistances.Length; j++) { SelectableNavigationDirection direction = (SelectableNavigationDirection)j; RectTransform optionRectTransform = cachedTransforms[i]; RaycastHit2D hit = Physics2D.Raycast(GetRaycastOrigin(direction, optionRectTransform), SelectableNavigationUtils.GetDirectionVector(direction), maxSearchDistances[j]); if (hit.transform != null) { SelectableOptionBase hitOption = hit.transform.GetComponent <SelectableOptionBase>(); if (hitOption != null) { options[i].SetNextOption(direction, hitOption); } } } } //Destroy the previously created BoxColliders and restore previous layer for (int i = 0; i < options.Count; i++) { if (Application.isPlaying) { Object.Destroy(cachedColliders[i]); } else { Object.DestroyImmediate(cachedColliders[i]); } options[i].gameObject.layer = cachedLayerIndices[i]; } }
public static void DrawNavigationForSelectable(SelectableOptionBase option) { if (option == null) { return; } Transform transform = option.transform; bool active = Selection.transforms.Any(e => e == transform); Handles.color = new Color(1.0f, 0.9f, 0.1f, active ? 1.0f : 0.4f); DrawNavigationArrow(-Vector2.right, option, option.GetNextOption(SelectableNavigationDirection.LEFT)); DrawNavigationArrow(Vector2.right, option, option.GetNextOption(SelectableNavigationDirection.RIGHT)); DrawNavigationArrow(Vector2.up, option, option.GetNextOption(SelectableNavigationDirection.UP)); DrawNavigationArrow(-Vector2.up, option, option.GetNextOption(SelectableNavigationDirection.DOWN)); }
private void OnEnable() { option = (SelectableOptionBase)target; }