Search Unity

Question Keyboard only inputs

Discussion in 'Input System' started by Almost73, May 3, 2023.

  1. Almost73

    Almost73

    Joined:
    Apr 28, 2023
    Posts:
    8
    Hello everyone,
    I am extremely new in Unity (started last week) and my only other experience with making games is playing around a bit with RPGMaker.
    I was looking through courses and its pretty hard to find exactly what I am looking for.

    Is there a way to make the game completely mouse free? both in game and also in menus and be done in a easy way for a complete begginer?
     
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,934
    Absolutely. Though that's up for you to integrate that functionality where it doesn't exist out of the box. Things like navigating between selectables should work out of the box, but more complex things you will need to add yourself. But features like opening specific menus/windows with a shortcut are trivial to implement.

    If you were to do such a project I would say you should use uGUI/TMPro over UI Toolkit as it has better input flow control.

    And you will ultimately need to learn some degree of C#.

    Hell back in the day there were no mice and most games were keyboard only.
     
    Almost73 likes this.
  3. John_Leorid

    John_Leorid

    Joined:
    Nov 5, 2012
    Posts:
    651
    This is actually somewhat complicated, fortunately I attended LD53 and with my friend & roommate, I made this game:

    https://leorid.itch.io/field-supply

    And it has full keyboard controls for the menu. Here are the (slightly modified) scripts I used:

    UI_KeepUISelection -> if you click on an empty space with the mouse, the UI selection is gone and without a selection, you can't navigate with Keyboard-Buttons, so this script will select the referenced object "_defaultSelection" when:
    • nothing else is selected
    • "_defaultSelection" is active in the hierarchy
    • no higher (lower in the hierarchy or on a canvas with higher sorting order) instance of UI_KeepUISelection is active
    Attach this script to a "navigation-layer" e.g. a window in which you want to navigate. The Upgrade Menu in the Game linked above has one such Script attached to it's topmost parent, all Buttons and so on are childs

    Hierarchy
    - Canvas
    - - UpgradeMenu (Empty, has "UI_KeepUISelection" Component)
    - - - Empty
    - - - - Button1
    - - - - Button2
    - - - Button3
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.EventSystems;
    5. using UnityEngine.UI;
    6.  
    7. namespace JL
    8. {
    9.     public class UI_KeepUISelection : MonoBehaviour
    10.     {
    11.         [SerializeField] Transform _selectionGroupParent;
    12.         public Selectable _defaultSelection;
    13.  
    14.         static HashSet<UI_KeepUISelection> _activeCount = new();
    15.         public static UI_KeepUISelection activeSelector;
    16.         [SerializeField, ReadOnly] UI_KeepUISelection _debugActiveSelector;
    17.  
    18.         static List<Transform> _cache = new();
    19.  
    20.         [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
    21.         static void DomainReload()
    22.         {
    23.             _activeCount.Clear();
    24.             activeSelector = null;
    25.             _cache.Clear();
    26.         }
    27.      
    28.         void Update()
    29.         {
    30.             _debugActiveSelector = activeSelector;
    31.  
    32.             if (!_defaultSelection.gameObject.activeInHierarchy)
    33.             {
    34.                 if (activeSelector == this) activeSelector = null;
    35.                 _activeCount.Remove(this);
    36.                 return;
    37.             }
    38.             _activeCount.Add(this);
    39.  
    40.             if (activeSelector && !IsInFront(transform, activeSelector.transform))
    41.             {
    42.                 return;
    43.             }
    44.  
    45.             if (activeSelector != this)
    46.             {
    47.                 activeSelector = this;
    48.                 _defaultSelection.Select();
    49.             }
    50.  
    51.             GameObject selectedGO = EventSystem.current.currentSelectedGameObject;
    52.             if (!selectedGO ||
    53.                 !selectedGO.transform.IsAnyChildOf(_selectionGroupParent))
    54.             {
    55.                 _defaultSelection.Select();
    56.             }
    57.         }
    58.  
    59.         bool IsInFront(Transform a, Transform b)
    60.         {
    61.             Canvas canvasA = a.GetComponentInParent<Canvas>();
    62.             Canvas canvasB = b.GetComponentInParent<Canvas>();
    63.             if (!canvasA)
    64.             {
    65.                 Debug.LogError("No Canvas in Parent " + a, a);
    66.                 return false;
    67.             }
    68.             if (!canvasB)
    69.             {
    70.                 Debug.LogError("No Canvas in Parent " + b, b);
    71.                 return false;
    72.             }
    73.             if (canvasA != canvasB)
    74.             {
    75.                 if (canvasA.sortingOrder == canvasB.sortingOrder)
    76.                 {
    77.                     Debug.LogError($"Canvases with same sorting order {canvasA}, {canvasB}", a);
    78.                     return false;
    79.                 }
    80.                 else
    81.                 {
    82.                     if (canvasA.sortingOrder > canvasB.sortingOrder)
    83.                     {
    84.                         return true;
    85.                     }
    86.                     else
    87.                     {
    88.                         return false;
    89.                     }
    90.                 }
    91.             }
    92.             canvasA.GetComponentsInChildren(true, _cache);
    93.             _cache.Reverse();
    94.  
    95.             foreach (Transform ordered in _cache)
    96.             {
    97.                 if (ordered == a)
    98.                 {
    99.                     return true;
    100.                 }
    101.                 if (ordered == b)
    102.                 {
    103.                     return false;
    104.                 }
    105.             }
    106.  
    107.             Debug.LogError("Objects not found in List", a);
    108.  
    109.             return false;
    110.         }
    111.  
    112.  
    113.         private void Reset()
    114.         {
    115.             _selectionGroupParent = transform;
    116.             _defaultSelection = GetComponentInChildren<Selectable>(true);
    117.         }
    118.     }
    119. }
    120.  
    UI_SelectOnHover -> this script is attached to every single Button and will set the "selection" (which is used for keyboard-controls) on hover if it's part of the current active "UI_KeepUISelection"-Group. If you don't want to use a mouse at all, you probably don't need this. But when using Mouse and Keyboard combined, this is a common behaviour as seen in most (AAA-)Game-Menus.
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.EventSystems;
    3. using UnityEngine.UI;
    4.  
    5. namespace JL
    6. {
    7.     public class UI_SelectOnHover : MonoBehaviour, IPointerEnterHandler
    8.     {
    9.         [SerializeField] Selectable _selectable;
    10.  
    11.         private void Reset()
    12.         {
    13.             _selectable = GetComponentInChildren<Selectable>();
    14.         }
    15.  
    16.         public void OnPointerEnter(PointerEventData eventData)
    17.         {
    18.             if(GetComponentInParent<UI_KeepUISelection>() != UI_KeepUISelection.activeSelector)
    19.             {
    20.                 return;
    21.             }
    22.  
    23.             _selectable.Select();
    24.         }
    25.     }
    26. }
    27.  
    Sound Scripts, because you probably don't want your buttons to be silent:

    UI_SelectSound -> this script only exists once in the scene and will play a sound every time you switch the current selection (either by mouse or by keyboard controls). I outcommented the lines that actually play the sound, because they are using my AudioSystem and because you don't have that, you'd get compile errors. So if you really want to use it, you have to play the sound on your own using AudioSource or whatever AudioManager-Asset/Package you want.

    Code (CSharp):
    1. //using JL.AudioSystem;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using UnityEngine.EventSystems;
    6.  
    7. namespace JL
    8. {
    9.     public class UI_SelectSound : MonoBehaviour
    10.     {
    11.         //[SerializeField] SoundEffectReference _selectSound;
    12.  
    13.         GameObject _lastSelection;
    14.         UI_KeepUISelection _lastSelectionTop;
    15.  
    16.         void Update()
    17.         {
    18.             GameObject selection = EventSystem.current.currentSelectedGameObject;
    19.  
    20.             if (_lastSelection != selection)
    21.             {
    22.                 _lastSelection = selection;
    23.  
    24.                 if (!selection)
    25.                 {
    26.                     _lastSelectionTop = null;
    27.                     return;
    28.                 }
    29.  
    30.                 UI_KeepUISelection newTop = selection.GetComponentInParent<UI_KeepUISelection>();
    31.                 if (_lastSelectionTop != newTop)
    32.                 {
    33.                     _lastSelectionTop = newTop;
    34.                 }
    35.                 else
    36.                 {
    37.                     //_selectSound.Play(this);
    38.                 }
    39.             }
    40.         }
    41.     }
    42. }
    43.  
    UI_ButtonSound -> Attach this to every button to play a sound when it is clicked (wether with Mouse or Keyboard). And same as above, I outcommented the lines referring to my custom AudioSystem.

    Code (CSharp):
    1. //using JL.AudioSystem;
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4.  
    5. namespace JL
    6. {
    7.     public class UI_ButtonSound : MonoBehaviour
    8.     {
    9.         //[SerializeField] SoundEffectReference _sound;
    10.         private void Awake()
    11.         {
    12.             Button button = GetComponent<Button>();
    13.             button.onClick.AddListener(OnClicked);
    14.         }
    15.  
    16.         void OnClicked()
    17.         {
    18.             //_sound.Play(this);
    19.         }
    20.     }
    21. }
    22.  
    And that's the whole code to make the entire UI work with Keyboard Inputs, Controller and Mouse at the same time. Everything else is made with Unity default components or classes derived from them (actually I only did this for the Backpack Menu where pressing left/right will decrease/increase the resource amount in your inventory. It's the only UI-derived class I used).
     
    Last edited: May 5, 2023