Skip to content

Instantly share code, notes, and snippets.

@willnationsdev
Last active June 16, 2022 14:41
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save willnationsdev/437eeaeea2e675c0bea53343f7ecd4cf to your computer and use it in GitHub Desktop.
Save willnationsdev/437eeaeea2e675c0bea53343f7ecd4cf to your computer and use it in GitHub Desktop.
A quick guide on building a GDNative C++ project

Creating a GDNative Library (Windows)

This guide specifically shows how to get a Windows 10 x64 OS compiled for a Windows x64 Debug dynamic library and integrate it into a Godot project.

To begin, make sure you are running the appropriate command line tool as an administrator.

Create a SimpleLibrary directory and enter it. This is where our Godot and GDNative projects will exist. For later use, we will also create subdirectories for libraries (lib) and source code (src). Once inside, we get a copy of each necessary repository for C++ NativeScript bindings generation and enter into the cpp_bindings directory.

mkdir SimpleLibrary
cd SimpleLibrary
mkdir lib
mkdir src
git clone https://github.com/GodotNativeTools/cpp_bindings
git clone https://github.com/GodotNativeTools/godot_headers

cd cpp_bindings

Run the following scons command, where godotbinpath points to the relative location of your godot installation and headers points to the godot_headers directory you just installed.

scons godotbinpath="..\..\godot\bin\godot.windows.tools.64.exe" headers="..\godot_headers" p=windows generate_bindings=yes

Note that on Windows, godotbinpath MUST use backslashes ('\'), otherwise you will receive an error telling you that '..' is not a command. This is because the app at godotbinpath is executed and cmd.exe uses forward slashes ('/') for command options instead of the bash-like dash ('-'), so it thinks you are running...

.. /.. /.. /godot .....etc. (as if these were each options for a '..' command, e.g.)
.. -.. -.. -godot .....

Also note that if you encounter a bug saying "fatal error", something about AABB not finding stuff, you can try deleting and re-cloning thr cpp_bindings repository (worked for me the first time around).

You should see it generate several files. After a few minutes, it will conclude with "scons: done building targets".

Now it will have generated a static library file (.lib) for cpp_bindings that we must copy into our lib\ directory.

   copy bin\cpp_bindings.windows.64.lib ..\lib\

Because there doesn't seem to be a vsproj=yes command that works anymore to build a vsproject for us, we'll need to make it ourselves.

Create a Visual Studio project. The name should be SimpleLibrary and it should NOT create a directory (uncheck the box). You'll want to select the SimpleLibrary directory as the location to put the project. Choose the Win32 Desktop Wizard template. Select options for both a dynamic library (.dll) and an empty project.

Afterwards, things should look like this:

SimpleLibrary -cpp_bindings -godot_headers -lib -SimpleLibrary --.vs --SimpleLibrary.sln --SimpleLibrary.vcxproj --SimpleLibrary.vcsproj.filters -src

In Visual Studio, we will need to configure a few things. Make sure you have a debug, x64 configuration for the solution. The options are located in the toolbar at the top left, just before the "Local Windows Debugger" button with the green play icon.

Then go to "Project > SimpleLibrary Properties..." to open the project properties.

(note that for the next step, for things that involve paths, it's advised that you use the "..." button on new entries to create records)

Ensure that you are on the x64 Debug configurations at tge top (or just do all platforms) and make the following changes...

  1. VC++ Directories > Include Directories. Add 'SimpleLibrary\cpp_bindings\include', 'SimpleLibrary\cpp_bindings\include\core', and 'SimpleLibrary\godot_headers' to the list.
  2. VC++ Directories > Library Directories. Add 'SimpleLibrary\lib'.
  3. VC++ Directories > Source Directories. Add 'SimpleLibrary\src'.
  4. C/C++ > Linker > System. Subsystem = "Console (/SUBSYSTEM:CONSOLE)"
  5. C/C++ > Linker > Input. Add "cpp_bindings.windows.64.lib" (without quotes) to the Additional Dependencies parameter.

Click Apply and then Save to close the window. Under Source Files in the Solution Explorer of Visual Studio, create a pair of new items: an init.cpp file and a SimpleClass.cpp file in SimpleLibrary\src.

SimpleClass.cpp should be...

//SimpleClass.cpp

#include <core/Godot.hpp>
#include <Reference.hpp>

using namespace godot;

class SimpleClass : public GodotScript<Reference> {
    GODOT_CLASS(SimpleClass);
public:
    SimpleClass() { }

    void test_void_method() {
        Godot::print("This is test");
    }

    Variant method(Variant arg) {
        Variant ret;
        ret = arg;

        return ret;
    }

    static void _register_methods() {
        register_method("method", &SimpleClass::method);

        /**
        * How to register exports like gdscript
        * export var _name = "SimpleClass"
        **/
        register_property((char *)"base/name", &SimpleClass::_name, String("SimpleClass"));

        /** For registering signal **/
        // register_signal<SimpleClass>("signal_name");
    }

    String _name;
};

and init.cpp should be...

#include "SimpleClass.cpp"

/** GDNative Initialize **/
extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o)
{
    godot::Godot::gdnative_init(o);
}

/** GDNative Terminate **/
extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o)
{
    godot::Godot::gdnative_terminate(o);
}

