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

Add support for cross-language mixins using MixinScript (aka MultiScript) #92

Merged
merged 30 commits into from
Jun 22, 2021

Conversation

Xrayez
Copy link
Contributor

@Xrayez Xrayez commented Jun 19, 2021

This is a resurrection of multiscript PR in Godot, see godotengine/godot#8718. The feature got re-added in development versions of Godot in 2017, but got quickly removed again, so most users have never seen MultiScript in Godot.

See also godotengine/godot-proposals#1346, godotengine/godot-proposals#758, godotengine/godot#23101.

The reason why it was removed is because of various limitations inherent to cross-language implementation of having multiple scripts per instance, and the usability of the feature was really lacking. I've took some time to improve upon the previous implementation in Godot and even made an editor for it, and now it's an actual language which is recognized via ms extension:

Mixin script create dialog

image

Mixin script editor

image

The reason why I've rebranded the MultiScript to MixinScript is mainly because it better reflects the logic behind the inner workings, and potentially allows to avoid ambiguity with the notion of having "multiple" scripts per node: https://stackoverflow.com/questions/925609/mixins-vs-traits.

With this feature, it's possible to combine the methods, properties, signals of GDScript, VisualScript, NativeScript etc. into a single instance. Notice that I've emphasized instance because MixinScript only extends run-time functionality of objects/nodes. For example, here's how editing a Mixin looks like:

extends Mixin
#extends KinematicBody2D # The real object.

const GRAVITY = 980
var velocity = Vector2()

func _ready():
	print("Character is ready (character_main.gd)")

func _physics_process(_delta):
	velocity.y += GRAVITY * _delta
	velocity = owner.move_and_slide(velocity, Vector2.UP)

func jump():
	if owner.is_on_floor():
		velocity.y -= GRAVITY * 0.5

So, the real limitation is that all objects must inherit Mixin class (at least in GDScript), but at the end of the day, you can code the real object via owner property (owner has MixinScript attached). If other mixins define new methods, those will be available in owner as well, regardless of whether they are written in GDScript, VisualScript, C# etc. This allows you to avoid depending on node hierarchy.

If you're dealing with the MixinScript instance from outside of the class (say when you instantiate a scene), then you won't have to use owner property; all properties and methods should be made available as if they were defined in the same script for real.

To-do

Depending on whether the feature will be useful for other developers, there are still quite some work to do on the editor side to make the development experience more smooth:

  • Drag-n-drop.
  • Undo-redo.
  • Update list on filesystem changes.

Test project

https://github.com/goostengine/goost-examples/tree/8e634b4a30404a2a55fb48dfb7382b4eabbbace5/scripting/mixins

Xrayez added 26 commits June 15, 2021 00:30
WIP: based on commit 15bce7f75f0dfab9863db5d4c7a51f5fc96fdb89 in Godot.
Seems to work at runtime as well!
Consistent with `visual_script` module.
Nodes extended with `MixinScript` are not very useful unless they have ability to be edited just like any other script from other languages.
This introduces `MixinScriptEditor` with the purpose of handling script editing. The editor will delegate script editing to other editors that handle `main_script`, if it's valid.
Allows to conveniently attach main script to `MixinScript`. The complexity here is that we cannot use regular mechanism of attaching scripts, because usually scripts are attached to nodes, not scripts!
Allows to manually switch to mixin script editor when main script is edited automatically.
Those are not useful anymore, and they are limited to the number of letters. There's `MixinScriptEditor` which allows to edit `MixinScript` now.
Makes everything more consistent.
Unfortunately, we cannot provide overrides for arbitrary instances.
However, the real object is still available to all mixins via `owner`
property.
@Xrayez Xrayez added feature 💡 New feature proposal topic:core labels Jun 19, 2021
@Xrayez Xrayez added this to the gd3 milestone Jun 19, 2021
They are identical, and the former is removed in Godot 3.x.
Since there's no way to make arbitrary instances to use base types other than `Mixin`, there's not much benefit in disntinguishing between the "main" script and "mixins".

However, the first script added to `MixinScript` will be treated as the "main" one, in the sense that script editing is going to be automatically delegated to the first script.

This greatly simplifies both the core and the editor.
@Xrayez
Copy link
Contributor Author

Xrayez commented Jun 22, 2021

YOLO.

@ghost
Copy link

ghost commented Nov 19, 2021

This is such an amazing feature!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature 💡 New feature proposal topic:core
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant