tag:gamedevadventures.posthaven.com,2013:/posts Game Development Adventures 2021-08-22T10:36:02Z Dan tag:gamedevadventures.posthaven.com,2013:Post/1260490 2018-03-13T10:38:00Z 2021-08-22T10:36:02Z Using C++ and GDNative in Godot - Part 1

This is a tutorial to help you get up and running with C++ GDNative plugins in Godot 3. We will install all of the prerequisites, write a some simple C++ code, make it available from Godot and access it from GDScript. Then, in Part 2, we will create a Player class which implements KinematicBody2D, exports properties to the Godot editor, emits signals, accesses other nodes from the scene and calls a GDScript function to implement user callbacks.

The sample code for this tutorial can be found on GitHub.

So, lets get started!

The first thing we have to do is to install the dependencies. In order to use C++, we need to install godot-cpp, which itself relies on godot_headers. We also need to get the SCons build tool, which is used by Godot and its sub-projects and we will also use it to build our code. You can find some instructions for installing SCons in its documentation. On OSX, you can simply run:

Open up your terminal and create a new directory (I'll refer to it as the tutorial root directory from now on). We will do everything inside this directory for now. Enter the directory and clone the godot_headers and godot-cpp repositories:

Next, we need to generate the C++ wrapper classes and build the library. You can use either GCC or clang/LLVM (on Windows, VC++ is always used, if you use mingw or something else, you will need to modify the SConstruct file). The wrapper classes are largely auto-generated from an export by the Godot binary, so we need to tell scons where to find it on your system.

Change into the godot-cpp directory and build. On my OSX machine, I have Godot installed to my Applications and the command is:

Be sure to edit godotbinpath to point at your Godot binary, remove use_llvm=yes if you do not use llvm and change platform=osx to match your platform (valid options are platform=linux, platform=windows and platform=osx). If you are compiling for a 32bit platform, add arch=32 to the end of the above command. This will now generate C++ and compile the source files for all of the various classes (Node-derived classes and others) that your version of Godot has installed (including modules). This process expects to find the godot headers in ../godot_headers, but if you wish to store them elsewhere or name the directory something else (for example, if you are building Godot from source, you may wish to use the headers from that to make sure you have the latest), you can specify their location with headers=new/path/. The compilation may take a few minutes.

You can inspect the newly generated headers in include/ and the generated sources in the src/ directory. It will also compile them into a binary, libgodot-cpp.a on OSX, libgodot-cpp.linux.64.a on 64bit Linux and similar names on other platforms/architectures.

Go back to the tutorial root directory and create a new subdirectory, lets call it simple. Go into this directory. We will place everything for our first GDNative experiment into this directory. In a real project, you will want to organise the various files better, instead of just dumping everything here. Now its time to get into some code! For this project, we will put everything into one source file. So create a file, simple.cpp, and open it in your favourite text editor.

Add the following code:

First, we define our class, Simple, which is a Reference (basically, the most fundamental simple object type we can use in Godot). GodotScript is a wrapper class, which may go away in the future. For now, just know that we need to derive from it with our "actual" base class as a template parameter. The GODOT_CLASS(Simple) magic macro is important, as without it, Godot won't correctly recognise your class and your properties and signals won't be visible in the editor.

Next, we implement our class’ methods. For this example, we will implement two methods: one which takes an argument and prints it to the Godot console using Godot's built-in print function, and another one which takes a string argument, constructs a message and returns it. This way you get to see both arguments and return values in action, as well as how to use Godot's string formatting. The only thing to note here is that we are using Godot's String class and not std::string or something else. In general, we use Godot's data structures and classes where possible as this is necessary for both calling Godot's methods and Godot calling ours (eg, from GDScript or built-in methods like _ready).

The string formatting is perhaps a little counter-intuitive, but its worth learning: String("Hello, {0}!").format(Array::make(target)) First, a format string is created with String("Hello, {0}!"). The {0} is the placeholder that will be replaced. Placeholders are numbered, so if you have multiple placeholders, you can arrange them in a different order from the input array as well as reference them multiple times (neither of which are possible with the printf-style string formattting we have in GDScript). Then we call the format method, which takes an array of values to insert in place of the placeholders. In this example, there is only one value, target. We construct this array using Array::make(...), which is a variadic function which packs its arguments into a new Array object.

It is also possible to have named placeholders. We use them by replacing the index in the format string with a name "Hello, {person}!" and then passing a Dictionary instead of an Array. We can use Dictionary::make(key, value, ...) to construct a Dictionary object. So, for example, our above code could be replaced with the following instead and it would work the exact same, except that it uses a named placeholder:

String("Hello, {target}!").format(Dictionary::make("target", target))

Next up, we have the register_methods static method. Every custom class that we wish Godot to know about must implement this and its how we tell Godot about our classes methods, properties and signals. Our class has only two methods, which we register here in turn, using the register_method function, which takes as its first argument a string name of the method (this is the name that Godot and, importantly, GDScript will see it as. Its second argument is a reference to the method itself.

We're almost done, just a little boilerplate to go! The godot_gdnative_init and godot_gdnative_terminate functions are used to setup and tear down the GDNative library. We don't do anything here, but if you need to initialise and terminate a third party library, you can do so here. Finally, godot_nativescript_init initialises the GDNativeScirpt system, which is how our C++ code can be used in place of GDScirpt (and attached to Nodes). The first line is some necessary GDNativeScript boilerplate, but the second line is ours and is important if we want Godot to know about our class! We simply call register_class with our custom class as a template parameter and now Godot knows all about it!

And that's it for our simple GDNativeScirpt! One final note, much of the functions are in the godot namespace, eg godot::register_class, but since we specified using namespace godot; at the top of the file, for convenience, the function names are not namespace qualified.

Before we can compile, we need a build file for scons, so create a file called SConstruct now, and insert the following:

This file is largely boilerplate. The most important details are: lines 14 and 15 tell it where godot_headers and godot-cpp are located. You can override this by setting the GODOT_HEADERS and CPP_BINDINGS environment variables. Line 56 tells it where to find the source files. If you were to put the sources into a src/ directory, you would change "." to "src/". Finally, line 58 specifies the name and location of the library we're building. If you wanted to call it libFoo and place it in a bin/ directory instead, you can do so here by changing target="libSimple" to target="bin/libFoo".

The actual library name will depend on your platform: on OSX its libSimple.dylib, on Linux its libSimple.so and on Windows its libSimple.dll

We are now ready to compile, and doing so is easy:


Remember to set your platform appropriately, as before, and remove use_llvm if you do not wish to use clang/LLVM to compile. If you are compiling for a 32bit platform, add arch=32 to the end of the above command. After a few moments, it should have compiled and you'll have a brand new binary ready for use!

The next order of business is to fire up Godot. Create a new project and save it to your 'simple' directory, alongside your C++ code and library. In a real project, you will want to organise this more neatly.

Create a scene and add a Node to it. Save the scene as Main.tscn (or whatever you want).


Now we get onto the important bits. Click on the "Create new resource in memory and edit it" button, in the Inspector:Next, create a new GDNativeLibrary resource:


In the GDNativeLibrary panel at the bottom of the Godot editor, scroll down to your platform and click the folder icon. For me, on 64bit OSX, I select the marked icon:

Select your new library. On OSX, mine is libSimple.dylib, yours will be different if you are on Linux or Windows:

Next, you have to save your GDNativeLibrary settings. This is very important and easy to forget. You must save it using the save icon in the inspector:

Save the resource as simple.gdnlib:

We now need to create a NativeScirpt resource. Its possible to call your GDNativeLibrary directly, as seen here (you would use "res://simple.gdnlib"), but its more cumbersome. As before, you use the "Create new resource in memory and edit it" button from the inspector.

In the inspector, for this resource, enter your class name, Simple, as the "Class Name" property, then select Library and click on Load:

Now select your simple.gdnlib file:

Save the file using the save icon in the inspector (make sure that the Class Name and Library are correctly set):

Save this resource as simple.gdns:

You have now created and setup a GDNative library and NativeScirpt resource. The only thing left to do is to use it!

To use it, we will attach a GDScript to our scenes node:

Create a new GDScript as normal. I simply called mine Main.gd:

Now enter the GDScript code:

The first line in the _ready function loads our simple.gdns NativeScript class and creates a new instance of it. The variable simple now contains an instance of our C++ Simple class. The second line calls our two functions. We pass the return value of hello as the argument to say.

Now run the Main.tscn scene, and you should see the message in Godot's output window:


Congratulations! You have successfully created and called a GDNative NativeScript C++ library.

Don't forget to check out the sample code and Godot project for this tutorial, if you are having any difficulties, and it is also worth checking out the official GDNative demos.

In Part 2, we will expand on this by looking at the godot-cpp wrapper classes in a little more detail, by implementing a subclass of KinematicBody2D, registering properties and signals, accessing the scenes nodes and calling GDScript functions.





]]>
Dan