Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

與 Android API 整合

Android 平台擁有眾多 API 與豐富的第三方程式庫生態系,涵蓋推播通知、分析、身份驗證、廣告等各式功能。

這些功能不適合放進 Godot 核心,因此 Godot 早就提供了 Android 外掛系統Android 外掛系統 讓開發者能以 Java 或 Kotlin 編寫 Godot 的 Android 外掛,並從 GDScript、C# 或 GDExtension 在 Godot 專案中存取與使用 Android API 與第三方程式庫。

class MyAndroidSingleton(godot: Godot?) : GodotPlugin(godot) {
        @UsedByGodot
        fun doSomething(value: String) {
                // ...
        }
}

然而撰寫 Android 外掛需要 Java 或 Kotlin 的知識,而多數 Godot 開發者並不具備。因此相當多的 Android API 與第三方程式庫沒有可供對接的 Godot 外掛。事實上,這也是許多開發者無法從其他遊戲引擎轉用 Godot 的主因之一。

為了解決這個問題,我們在 Godot 4.4 引入了幾個工具,簡化開發者存取 Android API 與第三方程式庫的流程。

JavaClassWrapper(Godot 單例)

JavaClassWrapper 是一個 Godot 單例,讓你僅用 GDScript、C# 或 GDExtension 就能建立 Java / Kotlin 類別的實例並呼叫其方法。

var LocalDateTime = JavaClassWrapper.wrap("java.time.LocalDateTime")
var DateTimeFormatter = JavaClassWrapper.wrap("java.time.format.DateTimeFormatter")

var datetime = LocalDateTime.now()
var formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss")

print(datetime.format(formatter))

在上述程式碼片段中,透過 GDScript 使用 JavaClassWrapper 來存取 Java 的 LocalDateTimeDateTimeFormatter 類別。透過 JavaClassWrapper,我們可以像呼叫 GDScript 方法一樣,直接從 GDScript 呼叫這些 Java 類別的方法。

AndroidRuntime 外掛

JavaClassWrapper 很強大,但在 Android 上做許多事情需要存取各種生命週期/執行期物件。AndroidRuntime 外掛是 Godot 內建的 Android 外掛,可滿足這些需求。

結合 JavaClassWrapperAndroidRuntime 外掛, 開發者無需離開 GDScript, 也不需要 Godot 以外的工具, 就能存取並使用 Android API。這對推廣以 Godot 進行 Android 開發而言影響 重大 :

  • 如果你只需要做些簡單的事,或只用到第三方程式庫的一小部分,就不必製作外掛

  • 它讓開發者能快速整合 Android 功能

  • 它讓開發者僅用 GDScript 與 JavaClassWrapper 就能打造 Godot 外掛(不需要 Java 或 Kotlin)

備註

對於使用 gradle 的匯出,Godot 會自動納入在專案 addons 目錄中找到的 .jar.aar 檔案。因此要使用第三方程式庫,只需把其 .jar.aar 檔放進 addons 目錄,並透過 JavaClassWrapper 直接在 GDScript 中呼叫其方法即可。

範例:顯示 Android toast

# Retrieve the AndroidRuntime singleton.
var android_runtime = Engine.get_singleton("AndroidRuntime")
if android_runtime:
    # Retrieve the Android Activity instance.
    var activity = android_runtime.getActivity()

    # Create a Godot Callable to wrap the toast display logic.
    var toast_callable = func():
        # Use JavaClassWrapper to retrieve the android.widget.Toast class, then make and show a toast using the class APIs.
        var ToastClass = JavaClassWrapper.wrap("android.widget.Toast")
        ToastClass.makeText(activity, "This is a test", ToastClass.LENGTH_LONG).show()

    # Wrap the Callable in a Java Runnable and run it on the Android UI thread to show the toast.
    activity.runOnUiThread(android_runtime.createRunnableFromGodotCallable(toast_callable))

範例:讓裝置震動

# Retrieve the AndroidRuntime singleton.
var android_runtime = Engine.get_singleton("AndroidRuntime")
if android_runtime:
    # Retrieve the Android Vibrator system service and check if the device supports it.
    var vibrator_service = android_runtime.getApplicationContext().getSystemService("vibrator")
    if vibrator_service and vibrator_service.hasVibrator():
        # Configure and run a VibrationEffect.
        var VibrationEffect = JavaClassWrapper.wrap("android.os.VibrationEffect")
        var effect = VibrationEffect.createOneShot(500, VibrationEffect.DEFAULT_AMPLITUDE)
        vibrator_service.vibrate(effect)

範例:存取內部類別

Java 的內部類別可使用 $ 符號來存取:

# Accessing 'VERSION' class, which is an inner class from the 'android.os.Build' class.
var version = JavaClassWrapper.wrap("android.os.Build$VERSION")
var sdk_int = version.SDK_INT
if sdk_int == 30:
    # Do something specific on android 11 devices.
else:
    # All other devices

範例:呼叫建構函式

建構函式是以與類別同名的方法來呼叫的。

此範例會建立一個用來傳送文字的 intent:

# Retrieve the AndroidRuntime singleton.
var android_runtime = Engine.get_singleton("AndroidRuntime")
if android_runtime:
    var Intent = JavaClassWrapper.wrap("android.content.Intent")
    var activity = android_runtime.getActivity()
    var intent = Intent.Intent() # Call the constructor.
    intent.setAction(Intent.ACTION_SEND)
    intent.putExtra(Intent.EXTRA_TEXT, "This is a test message.")
    intent.setType("text/plain")
    activity.startActivity(intent)