using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
///
/// Builds a Navigation scheme for children of this game object that include a Selectable component (eg. Button, Toggle, InputField)
/// THIS CAN BE ACHIEVED IN THE EDITOR: Use the Navigation property on the Selectable to set it up, but in case you want a custom, dynamic system, this shows how to interact with various elements through script.
///
public class MenuController : MonoBehaviour
{
// Assign in editor
[SerializeField]
private EventSystem eventSystem = null;
public enum NavigationMode { Vertical, Horizontal }
[SerializeField]
private NavigationMode navigationMode = NavigationMode.Vertical;
[SerializeField]
private bool circular = false;
[SerializeField]
private bool startingPoint = false;
private List menuElements = null; // All selectable UI elements inherit from UnityEngine.UI.Selectable - using this type in the list allows for mixing of different elements
void Start()
{
BuildNavigation();
}
private void BuildNavigation()
{
// grab all Selectables that are children on current gameObject
FindMenuElements();
// Set up the navigation for a circullar list following the order of objects in hierarchy and ignoring non-interactable elements
CreateNavigation();
// Set the first interactable element as selected in the EventSystem
if (startingPoint && menuElements.Count > 0)
{
Selectable firstSelected = menuElements.FirstOrDefault((e) => e.interactable);
if (firstSelected != null)
{
eventSystem.SetSelectedGameObject(firstSelected.gameObject, new BaseEventData(eventSystem));
}
else
{
eventSystem.SetSelectedGameObject(menuElements[0].gameObject, new BaseEventData(eventSystem));
}
}
}
private void FindMenuElements()
{
menuElements = new List();
foreach (Transform child in gameObject.transform)
{
Selectable selectabe = child.GetComponent();
if (selectabe != null)
{
menuElements.Add(selectabe);
}
}
}
private void CreateNavigation()
{
for (int i = 0; i < menuElements.Count; i++)
{
Selectable next = null;
Selectable previous = null;
FindNeighbours(i, ref next, ref previous);
// Create an initialise new Navigation object, then assign it to the current Selectable
Navigation navigation;
switch (navigationMode)
{
case NavigationMode.Vertical:
navigation = new Navigation() { mode = Navigation.Mode.Explicit, selectOnUp = previous, selectOnDown = next };
break;
case NavigationMode.Horizontal:
navigation = new Navigation() { mode = Navigation.Mode.Explicit, selectOnLeft = previous, selectOnRight = next };
break;
default:
navigation = new Navigation() { mode = Navigation.Mode.Explicit };
break;
}
menuElements[i].navigation = navigation;
}
}
private void FindNeighbours(int i, ref Selectable next, ref Selectable previous)
{
int nextIndex = i + 1;
int previousIndex = i - 1;
if (circular)
{
nextIndex %= menuElements.Count;
previousIndex = previousIndex < 0 ? menuElements.Count - 1 : previousIndex;
next = menuElements[nextIndex];
previous = menuElements[previousIndex];
}
else
{
if (nextIndex < menuElements.Count)
{
next = menuElements[nextIndex];
}
if (previousIndex >= 0)
{
previous = menuElements[previousIndex];
}
}
}
}