程式碼樣式方針

在參與貢獻 Godot 原始碼時需要遵守下述樣式方針。其中的一些規則會在 CI (持續整合, Continuos Integration) 過程中自動檢查,而審閱者也會要求你修正一些潛在的問題。因此,最好先依照下屬方式設定系統,並確保你的 Commit 都有符合本方針。

C++ 與 Objective-C

C++ 與 Objective-C 沒有明文的方針,但我們有由各個開發者認定的樣式,且會依照 clang-format 程式碼美化工具強制套用。該工具會處理我們所有的管理。下面舉例幾個:

  • 縮排與對齊都是使用 Tab 字元 (分別使用 1 個與 2 個 Tab)

  • 在數學與賦值運算子的周圍以及逗號後加上 1 個空格

  • 指標與參照運算子要加到變數識別項前,而不是型別名稱前

  • 有關標頭檔引用,請繼續閱讀以瞭解詳情

clang-format 所使用的規則列在 Godot 儲存庫中的 .clang-format 檔案內。

基本上只要確保你寫的程式碼與周圍程式碼的樣式一致、並且行尾沒有多餘的空格、且不使用空格來縮排就沒什麼問題。如果未來還有打算持續參與貢獻的話,我們強烈建議在本機開發環境上安裝 clang-format 以自動檢查並修正所有的 Commit。

警告

Godot 的程式碼編寫樣式 不應 用於第三方程式碼,即 Godot 原始碼中包含但不是特別為 Godot 專案撰寫的程式碼。這些程式通常來自不同的上游專案,有 (或沒有) 各自的樣式指南,且不應套用 Godot 的樣式以避免造成與上游的不同並難以更新。

第三方程式碼通常放在 thirdparty/ 資料夾內,因此可以輕鬆地在格式化腳本中排除掉。但有些稀有的例子,我們可能會直接在 Godot 的檔案中使用第三方程式碼片端,這時候可以使用 /* clang-format off *//* clang-format on */ 來讓 clang-format 忽略一段程式碼。

在本機上使用 clang-format

首先,必須要安裝 clang-format。目前,必須要使用 clang-format 8.x 以相容 Godot 的格式。更新版本可能適用,但較舊版本有 Bug 且會使目前程式碼的格式跑掉。

安裝

下列為如何安裝 clang-format:

  • Linux:通常直接於發行版的 clang 工具鏈中附帶提供。若你使用的發行版提供了不合需求的版本,可以從 LLVM 網站 上下載預先編譯好的版本。而若使用 Debian 衍生版本,則可以使用這個 上游軟體倉

  • macOS 與 Windows:可以從 LLVM 網站 下載預先編譯好的二進位檔。可能會需要將二進位檔的資料夾加到系統的 PATH 環境路徑中以便直接呼叫 clang-format

要將 clang-format 套用到修改中有幾個方法:

手動使用

可以通過下列指令手動將 clang-format 套用至一個或多個檔案:

clang-format -i <path/to/file(s)>
  • -i 代表改動應直接寫入到檔案內 (clang-format 預設只會將修正後的版本輸出到終端機上)。

  • 該路徑可指向多個檔案,可以直接指定多個檔案,或是像一般 Unix Shell 一樣使用萬用字元。在使用萬用字元時請注意不要將 clang-format 套用到 Godot 資料夾內編譯後的物件上 (.o 與 .a 檔案)。因此最好使用 core/*.{cpp,h} 而不是 core/*

Pre-commit Hook

為了更容易使用,我們提供了用於 Git 的 Pre-commit Hook,可以自動在所有 Commit 上執行 clang-format 並進行檢查,然後能將其更改套用到最終 Commit 上。

這個「Hook」是放在 misc/hooks 中的腳本,請參考該資料夾的 README.md 以瞭解詳細的安裝步驟。

如果 clang-format 不在 PATH 中的話,則可能需要編輯 pre-commit-clang-format 來指向正確的二進位檔才能使用。該 Hook 已在 Linux 與 macOS 上測試過,但在 Windows 上的 Git Shell 應該也可以使用。

IDE 外掛

大多數 IDE 或程式碼編輯器都有可以設定自動執行 clang-format 的美化插件,如可以在保存檔案時自動執行。

此處僅列出部分用於一些 IDE 的美化外掛:

(歡迎建立 PR 以擴充此列表來包含更多經過測試的外掛。)

標頭引用

建立新的 C++ 或 Objective-C 檔案或在現有檔案內引用新標頭檔時,必須遵守下列規則:

  • 檔案的頭幾行必須為 Godot 的版權標頭與 MIT 授權協議,請從其他檔案內複製過來。請記得更改檔案名稱。

  • .h 標頭檔內,Include 防護 (Include Guard) 應為 FILENAME_H 的格式。

  • .cpp 檔案 (如 filename.cpp) 中,第一個 Include 應為類別定義的檔案 (如 #include "filename.h"),並接上一行空行以作分隔。

  • 接下來的是 Godot 程式碼的其他標頭檔,按字母順序引入 (會由 clang-format 強制執行),並使用相對於根目錄的路徑。這些 Include 應以引號包含,如 #include "core/object.h" 。Godot 標頭檔 Include 區塊後方應包含一行空行以作分隔。

  • 最後接著是第三方標頭檔 (也就是從 thirdparty 或系統 Include 路徑內來的),應以 < 與 > 符號來 Include,如 #include <png.h> 。第三方標頭檔區塊後也應加上一行空行以作區隔。

  • Godot 標頭檔與第三方標頭檔都應在需要這些標頭檔的檔案內 Include,也就是當使用宣告式程式碼時應在 .h 標頭內,只有使用命令式程式碼時才應在 .cpp 內引入。

範例:

/*************************************************************************/
/*  my_new_file.h                                                        */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#ifndef MY_NEW_FILE_H
#define MY_NEW_FILE_H

