文章重點
本文用計數器 App 當第一個驗證專案,確認 Unity UI、Button 事件、C# 腳本與 Android 開發流程有沒有真的接通。專案夠小,問題才看得清楚。
到了這一步,我們真正要確認的,不是能不能做出一個漂亮的小 App,而是前面的 Android 環境、Unity UI、VS Code 和 C# 腳本到底有沒有真的接通。
前面兩篇其實已經先處理掉兩件更前面的事:Android 開發環境有沒有準備好,以及 Unity 和 VS Code 之間能不能正常連動。到了這一步,才適合開始做第一個真的會動的專案。
如果還沒看過這條路徑的總論,可以先讀 Unity Android 入門,先別急著做第一個 App。那篇先把題目判斷、工具鏈和驗證專案之間的關係整理清楚,本文則專心用一個夠小的功能把流程跑通。
本文的重點,是用一個夠小的功能去驗證前面的流程是不是已經接通,不是把計數器做得多漂亮。整篇會依照同一個順序往下走:先把最基本的按鈕放進場景,再把畫面整理到能看,接著把腳本掛回去,最後才讓它真的動起來。這樣做的好處是,每一步都能知道自己在驗證哪個環節,問題也不會全部混在一起。
建立基礎按鈕介面
我們先從最單純的互動開始,也就是一個能被點擊的按鈕。請在 Hierarchy 左上角的加號選單,依序選擇 UI > Button - TextMeshPro,把最基本的按鈕放進場景裡。
這一步最重要的,是確認建立的是帶有 TextMeshPro 文字的 Button,不只是把按鈕生出來。因為後面會直接拿它底下的文字來顯示計數,如果一開始建立錯類型,後面就得回頭補救。TextMeshPro 目前仍是 Unity 主要的文字方案,官方文件也把套件定位與必要資源整理在 TextMesh Pro。

如果先前沒有匯入 TextMeshPro 必要資源,Unity 會跳出 TMP Importer 提示。這裡請先把 Essentials 匯入,後面才能正常編輯按鈕上的文字。

匯入完成後,場景裡會自動多出 Canvas、Button 和 EventSystem 等 UI 相關物件。接下來先不要急著看按鈕好不好看,先把版面位置整理好。先把 Scene 視圖切成 2D,比較容易直接對著 Canvas 排版,再把按鈕收回畫面中央。

切到 2D 之後,再把 Button 本身的錨點和位置整理回中央。

位置定下來之後,再把尺寸調整成適合手機點擊的大小。本文先用 300 x 300 當作主按鈕的起點,目的是讓畫面中央先有一個清楚的互動核心。

這一段看起來像純排版,其實是在確認版面控制有沒有穩住。版面穩住之後,後面換背景、改文字和接腳本時,問題才不會一直回到最前面。
建立按鈕背景並整理文字顯示
按鈕放穩之後,先不要急著寫腳本,先把它整理成比較像計數器的畫面。本文會用一張簡單的圓形 Texture 當作按鈕背景,目的是讓畫面先長出一個容易辨識的主按鈕。
準備可供 UI 使用的圓形 Sprite
如果我們想直接在 Unity 裡整理 2D 圖片素材,可以先從 Package Manager 安裝 2D Sprite 相關套件,再建立一個專門放素材的資料夾。這不是每個專案都一定要做,但對剛開始的教學專案來說,先把素材放進清楚的位置,後面會順很多。

套件補齊之後,先在 Assets 底下建立一個 Textures 資料夾,讓按鈕背景素材有固定位置。

接著把要拿來當按鈕背景的圓形圖片匯入專案。這一步要做的是把素材整理成後面真的能掛到 UI Image 上的狀態,不只是把圖片拖進來。

素材匯入之後,請在 Inspector 裡確認匯入設定。Unity 匯進來的圖片預設不一定就是 UI 要用的格式,所以這裡要先把 Texture Type 改成 Sprite。


欄位改完之後,記得按 Apply,讓前面的設定真正生效。

把文字整理成計數器會用的狀態
設定完成之後,就可以把這張 Sprite 指定到 Button 的 Image 元件,讓按鈕主體從預設樣式換成圓形底圖。接著再選取 Button 底下的 Text (TMP) 子物件,把顯示文字改成 0,順手把字級、對齊和 Auto Size 一起整理好。