/** NativeScript Initialize **/
extern "C" void GDN_EXPORT godot_nativescript_init(void *handle)
{
    godot::Godot::nativescript_init(handle);
    
    godot::register_class<SimpleClass>();
}

Now build Visual Studio (make sure you have it set to Debug x64! If you did not do this at the beginning of the process, all of the project properties will have been assigned to the wrong configuration and you will need to redo them all!)

If all goes well, the solution should build successfully.

Next step, create a Godot project folder in the main SimpleLibrary directory (where lib and src are located). You will start to see Godot import all of the obj files you've generated.

EDIT: Godot will now only allow you to create a project in an empty directory, so instead, create a directory, make the project, and then cut/paste the files into the SimpleLibrary directory.

Using a text editor (because Godot does not yet have a button somewhere to do this), create a .gdnlib file with the following:

[general]

symbol_prefix="godot_"
singleton=false
load_once=true

[entry]

Windows.64=<YOUR DLL PATH HERE>

[dependencies]

Windows.64=[  ]

Note: .gdnlib as a filetype is currently being used in place of a GDNativeLibrary.tres type, but at this time, the editor only knows how to create the .tres version, even if it can respond to and edit both of them. I do not know if they will eventually be converted back into .tres for consistency's sake or if they will add support for creating .gdnlib files directly from the editor.

Note: As an example, the value I had for my <YOUR DLL PATH HERE> was "SimpleLibrary/x64/Debug/SimpleLibrary.dll".

Create a gdnlibs folder (not important, just for organizational purposes) in your project and save the file in there with the name simple_class.gdnlib. You may need to rename the file from .gdnlib.txt to just .gdnlib depending on which text editor you used.

If you want to be EXTRA sure that you got this step right (recommended), then in your Godot project, open the .gdnlib file. It will have a GUI editor that pops up in the bottom panel. Find "Windows" in the list, click the folder icon on the 64 row, and select your .dll file. This allows Godot to know where your .dll exists.

Go to the Script Editor. In its toolbar, select File > New to create a new script. Set the type of the script to NativeScript. Because our SimpleClass demo type inherits from GodotScript<Reference>, you should type "Reference" in the text box labeled "Inherits". Specify the name of the class ("SimpleClass" - this must match the contents of the GODOT_CLASS macro in SimpleClass.cpp) and give it a filename of simple_class.gdns.

Once the script has been created, check the Inspector. It will have a null GDNativeLibrary field. Click the arrow and pick Load to load your .gdnlib file as its GDNativeLibrary for the .gdns file. This will ensure that the engine's Script object will know which .dll (depending on the platform) to look at when accessing "SimpleClass" properties and methods.

Now, VERY IMPORTANT: click the save icon in the Inspector. If you do not do this, the changed GDNativeLibrary setting on the NativeScript resource will only exist on the NativeScript resource instance in the Inspector rather than in the .gdns file in the file system. Because you load scripts via the file at the path, you won't actually get a NativeScript that is pointing to the GDNativeLibrary unless you do this.

Finally, create a new scene. Add a node of type Node with a built-in GDScript with the following code:

extends Node

func _ready():
    var simple = preload("simple_class.gdns").new()
    print(simple.method("Hello World from NativeScript!"))

Save the scene (name doesn't matter) and run it. When the game loads, you should see our string printed to the console. We passed it to a NativeScript class, which passed it along to the native implementation (done in C++, but it doesn't really know that, nor does it care), and C++ returned that value as a Variant for GDScript to catch, convert to a String, and print.

@MohabMohamed
Copy link

add reloadable=true to the .gdnlib file as the project gives me errors without it

@Galileo53
Copy link

Thank you for this guide with really clear explanations. It helped a lot.
As on Godot 3.0.2, I had two items that were different to your guide:
For all the path specifications in Visual Studio, you have to specify an absolute path, e.g. "\SimpleLibrary\src" instead of "SimpleLibrary\src".
In the .gdnlib file you have to specify a Godot path for the , and put quotes around it, so it looks like this:
Windows.64="res://SimpleLibrary/x64/Debug/SimpleLibrary.dll"

@ErikTheBerik
Copy link

Hey! I know this is a year old already, but might as well try to ask here...
Is there any way of debugging the c++ code? I tried attaching the vs project into the godot process but when checking the .dll in the modules it said symbols where not loaded because the binaries were not build for debug (even though I built in debug mode).

Any idea as to why I can't load the symbols/debug the c++ code from visual studio?

@GeminiSquishGames
Copy link

I don't know if it's 3.3 or what but I can't get any tutorials to work. I even tried the helper plugin and both couldn't find files the scons script was looking for... which can be moved manually to the right place but I don't want to hack this in, that's not reliable at all.

@willnationsdev
Copy link
Author

@gff1979 Yeah, I haven't really messed with GDNative much since writing this tutorial, and this was even back in NativeScript 1.0 I think. Sorry. I wouldn't really know how to help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment