Creating an Autoclass Registration Project

In this section we are gonna walk through the process of creating a very simple scene where we are gonna attach a NativeScript made with Go using autoclass registration and compiled as a C shared library.

Pre-requisites

In order to build the example described in this page you need the following packages to be present in your computer:

  • GDNative-Go library
  • gogdc compiler (part of GDNative-Go refer to the installation guide)
  • a C compiler (GCC or Clang or Mingw-w64)
  • An up to date version of the Go runtime and compiler (1.15.3 at the moment of writing this lines)
  • Godot 3.2.1 or superior
  • a source code editor

Windows users please refer to the Godot documentation to check which options do you have in order to compile godot and use the same compiler to compile NativeScript libraries

Create a new Godot project

First we need to create a new Godot project, as it is shown in the screenshot below:

CreateProject

you can click the Create Directory button and Godot will create the target directory for your new project automagically

Add directories and main scene

Now create two directories in the root of the project called bin and src. When done create a new scene of type control. Set its anchor to full rect. Add a button child node and move it to the center of the 2D canvas, set its size to x:246, y:56 under Rect property in the Inspector panel. Add new child label to the control node give it the same size than the button in the previous step and move it below the button. After completing these steps you shall have something like the screenshot below

MainScene

Create a simple.go file into src directory

Create a new Go file with your preferred text editor in the src directory, we recommend Visual Studio Code with the Go and Godot Tools extensions installed but you can use whatever you feel comfortable with.

you can not edit Go files within the integrated GDScript Godot editor even if you can create the file from Godot UI itself, Godot has no clue what a .go file is

Add the Go code to the simple.go file

Copy and paste the block of code below into simple.go and save the file

package main

import (
	"fmt"

	"gitlab.com/pimpam-games-studio/gdnative-go/gdnative"
)

// Ignored is ignored by gdnativego compiler
type Ignored struct{} //nolint:deadcode,unused

// SimpleClass is a structure that we can register with Godot.
//godot::register as SIMPLE
type SimpleClass struct {
	Hit         gdnative.Signal
	HP          gdnative.Int     `hint:"range" hint_string:"The player's Hit Points" usage:"Default"`
	Mana, Blood gdnative.Int     `hint:"range" hint_string:"The player points to cast spells"`
	Position    gdnative.Vector2 `hint:"none" hint_string:"The player position"`

	// IgnoreMe property will be ignored
	IgnoreMe gdnative.Float `-` //nolint
}

// New creates a new SimpleClass value and returns a pointer to it
//godot::constructor(SimpleClass)
func New() *SimpleClass {

	sc := SimpleClass{
        HP: 100,
        // signals need to be defined as literals (sorry about that)
		Hit: gdnative.Signal{
			Name:           "hit",
			NumArgs:        gdnative.Int(1),
			NumDefaultArgs: gdnative.Int(1),
			Args: []gdnative.SignalArgument{
				{
					Name:         gdnative.String("power"),
					Type:         gdnative.Int(gdnative.VariantTypeInt),
					Hint:         gdnative.PropertyHintRange,
					HintString:   "Hit power value",
					Usage:        gdnative.PropertyUsageDefault,
					DefaultValue: gdnative.NewVariantInt(gdnative.Int64T(0)),
				},
			},
			DefaultArgs: []gdnative.Variant{
				gdnative.NewVariantInt(gdnative.Int64T(0)),
			},
		},
	}
	return &sc
}

// GetData is automatically registered to SimpleClass on Godot
//godot::export as get_data
func (sc *SimpleClass) GetData() gdnative.Variant {

	gdnative.Log.Println("SIMPLE.get_data() called!")

	data := gdnative.NewStringWithWideString(fmt.Sprintf("Hello World from gdnative-go instance! HP value: %d", sc.HP))
	return gdnative.NewVariantWithString(data)
}

// This never gets called, but it necessary to export as a shared library.
func main() {
}

the code is fully commented, take some time to walk through it

As you can see we didn’t needed that much code compared with the manual approach and we added properties and signals to expand our previous SimpleClass

Create a simple enough Makefile

Create a simple Makefile in the root of the project with the following contents (Linux version)

all: generate build

generate:
	${GOPATH}/bin/gogdc generate --path="./src"

