Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[HTML5] [3.x] Implement Godot <-> JavaScript interface. #48691

Merged
merged 1 commit into from
May 20, 2021

Conversation

Faless
Copy link
Collaborator

@Faless Faless commented May 13, 2021

In this PR:

Some changes compared to the proposal:

  • JS results are converted automatically when they are base types (number/string/boolean/null).
  • Accessing properties and calling functions is based on _get/_set/getvar/setvar/call to feel more natural (see example below)
  • No need for engine.addInterface:
    JavaScript.get_interface("name") will return window["name"]. E.g.: JavaScript.get_interface("document") will return the JavaScript window.document
  • Callbacks are supported, but must be created explicitly, and their references kept. They will be called with a single Array argument that will contain the arguments passed by JavaScript.
  • You can use JavaScript.create_object(typeName) to create a object with the new constructor. E.g. : JavaScript.create_object("ArrayBuffer", 10) will be the equivalent of calling new window["ArrayBuffer"](10) in JavaScript.

An almost complete example:

var _my_js_callback = JavaScript.create_callback(self, "myCallback") # This reference must be kept
var console = JavaScript.get_interface("console")

func _init():
	var buf = JavaScript.create_object("ArrayBuffer", 10) # new ArrayBuffer(10)
	print(buf) # prints [JavaScriptObject:OBJECT_ID]
	var uint8arr = JavaScript.create_object("Uint8Array", buf) # new Uint8Array(buf)
	uint8arr[1] = 255
	prints(uint8arr[1], uint8arr.byteLength) # prints 255 10
	console.log(uint8arr) # prints in browser console "Uint8Array(10) [ 0, 255, 0, 0, 0, 0, 0, 0, 0, 0 ]"

	# Equivalent of JavaScript: Array.from(uint8arr).forEach(myCallback)
	JavaScript.get_interface("Array").from(uint8arr).forEach(_my_js_callback)

func myCallback(args):
	# Will be called with the parameters passed to the "forEach" callback
	# [0, 0, [JavaScriptObject:1173]]
	# [255, 1, [JavaScriptObject:1173]]
	# ...
	# [0, 9, [JavaScriptObject:1180]]
	print(args)

You can of course still add your own library, and get it as in the proposal, e..:

In the html include (or the HTML custom template):

<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/9.0.0/math.min.js"></script>

Within the script:

var mathjs = JavaScript.get_interface("math")

This should be finished, it's in draft because I'll have to port it to 4.x's Callables, but would like some feedback before doing so.

@Faless Faless added this to the 3.4 milestone May 13, 2021
@Faless Faless requested review from vnen and neikeq May 13, 2021 15:01
@Faless Faless marked this pull request as ready for review May 14, 2021 12:02
@Faless Faless requested review from a team as code owners May 14, 2021 12:02
doc/classes/JavaScript.xml Outdated Show resolved Hide resolved
@akien-mga akien-mga merged commit f0fa8a1 into godotengine:3.x May 20, 2021
@akien-mga
Copy link
Member

Thanks!

@agorangetek
Copy link

Hi, I am having trouble understanding how to use an external Javascript lib. For example, i have

<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.26/Tone.min.js" integrity_no="sha512-y/gX3imWQKHaL1WVyuaK5HE6gMjpju/NV+nC9nz5cmoCS3pjgwOYlZ8ToBNlu4qxiQ/EIMRnYhY/WX70tVwuTw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

added to my Project HTML5 Export "Head Include" section. I then do
var tone = JavaScript.get_interface("Tone")
print(tone.version)

and the output is correct. What I don't understand is how to convert the javascript code below;

const synth = new Tone.Synth().toDestination();

Any help would be much appreciated as this functionality in Godot HTML5 export is a game changer
Regards

@Faless
Copy link
Collaborator Author

Faless commented Jul 23, 2021

@agorangetek please use the other community channels for support in the future (since GitHub is for bug reports an proposal).
Anyway, as mentioned in the blog post to call the new constructor you have to use the JavaScript.create_object function, but in your case the object is not in the global scope, so it won't work actually. The fastest way to do that is probably to create a JavaScript function: window['createSynth'] = function() { return new Tone.Synth().toDestination(); } and call it via JavaScript.get_interface("window").createSynth()

@agorangetek
Copy link

agorangetek commented Jul 23, 2021

It works!
Thanks. I'll use the community channels in future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants