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.

Синглтон JavaScriptBridge

У веб-версіях синглтон JavaScriptBridge дозволяє взаємодіяти з JavaScript і веб-браузерами та може використовуватися для реалізації деяких функцій, унікальних для веб-платформи.

Взаємодія з JavaScript

Іноді під час експорту Godot для Інтернету може знадобитися взаємодія із зовнішнім кодом JavaScript, як-от SDK сторонніх розробників, бібліотеки, або просто отримати доступ до функцій браузера, які Godot безпосередньо не відкриває.

Синглтон JavaScriptBridge надає методи для обгортання рідного об’єкта JavaScript у Godot JavaScriptObject, який намагається виглядати природно в контексті сценаріїв Godot (наприклад, GDScript і C#).

Метод 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, викликаючи їхні методи та отримуючи (або навіть встановлюючи) їхні властивості.

Базові типи (int, floats, strings, booleans) автоматично перетворюються (floats можуть втратити точність під час перетворення з Godot на JavaScript). Усе інше (тобто об’єкти, масиви, функції) розглядається як JavaScriptObjects.

Зворотні дзвінки

Виклик коду JavaScript з Godot – це добре, але іноді замість цього потрібно викликати функцію Godot з JavaScript.

Цей випадок трохи складніший. JavaScript покладається на збирання сміття, тоді як Godot використовує підрахунок посилань для керування пам’яттю. Це означає, що ви повинні явно створити зворотні виклики (які повертаються як самі JavaScriptObjects), і ви повинні зберегти їх посилання.

Аргументи, передані JavaScript до зворотного виклику, будуть передані як єдиний масив Godot.

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 перетворено на масив. Інакше метод зворотного виклику не буде викликано.

Ось ще один приклад, який запитує у користувача Дозвіл на сповіщення та чекає асинхронної доставки сповіщення, якщо дозвіл надано:

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

Чи можу я користуватися улюбленою бібліотекою?

Ви, швидше за все, зможете. По-перше, ви повинні включити свою бібліотеку на сторінку. Ви можете налаштувати Head Include під час експорту (див. нижче) або навіть write your own template.

У наведеному нижче прикладі ми налаштовуємо Head Include, щоб додати зовнішню бібліотеку (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.

func my_func():
    JavaScriptBridge.eval("alert('Calling JavaScript per GDScript!');")

Значення останнього оператора JavaScript перетворюється у значення GDScript і повертається eval() за певних обставин:

  • число JavaScript повертається як float

  • JavaScript boolean повертається як bool

  • Рядок JavaScript повертається як class_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'

Будь-яке інше значення JavaScript повертається як null.

Для покращення безпеки шаблони експорту HTML5 можуть бути built без підтримки єдиного елемента. З такими шаблонами та на платформах, відмінних від 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")

Порада

Багаторядні рядки GDScript, оточені 3 лапками, """ як зазначено вище в 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)

Завантаження файлів

Завантаження файлів (наприклад, збереження гри) із веб-експорту Godot на комп’ютер користувача можна здійснити шляхом безпосередньої взаємодії з JavaScript, але, оскільки це дуже поширений випадок використання, Godot надає цю функцію сценарію через спеціальний JavaScriptBridge.download_buffer(), яка дозволяє завантажувати будь-який згенерований буфер.

Ось мінімальний приклад того, як його використовувати:

розширює вузол

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