Godot 近年來已成為最受歡迎的遊戲引擎之一。它免費、採用 MIT 授權開源,且體積小巧,幾分鐘內即可下載並開始使用。大多數 Godot 遊戲都使用引擎自有的腳本語言 GDScript 編寫。

然而,許多專案遲早會需要現有的 C 或 C++ 函式庫,例如模擬函式庫、資料庫、網路協定或機器學習運行時。GDScript 無法直接呼叫原生程式碼,但 Godot 可以透過 GDExtension 加載它,而官方的 C++ 綁定 godot-cpp 則能讓您將該程式碼公開為一般的引擎類別。編寫 C++ 程式碼相對容易,但建置才是難點:godot-cpp 必須與您的 Godot 版本相符,且您新增的每個函式庫都必須為您要發行的每個平台進行編譯。

本文將簡要介紹 Godot,解釋 C++ 擴充功能如何運作,並展示如何使用 Conan 和 godot-cpp 10 將 C++ 函式庫整合到 Godot 遊戲中,godot-cpp 10 現已在 ConanCenter 上提供。我們將以 flecs(一個實體元件系統函式庫)為例,模擬 Godot 場景中 10 萬個粒子。

Godot 是一款用於 2D 和 3D 遊戲的通用引擎。Godot 專案中的一切都基於兩個概念建構:

行為通常透過將腳本附加到節點來實現。GDScript 是一種類似 Python 的語言,專為引擎設計,非常適合遊戲邏輯,因為變更會立即顯示,無需編譯步驟。

Godot 對 C++ 開發者有吸引力之處在於,引擎本身是用 C++ 編寫的,並且可以在不重新編譯的情況下加載用 C++ 編寫的擴充功能。來自這些擴充功能的一個類別會成為一般的引擎類別:它會與內建節點一起出現在編輯器中,其屬性顯示在檢查器中,並且 GDScript 可以像使用其他節點一樣使用它。下一節將解釋這些擴充功能如何運作。

有兩種方法可以為 Godot 添加 C++ 程式碼:

GDExtension 是大多數專案推薦的方法,許多熱門外掛程式都是透過此方式發行。由於 C 介面使用起來較為冗長,Godot 團隊維護著 godot-cpp,這是一個 C++ 函式庫,它使用與引擎內部使用的 API 非常接近的 API 來封裝 C 介面。它為每個引擎類別(如 Node2D、Sprite2D 或 Input)提供一個 C++ 類別。

您自己的類別是繼承自這些類別的一般 C++ 程式碼。使用 godot-cpp 編寫的節點看起來像這樣:

從 10.0 版本開始,單一 godot-cpp 版本適用於 4.3 及以上的所有 Godot 版本。您透過 api_version 建置選項選擇一個版本,godot-cpp 會根據該版本的 API 從 API 生成其 C++ 類別。為 Godot 4.3 建置的擴充功能在新版本中也能運作,但無法在舊版本中運作,因此您通常會選擇您想支援的最舊 Godot 版本。

在建置任何東西之前,還需要了解一個概念。godot-cpp 會針對三個目標之一進行編譯,這些目標以載入該函式庫的 Godot 建置命名:

Godot 在執行階段透過一個小的 .gdextension 檔案決定載入哪個函式庫。它將功能標籤映射到函式庫路徑。debug 標籤對應編輯器和 debug 導出版本,release 標籤對應 release 導出版本:

Godot 文件建議將 godot-cpp 作為 git submodule 添加到您的儲存庫中,並使用 SCons 與您的函式庫一起建置。這對於第一個擴充功能來說效果很好,但每個專案最終都會為每個目標、平台和架構編譯自己的 godot-cpp,並且您包裝的任何第三方函式庫(如物理引擎或機器學習運行時)都必須為 Godot 導出的每個平台進行供應商化並使用相符的標誌進行建置。

這兩種情況正是 Conan 的設計初衷。

透過 ConanCenter 中的 godot-cpp 配方,godot-cpp 成為一個常規套件。前面討論的兩個參數是 Conan 選項:

每個組合只建置一次,然後由每個需要的專案重複使用,而不是在每個擴充功能內部進行編譯。

您的 GDExtension 變成另一個具有依賴項的 C++ 專案。ConanCenter 中的任何一個(超過 1,900 個函式庫)或您自己用 Conan 配方打包的函式庫,都可以與 godot-cpp 並排放置,Conan 會為您目標的每個平台一致地建置所有這些函式庫。

為了展示實際運作方式,我們將編寫一個 GDExtension,它會註冊一個新的 Swarm 節點。它模擬 10 萬個粒子,這些粒子會逃離滑鼠游標並從視窗邊緣反彈,並在 Godot 場景中繪製所有粒子。

模擬運行在 flecs 上,這是一個用於 C 和 C++ 的實體元件系統 (ECS) 函式庫。在 ECS 中,實體是純粹的 ID,元件是附加到它們上面的純數據結構,系統是針對擁有特定元件集的每個實體執行的函數。相同類型的元件在記憶體中一起儲存,這使得遍歷大量實體非常快速。這就是為什麼 ECS 是模擬、人群或彈幕遊戲的熱門選擇。這也是原生程式碼發揮作用的類型,因為與 GDScript 相比,在 C++ 中更新這麼多實體每一幀都快得多。

