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...
Il singleton JavaScriptBridge
Nelle build web, il singleton JavaScriptBridge consente di interaggire con JavaScript e i browser web, e può essere utilizzato per implementare alcune funzionalità esclusive della piattaforma web.
Interagire con JavaScript
A volte, quando si esporta Godot per il Web, potrebbe essere necessario interfacciarsi con codice JavaScript esterno come SDK di terze parti, librerie o semplicemente accedere a funzionalità del browser che non sono direttamente esposte da Godot.
Il singleton JavaScriptBridge fornisce metodi per racchiudere un oggetto JavaScript nativo in un JavaScriptObject di Godot, che cerca di risultare naturale nei contesti di scripting in Godot (ad esempio GDScript e C#).
Il metodo JavaScriptBridge.get_interface() recupera un oggetto nell'ambito globale.
extends Node
func _ready():
# Retrieve the `window.console` object.
var console = JavaScriptBridge.get_interface("console")
# Call the `window.console.log()` method.
console.log("test")
Il metodo JavaScriptBridge.create_object() crea un nuovo oggetto tramite il costruttore new di JavaScript.
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)
Come si può notare, inserendo oggetti di JavaScript in JavaScriptObject è possibile interagire con essi come se fossero oggetti nativi di Godot, chiamando i loro metodi e recuperando (o addirittura impostando) le loro proprietà.
I tipi base (int, float, stringhe, booleani) vengono convertiti automaticamente (i float potrebbero perdere precisione durante la conversione da Godot a JavaScript). Qualsiasi altro tipo (ad esempio oggetti, array, funzioni) è considerato a sua volta un JavaScriptObject.
Callback
Chiamare il codice JavaScript da Godot è piacevole, ma a volte è necessario chiamare una funzione di Godot da JavaScript.
Questo caso è un po' più complicato. JavaScript dipende dalla garbage collection, mentre Godot utilizza il conteggio dei riferimenti per gestire la memoria. Ciò significa che è necessario creare esplicitamente dei callback (che sono restituiti come JavaScriptObjects) e mantenerne il riferimento.
Gli argomenti passati da JavaScript al callback saranno passati come un singolo Array di 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 = ''
Avvertimento
I metodi di callback creati tramite JavaScriptBridge.get_interface() (_my_callback nell'esempio precedente) devono accettare esattamente un argomento Array, che sarà l'oggetto arguments di JavaScript convertito in un array. Altrimenti, il metodo di callback non verrà chiamato.
Ecco un altro esempio che chiede all'utente l'autorizzazione di notifica e attende in modo asincrono di inviare una notifica se l'autorizzazione viene concessa:
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.")
Posso utilizzare la mia libreria preferita?
Probabilmente sì. Per prima cosa, è necessario includere la libreria nella pagina. È possibile personalizzare Head Include durante l'esportazione (vedere in seguito), o anche scrivere il proprio modello.
Nell'esempio seguente, personalizziamo Head Include per aggiungere una libreria esterna (axios) da una rete di distribuzione di contenuti e un secondo tag <script> per definire la nostra funzione personalizzata:
<!-- Axios -->
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<!-- Custom function -->
<script>
function myFunc() {
alert("My func!");
}
</script>
Possiamo quindi accedere sia alla libreria sia alla funzione da Godot, come abbiamo fatto negli esempi precedenti:
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")
L'interfaccia eval
Il metodo eval funziona in modo simile all'omonima funzione in JavaScript. Accetta una stringa come argomento e la esegue come codice JavaScript. Ciò permette di interagire con il browser in modi non possibili con i linguaggi di scripting integrati in Godot.
func my_func():
JavaScriptBridge.eval("alert('Calling JavaScript per GDScript!');")
private void MyFunc()
{
JavaScriptBridge.Eval("alert('Calling JavaScript per C#!');")
}
il valore dell'ultima istruzione JavaScript è convertito in un valore GDScript e restituito da eval(), in determinate circostanze:
Un
numberdi JavaScript viene restituito come floatUn
booleandi JavaScript viene restituito come boolUn
stringdi JavaScript viene restituito come StringUn
ArrayBuffer,TypedArrayeDataViewdi JavaScript sono restituiti come 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'
}
Qualsiasi altro valore di JavaScript è restituito come null.
I modelli di esportazione HTML5 si possono compilare senza supportare il singleton per sicurezza. Con tali modelli, e su piattaforme diverse da HTML5, anche la chiamata a JavaScriptBridge.eval restituirà null. La disponibilità del singleton si può verificare con il tag di funzionalità web:
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");
}
}
Suggerimento
Le stringhe multi-riga di GDScript, racchiuse tra 3 virgolette """ come in my_func3() sopra, sono utili per mantenere leggibile il codice JavaScript.
Il metodo eval accetta anche un secondo argomento booleano facoltativo, che specifica se eseguire il codice nel contesto di esecuzione globale, il cui valore predefinito è false per evitare di contaminare lo spazio dei nomi globale:
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);
}
Scaricare file
Lo scaricamento di file (ad esempio un salvataggio di una partita) dall'esportazione Web di Godot sul computer dell'utente si può effettuare interagendo direttamente con JavaScript, ma poiché si tratta di un caso d'uso molto comune, Godot espone questa funzionalità allo scripting tramite una funzione dedicata JavaScriptBridge.download_buffer() che consente di scaricare qualsiasi buffer generato.
Ecco un esempio minimo di come utilizzarlo:
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")
Ed ecco un esempio più completo su come scaricare un file salvato in precedenza:
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")