這一段看起來像是在修畫面,但其實也是在確認 UI 階層、尺寸與對齊方式都在可控的狀態。走到這裡,我們已經知道畫面中央會是一顆圓形按鈕,上面有一段準備拿來顯示數字的文字。後面掛腳本時,就比較能清楚知道自己到底是在控制哪個物件。
建立並掛載 CounterButton 腳本
畫面雛形整理到這裡,才適合開始準備腳本。這一段不急著寫功能本身,而是先把腳本的存在位置、掛載位置和編輯器同步都確認好。這幾步如果沒通,後面程式碼就算抄對了,也不會真的接到畫面上。
建立腳本檔案
先在 Scripts 目錄上按右鍵,選擇 Create > MonoBehaviour Script,建立 CounterButton 腳本。這一步的目的很單純,就是先把這顆按鈕未來要用的行為腳本準備好。

建立完成之後,先確認腳本真的有出現在專案裡。

這裡確認的是「專案裡已經有這份腳本」,還不是「按鈕已經會用這份腳本」。MonoBehaviour 本來就是透過掛在 GameObject 上運作,Unity 官方在 MonoBehaviour 也有把這個模型說清楚。
把腳本掛到 Button 物件
建立完腳本之後,回到 Button 物件本身,點擊 Add Component,把 CounterButton 掛載上去。這一步很重要,因為只有掛到物件上,後面寫的程式才會真的跟著這顆按鈕一起運作。

把元件加上去之後,再回頭確認 Inspector 裡真的出現 CounterButton。

這個差別很重要,因為「專案裡有腳本」和「物件上有掛腳本」是兩件事。走到這裡,腳本和畫面上的按鈕才真的連在一起。
確認 VS Code 專案同步
腳本已經掛回 Button 之後,最後還要回頭確認編輯器這一側是不是也同步正常。這一步不牽涉功能邏輯,但會直接決定後面寫程式時順不順手。

把 C# 專案打開,檢查 VS Code 能不能正確讀到 Unity 產生的解決方案和腳本檔。這一步看起來只是開檔案,但其實是在驗證前一篇 Unity Android 開發:讓 VS Code 和 Unity 正常連動 處理的那些事情,現在是不是都真的接好了。
實作點擊事件與計數邏輯
前面的準備做到這裡,程式碼本身其實就不複雜了。核心只有三件事:
- 取得 Button 元件
- 取得按鈕底下的文字元件
- 在點擊時更新計數值
本文會用一段一段補上的方式來做,而不是一開始就直接貼完整版本。這樣每補一段,我們都知道自己現在是在驗證哪個環節。
先取得 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}
這時候還沒有計數功能,只是先把按鈕物件握在手上。這一步確認的是:腳本已經能碰得到場景裡這顆 Button。
再取得按鈕底下的文字
Button 能抓到了之後,下一段才把按鈕底下的文字元件也一起抓進來,因為後面每次加一都要改到這段文字。
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>(),本質上就是從目前物件往子物件往下找符合型別的 Component。Unity 也在 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}
到這裡,互動流程就已經開始成形了,因為按鈕每次被按下時,都會呼叫 OnClick()。

接著直接用快速修正把方法骨架補出來,先讓事件路徑完整。

加入計數值並更新畫面
事件路徑接通之後,下一段才加入真正會變動的資料,也就是計數值本身。
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}
最後把 _countText.text 更新上去,按鈕上的數字就會跟著每次點擊往上加。這時候這個專案才算真的完成了「畫面會跟互動一起變化」這件事,也就是本文一開始要驗證的核心。

這個結果看起來很小,但它其實同時證明了幾件事:按鈕點擊有觸發、腳本有被執行、文字有被正確抓到,而且畫面更新也有真的發生。前面分開處理的那些步驟,到這裡才算第一次在同一個功能上全部接起來。
驗證結果與下一步
走到這裡,計數器本身其實不是重點。真正有價值的是,我們已經用它驗證了幾件很基礎,但很重要的事情:
- UI 能正常建立
- Button 事件能正確觸發
- C# 腳本能和畫面互動
- VS Code 寫好的程式碼能被 Unity 正常套用
如果這些都成立,就代表前面幾篇文章處理的環境、編輯器連動和最基本的互動流程,大致上都已經接上了。這也是這個小專案真正的價值:它是在證明前面的整條開發路徑沒有斷掉,不是在證明我們能做出一個計數器。
接下來如果要繼續往下做,最自然的方向就是把這個專案真的 Build 成 Android App,或者在它上面再加更完整的互動,例如重置按鈕、資料保存、不同版面配置,甚至更多 UI 狀態切換。這時候再擴充,判斷會比一開始就直接做完整 App 清楚很多。
如果想回頭對照本文在整條路徑裡扮演什麼角色,可以再看一次 Unity Android 入門,先別急著做第一個 App。如果前面的環境還沒完全處理好,回頭補 Unity Android 開發環境起手式:先把 Editor、SDK、NDK、JDK 準備好 和 Unity Android 開發:讓 VS Code 和 Unity 正常連動 會更順。