快速摘要
本文用一個夠小的計數器 App,把 Unity UI、Button 事件、C# 腳本與 VS Code 連動一次接起來。題目小,問題才藏不住。
第一個 Unity Android 專案,題目最好小一點。計數器 App 的價值,不在於功能多完整,而是它剛好能把 UI、Button 事件、C# 腳本和編輯器同步一次接起來,功能又小到問題不容易被藏住。
如果前面的環境建置和 VS Code 連動還沒穩定,先回頭補 Unity Android 開發:讓 VS Code 和 Unity 正常連動。這篇假設那些前提都已經通了,現在要做的是把第一個真的會動的畫面做出來。
先把按鈕放進場景中央
請在 Hierarchy 左上角的加號選單中,依序選擇 UI > Button - TextMeshPro,先建立一顆最基本的按鈕。
這裡要留意的是型別。本文後面會直接更新按鈕底下的文字,所以一開始就用帶 TextMeshPro 的按鈕比較順。Unity 目前仍把 TextMesh Pro 當成主要文字方案,請參考:TextMesh Pro。

如果專案第一次碰到 TextMesh Pro,Unity 會跳出 TMP Importer。先把 Essentials 匯入,後面的文字元件才會正常工作。

建立完成後,場景裡通常會一起出現 Canvas、Button 和 EventSystem。先不要急著調樣式,先把畫面位置整理乾淨。把 Scene 視圖切到 2D,比較容易對著 Canvas 排版,再把 Button 的錨點和位置拉回中央。


接著把尺寸改成適合手機點擊的大小。本文先用 300 x 300,目的很單純,就是讓畫面中央先有一個清楚的互動核心。

把按鈕整理成計數器的樣子
畫面位置定下來之後,再來處理外觀。本文會用一張圓形圖片當按鈕背景,讓中央那顆按鈕更容易辨識。
先在 Assets 底下建立一個 Textures 資料夾,把要用的圓形圖片放進來。


圖片匯入之後,記得先把 Texture Type 改成 Sprite,按下 Apply。Unity 預設匯入的圖片不一定能直接拿來當 UI 圖像,這裡先調整,後面指定給 Button 的 Image 元件才會順。



設定完成後,把這張 Sprite 指定到 Button 的 Image 元件,再選取 Button 底下的 Text (TMP) 子物件,把文字改成 0,順手把字級與置中也一起整理好。

做到這裡,畫面應該已經很明確了:中央有一顆圓形按鈕,上面有一個初始值為 0 的數字。接下來才輪到腳本。
建立並掛上 CounterButton 腳本
先在 Scripts 資料夾裡建立一個新的 MonoBehaviour Script,命名為 CounterButton。


建立完檔案後,回到 Button 物件本身,點擊 Add Component,把 CounterButton 掛上去。MonoBehaviour 的運作前提,本來就是元件要掛在 GameObject 上,請參考:MonoBehaviour。


這裡有個常見落差:專案裡看得到腳本,和畫面上的物件真的有用到這份腳本,是兩件不同的事。Inspector 裡看到 CounterButton 出現,才代表這條線有接上。
最後再用 VS Code 把這個腳本打開,確認 Unity 產生的 C# 專案能正常同步進編輯器。

讓按鈕點下去真的會加一
程式碼本身不複雜。本文會分幾小段補上,這樣每次出錯時,比較知道卡在哪一層。
先抓到 Button 元件
第一段只做一件事:取得目前這顆按鈕身上的 Button 元件。
1using UnityEngine;
2using UnityEngine.UI;
3
4public class CounterButton : MonoBehaviour
5{
6 private Button _button;
7
8 private void Start()
9 {
10 _button = GetComponent<Button>();
11 }
12}
這時候按鈕還不會計數,但腳本已經能碰到同一個 GameObject 上的 UI 元件了。
再抓到底下的文字元件
接著把顯示數字的文字也抓進來,後面每次點擊時都會改到它。
1using TMPro;
2using UnityEngine;
3using UnityEngine.UI;
4
5public class CounterButton : MonoBehaviour
6{
7 private Button _button;
8 private TMP_Text _countText;
9
10 private void Start()
11 {
12 _button = GetComponent<Button>();
13 _countText = _button.GetComponentInChildren<TMP_Text>();
14 }
15}
這裡用到的 GetComponentInChildren<T>(),就是從目前物件往子物件往下找符合型別的元件。請參考:GetComponentInChildren。
把點擊事件接起來
按鈕和文字都抓到之後,再把點擊事件綁上去。
1using TMPro;
2using UnityEngine;
3using UnityEngine.UI;
4
5public class CounterButton : MonoBehaviour
6{
7 private Button _button;
8 private TMP_Text _countText;
9
10 private void Start()
11 {
12 _button = GetComponent<Button>();
13 _countText = _button.GetComponentInChildren<TMP_Text>();
14 _button.onClick.AddListener(OnClick);
15 }
16}
按鈕被按下時,Unity 會呼叫 OnClick()。如果 VS Code 提示方法還不存在,可以直接用快速修正先補出骨架。


加入計數值並更新畫面
最後補上實際的計數值。
1using TMPro;
2using UnityEngine;
3using UnityEngine.UI;
4
5public class CounterButton : MonoBehaviour
6{
7 private Button _button;
8 private TMP_Text _countText;
9 private int _count;
10
11 private void Start()
12 {
13 _button = GetComponent<Button>();
14 _countText = _button.GetComponentInChildren<TMP_Text>();
15 _button.onClick.AddListener(OnClick);
16 _count = 0;
17 }
18
19 private void OnClick()
20 {
21 _count += 1;
22 _countText.text = _count.ToString();
23 }
24}
現在按一下,數字就會往上加一。畫面有變化,代表事件有進來、腳本有執行、文字也有成功更新。

總結
計數器本身很小,但它剛好足夠把第一條 Unity Android 開發流程跑完一次。從 UI 建立、按鈕事件、腳本掛載,到 VS Code 編輯後回到 Unity 重新編譯,這幾條線只要有一條斷掉,這個小專案就不會正常動起來。
也因為題目夠小,我們比較容易看清楚問題在哪裡。等這個最小驗證專案穩了,再往 APK 輸出、資料保存、重置按鈕或更多 UI 狀態擴充,節奏會順很多。