godot-haskell
Haskell bindings for the Godot game engine.
- Low-level (GDNative) in Godot.Gdnative
- Nativescript (binding classes/methods/etc) in Godot.Nativescript
- High-level (classes generated from the API ) in Godot.Api
- Access methods through Godot.Core
Getting started with the examples
The easiest way to get started is to have a look at the demos included in the examples directory. First check out "Dodge the Creeps!", your first game from the Godot documentation. Following along with the documentation and the code should make everything understandable.
To build:
git clone --recursive https://github.com/SimulaVR/godot-haskell
stack install godot-haskell:exe:godot-haskell-project-generator
cd godot-haskell/examples/dodge-the-creeps
make
To make changes to the game, in two different terminals:
make stack-watch
make project-watch
The first command will constantly build Haskell code and copy the shared library into the Godot project, demo. The second command will constantly scan the Godot project and build Haskell code out of it.
Load up the game by importing game/project.godot
into the editor, which you
can do from the commandline with godot game/project.godot
. To run the game in
the editor press F5, stop it with F8.
Understanding the examples
There are two parts to every project. examples/dodge-the-creeps/game
which is
the Godot project and examples/dodge-the-creeps/src
which are the Haskell
sources. When you run cd examples/dodge-the-creeps && make stack
to build the
demo, it builds the project locally with stack build
and then does a cp
to
copy the resulting shared library into the right place in
examples/dodge-the-creeps/game
. This way Godot will pick it up. If you just do
a stack build
without copying, your shared library will never update and Godot
will run the old code.
You must regenerate examples/dodge-the-creeps/src/Project
any time you modify
the Godot project. This directory contains the Godot project mirrored into
Haskell, just like @Servant@ provides you with API safety by declaring APIs in
Haskell. When you change the name of a node in Godot, this will update a Haskell
class instance, which will lead to a type error in your project. You can do this
with stack exec godot-haskell-parse-game game src
which will watch your
project for changes.
Known issues & inconveniences
- Script variables only appear in the editor when you reload it.
- No type safety for call and call_deferred.
- Every time you add a new node which needs a native script you need to manually select the library. It's tedious right now. This is the procedure for adding a new node backed by Haskell code: create the node, right click it, attach a script, select nativescript, the script will open in the editor, in the inspector find the Library subheading under NativeScript, click [empty], pick Load, the file picker will open, open lib, and select libmyproject.dnlib or whatever you've renamed the library to. That's it. Don't edit the empty file that's been opened. Now in Haskell, you can create a class with the same name as the Godot one and that inherits from the same type. See the demo. There is a ticket in Godot to automate this process :(
Setting up your own project.
It's best to start with one of the existing examples, make a copy, and rename
the project. If you want to start another project use the stack template
template/godot-haskell.hsroots
Alternatively, fetch it directly from git:
stack new myproject https://raw.githubusercontent.com/SimulaVR/godot-haskell/master/template/godot-haskell.hsfiles
Changing Godot versions
You will need to regenerate the bindings if you switch Godot versions. At present, these are generated for the version that corresponds to the godot_headers submodule included here. This was 3.1 at the time of writing.
To regenerate bindings:
- Replace godot_headers with a version that corresponds to your install. Instructions are included there, but that generally means either checking out the corresponding files or rebuilding Godot.
- Check out a copy of the godot repo and make sure you're on the commit corresponding to your version of godot. You don't need to build this, but you probably will anyway. We need it because documentation files are not included in the api.json file found in godot_headers.
- Summarize the documentation xml files into a json file:
# Prerequisite
sudo apt-get install jq libcurl4-gnutls-dev # Or equivalent on you OS/Distro
cabal install xml-to-json
# Generate the JSON
xml-to-json godot-install-directory/doc/classes/*.xml | jq -n '[inputs]' &> godot_doc_classes.json
- Build the bindings themselves:
cd classgen
stack build
rm ../src/Godot/Core/* ../src/Godot/Tools/*
stack exec godot-haskell-classgen -- ../godot_headers/api.json ../godot_doc_classes.json ../
That's it! The rest of the bindings are fairly lightweight with few dependencies, so you shouldn't see much breakage in the rest of the package.
If you want to get an idea of what the Haskell libraries are doing, set the
environment variable HS_GODOT_DEBUG
.
Questions
The primary method of contact is the SimulaVR Discord server. Mirrors are available at and in the SimulaVR channel at Matrix.
Docs
Most of the API is documented but the lowest-level bindings, like GDNative functions don't have documentation. They are quite intuitive to use though.