Godot Android 外掛

前言

Android 外掛是強大工具,可以透過存取 Android 平台與生態系統所提供的功能,來擴展 Godot 引擎的能力。

舉例來說,在 Godot 4 中,Android 外掛被用來支援多種基於 Android 的 XR 平台,且無需將特定廠商的程式碼或二進位檔案混入核心程式碼庫。

Android 外掛

Android 外掛系統的 第一版 (v1) 最早於 Godot 3 推出,並相容於 Godot 4.0 和 4.1。這個版本允許開發者用 Java、Kotlin 及原生功能來擴充 Godot 引擎。

從 Godot 4.2 起,以 v1 架構建立的 Android 外掛已經被棄用。Godot 4.2 引入了全新的 第二版 (v2) Android 外掛架構。

v2 架構

備註

Godot Android 外掛採用 Gradle 建構系統

延續 v1 架構,Android 外掛依然基於 Android 歸檔庫 (AAR 檔案)。

根本上,Godot Android 外掛 v2 是一個依賴 Godot Android 函式庫 的 Android 函式庫,並且包含自訂的 Android 函式庫資訊清單(manifest)。

這種架構讓 Android 外掛可以用下列方式擴充引擎功能:

  • Android 平台 API

  • Android 函式庫

  • Kotlin 與 Java 函式庫

  • 原生函式庫(透過 JNI)

  • GDExtension 函式庫

每個外掛都必須有一個繼承自 GodotPlugin 類別的初始化類別,該類別由 Godot Android 函式庫 提供。

GodotPlugin 類別提供 API 來存取執行中的 Godot 實例,並可掛勾其生命週期。該類別會於執行時由 Godot 引擎載入。

v2 封裝格式

v1 版 Android 外掛需要自訂 gdap 設定檔,供 Godot 編輯器偵測與載入外掛。但這種方式有諸多缺點,最主要是彈性不足,且脫離了 現有的 Godot EditorExportPlugin 格式、發佈與安裝流程

v2 版 Android 外掛已經棄用 gdap 封裝與設定機制,改採現有的 Godot EditorExportPlugin 封裝格式。同時也擴充了 EditorExportPlugin API,以正確支援 Android 外掛。

建置 v2 Android 外掛

官方提供了 github 專案範本:https://github.com/m4gr3d/Godot-Android-Plugin-Template,讓你可以快速建立適用於 Godot 4.2+ 的 Android 外掛。你可以依循 範本 README 來設定自己的 Godot Android 外掛專案。

為了幫助理解,以下分解了建立專案範本的步驟:

  1. 依照 這份官方說明 建立 Android 函式庫模組

  2. 將 Godot Android 函式庫加入相依性,方法是編輯模組的 gradle 建置檔

    dependencies {
        implementation("org.godotengine:godot:4.2.0.stable")
    }
    

Godot Android 函式庫已發佈到 MavenCentral,每個新版本都會更新。

  1. 建立 GodotAndroidPlugin,該類別需繼承自 GodotPlugin

    • 如果外掛要讓 GDScript 呼叫 Kotlin 或 Java 方法,這些方法必須加註 @UsedByGodot 標註。GDScript 呼叫方法時,名稱必須完全相符不會自動轉換 snake_casecamelCase。例如:

      if Engine.has_singleton("MyPlugin"):
          var singleton = Engine.get_singleton("MyPlugin")
          print(singleton.myPluginFunction("World"))
      
    • 如果外掛有使用 signals,初始化類別必須覆寫 GodotPlugin::getPluginSignals() 方法來回傳所有使用到的 signal。發送 signal 時可使用 GodotPlugin::emitSignal(...) 方法。

  2. 編輯外掛的 AndroidManifest.xml 檔案,並加入下列 meta-data:

    <meta-data
        android:name="org.godotengine.plugin.v2.[PluginName]"
        android:value="[plugin.init.ClassFullName]" />
    

說明:

  • PluginName:外掛名稱

  • plugin.init.ClassFullName:外掛初始化類別的完整元件名稱(套件名稱 + 類別名稱),例如 org.godotengine.plugin.android.template.GodotAndroidPlugin

  1. 建立 EditorExportPlugin 設定檔 以封裝外掛。詳細步驟請參考下方 Packaging a v2 Android plugin 章節。

建置具備 GDExtension 能力的 v2 Android 外掛

如同 v1 Android 外掛支援 GDNative,v2 Android 外掛同樣支援整合 GDExtension 能力。

