[Unity] 不使用滑鼠來操作 UI (UI Navigation)

[Unity] 不使用滑鼠來操作 UI (UI Navigation)

[Unity] Control UI without the mouse (UI Navigation)


2022/11/30 03:26:26

在 Unity 中,如果要使用上下左右 (i.e. 鍵盤、搖桿、D-pad)控制 UI,就必須要先設定 UI Navigation。

手動啟用

先找到場景上的 Event System,設定好 First Select,也就是開啟場景時第一個被選擇的 UI 物件
(雖然這個欄位可以放所有 GameObject,但如果該 GameObject 不可以被選擇,視同沒有效果)

同時,Unity 預設的顏色都很白、不容易被察覺。這裡我們把「被選中」顏色設的明顯一點 (#39C5BB)。

使用 Navigation 設定該物件可以透過上下、左右或是我全都要來選中;
點選 Visulaization 可以看到 UI 互動的流程圖。

自動化腳本

如果懶得設定上面那些東西,我寫了個小腳本可以動態變更場景上的可選擇物件,取名叫 UiNavigationTweak 然後拉進場景裡就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.EventSystems;

public class UiNavigationTweak : MonoBehaviour
{
void Start()
{
Refresh();
}

public void Refresh()
{
var selectables = Transform.FindObjectsOfType<Selectable>();
var eventSystem = GameObject.FindObjectOfType<EventSystem>();
Color32 mikuColor = new Color32(0x39, 0xc5, 0xbb, 0xff);
int i = 0;
// mod all selected color
foreach(var selectable in selectables)
{
var c = selectable.colors;
c.selectedColor = mikuColor;
selectable.colors = c;
i++;
}
// evs
if(i > 0 && eventSystem)
{
eventSystem.SetSelectedGameObject(selectables[0].gameObject);
Debug.Log($"[UiNavigationTweak] Modded {i} objects.");
}
}
}

:::spoiler 如果專案會動態改變 UI,可以利用定期偵測這些變更把它套用

也可以由外部腳本呼叫 Refresh()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.EventSystems;

public class UiNavigationTweak : MonoBehaviour
{

/// <summary>
/// Start is called on the frame when a script is enabled just before
/// any of the Update methods is called the first time.
/// </summary>
IEnumerator Start()
{
while(true)
{
Refresh();
yield return new WaitForSeconds(1);
}
}

/// <summary>
/// Start is called on the frame when a script is enabled just before
/// any of the Update methods is called the first time.
/// </summary>
public void Refresh()
{
var selectables = Transform.FindObjectsOfType<Selectable>();
var eventSystem = GameObject.FindObjectOfType<EventSystem>();
Color32 mikuColor = new Color32(0x39, 0xc5, 0xbb, 0xff);
int i = 0;
// mod all selected color
foreach(var selectable in selectables)
{
var c = selectable.colors;
c.selectedColor = mikuColor;
selectable.colors = c;
i++;
}
// evs
if(i > 0 && eventSystem)
{
Debug.Log($"[UiNavigationTweak] Modded {i} objects.");
if(!eventSystem?.currentSelectedGameObject?.activeInHierarchy ?? true)
eventSystem.SetSelectedGameObject(selectables[0].gameObject);
}
}
}

Unity 寫了那麼久還頭一次碰到這樣的內容…