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")