官方亦提供 https://github.com/m4gr3d/GDExtension-Android-Plugin-Template github 專案範本,可快速建立適用於 Godot 4.2+ 的 GDExtension Android 外掛。你可以參考 範本 README 來設定自己的外掛專案。

將 v1 Android 外掛移植至 v2

如果你有 v1 Android 外掛想要移植到 v2,請依照下列步驟進行:

  1. 更新外掛的 manifest 檔案:

    • org.godotengine.plugin.v1 前綴改為 org.godotengine.plugin.v2

  2. 更新 Godot Android 函式庫建置相依性:

    • 你可以繼續使用從 Godot 下載頁 取得的 godot-lib.<版本>.<狀態>.aar 二進位檔,但請確認已更新到最新穩定版。

    • 或是改用 MavenCentral 上提供的相依性:

      dependencies {
          implementation("org.godotengine:godot:4.2.0.stable")
      }
      
  3. 更新 Godot Android 函式庫相依性後,請同步或建置外掛,並解決所有編譯錯誤:

    • GodotPlugin::getGodot() 所回傳的 Godot 實例不再能直接存取 android.content.Context,請改用 GodotPlugin::getActivity() 取得。

  4. 請刪除 gdap 設定檔,並依照 Packaging a v2 Android plugin 章節的說明重新設定外掛。

封裝 v2 Android 外掛

