Godot-Squeak is a Squeak/Smalltalk language binding for the Godot game engine.
This is achieved by compiling a Squeak VM as a shared library and running it in a thread alongside Godot. Using this language binding, we now have access to the Squeak environment and ecosystem during development with Godot. This includes the Squeak debugger and advanced projects like Sandblocks. Workflows within Godot and Squeak remain largely unchanged and interact well with each other. Furthermore, the code used to define Godot logic is very similar to its GDScript-equivalent while being idiomatic Smalltalk. We thus expect that this language binding is easy and intuitive to use for Godot and Smalltalk developers alike.
Developed for the Programming Experience seminar WS21/22.
Note: This project was developed for Godot 3.3 and may not work with other versions.
Currently only supported on Linux.
git submodule update --init
cd plugin
PATH=$PATH:/path/to/smalltalkci/bin make
This will create a Godot-ready image in the plugin/addons/squeak/image
directory.
plugin/scripts/createImage.sh plugin/addons/squeak/image
If you want to create a Godot-ready image manually, e.g. if you want to use a different image than the default, first install the Godot-API package:
Metacello new
baseline: 'Godot';
repository: 'github://hpi-swa-lab/godot-squeak:main/squeak';
load.
Then, start the message listener:
GDMessageListener restart
Place a symlink to the plugin/addons
directory into the Godot project directory.
ln -s "$PWD/addons" path/to/godot-project
A Squeak image named squeak.image
must be placed in plugin/addons/squeak/image
.
See creating the image.
You need to start Godot with the LD_LIBRARY_PATH containing the path to plugin/addons/squeak
.
LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/path/to/plugin/addons/squeak" ./Godot
For the plugin to work, a message listener process must be running in Squeak. If the process is not running, you may start it with
GDMessageListener restart
If the message listener is not running, Godot may freeze. Only one message listener process should be active.
You can attach Smalltalk scripts to Nodes just like you would with GDScript.
Make sure to select Smalltalk as the language in the script creation window.
Creating a script will create a corresponding class in the Godot-Scripts
category within Squeak.
Just like in Godot, the script inherits from the respective Godot object.
This means that it has access to all of its parent's methods and properties via self
.
Within that class you will find pre-generated methods like #ready
and #process
.
They have the same role as their Godot equivalents.
You may delete them if they are unnecessary.
Methods that are part of the Godot API, such as Node.add_to_group(...)
have pre-generated Squeak equivalents.
You can find them in their respective Squeak class (e.g. GodotNode>>addToGroup:
)
You may need to call a method that does not exist in Squeak, such as a method defined in a custom GDScript. In that case, an ad-hoc Squeak method with the same name up to the first colon will call it. For instance, a GDScript method defined as follows
func my_method(argumentOne, argumentTwo):
print(argumentOne + argumentTwo)
may be called with this Squeak code:
aGodotObject my_method: 'one' and: 'two'
If the Godot method has varargs, they may be passed as an array to the withArguments:
parameter.
A Squeak method may be called from GDScript by replacing the colons of its selector with underscores. For instance, a Squeak method defined as follows
GDSCustomScript>>add: aNumber and: anotherNumber
^ aNumber + anotherNumber
may be called with this GDScript code:
aSqueakObject.add_and_(1, 2)
Note the trailing underscore which is necessary if the method takes at least one argument.
Custom properties may be exported by writing a getter/setter pair.
The getter must contain an export:
pragma.
This pragma takes a string which returns the default value of the property when evaluated with value
.
For simple values, simply passing them as a string should suffice, but you may also put them into a block within the string.
Example:
GDSCustomScript>>customProperty
<export: '[true]'>
^ customProperty
GDSCustomScript>>customProperty: value
customProperty := value
The names of any signals that the script can emit should be returned in the class-side #signal
method within an array.
GDSCustomScript class>>signals
^ #('signalOne' 'signalTwo')
Signals can be emitted with the #emitSignal:
method
Godot's globals also exist as globals in Squeak. Their names are in snake-case.
(input isActionJustPressedAction: 'left') ifTrue: [self goLeft].