Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unity: Use automatic navigation in explicit navigation

I want to use explicit navigation for all buttons in application. But for some buttons I want leave the "Select on Right" to use the automatic navigation (for example: I know the next buttons for left, up and down but I don't know the next right button).

like image 258
Bilal Sehwail Avatar asked Jan 03 '18 10:01

Bilal Sehwail


2 Answers

You need to create a custom Selectable that can use explicit navigation if values are setted. You can use this code for your case, just leave empty fields from inspector where you need to use automatic navigation.

using UnityEngine;
using UnityEngine.UI;

namespace UI.CustomSelectable
{
    public class CustomSelectable : Selectable
    {
        [SerializeField]
        private Selectable upSelectable;

        [SerializeField]
        private Selectable downSelectable;

        [SerializeField]
        private Selectable leftSelectable;

        [SerializeField]
        private Selectable rightSelectable;

        public override Selectable FindSelectableOnUp()
        {
            return upSelectable != null ? upSelectable : base.FindSelectableOnUp();
        }

        public override Selectable FindSelectableOnDown()
        {
            return downSelectable != null ? downSelectable : base.FindSelectableOnDown();
        }

        public override Selectable FindSelectableOnLeft()
        {
            return leftSelectable != null ? leftSelectable : base.FindSelectableOnLeft();
        }

        public override Selectable FindSelectableOnRight()
        {
            return rightSelectable != null ? rightSelectable : base.FindSelectableOnRight();
        }
    }
}
like image 164
Liolik Avatar answered Nov 18 '22 22:11

Liolik


I wrote a Custom Button class based on the overriding Find...() method idea. Maybe it will be useful to others. It adds some convenience features to the original answer like lists of possible selection candidates and inspector buttons to automatically populate your selection candidates.

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif

namespace YourNameSpace
{
    /// <summary>
    /// Adds lists for each navigation direction.
    /// The first valid selectable in each list will be used or (if none is valid)
    /// then auto navigation will be used as fallback.
    /// </summary>
    public class ButtonSmartNavigation : Button
    {
        public List<Selectable> UpSelectables;
        public List<Selectable> DownSelectables;
        public List<Selectable> LeftSelectables;
        public List<Selectable> RightSelectables;

        public override Selectable FindSelectableOnUp()
        {
            var upSelectable = getFirstSelectable(UpSelectables, this.navigation.selectOnUp);
            return upSelectable != null ? upSelectable : base.FindSelectableOnUp();
        }

        public override Selectable FindSelectableOnDown()
        {
            var downSelectable = getFirstSelectable(DownSelectables, this.navigation.selectOnDown);
            return downSelectable != null ? downSelectable : base.FindSelectableOnDown();
        }

        public override Selectable FindSelectableOnLeft()
        {
            var leftSelectable = getFirstSelectable(LeftSelectables, this.navigation.selectOnLeft);
            return leftSelectable != null ? leftSelectable : base.FindSelectableOnLeft();
        }

        public override Selectable FindSelectableOnRight()
        {
            var rightSelectable = getFirstSelectable(RightSelectables, this.navigation.selectOnRight);
            return rightSelectable != null ? rightSelectable : base.FindSelectableOnRight();
        }

        protected Selectable getFirstSelectable(List<Selectable> list, Selectable defaultExplicit)
        {
            if (list == null)
                return null;

            for (int i = 0; i < list.Count; i++)
            {
                if (list[i] != null && list[i].IsInteractable() && list[i].gameObject.activeInHierarchy)
                {
                    return list[i];
                }
            }

            return defaultExplicit;
        }

        public void AddUpSelectable(Selectable selectable, bool prioritize = false)
        {
            addSelectable(UpSelectables, selectable, prioritize);
        }

        public void AddDownSelectable(Selectable selectable, bool prioritize = false)
        {
            addSelectable(DownSelectables, selectable, prioritize);
        }

        public void AddLeftSelectable(Selectable selectable, bool prioritize = false)
        {
            addSelectable(LeftSelectables, selectable, prioritize);
        }

        public void AddRightSelectable(Selectable selectable, bool prioritize = false)
        {
            addSelectable(RightSelectables, selectable, prioritize);
        }

        protected void addSelectable(List<Selectable> list, Selectable selectable, bool prioritize)
        {
            if (list == null)
            {
                list = new List<Selectable>();
            }

            if (prioritize)
            {
                list.Insert(0, selectable);
            }
            else
            {
                list.Add(selectable);
            }
        }

        public void Clear()
        {
            UpSelectables.Clear();
            DownSelectables.Clear();
            LeftSelectables.Clear();
            RightSelectables.Clear();
        }
    }

#if UNITY_EDITOR
    [CustomEditor(typeof(ButtonSmartNavigation), true)]
    [CanEditMultipleObjects]
    public class ButtonSmartNavigationEditor : UnityEditor.UI.ButtonEditor
    {
        protected SerializedProperty upSelectables;
        protected SerializedProperty downSelectables;
        protected SerializedProperty leftSelectables;
        protected SerializedProperty rightSelectables;

        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            var target = this.target as ButtonSmartNavigation;

            if (upSelectables == null) upSelectables = serializedObject.FindProperty("UpSelectables");
            if (downSelectables == null) downSelectables = serializedObject.FindProperty("DownSelectables");
            if (leftSelectables == null) leftSelectables = serializedObject.FindProperty("LeftSelectables");
            if (rightSelectables == null) rightSelectables = serializedObject.FindProperty("RightSelectables");

            EditorGUILayout.LabelField("Smart Navigation", EditorStyles.boldLabel);

            GUILayout.BeginHorizontal();
            if (GUILayout.Button("Up"))
            {
                target.UpSelectables.Clear();
                target.AddUpSelectable(target.FindSelectableOnUp());
                EditorUtility.SetDirty(target);
            }
            if (GUILayout.Button("Down"))
            {
                target.DownSelectables.Clear();
                target.AddDownSelectable(target.FindSelectableOnDown());
                EditorUtility.SetDirty(target);
            }
            if (GUILayout.Button("Left"))
            {
                target.LeftSelectables.Clear();
                target.AddLeftSelectable(target.FindSelectableOnLeft());
                EditorUtility.SetDirty(target);
            }
            if (GUILayout.Button("Right"))
            {
                target.RightSelectables.Clear();
                target.AddRightSelectable(target.FindSelectableOnRight());
                EditorUtility.SetDirty(target);
            }
            if (GUILayout.Button("Clear"))
            {
                target.Clear();
                EditorUtility.SetDirty(target);
            }
            GUILayout.EndHorizontal();

            serializedObject.Update();
            EditorGUILayout.PropertyField(upSelectables, new GUIContent("Up Selectables"));
            EditorGUILayout.PropertyField(downSelectables, new GUIContent("Down Selectables"));
            EditorGUILayout.PropertyField(leftSelectables, new GUIContent("Left Selectables"));
            EditorGUILayout.PropertyField(rightSelectables, new GUIContent("Right Selectables"));
            serializedObject.ApplyModifiedProperties();
        }
    }
#endif
}
like image 45
geoathome Avatar answered Nov 18 '22 21:11

geoathome