#include "core/hash_map.h"
#include "core/list.h"
#include "scene/gui/control.h

#include <png.h>

...

#endif // MY_NEW_FILE_H
/*************************************************************************/
/*  my_new_file.cpp                                                      */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "my_new_file.h"

#include "core/math/math_funcs.h"
#include "scene/gui/line_edit.h

#include <zlib.h>
#include <zstd.h>

Java

Godot 的 Java 程式碼 (大部分都在 platform/android 內) 也會強制由 clang-format 套用,因此請參考上方的說明設定 clang-format。請注意,樣式指南僅套用至由 Godot 撰寫且由 Godot 維護的程式碼,如 java/src/com/google 子資料夾內的第三方程式碼則不應套用。

Python

Godot 的 SCons 建置系統是以 Python 撰寫的,而程式碼中還有多個腳本也是使用 Python。

這些檔案,我們遵守 Black style guide 。請使用 Black 來黑化你的 Python 程式碼。

在本機上使用 Black

首先,必須要先安裝 Black。要執行 Black 需要至少 Python 3.6.0+。

安裝

安裝 Black:

pip3 install black --user

接著有幾種方式可以將 Black 套用至修改上:

手動使用

可以使用下列指令手動套用 black 至一個或多個檔案:

black -l 120 <path/to/file(s)>
  • -l 120 表示每行最多允許 120 個字元。這個數字是由多位開發人員公認的。

  • 路徑可以指向多個檔案,可以直接寫出多個檔案,或是使用如一般 Unix Shell 的萬用字元。

Pre-commit Hook

為了簡化使用,我們也提供了用於 Git 的 Pre-commit Hook,會在所有 Commit 上自動執行 Black,並允許將這些修改套用至最終的 Commit 上。

該「hook」是放在 misc/hooks 內的一個腳本檔。請參考該資料夾中的 README.md 以瞭解安裝說明。

編輯器整合

許多 IDE 與程式碼編輯器都有可設定自動執行 Black 的美化外掛,如可在每次保存檔案時自動執行。詳細資訊可參考 Black editor integration 一頁。

Comment style guide

This comment style guide applies to all programming languages used within Godot's codebase.

  • Begin comments with a space character to distinguish them from disabled code.

  • Use sentence case for comments. Begin comments with an uppercase character and always end them with a period.

  • Reference variable/function names and values using backticks.

  • Wrap comments to ~100 characters.

  • You can use TODO:, FIXME:, NOTE:, or HACK: as adominitions when needed.

Example:

// Compute the first 10,000 decimals of Pi.
// FIXME: Don't crash when computing the 1,337th decimal due to `increment`
//        being negative.

Don't repeat what the code says in a comment. Explain the why rather than how.

Bad:

// Draw loading screen.
draw_load_screen();

You can use Javadoc-style comments above function or macro definitions. It's recommended to use Javadoc-style comments only for methods which are not exposed to scripting. This is because exposed methods should be documented in the class reference XML instead.

Example:

/**
 * Returns the number of nodes in the universe.
 * This can potentially be a very large number, hence the 64-bit return type.
 */
uint64_t Universe::get_node_count() {
    // ...
}

For member variables, don't use Javadoc-style comments but use single-line comments instead:

class Universe {
    // The cached number of nodes in the universe.
    // This value may not always be up-to-date with the current number of nodes
    // in the universe.
    uint64_t node_count_cached = 0;
};