# Coding Style Guide ## 1. 專案結構 (Project Structure) * **資料夾分層** ``` Assets ├─ 00_Scenes // 放各種場景 ├─ 01_Images // 圖片素材(應根據場景劃分) ├─ 02_Animations // 自製動畫(Animator & Animation) ├─ 03_Audios // 音效 ├─ 04_Fonts // 字型 ├─ xx_SpineAnimations // Spine 動畫檔案 ├─ xx_Models // 3D model ├─ 98_Prefabs ├─ 99_Settings ├─ Plugins ├─ Resources ├─ Scripts ├─ Core // 基礎系統 (Manager、Service、Utility) ├─ Gameplay // 遊戲邏輯 ├─ ... ``` * **一個檔案一個類別 (One File One Class)** 每個 `.cs` 檔案只包含一個 public class,檔名必須與類別名稱相同。 --- ## 2. 命名規範 (Naming Conventions) ### 2.1 類別與結構 * **PascalCase** ```csharp public class PlayerController {} public struct DamageInfo {} ``` ### 2.2 方法與屬性 * **PascalCase** ```csharp public void TakeDamage(int amount) {} public int Health { get; private set; } // private get/set 也是採用 PascalCase ``` ### 2.3 變數 * **私有欄位 (Private Field)** → `_camelCase` * **公有欄位 (Public Field)** → `PascalCase` (盡量少用,除非要在 Inspector 暴露) * **區域變數與參數** → `camelCase` * **常數** → `ALL_CAPS_WITH_UNDERSCORE` ```csharp public class Player : MonoBehaviour { [SerializeField] private float _moveSpeed = 5f; private int _health; public const int MAX_HEALTH = 100; public void Move(Vector3 direction) { float speed = _moveSpeed * Time.deltaTime; transform.Translate(direction * speed); } } ``` ### 2.4 事件 * **過去式動詞**,使用 `Action` 或 `UnityEvent` * 若使用 `Action` 建議前面加上 `event` 以確保安全性 ```csharp public event Action OnDead; public event Action<int> OnScoreChanged; ``` --- ## 3. 程式碼格式 (Code Formatting) ### 3.1 縮排與括號 * 使用 **4 空格縮排 (Space,不用 Tab)** * **大括號換行** ```csharp if (condition) { DoSomething(); } else { DoOtherThing(); } ``` ### 3.2 每行長度 * 建議 **每行 ≤ 120 字元** ### 3.3 空白 * 運算子兩邊加空白 ```csharp int damage = attack - defense; ``` --- ## 4. Unity 特有規範 ### 4.1 MonoBehaviour * 避免直接使用 `public` 欄位,改用 `[SerializeField] private` + 屬性 (property) * Unity 生命週期函式依序排列: ```csharp // Unity LifeCycle private void Awake() {} private void OnEnable() {} private void Start() {} private void Update() {} private void FixedUpdate() {} private void LateUpdate() {} private void OnDisable() {} private void OnDestroy() {} ``` ### 4.2 Update 避免過度使用 * 若可用 **Event** 或 **Coroutine** 取代,避免每幀輪詢 ```csharp // Bad private void Update() { if (Input.GetKeyDown(KeyCode.Space)) Jump(); } // Good private void OnEnable() { InputManager.OnJumpPressed += Jump; } ``` ### 4.3 GetComponent * `Awake` / `Start` 中快取 `GetComponent`,不要在 `Update` 中反覆呼叫 ```csharp private Rigidbody _rigidbody; private void Awake() { _rigidbody = GetComponent<Rigidbody>(); } ``` ### 4.4 Magic Number * 避免寫死數值,使用 **常數/ScriptableObject/Inspector 參數** ```csharp // Bad transform.Translate(Vector3.forward * 0.016f); // Good [SerializeField] private float _moveSpeed = 5f; transform.Translate(Vector3.forward * _moveSpeed * Time.deltaTime); ``` --- ## 5. 註解規範 (Comment Style) * 使用 **`///` XML Doc** 產生 API 文件 ```csharp /// <summary> /// 玩家控制器,處理移動與攻擊。 /// </summary> public class PlayerController : MonoBehaviour {} ``` * 方法內僅在必要時用 `//` 短註解 ```csharp // Apply knockback force _rigidbody.AddForce(force, ForceMode.Impulse); ``` --- ## 6. 編碼最佳實踐 (Best Practices) * **盡量在未規劃的情況下少用 static** (避免難以測試與管理狀態) * **避免在 OnDestroy 中呼叫其他物件的方法** (可能已被銷毀) * **使用 ScriptableObject 作為設定檔** (避免硬編碼) * **物件池 (Object Pooling)** 取代 `Instantiate/Destroy` 頻繁操作 * **Controller** 應該為單一模組的控制中心 * **Manager** 應該是能夠跨Controller的中樞管理(比如音效, 跨Controller的參數控制) --- ## 7. 範例程式碼 (整合規範) ```csharp using UnityEngine; /// <summary> /// 控制玩家移動與跳躍 /// </summary> public class PlayerController : MonoBehaviour { [SerializeField] private float _moveSpeed = 5f; [SerializeField] private float _jumpForce = 5f; private Rigidbody _rigidbody; public event Action OnJumped; private void Awake() { _rigidbody = GetComponent<Rigidbody>(); } private void Update() { HandleMovement(); if (Input.GetKeyDown(KeyCode.Space)) { Jump(); } } private void HandleMovement() { float h = Input.GetAxis("Horizontal"); Vector3 move = new Vector3(h, 0, 0); transform.Translate(move * _moveSpeed * Time.deltaTime); } private void Jump() { _rigidbody.AddForce(Vector3.up * _jumpForce, ForceMode.Impulse); OnJumped?.Invoke(); } } ``` --- ## 8. 場景物件命名規則 * Label -> Lbl_XX * Button -> Btn_XX * Image -> Img_XX * Toggle -> Tgl_XX * Slider -> Sld_XX * Spine -> Sp_XX * Scroll View -> SV_XX * Input Field -> IF_XX * DropDown -> DD_XX * Mask -> Mask_XX or Mask ## 9. Git ### Commit格式 ``` type: title content ``` e.g. ``` fix: 自訂表單新增/編輯頁面,修正離開頁面提醒邏輯 問題: 1. 原程式碼進入新增頁面後,沒做任何動作之下,離開頁面會跳提醒 2. 原程式碼從新增/編輯頁面回到上一頁後(表單列表頁面),離開頁面會跳提醒 原因: 1. 新增頁面時,頁面自動建立空白題組會調用 sort_item,造成初始化 unload 事件處理器。 2. 回到上一頁後,就不需要監聽 unload 事件,應該把 unload 事件取消。 ``` #### type feat: 新增/修改功能 (feature)。 fix: 修補 bug (bug fix)。 docs: 文件 (documentation)。 style: 格式 (不影響程式碼運行的變動 white-space, formatting, missing semi colons, etc)。 refactor: 重構 (既不是新增功能,也不是修補 bug 的程式碼變動)。 perf: 改善效能 (A code change that improves performance)。 test: 增加測試 (when adding missing tests)。 chore: 建構程序或輔助工具的變動 (maintain)。 revert: 撤銷回覆先前的 commit 例如:revert: type(scope): subject (回覆版本:xxxx)。