如前所述,v2 Android 外掛現在以 EditorExportPlugin 外掛的形式提供給 Godot 編輯器,因此其封裝流程大致與 一般編輯器外掛 相同。

  1. 將外掛產生的二進位檔案放入外掛目錄(例如:addons/<plugin_name>/

  2. 在外掛目錄(例如: addons/<plugin_name>/ )中加入匯出用的 tool script

    • 建立的腳本必須為 @tool 腳本,否則將無法正常運作

    • 匯出工具腳本負責設定 Android 外掛,並將其掛接於 Godot 編輯器的匯出流程。大致範例如下:

      @tool
      extends EditorPlugin
      
      # A class member to hold the editor export plugin during its lifecycle.
      var export_plugin : AndroidExportPlugin
      
      func _enter_tree():
          # Initialization of the plugin goes here.
          export_plugin = AndroidExportPlugin.new()
          add_export_plugin(export_plugin)
      
      
      func _exit_tree():
          # Clean-up of the plugin goes here.
          remove_export_plugin(export_plugin)
          export_plugin = null
      
      
      class AndroidExportPlugin extends EditorExportPlugin:
          # Plugin's name.
          var _plugin_name = "<plugin_name>"
      
          # Specifies which platform is supported by the plugin.
          func _supports_platform(platform):
              if platform is EditorExportPlatformAndroid:
                  return true
              return false
      
          # Return the paths of the plugin's AAR binaries relative to the 'addons' directory.
          func _get_android_libraries(platform, debug):
              if debug:
                  return PackedStringArray(["<paths_to_debug_android_plugin_aar_binaries>"])
              else:
                  return PackedStringArray(["<paths_to_release_android_plugin_aar_binaries>"])
      
          # Return the plugin's name.
          func _get_name():
              return _plugin_name
      
    • 以下是這個工具腳本最常用的 EditorExportPlugin API

      這些 _get_android_manifest_* 方法可讓外掛自動對應用程式 manifest 做出修改,而且這些修改在 Godot 編輯器更新時仍能保留,解決了 v1 Android 外掛長期以來的問題。

  3. 建立 plugin.cfg,這是一個包含外掛資訊的 INI 格式檔案:

    [plugin]
    
    name="<plugin_name>"
    description="<plugin_description>"
    author="<plugin_author>"
    version="<plugin_version>"
    script="<relative_path_to_the_export_tool_script>"
    

供參考,這是 Godot Android 外掛專案範本 的資料夾結構。建置時,export_scripts_template 目錄內容與產生的外掛二進位檔會複製到 addons/<plugin_name> 目錄:

export_scripts_template/
|
+--export_plugin.gd         # export plugin tool script
|
+--plugin.cfg               # plugin INI file

封裝支援 GDExtension 的 v2 Android 外掛

針對 GDExtension 外掛,步驟與 Packaging a v2 Android plugin 相同,另外在 plugin.cfg 同目錄下加入 GDExtension 設定檔

供參考,這是 GDExtension Android 外掛專案範本 的目錄結構。建置時,export_scripts_template 目錄內容與產生的外掛二進位檔會複製到 addons/<plugin_name> 目錄:

export_scripts_template/
|
+--export_plugin.gd         # export plugin tool script
|
+--plugin.cfg               # plugin INI file
|
+--plugin.gdextension       # GDExtension config file

以下為 plugin.gdextension 設定檔範例:

[configuration]

entry_symbol = "plugin_library_init"
compatibility_minimum = "4.2"
android_aar_plugin = true

[libraries]

android.debug.arm64 = "res://addons/GDExtensionAndroidPluginTemplate/bin/debug/arm64-v8a/libGDExtensionAndroidPluginTemplate.so"
android.release.arm64 = "res://addons/GDExtensionAndroidPluginTemplate/bin/release/arm64-v8a/libGDExtensionAndroidPluginTemplate.so"
...

需要注意的是 android_aar_plugin 欄位,這代表此 GDExtension 模組是 v2 Android 外掛的一部分。在匯出流程中,這可讓 Godot 編輯器辨識 GDExtension 原生共享函式庫由 Android 外掛的 AAR 檔案提供。

對於 GDExtension Android 外掛,初始化類別必須覆寫 GodotPlugin::getPluginGDExtensionLibrariesPaths() 方法,並回傳附帶的 GDExtension 函式庫設定檔(*.gdextension)路徑。

這些路徑必須是相對於 Android 函式庫的 assets 目錄。執行時,外掛會將這些路徑提供給 Godot 引擎,引擎便會載入並初始化所附的 GDExtension 函式庫。

使用 v2 Android 外掛

備註

  • 需要 Godot 4.2 或更高版本

  • v2 Android 外掛必須配合 Gradle 建構流程 使用。

  • 上述 github 專案範本都附有示範 Godot 專案,方便快速測試。

  1. 將外掛的輸出目錄(addons/<plugin_name>)複製到目標 Godot 專案目錄下

  2. 用 Godot 編輯器開啟專案,編輯器應會自動偵測到該外掛

  3. 進入 專案專案設定...外掛,確認外掛已啟用

  4. 點選 專案安裝 Android 建置範本... 來安裝 Godot Android 建置範本

  5. 進入 專案匯出...

  6. 匯出 視窗中建立一個 Android 匯出預設值

  7. Android 匯出預設值 中,找到 Gradle Build 設定,將 Use Gradle Build 設為 true

  8. 根據需要修改專案腳本以存取外掛功能。例如:

    if Engine.has_singleton("MyPlugin"):
            var singleton = Engine.get_singleton("MyPlugin")
            print(singleton.myPluginFunction("World"))
    
  9. 將 Android 裝置連接到電腦並在裝置上執行專案

以 Android 函式庫形式使用 v2 Android 外掛

由於 v2 Android 外掛本質上也是 Android 函式庫,因此可以不經 EditorExportPlugin 封裝,直接將 AAR 檔案作為原生函式庫,讓 Android 應用程式與 Godot Android 函式庫 一同使用。

若採用此用法,請務必在文件中詳細說明如何將 AAR 檔案納入(例如:如何在 Android app 的 manifest 中加入自訂內容)。

參考實作

提示與建議

簡化存取外掛 Java / Kotlin API 的方式

為方便在 Godot 編輯器中存取外掛暴露的 Java / Kotlin API,建議你提供一組或多組 GDScript 包裝類別,讓外掛使用者直接操作。

例如:

class_name PluginInterface extends Object

## Interface used to access the functionality provided by this plugin.

var _plugin_name = "GDExtensionAndroidPluginTemplate"
var _plugin_singleton

func _init():
    if Engine.has_singleton(_plugin_name):
        _plugin_singleton = Engine.get_singleton(_plugin_name)
    else:
        printerr("Initialization error: unable to access the java logic")

## Print a 'Hello World' message to the logcat.
func helloWorld():
    if _plugin_singleton:
        _plugin_singleton.helloWorld()
    else:
        printerr("Initialization error")

支援在 Godot 編輯器內使用 GDExtension 功能

如果你打算在 Godot 編輯器中也能使用 GDExtension 功能,建議同時替 Android 及開發者/使用者用來執行 Godot 編輯器的作業系統編譯 GDExtension 原生二進位檔。否則,開發者/使用者將無法在編輯器內撰寫與測試存取外掛的程式碼。

這有時可能需要為主機作業系統建立虛擬外掛(dummy plugin),以便將 API 發佈到編輯器。你可以參考 godot-cpp-template github 範本。

Godot 載入時當機

請檢查 adb logcat 以尋找潛在問題,然後:

  • 請確認外掛暴露的方法僅使用以下 Java 型別:voidbooleanintfloatjava.lang.Stringorg.godotengine.godot.Dictionaryint[]byte[]float[]java.lang.String[]

  • 目前暫不支援更複雜的資料型別。