Skip to main content

Unboxed Arguments

Unboxed arguments is a default-enabled property on a Sandbox node. It makes function calls into the Sandbox unwrap Variant arguments into primitive types or class wrappers when available. These class wrappers aim to provide the functionality of both GDScript and godot-cpp when it makes sense to do so.

Conversion table

When making a function call into a Sandbox program, sandbox.vmcall("my_function", ...) or sandbox.my_function(...), the arguments will translate into the column on the right:

VariantGDScript func argumentUnboxed type
NILnullN/A
BOOLtrue / falsebool
INT1234int, long
FLOAT5678.0double
STRING"Hello World!"String
VECTOR2Vector2(1, 2)Vector2
VECTOR2iVector2i(1, 2)Vector2i
VECTOR3Vector3(1, 2, 3)Vector3
VECTOR3iVector3i(1, 2, 3)Vector3i
TRANSFORM2DTransform2D(...)Transform2D
VECTOR4Vector4(1, 2, 3, 4)Vector4
VECTOR4iVector4i(1, 2, 3, 4)Vector4i
PLANEPlane(...)Plane
QUATERNIONQuaternion(...)Quaternion
AABBAABB(...)Variant
BASISBasis(...)Basis
TRANSFORM3DTransform3D(...)Transform3D
PROJECTIONProjection(...)Variant
COLORColor(...)Color
STRING_NAMEStringName(...)String
NODE_PATHNodePath(...)String
RIDRID()RID
OBJECTObject
CALLABLECallable
SIGNALVariant
DICTIONARYDictionary(...)Dictionary
ARRAYArray(...)Array
Packed ArrayGDScript func argumentUnboxed type
PACKED_BYTE_ARRAYPackedByteArray(...)PackedByteArray
PACKED_INT32_ARRAYPackedInt32Array(...)PackedInt32Array
PACKED_INT64_ARRAYPackedInt64Array(...)PackedInt64Array
PACKED_FLOAT32_ARRAYPackedFloat32Array(...)PackedFloat32Array
PACKED_FLOAT64_ARRAYPackedFloat64Array(...)PackedFloat64Array
PACKED_STRING_ARRAYPackedStringArray(...)PackedStringArray
PACKED_VECTOR2_ARRAYPackedVector2Array(...)PackedVector2Array
PACKED_VECTOR3_ARRAYPackedVector3Array(...)PackedVector3Array
PACKED_VECTOR4_ARRAYPackedVector4Array(...)PackedVector4Array
PACKED_COLOR_ARRAYPackedColorArray(...)PackedColorArray

An astute reader will notice that whenever a wrapper class is not yet implemented, the Variant type is passed unchanged, as a Variant. Variants can use calls, and so even though many are already implemented as wrapper classes, here is an example of how to use any class through a Variant:

Variant plane = ...;
float distance = plane("distance_to", Vector3(1, 2, 3));

Variant quaternion = ...;
quaternion = quaternion("inverse");

And when there is a wrapper, it's designed to be passed by value as a function argument:

Variant my_plane(Plane p) {
return p.distance_to(Vector3(1, 2, 3));
}

Variant my_quaternion(Quaternion q) {
return q.inverse();
}
note

Sandboxed properties does NOT use unboxed arguments. All arguments and the return type are Variants. Function return values are always Variant.

Conversion table (Objects)

ClassGodot Sandbox API
ObjectObject
NodeNode
Node2DNode2D
Node3DNode3D

Nodes inherit from Object.

See the API sugaring documentation to see how to add your own wrapper classes.

Example function calls

A player node

func gdscript():
sandbox.vmcall("handle_player_physics", get_node("Player"), 1.0)

Which we can choose to receive as a CharacterBody2D, Node2D, Node or even Object in the Sandbox program:

extern "C" Variant handle_player_physics(CharacterBody2D player, double delta) {

Input input = Input::get_singleton();

// Handle jump.
Vector2 velocity = player.velocity();
if (input.is_action_just_pressed("jump") && player.is_on_floor())
velocity.y = jump_velocity;

// Get the input direction and handle the movement/deceleration.
float direction = input.get_axis("move_left", "move_right");
if (direction != 0)
velocity.x = direction * player_speed;
else
velocity.x = fmin(velocity.x, player_speed);
player.set_velocity(velocity);

return player.move_and_slide();
}

Since the Player node is a CharacterBody2D, we should prefer using that, since it gives us access to the most functionality.

Godot can call Sandbox functions

We can for example handle inputs with _input directly, which passes an Object of some kind of Input-derivative as the first argument:

extern "C" Variant _input(InputEvent event) {
if (event.is_action_pressed("jump")) {
get_node().set("modulate", 0xFF6060FF);
} else if (event.is_action_released("jump")) {
get_node().set("modulate", 0xFFFFFFFF);
}
return Nil;
}

If we attach a Sandbox as a script to a Node that can receive inputs, Godot will directly call this function for us with an InputEvent as argument.

The above example modulates the current node based on the jump action. We know the functions provided by InputEvent from reading the Godot documentation on InputEvent. The current Node is our Node2D coin, and it can be modulated. We could cast it to a Node2D and do a Node2D(get_node()).set_modulate(0xFF6060FF);, but it was just easier to set the property using .set().

note

All Sandbox functions must return a Variant.