Search code examples
godotgodot4

How to mimic the Transform property in the inspector in Godot


I am using Godot 4.1. I want to add a property via a script export to my node3d that has translation and rotation tools in the inspector, exactly like a Node3D's existing Transform property.

This is what I want to achieve:

The controls I am referring to

Precisely, I want an X, Y, Z position field, followed by a X, Y, Z Rotation field (ideally with the little sliders too). I would not mind also having the Scale, Rotation Edit Mode, etc either, if it makes the solution easy to achieve.

I have tried adding a Transform3D, but that exposes a transformation matrix, which is not the desired control:

Script of failed attempt Resulting transformation matrix

How can the desired controls be achieved?


Solution

  • Those two fields are pretty close to Vector3. So I think the best approximation you're going to get is

    @export var position: Vector3
    @export var rotation: Vector3
    

    which looks like

    screenshot of the Godot editor's rendered fields

    This doesn't have the sliders or the nice "degree sign" and "meters" suffixes. As far as I can tell, that's not possible in pure GDScript. The C++ core library that Godot is written in allows for some more flexibility in property hints. This is what Node3D's properties are actually defined as

    ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position", PROPERTY_HINT_RANGE, "-99999,99999,0.001,or_greater,or_less,hide_slider,suffix:m", PROPERTY_USAGE_EDITOR), "set_position", "get_position");
    ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_less,or_greater,radians", PROPERTY_USAGE_EDITOR), "set_rotation", "get_rotation");
    

    That syntax, in C++ at least, allows a ton of flexibility. But on the GDScript side, we can only use PROPERTY_HINT_RANGE with floats, not vectors. That is, this fails

    @export_range(-360, 360) var rotation: Vector3
    

    Despite being a mostly-faithful translation of the C++ code above.

    Now, if you're willing to give up the sliders, this can still do whatever you want. If you have a @tool script, then set and get declarations on a variable are respected, even in the editor.

    @tool
    extends Node
    
    # The real transformation that's used on the backend
    var transform: Transform3D
    
    @export var position: Vector3:
        set(value):
            # Do whatever on earth you want here :)
            position = value
            transform = whatever
    @export var rotation: Vector3:
        set(value):
            # Do whatever on earth you want here :)
            rotation = value
            transform = whatever
    

    The set(...) block will be called every time the variable is set from outside the class itself, and that includes being set by the editor textboxes. I've done this several times: There's a variable that shows up in the editor but is never used at runtime, instead being immediately replaced by the real backend variable that I actually want. But it looks nice in the editor.

    If you do decide to go that way and have a custom set(...), make sure to still set the original position and rotation variables in your setter (or write a getter as well). Even if you never use them at runtime, the values of the variables (resp. the values returned by the getters) will be used as the current value shown in the editor.