Hi everyone,
I just finished going through the 'Your First 2D Game' tutorial using the C++ code shown in the tutorial. There were a couple challenges I faced along the way, so I kept notes on how I resolved them. I thought I would share those notes here in case they prove helpful to anyone else trying to complete this project. (I know that GDNative is being phased out in Godot 4.0, so this tutorial will only be relevant for Godot 3.)
Using a completed version of the project as a reference:
This completed version of the project was a huge help with troubleshooting my code. If something wasn't working as expected, going to this original project often helped me figure out what I was doing wrong.
To compile this completed version of the project, I took the following steps:
- Created a folder (which I’ll call the compilation folder) in which I could store the different components of the game
- Downloaded and unzipped the gdnative-demos repository, then moved the materials within the dodge_the_creeps folder into the compilation folder
- Downloaded and unzipped the version of godot-cpp referenced in the dodge_the_creeps folder, then copied its contents into the (empty) ‘godot-cpp’ folder within the file structure of dodge_the_creeps (using the dodge_the_creeps GitHub page as a guide)
- Downloaded and unzipped the version of godot-headers referenced in the godot-cpp folder, then copied its contents into the (empty) ‘godot-headers’ folder within the file structure of godot-headers (using the godot-cpp GitHub pages as a guide)
- Using X64 Native Tools Command Prompt for VS 2022, I navigated to the compilation folder (e.g. at the level where the SConstruct file appeared), then ran ‘scons platfrom=windows’. This compiled the project successfully.
Setup:
The project setup page had a link to a 'dodge_assets_with_gdnative.zip' file that I downloaded. I also downloaded and generated bindings for the 3.4 version of the godot-cpp and godot-headers repositories.
I was not able to compile this version of the project, as a few lines needed to be commented out first. See my question and answer at this thread for more details.
Coding the Player section:
I ran into the following error when trying to export the project right before the Choosing Animations section of Coding the Player:
entry.obj : error LNK2019: unresolved external symbol "public: static void __cdecl Play-er::_register_methods(void)" (?_register_methods@Player@@SAXXZ) referenced in func-tion "void __cdecl godot::register_class<class Player>(void)" (??$regis-ter_class@VPlayer@@@godot@@YAXXZ)
project\gdnative\windows\libdodgethecreeps.dll : fatal error LNK1120: 1 unresolved ex-ternals
scons: *** [project\gdnative\windows\libdodgethecreeps.dll] Error 1120
scons: building terminated because of errors.
It looks like this occurred because the register_methods function in player.cpp was incomplete at this point.
At this point, only the ready() and process() functions had been included within player.cpp, so I included those parts from the completed copy of the project:
void Player::_register_methods() {
godot::register_method("_ready", &Player::_ready);
godot::register_method("_process", &Player::_process);
}
After including those items, the code compiled successfully.
Also note that, in order to see custom properties (such as a sprite’s ‘speed’ property) as a variable within Godot, you need to register those properties within the register_methods section also. (See the ‘Preparing for collisions’ section of the ‘Your first 2D Game’ tutorial.
If you try to compile a script with register_methods() components that don’t have a corresponding entry in another .cpp file, you’ll get an ‘unresolved externals’ error message—so make sure that both are in place (or do the other entry first).
For example, the following code:
godot::register_method("_on_Player_body_entered", &Player::_on_Player_body_entered)
will raise a compilation error until you add in the following code to player.cpp:
// This code goes in `player.cpp`.
void Player::_on_Player_body_entered(godot::Node2D *_body) {
hide(); // Player disappears after being hit.
emit_signal("hit");
// Must be deferred as we can't change physics properties on a physics callback.
_collision_shape->set_deferred("disabled", true);
}
(Both of these lines of code come from the preparing for collisions section of the tutorial. )
The Main Game Scene:
When trying to compile the code within the The Main Game Scene section of the tutorial, I received errors related to line 21 of the main.hpp file. This was probably because the code in question (HUD* _hud;
) was referring to an HUD item that had not yet been created. I needed to comment out this code (along with other references to the HUD) in order for the code to compile.
I also didn’t see the ‘mob scene’ property in the Script Variables component of the inspector at this point. This was likely because at least one ‘include’ line related to the mob scene had been commented out. (I think the line in question was the #include "mob.hpp" section within entry.cpp; however, I probably needed to uncomment other lines also.)
When I put them back in, I received the following error:
entry.obj : error LNK2019: unresolved external symbol "public: static void __cdecl Mob::_register_methods(void)" (?_register_methods@Mob@@SAXXZ) referenced in function "void __cdecl godot::register_class<class Mob>(void)" (??$register_class@VMob@@@godot@@YAXXZ)
project\gdnative\windows\libdodgethecreeps.dll : fatal error LNK1120: 1 unresolved externals
I figured that one of my ‘mob’ source files might be missing some lines of code and thus causing this error.
In the completed file, I found this additional code within mob.cpp:
void Mob::register_methods() {
godot::register_method("ready", &Mob::ready);
godot::register_method("on_VisibilityNotifier2D_screen_exited", &Mob::_on_VisibilityNotifier2D_screen_exited);
}
I then entered it into my project’s mob.cpp file. Once I included them, the scons process ran successfully.
In addition, once I went back into Godot, I finally saw the Mob Scene item within the Script Variables section of Main.tscn’s Main node. After dragging Mob.tscn into this box (as specified in the instructions), I was able to play the scene and get the expected results.
Heads Up Display section:
Within the Heads Up Display section, I changed the name of StartMessageTimer and similar variable names in the C++ code so that they matched the 'MessageTimer' name provided in the tutorial. In retrospect, it probably would have been easier to just change the 'MessageTimer' name to 'StartMessageTimer.'
I needed to uncomment additional HUD-related code from multiple files in order for this code to work correctly. (For instance, I wasn’t seeing a start_game() signal within the Signals list of the HUD node within Main.tscn. That was because I had commented out the godot::register_class<Player>(); line within entry.cpp.
When I tried running the scene at the “Now you're ready to play! Click the "Play the Project" button” part of the HUD code, the game crashed after pressing the ‘Start’ button, and I received the following error:
“ERROR: (Node not found: "GetReadyMessageTimer" (relative to "/root/Main/HUD").)
at: (scene/main/node.cpp:1325)
ERROR: (Node not found: "StartButtonTimer" (relative to "/root/Main/HUD").)
at: (scene/main/node.cpp:1325)”
(I also received an error saying that the ‘MessageTimer’ node could not be found, but after I updated the code to use ‘Message’ instead of ‘MessageTimer,’ this issue went away.)
Indeed, these nodes were not shown within the project. They weren’t present in the HUD setup documentation, but they were shown in the completed version of the project that I downloaded from GitHub, so I added them based on how they appeared there (including their signal connections).
In order to connect the StartButtonTimer’s timeout() signal to the StartButton’s show() method (as shown in the completed project), I needed to select ‘advanced’ within the signal connection page; click on StartButton; and then type in ‘show’ as the receiver method.
After completing these updates, the game ran as expected.
This section didn’t have corresponding C++ code for the GodotScript music code, so I found the relevant lines in the completed version of the game.
Here are the equivalent C++ lines for the GodotScript lines shown in the walkthrough. (You can add them below the existing main.cpp() code within in each function; see the walkthrough for the functions to which these lines should be added)
$Music.play(): _music->play();
$Music.stop(): _music->stop();
$DeathSound.play(): _death_sound->play();
In addition, you’ll need to uncomment or add in the following lines within Main::_ready():
_music = get_node<godot::AudioStreamPlayer>("Music");
_death_sound = get_node<godot::AudioStreamPlayer>("DeathSound");