您可以在 Conan examples2 儲存庫中找到完整的範例:

src 資料夾包含擴充功能程式碼,demo 是一個載入它的常規 Godot 專案。

conanfile.py 從 ConanCenter 要求 godot-cpp 和 flecs:

唯一特定於 Godot 的細節在 generate() 中。我們讀取 godot-cpp 依賴項的 target 選項並將其傳遞給 CMake,因此函式庫的名稱始終與其連結的 godot-cpp 二進位檔相符。

這是一個完全標準的 CMake 專案。該擴充功能是一個共享函式庫,它靜態連結 godot-cpp 和 flecs,因此只有一個函式庫檔案需要發行。我們將其直接寫入 demo/bin,以便 Godot 在無需額外複製步驟的情況下找到它。

Swarm 類別繼承自 Node2D 並擁有 flecs world。每個粒子的元件是純粹的結構。GDCLASS 宏添加了 Godot 類別系統所需的樣板程式碼,而 _bind_methods() 聲明了 Godot 可以看到的內容,在這裡是 count 和 flee_radius 屬性。一旦類別被註冊,它們就會出現在檢查器中,並可以從 GDScript 使用。這是該類別的簡化視圖:

節點的其餘部分連接了兩個世界。_ready() 為每個粒子創建一個 flecs 實體,並創建一個 flecs 系統來更新它們。它還設置了一個 MultiMesh,它在單一繪圖呼叫中繪製相同網格的許多實例,因為每個粒子一個 Godot 節點對於 10 萬個粒子來說太重了。

每一幀,_process() 將滑鼠位置傳遞給 flecs,使用 world.progress() 運行系統,並將結果位置複製回 MultiMesh。同樣,這是一個簡化視圖,完整程式碼位於儲存庫中:

最後,register_types.cpp 在 Godot 加載函式庫時註冊該類別:

我們使用 GDREGISTER_RUNTIME_CLASS 註冊 Swarm。預設情況下,GDExtension 類別的程式碼也會在編輯器中運行,因此 _ready() 和 _process() 會在您編輯場景時啟動模擬。runtime class 在編輯器中只是一個佔位符:您可以將其添加到場景並設置其屬性,但其程式碼僅在遊戲運行時執行。

同一個檔案定義了 gdexample_library_init(),這是 .gdextension 檔案中命名的進入點。它是一些樣板程式碼,在每個擴充功能中看起來都一樣。

一切就緒後,建置擴充功能只需一個命令:

Conan 解析 godot-cpp 和 flecs,在您的配置存在預先建置的二進位檔時從 ConanCenter 下載它們,從原始碼建置其餘部分,生成 CMake 整合,最後建置擴充功能。

注意:godot-cpp 需要 C++17。如果您的預設設定檔使用較舊的標準(MSVC 就是如此),請在命令中添加 -s compiler.cppstd=17。

現在啟動 Godot 4.7,在專案管理器中點擊「匯入」,然後選擇 demo/project.godot。專案打開後,Godot 會讀取 bin/gdexample.gdextension,載入函式庫,Swarm 就會像任何內建節點一樣可用。您可以在「建立新節點」對話方塊中找到它,位於 Node2D 下方:

Demo 的主場景已經包含一個 Swarm 節點。選取它會在檢查器中顯示我們在 _bind_methods() 中綁定的兩個屬性 count 和 flee_radius:

按下播放來運行場景,然後將滑鼠移到視窗上方來推動粒子。然後停止它,在檢查器中更改 count 或 flee_radius,然後再次播放,看看群體在更多粒子或更寬的逃逸半徑下的行為。

GDExtension 和 godot-cpp 讓您能夠用 C++ 編寫引擎類別,而 Conan 則負責建置 godot-cpp 和您的擴充功能所需的任何其他 C++ 函式庫。這在您發行擴充功能時也是一個巨大的優勢:為您要發行的每個平台建置它只需要更改建置的設定。

Linux 注意事項:預設情況下,擴充功能會動態連結 libstdc++,因此目標系統必須提供至少與建置時使用的版本一樣新的版本。為了廣泛的相容性,請針對與您打算支援的最舊發行版相容的工具鏈和系統函式庫來建置擴充功能及其依賴項。或者,靜態連結 libstdc++ 並使用連結器版本腳本隱藏其符號。

嘗試完整的範例並查看 godot-cpp 文件以進一步了解編寫擴充功能。如果您有任何意見回饋或遇到任何問題,請在 Conan GitHub 儲存庫中告知我們。

本文由 AI 輔助撰寫並由人工審閱。

在 Godot 中使用任何 C++ 函式庫在 Godot 中使用任何 C++ 函式庫在 Godot 中使用任何 C++ 函式庫在 Godot 中使用任何 C++ 函式庫在 Godot 中使用任何 C++ 函式庫在 Godot 中使用任何 C++ 函式庫