Skip to main content

C++ Scripting

In this chapter you'll learn how to get started with C++ Scripting using Jenova Framework in Godot.

Script Anatomy

A Jenova C++ Script consists of several parts. This is the most basic C++ Script in Jenova Framework:

Jenova C++ Script
/* ------ Script Imports ------ */

// Godot SDK
#include <Godot/godot.hpp>

// Jenova SDK
#include <JenovaSDK.h>

// Namespaces
using namespace godot;
using namespace jenova::sdk;

/* ------ Script Features ------ */

// Set Class Name
JENOVA_CLASS_NAME("Jenova C++ Script")

/* ------- Script Block ------- */

// Script Block Begin
JENOVA_SCRIPT_BEGIN

/* Write C++ Script Here */

// Script Block End
JENOVA_SCRIPT_END

Script Imports

To start working with the engine, you need to import the required types from GodotSDK and JenovaSDK. While JenovaSDK is optional, it is recommended because of its helpful types and functions. After importing, include the relevant namespaces to simplify coding and avoid namespace prefixes for each type or function.

Script Features

Jenova provides features you can use in your script :

  • JENOVA_CLASS_NAME(<string>): Assigns a unique name to your C++ Script, displayed in the Inspector.
  • JENOVA_TOOL_SCRIPT: Executes the C++ Script within the editor. See Tool Scripts for more details.
  • JENOVA_PROPERTY(<type>, <name>, <value>, ...): Exposes properties from your script to the Inspector. This is particularly useful when working with scene data, allowing you to customize values without modifying the script. See Properties for more information.
  • JENOVA_GODOT_SDK: Includes the entire GodotSDK headers in your script. This option is not recommended as it significantly slows down script compilation.

Script Block

Jenova C++ Scripts require that you write code within a Script Block, defined using these macros :

  • Script Block Begin Macro (JENOVA_SCRIPT_BEGIN)
  • Script Block End Macro (JENOVA_SCRIPT_END)

Functions and properties within the Script Block are parsed and serialized into Metadata by the Jenova Symbol Parser making them accessible to the game engine.

Note

You can place any function and value outside of the Script Block. The only difference is that they won't be serialized but can still be used anywhere in the code, Including inside the Script Block. It's recommended to place utility and helper functions outside of the Script Block.

Functions inside a Script Block are called "Script Callbacks", They are equivalent to GDScript Signals. Depending on the type of Node the script is assigned to, You can define signals that will be called by the Interpreter. Some common callbacks available for all Nodes include :

  • OnReady : Translates to _ready
  • OnAwake : Translates to _enter_tree
  • OnDestroy : Translates to _exit_tree
  • OnProcess : Translates to _process
  • OnPhysicsProcess : Translates to _physics_process
  • OnInput : Translates to _input

Additionally, you can use any other function exposed by a Node based on its type.

For example, if a C++ Script is added to a Control or CanvasItem you can define:

void _gui_input(const InputEvent* p_event)
{
/* Handle Input Events */
}
Note

Keep in mind that you are not forced to use the simplified function names and You're allowed to use the original name.

For instance You can use _ready instead of OnReady or use _enter_tree instead of OnAwake It's up to you!

Caution
  • All functions defined within the Script Block must use parameter types compatible with godot::Variant
  • On Linux do not use typedefs as function return types. Use Godot-compatible types directly.

Tool Scripts

C++ Scripts can execute within the Godot Editor by including JENOVA_TOOL_SCRIPT in the script code. This is similar to @tool in GDScript.

Tool Scripts are useful for implementing tools and utilities in the Godot Editor.

Jenova C++ Script
// Godot SDK
#include <Godot/godot.hpp>

// Jenova SDK
#include <JenovaSDK.h>

// Namespaces
using namespace godot;
using namespace jenova::sdk;

// Enable Execution in Editor
JENOVA_TOOL_SCRIPT

// Script Block Begin
JENOVA_SCRIPT_BEGIN

// Callbacks
void OnProcess(double delta)
{
Output("Editor Tool Script Executed with Delta: %lf", delta);
}

// Script Block End
JENOVA_SCRIPT_END

After compiling the above script, you will see messages printed in the Output window.

Screenshot_JenovaToolScriptExecution

Output Window, Tool Script executing in Editor

Note

Due to the mechanism of Godot's scripting backend, You must close and reopen your scene to see the effects of Tool Scripts.

Macros

The Jenova Compiler Interface defines some macros for every C++ Script source that can be useful :

  • JENOVA_VERSION: Represents the version of the Jenova Runtime as a X.X.X.X string for feature control.

    #define JENOVA_VERSION "0.3.6.5"
  • JENOVA_COMPILER: Indicates the compiler name used to compile scripts as a string.

    #define JENOVA_COMPILER "Microsoft Visual C++ Compiler"
  • XXXX_COMPILER: Unique to each compiler, This macro allows cross-compiler code control to define features specific to each compiler.

    #define MSVC_COMPILER
    #define MINGW_COMPILER
    #define GCC_COMPILER
    #define CLANG_COMPILER
  • TOOL_SCRIPT: When using JENOVA_TOOL_SCRIPT option inside your script, You can determine and define which part of code must be run inside editor as tool.

    Jenova C++ Script
    void OnReady()
    {
    #ifdef TOOL_SCRIPT
    jenova::sdk::Output("Hello from C++ Tool Script!");
    #else
    jenova::sdk::Output("Hello from C++ Script!");
    #endif // TOOL_SCRIPT
    }
  • JENOVA_CALLBACK: Enables inline handling of signals, making tasks like updating UI elements after animations more efficient. [ Advanced ]

  • JENOVA_EXPORT: Allows exporting a function from the Jenova Module for use in third-party modules and libraries. [ Advanced ]

You can also define custom definitions at Editor Settings > Jenova > Preprocessor Definitions

It's possible to define Flag, Value and Function Macros. Just do not forget to separate them by a semicolon (;)

Example : WIN64_JENOVA;Author "Hamid.Memar";FUNC_ADD(x,y) x+y

📖 Learn more about Jenova Editor Settings

NitroJIT vs Meteora

When using different Interpreter Backends it's crucial to understand their differences to prevent conflicts and crashes.

Jenova currently employs two Interpreter backends :

  • NitroJIT
  • Meteora

Below is a minimal example to demonstrate the differences between these two backends.

Jenova C++ Script using NitroJIT Backend
void _process(Caller* instance, Variant& delta)
{
Output("Delta : %lf", double(delta));
}
Variant _get_configuration_warnings()
{
PackedStringArray warnings;
warnings.push_back("Some Configuration Warning");
return warnings;
}
Jenova C++ Script using Meteora Backend
void _process(Caller* instance, double delta)
{
Output("Delta : %lf", delta);
}
PackedStringArray _get_configuration_warnings()
{
PackedStringArray warnings;
warnings.push_back("Some Configuration Warning");
return warnings;
}