The Glodot is real!

"Can we get much higher?"

What?

Glodot == Godot + Gleam. It's a way to run Gleam code inside Godot.

Who?

"Who discover this?" - Me!

"Glodot is for whom?" - It's for people who like FP (Functional programming) and want to use it for gamedev.

When/where?

Glodot is ideal for the domain logic (like writing complex algorithms). Even for simple stuff like handling result from swipe gestures in a 2048 game, writing in Gleam is way more breathable compared to GDScript.

However, it's hard to interacts directly outside of domain logic from the Gleam code (like interacts heavily with the node tree and handling physics for exmaple)...

Why?

Godot is a great game engine but GDscript is not a good language to do domain logic stuffs. And yes, C# is better but not as good as a real FP langauge.

Gleam is the savior that fill the gap in the Godot ecosystem. Its syntax is beautiful, modern, familiar, easy to read, to manage and to learn (can be learn in a day). Plus Gleam's tooling is convenient, more than its of Lua, Python, C#...

Therefore, this is the perfect combination:

  • GDscript for convenient "real world" interactions and handle physic.
  • Gleam for writing domain logic functions with predefined input and output, like complex algorithms/calculations.

How?

The road to connect from Godot to Gleam is as follows:

Your Glodot files structure should look like this:

.  (Root of Godot project)
├── core  (Root of Gleam package)
│   ├── gleam.toml
│   ├── manifest.toml
│   ├── package.json
│   ├── src
│   │   └── core
│   │       ├── core.gleam  (The file that contains all functions to be accessible from outside Gleam)
│   │       └── ...
├── project.godot
├── project.csproj
├── core.txt  (Bundled Gleam-to-JS code)
├── core.gd  (Util to execute bundled code in Godot)
└── V8.cs  (Util to execute JS code in Godot)

Gleam side

First create a Gleam package inside your Godot project's root:

gleam new core # It's could be named anything.

Then write your domain logic stuffs here. When you done, make a signle file that contains all functions to be accessible from outside of Gleam (name it core.gleam):

import core/a_stuffs
import core/b_stuffs

pub fn fn_a(input: String) -> String {
  a_stuffs.fn_a(input)
}

pub fn fn_b(input: String) -> String {
  b_stuffs.fn_b(input)
}

// ...

⚠️ WARNING! BIG WARNING! You cannot use custom Gleam type as input/output outside of Gleam, at least it's not behaved as you thinks. You can only use Int, Float and String. Even List and Dict is not the same as JS's Array and Object. The best solution is to warp input and output as JSON string.

So after you write you fancy code in a Gleam package for your game, make sure to set the target to JS in the gleam.toml config file:

  name = "core"
  version = "1.0.0"
+ target = "javascript"

...

Then bundler the JS codes after each time you compiler:

cd ./core
bun build ./build/dev/javascript/core/core/core.mjs --outfile ../core.txt

Yep it's end with .txt so that Godot can detect and read the texts inside that file.

And remember to remove the export part inside the bundled file so that V8 don't warning.

Godot side

First install ClearScript to your system:

nuget install Microsoft.ClearScript.Complete

Then add it to the project.csproj file:

  <Project Sdk="Godot.NET.Sdk/4.4.1">
    <PropertyGroup>
      <TargetFramework>net8.0</TargetFramework>
      <EnableDynamicLoading>true</EnableDynamicLoading>
      <RootNamespace>project</RootNamespace>
    </PropertyGroup>
+   <ItemGroup>
+     <PackageReference Include="Microsoft.ClearScript.Complete" Version="7.5.0" />
+   </ItemGroup>
  </Project>

Write an util name V8.cs in your project to execute JS code:

using Godot;
using System;
using Microsoft.ClearScript.V8;

public partial class V8 : Node
{
    private static V8ScriptEngine _engine = new V8ScriptEngine();

    public static string eval(string code)
    {
        return Convert.ToString(_engine.Evaluate(code));
    }
}

Finally, make an util to execute Gleam-to-JS bundled code (name it core.gd):

extends Node

const _v8 := preload("res://src/V8.cs")


func _ready() -> void:
    var core_file := FileAccess.open(&"res://core.txt", FileAccess.READ)
    var core_code := core_file.get_as_text()
    _v8.eval(core_code)


func fn_a(arg: Variant) -> Variant:
	return _invoke(&"fn_a", arg)


func fn_b(arg: Variant) -> Variant:
	return _invoke(&"fn_b", arg)


func _invoke(funcName: String, argument: Variant) -> Variant:
    var code: Variant = &"{funcName}(\"{argument}\")".format({
        &"funcName": funcName,
        &"argument": JSON.stringify(argument).json_escape(),
    })

    var result: Variant = _v8.eval(code)

    var json := JSON.new()
    var error := json.parse(result)

    if error:
        if result != &"":
            printerr(result)

        return null

    return json.data

Export to web

As said in the Godot document:

Projects written in C# using Godot 4 currently cannot be exported to the web. See this blog post for more information.

However you can fallback to use JavaScriptBridge to run JS code instate of C# + ClearScript when export to web.

Additional

Here are some Gleam libraries that are useful for gamedev:

  • gleam_community_maths: A basic mathematics library that contains some of the most fundamental mathematics functions and utilities.
  • prng: A lib to handle PRNG (Pure Random Number Generator).
  • vec: A vector library, has almost everything that can be done in Godot.
  • vec_dict: This is to represent and handle a 2D, 3D and even 4D grid.

Alternatives route?

The Godot Mono - ClearScript route mentioned above seems quite roundabout, but it's currently the most "official" way. Godot Mono is an official version of Godot, and ClearScript is a library from Microsoft (the creator of C#), make this approach quite stable.

Glodot could potentially be made by using GodotJS to replace the route above, connecting Godot to compiled Gleam code purely through JS/TS.

But that is a shortcut, because at the time of writing, GodotJS is quite buggy/janky. I hope it will improve soon, because if GodotJS becomes as stable and easy to set up as Godot Mono, it would be an ideal method.

But if I could wish, I want a Godot plugin that could automatically compile a specified Gleam package and then provide an API to easily run it.

Well that's all fork, stay gleamy~!