build:
	go build -v -buildmode=c-shared -o ./bin/libsimple.so ./src/*.go

clean:
	go clean
	rm ./bin/libsimple.so
	rm ./src/*_registrable.gen.go

.PHONY: all

on Mac OS X you should change the building line to: go build -v buildmode=c-shared -o ./bin/libsimple.dylib ./src/*.go

on Windows you should change the building line to: go build -v buildmode=c-shared -o ./bin/libsimple.dll ./src/*.go

if you use Windows check how to compile NativeScript libraries on your Windoes version in the official Godot documentation

Compile the library

As before we can compile the library using make command in the root of the project, if everything goes as expected you will end with a shared library file inside the bin directory. The process is pretty similar to the one in the previous section where we were creating all of our handlers manually and registering them into Godot’s NativeScript, the main difference is that gogdc will generate all the needed files to autoregister our classes for us.

if you get an error saying cannot find package "github.com/vitaminwater/cgo.wchar" that means that for some reason the needed cgo.wchar package wasn’t auto installed in your system by go get, proceed to just get them in order to fix it with go get github.com/vitaminwater/cgo.wchar and try make again

Create a new GDNativeLibrary script

We need to create a new GDNativeLibrary script to load our newly compiled library into Godot, to do so you have several options

Use the properties Inspector

Click the icon in the properties Inspector on Godot interface and write GDNativeLibrary in the search window, that will create a new GDNativeLibrary on memory and its properties editor will automatically open in the botton panel on Godot’s gui. Ignore the edition panel for now and click on the icon, select Save as... in the contextual menu that appears, select the bin directory if is not already selected and save the file as simple.gdnlib.

Use the ‘New Resource’ contextual menu

Put your mouse over the bin directory we created at the very beginning and click with the right (or secondary) button, on the contextual menu that appears select New Resource, write GDNativeLibrary in the search window (it should autocomplete for you), in the saving window that will appear, save the new resource file as simple.gdnlib and press enter.

Click on the two arrow icon at the bottom right corner of the bottom panel, just right to the Godot’s version, you will be presented with the GDNativeLibrary edition GUI maximized, in case that you don’t have the GDNativeLibrary panel opened at the bottom, go to the simple.gdnlib file in the resource explorer and double click on it.

you can expand any bottom panel to make it use all the window height using that double up arrow icon, to turn it back to its original shape you just have to click on it again and it will shrink to its original size in the bottom of the central panel

In the editor you will find configuration for many platforms, you should provide a binary shared library for each platform that you want to support in your games, for the sake of simplicity we just set Linux/X11 on this example.

Click on icon of the Dynamic Library column on the 64 bits row (or in the 32 if your system is 32 bits) and select the libsimple.so file in the file dialog. You will end with something like the screen below.

GNNativeLibrary

if you are working in other platform use the right architecture and bits size for your system

Make sure the Load Once checkbox is check on the properties Inspector, click on the icon and select Save in the contextual menu to save our changes.

Create the NativeScript file

Now that Godot knows how to load our code we have to tell it about our SIMPLE class. In order to do it, we need to create a new NativeScript resource file. As before, we also have two options to create it.

Use the properties Inspector

Click the icon in the properties Inspector on Godot interface and write NativeScript in the search window, that will create a new NativeScript on memory and we should be able to edit its properties in the property Inspector. Click on the Library [empty] select menu and the in the icon, go to the bin directory in the file dialog and select our simple.gdnlib. Fill the ClassName value with our class name SIMPLE, click on the save icon and save the file in the bin directory as simple.gdns.

Use the ‘New Resource’ contextual menu

Put your mouse over the bin directory we created at the very beginning and click with the right (or secondary) button, on the contextual menu that appears select New Resource, write NativeScript in the search window (it should autocomplete for you), in the saving window that will appear, save the new resource file as simple.gdns and press enter. In the property Inspector. Click on the Library [empty] select menu and the in the icon, go to the bin directory in the file dialog and select our simple.gdnlib. Fill the ClassName value with our class name SIMPLE and click on the save icon to save our resource.

Attach new script

Now that we are in position to load and refer to our SIMPLE class we can open our main scene and attach a new script to it, on language select GDScript, leave the inheritance as it is, choose empty template and save it as main.gd in the root of the project. Copy and paste the following GDScript code into it and save it.

extends Control

# load the SIMPLE library
const SIMPLE = preload("res://bin/simple.gdns")
var data = SIMPLE.new()

func _on_Button_pressed():
	# data comes directly from the Go context
	$Label.text = "Data = " + data.get_data() 
    
    # update HP by adding one
	data.HP += 1
    
    # print current Blood and add one to it
	print("data.Blood is ", data.Blood)
	data.Blood += 1

Run the project

The final step is to run the project, click on the play icon at the top right of the screen or simply press the F5 key, a dialog indicating that we don’t have set up a main scene yet will popup, click on Select and select the main.tscn

RunninProject

You will get something similar to the screenshot above when you click the button

Congratulations

Congratulations! you created you first functional autoregistered classes GDNative-Go Godot application.