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.
Checking the stable version of the documentation...
JavaScriptBridge 單例物件
在網頁專案建置時,JavaScriptBridge 單例物件允許與 JavaScript 及瀏覽器互動,能用於實作僅限網頁平台的特殊功能。
與 JavaScript 互動
有時將 Godot 匯出為網頁專案時,可能需要與外部 JavaScript 程式碼(如第三方 SDK、函式庫)串接,或存取 Godot 未直接提供的瀏覽器功能。
JavaScriptBridge 單例物件提供方法,可將原生 JavaScript 物件包裝成 Godot 的 JavaScriptObject,讓你在 GDScript 或 C# 中能像操作 Godot 原生物件一樣操作它。
JavaScriptBridge.get_interface() 方法可取得全域作用域的物件。
extends Node
func _ready():
# Retrieve the `window.console` object.
var console = JavaScriptBridge.get_interface("console")
# Call the `window.console.log()` method.
console.log("test")
JavaScriptBridge.create_object() 方法會透過 JavaScript 的 new 建構式建立新物件。
extends Node
func _ready():
# Call the JavaScript `new` operator on the `window.Array` object.
# Passing 10 as argument to the constructor:
# JS: `new Array(10);`
var arr = JavaScriptBridge.create_object("Array", 10)
# Set the first element of the JavaScript array to the number 42.
arr[0] = 42
# Call the `pop` function on the JavaScript array.
arr.pop()
# Print the value of the `length` property of the array (9 after the pop).
print(arr.length)
如上所示,將 JavaScript 物件包裝成 JavaScriptObject 後,你就能像操作 Godot 原生物件一樣呼叫其方法,存取或設定其屬性。
基本型別(整數、浮點數、字串、布林值)會自動轉換(浮點數從 Godot 轉換到 JavaScript 時可能會發生精度損失)。其他型別(如物件、陣列、函式)則會視為 JavaScriptObject。
回呼函式
從 Godot 呼叫 JavaScript 很方便,但有時你會需要反過來,從 JavaScript 呼叫 Godot 的函式。
這種情境會較為複雜。JavaScript 使用垃圾回收(GC),而 Godot 使用參考計數來管理記憶體。這代表你必須明確建立回呼函式(這些回呼會以 JavaScriptObject 回傳),而且要保存其參考。
由 JavaScript 傳遞給回呼函式的參數會以單一 Godot Array 傳入。
extends Node
# Here we create a reference to the `_my_callback` function (below).
# This reference will be kept until the node is freed.
var _callback_ref = JavaScriptBridge.create_callback(_my_callback)
func _ready():
# Get the JavaScript `window` object.
var window = JavaScriptBridge.get_interface("window")
# Set the `window.onbeforeunload` DOM event listener.
window.onbeforeunload = _callback_ref
func _my_callback(args):
# Get the first argument (the DOM event in our case).
var js_event = args[0]
# Call preventDefault and set the `returnValue` property of the DOM event.
js_event.preventDefault()
js_event.returnValue = ''
警告
透過 JavaScriptBridge.get_interface() 建立的回呼方法(如上例的 _my_callback)**必須**只接受一個 Array 參數,這個參數會是 JavaScript 的 arguments 物件 轉換成的陣列。否則該回呼方法將不會被呼叫。
以下是另一個範例,會請求使用者 通知權限,若允許則會非同步送出通知:
extends Node
# Here we create a reference to the `_on_permissions` function (below).
# This reference will be kept until the node is freed.
var _permission_callback = JavaScriptBridge.create_callback(_on_permissions)
func _ready():
# NOTE: This is done in `_ready` for simplicity, but SHOULD BE done in response
# to user input instead (e.g. during `_input`, or `button_pressed` event, etc.),
# otherwise it might not work.
# Get the `window.Notification` JavaScript object.
var notification = JavaScriptBridge.get_interface("Notification")
# Call the `window.Notification.requestPermission` method which returns a JavaScript
# Promise, and bind our callback to it.
notification.requestPermission().then(_permission_callback)
func _on_permissions(args):
# The first argument of this callback is the string "granted" if the permission is granted.
var permission = args[0]
if permission == "granted":
print("Permission granted, sending notification.")
# Create the notification: `new Notification("Hi there!")`
JavaScriptBridge.create_object("Notification", "Hi there!")
else:
print("No notification permission.")
我可以在 Godot 使用我喜歡的 JavaScript 函式庫嗎?
通常可以。首先,你必須將函式庫加入網頁。你可以在匯出時自訂 Head Include,或是 撰寫自己的 HTML 範本。
在下方範例中,我們自訂 Head Include,從 CDN 加入外部函式庫(如 axios),並加上一段 <script> 標籤來定義自訂函式:
<!-- Axios -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<!-- Custom function -->
<script>
function myFunc() {
alert("My func!");
}
</script>
接著我們就能如同先前範例,在 Godot 內直接存取該函式庫及自訂函式:
extends Node
# Here create a reference to the `_on_get` function (below).
# This reference will be kept until the node is freed.
var _callback = JavaScriptBridge.create_callback(_on_get)
func _ready():
# Get the `window` object, where globally defined functions are.
var window = JavaScriptBridge.get_interface("window")
# Call the JavaScript `myFunc` function defined in the custom HTML head.
window.myFunc()
# Get the `axios` library (loaded from a CDN in the custom HTML head).
var axios = JavaScriptBridge.get_interface("axios")
# Make a GET request to the current location, and receive the callback when done.
axios.get(window.location.toString()).then(_callback)
func _on_get(args):
OS.alert("On Get")
eval 介面
eval 方法的功能與 JavaScript 同名函式類似。它接受一個字串作為參數並將其作為 JavaScript 程式碼執行。這讓你可以以 Godot 腳本實現 Godot 內建腳本語言無法做到的瀏覽器互動。
func my_func():
JavaScriptBridge.eval("alert('Calling JavaScript per GDScript!');")
private void MyFunc()
{
JavaScriptBridge.Eval("alert('Calling JavaScript per C#!');")
}
特定情況下,最後一個 JavaScript 陳述式的值會轉換為 GDScript 值並由 eval() 回傳:
JavaScript
number型會以 GDScript float 回傳JavaScript
boolean型會以 GDScript bool 回傳JavaScript
string型會以 GDScript String 回傳JavaScript
ArrayBuffer、TypedArray以及DataView型會以 PackedByteArray 回傳
func my_func2():
var js_return = JavaScriptBridge.eval("var myNumber = 1; myNumber + 2;")
print(js_return) # prints '3.0'
private void MyFunc2()
{
var jsReturn = JavaScriptBridge.Eval("var myNumber = 1; myNumber + 2;");
GD.Print(jsReturn); // prints '3.0'
}
其他 JavaScript 值會回傳為 null。
HTML5 匯出範本可 建置 為不支援該單例的版本以提升安全性。若使用這類範本,或是在非 HTML5 平台,呼叫 JavaScriptBridge.eval 也會回傳 null。可以透過 web feature tag 來檢查單例是否可用:
func my_func3():
if OS.has_feature('web'):
JavaScriptBridge.eval("""
console.log('The JavaScriptBridge singleton is available')
""")
else:
print("The JavaScriptBridge singleton is NOT available")
private void MyFunc3()
{
if (OS.HasFeature("web"))
{
JavaScriptBridge.Eval("console.log('The JavaScriptBridge singleton is available')");
}
else
{
GD.Print("The JavaScriptBridge singleton is NOT available");
}
}
小訣竅
GDScript 的多行字串由三個引號 """ 包圍,如上方 my_func3() 範例所示,可讓 JavaScript 程式碼更易閱讀。
eval 方法也接受第二個可選的布林參數,用於指定是否要在全域執行環境中執行程式碼。預設為 false,以避免污染全域命名空間:
func my_func4():
# execute in global execution context,
# thus adding a new JavaScript global variable `SomeGlobal`
JavaScriptBridge.eval("var SomeGlobal = {};", true)
private void MyFunc4()
{
// execute in global execution context,
// thus adding a new JavaScript global variable `SomeGlobal`
JavaScriptBridge.Eval("var SomeGlobal = {};", true);
}
下載檔案
從 Godot 網頁版下載檔案(例如存檔)到使用者電腦雖然可以直接透過 JavaScript 實作,但由於這是非常常見的需求,Godot 也提供了專用的 JavaScriptBridge.download_buffer() 方法,讓你可以直接從腳本下載任何產生的緩衝區資料。
以下是最簡單的使用範例:
extends Node
func _ready():
# Asks the user download a file called "hello.txt" whose content will be the string "Hello".
JavaScriptBridge.download_buffer("Hello".to_utf8_buffer(), "hello.txt")
以下則是更完整的範例,說明如何下載先前儲存的檔案:
extends Node
# Open a file for reading and download it via the JavaScript singleton.
func _download_file(path):
var file = FileAccess.open(path, FileAccess.READ)
if file == null:
push_error("Failed to load file")
return
# Get the file name.
var fname = path.get_file()
# Read the whole file to memory.
var buffer = file.get_buffer(file.get_len())
# Prompt the user to download the file (will have the same name as the input file).
JavaScriptBridge.download_buffer(buffer, fname)
func _ready():
# Create a temporary file.
var config = ConfigFile.new()
config.set_value("option", "one", false)
config.save("/tmp/test.cfg")
# Download it
_download_file("/tmp/test.cfg")