EditorNode3DGizmo in Godot – Complete Guide

Understanding the power of EditorNode3DGizmo in Godot 4 can be a game-changer for developers seeking to create sophisticated and interactive 3D games. Gizmos are essential tools for providing visual feedback and precise control over the 3D objects in your scenes. In this tutorial series, we’ll uncover the capabilities of the EditorNode3DGizmo class and demonstrate how you can leverage them to enhance your game development workflow.

What is EditorNode3DGizmo?

The EditorNode3DGizmo class in Godot 4 is a high-level API designed to facilitate the creation of custom gizmos within the editor. Gizmos are interactive tools that appear in the 3D viewport, which aid in manipulating and editing Node3D objects directly. They enhance the visual representation and interaction of your game’s components, making them indispensable in refining your game design.

What is EditorNode3DGizmo used for?

The primary use of the EditorNode3DGizmo is to add visual handles, lines, meshes, and other editable elements to the editor’s 3D viewport. These elements help developers to transform and fine-tune their game objects without delving deep into numerical values. The gizmo serves both aesthetic and practical purposes by providing a real-time, intuitive way to adjust the properties of Node3D objects.

Why should I learn to use EditorNode3DGizmo?

Learning to use the EditorNode3DGizmo is valuable for several reasons:
– It offers enhanced control and precision when editing Node3D objects.
– Custom gizmos can be created to cater to specific game development needs, elevating the efficiency and ease of your workflow.
– Understanding this feature deepens your knowledge of Godot’s Editor, unlocking more of its robust, built-in tools.
– It empowers you to create professional and feature-rich games that utilize advanced interactions in the editor.

Unlocking these capabilities ensures you are equipped with a modern toolkit that lets you bring your most imaginative game ideas to life. Whether you’re a beginner or an experienced coder, grasping the intricacies of EditorNode3DGizmo will enrich your Godot expertise and facilitate the creation of amazing 3D games.

CTA Small Image
FREE COURSES AT ZENVA
LEARN GAME DEVELOPMENT, PYTHON AND MORE
ACCESS FOR FREE
AVAILABLE FOR A LIMITED TIME ONLY

Creating a Simple 3D Gizmo in Godot 4

In this section, we will walk through the steps involved in creating a simple 3D gizmo within Godot 4. We’ll start by setting up a custom gizmo plugin and then proceed to add basic visual elements.

First, create a script inheriting from EditorPlugin, which is where we’ll add our custom gizmo:

extends EditorPlugin

func _enter_tree():
    # Register the custom gizmo when the plugin is enabled
    add_custom_type("My3DGizmo", "EditorNode3DGizmoPlugin", preload("res://My3DGizmo.gd"), null)

func _exit_tree():
    # Unregister the custom gizmo when the plugin is disabled
    remove_custom_type("My3DGizmo")

Next, define the gizmo’s script. This script inherits from EditorNode3DGizmoPlugin:

extends EditorNode3DGizmoPlugin

func _get_name():
    return "My 3D Gizmo"

func _can_be_visible(editor):
    return true

func _has_gizmo(obj):
    # Replace "MyNode3D" with the actual node name you want the gizmo to work with
    return obj is MyNode3D

With the gizmo and plugin scripts ready, we can now add visual elements like handles and lines:

func _create_gizmo_instance():
    var gizmo = EditorNode3DGizmo.new()
    gizmo.add_handle(Vector3(1, 0, 0), true) # Add a move handle along the X-axis
    gizmo.add_lines(PoolVector3Array([Vector3.ZERO, Vector3(1, 0, 0)]), Color.red) # Add a red line along the X-axis
    return gizmo

This code creates a move handle that can be dragged along the X-axis and a red line indicating the handle’s path.

Interacting with Gizmo Elements

Now, considering we have visual elements, we need to define how we can interact with them.

Handle interaction:

func _commit_handle(idx, restore, cancel):
    var obj = get_edited_object()
    var xform = obj.get_global_transform()
    var handle_position = get_handle_position(idx)

    # Example transform, apply the movement along the X-axis.
    if idx == 0: # Check the handle index
        xform.origin.x = handle_position.x

    if not cancel:
        obj.set_global_transform(xform)

The _commit_handle function gets called when a handle is moved, allowing you to update the object’s transformation based on handle position.

Adding visual feedback:

func _redraw():
    var gizmo = get_gizmo()
    gizmo.clear()
    
    var obj = get_edited_object()
    var size = Vector3(1, 0, 0) * obj.my_custom_size # Assume 'my_custom_size' is a property on your node
    
    # Update handle position based on the object's property
    gizmo.add_handle(Vector3(size.x, 0, 0), true)
    gizmo.add_lines(PoolVector3Array([Vector3.ZERO, Vector3(size.x, 0, 0)]), Color.red)

With the _redraw method, we update the gizmo every time there is a change, ensuring the visual elements reflect the current state of the object you are editing.

This concludes Part 2 of our tutorial, where we’ve looked into creating a custom 3D gizmo in Godot 4. In the next section, we will continue to enhance our gizmo with more advanced features and interactions.

Remember, these code examples serve as a stepping stone into creating more complex and interactive gizmos tailored to your needs. We at Zenva encourage you to experiment and explore the vast possibilities that custom gizmos open up in your game development journey.

We’ve introduced basic gizmo creation and interaction in Godot 4. Let’s dive deeper and explore advanced features to make your gizmo more interactive and visually engaging.

Enhancing gizmo functionality can involve adding multiple handles, incorporating custom shapes, and even responding to user input in real-time. Here we will add a rotation handle and demonstrate how to react to user actions.

Adding a Rotation Handle: Let’s add a circular rotation handle that allows users to rotate an object about the Y-axis.

func _create_gizmo_instance():
    var gizmo = EditorNode3DGizmo.new()
    ...
    gizmo.add_unscaled_billboard(get_script().get_icon("Rotate", "EditorIcons"), 0.05)
    return gizmo

The unscaled billboard ensures that the rotation icon stays consistent in size regardless of the distance from the camera.

Reacting to Rotation: To rotate the object based on this handle, we adjust the _commit_handle function:

func _commit_handle(idx, restore, cancel):
    ...
    # Assuming handle 1 is our rotation handle
    if idx == 1:
        var rotation_delta = get_handle_value(idx)
        obj.rotate_y(rotation_delta.y)

In this example, rotation_delta captures the amount the handle has moved since the last commit, rotating the object according to this delta.

Customizing Handle Shape: Custom handle shapes can provide additional context about their function. Here’s how to add a custom shape for translation handles:

func _create_gizmo_instance():
    ...
    var mesh = CubeMesh.new()
    mesh.size = Vector3(0.1, 0.1, 0.1) # Define the handle's size
    gizmo.add_mesh(mesh, false, null, Color.green)
    return gizmo

This code snippet creates a small cube-shaped mesh to be used as a handle, giving users a clear visual cue for grabbing and moving it.

Updating Gizmo in Real-Time: If your object properties change outside the gizmo interaction, the gizmo should update accordingly. Here’s how to refresh the gizmo when properties change:

func _notification(what):
    if what == NOTIFICATION_PROPERTY_CHANGED:
        _redraw()

func _redraw():
    var gizmo = get_gizmo()
    ...
    # Add/update handles and lines based on new properties

The notification method listens for property changes on the editor, invoking the redraw method to update our gizmo accordingly.

Interactive Gizmo Elements: Lastly, if you want certain gizmo elements to have hover feedback or selectable states, you should implement these properties into your gizmo:

func is_handle_highlighted(idx):
    return idx == current_highlighted_handle_index

func select_handle(idx):
    current_selected_handle_index = idx
    # Additional code for handling selection state...

These methods create visual indicators for hovering over and selecting different gizmo handles, contributing to a more intuitive editor experience.

By incorporating these advanced techniques, your gizmo can become a powerful tool that complements the Godot editor’s functionality, allowing for efficient and precise modifications to objects within your scene.

We at Zenva believe that a hands-on approach to learning provides the most value. As you integrate these advanced gizmo features into your Godot projects, you’re not just enhancing your current game, but also building a versatile skill set for future 3D development endeavors.

Stay tuned for the next installment, where we’ll explore how to integrate your custom gizmos into a collaborative team environment and ensure maximum compatibility across different systems within Godot.

Continuing from our exploration of advanced gizmo features in Godot 4, we will focus on how to refine gizmo behavior, ensuring usability and integration that aligns with the Godot editor’s standards. Let’s add even more interactivity and control to our custom gizmo with the following examples.

Snapping Handles to Grid: Many developers find grid snapping instrumental in aligning elements precisely. Implement grid snapping for gizmo handles with these code modifications:

func get_handle_value(idx):
    var t = EditorNode3D.get_singleton().get_gizmo_transform()
    var d = get_drag_vector(idx, t.origin, true, Vector3(1, 0, 0)) # Assuming the X-axis handle
    return snap_to_grid(d, true) # Snap to grid enabled

func snap_to_grid(value, snap_enable):
    if snap_enable:
        return value.snapped(Vector3(1, 1, 1)) # Snap to unit grid
    else:
        return value

With the methods above, you enable snapping of handle movements to the nearest whole number when grid snapping is enabled in the editor.

Adding a Custom Property to the Inspector: Developers can offer more control over the gizmo by exposing custom properties that can be edited directly in the inspector. Here’s how you can add a custom property.

func _get_property_list():
    var properties = []
    properties.append({
        name = "my_gizmo.size",
        type = Variant.TYPE_FLOAT,
        hint = PROPERTY_HINT_RANGE,
        hint_string = "0.1,10,0.1"
    })
    return properties

func _get(property):
    if property == "my_gizmo.size":
        return my_gizmo_size

func _set(property, value):
    if property == "my_gizmo.size":
        my_gizmo_size = value
        _redraw()
        return true
    return false

These methods integrate a new size property into the inspector that, when changed, triggers a redraw of the gizmo using the updated size.

Convenience Functions with Undo/Redo: To encourage non-destructive editing and trial-and-error, support undo and redo by implementing convenience functions. Here’s a snippet that demonstrates updating a node’s position with undo/redo support:

func move_node(editor, node, new_position):
    editor.undo_redo.create_action("Move Node")
    editor.undo_redo.add_do_method(node, "set_translation", new_position)
    editor.undo_redo.add_undo_method(node, "set_translation", node.translation)
    editor.undo_redo.commit_action()

This code creates an undo/redo action every time the node is moved, allowing users to easily reverse changes.

Gizmo Visibility Layers: Sometimes, you might want your gizmo to only be visible under certain conditions or layers. To set up visibility layers for gizmos, use the following example:

func _has_layer(node, layer):
    return node.layers & layer != 0

func _redraw():
    var node = get_edited_object()
    var layer = EditorNode3D.get_singleton().get_visual_instance_editor().get_instance_visibility_mask()
    if _has_layer(node, layer):
        # Draw gizmo
    else:
        # Hide gizmo

Here we check if the node’s layer and the current visual instance editor visibility mask have any common layers; if not, we don’t draw the gizmo.

Adjusting Gizmo Opacity and Style: To ensure gizmos are not obstructive, you can adjust their opacity and style to make them less distracting or more prominent when needed:

func _create_gizmo_instance():
    ...
    gizmo.material_override = create_custom_material()

func create_custom_material():
    var mat = SpatialMaterial.new()
    mat.albedo_color = Color(1, 0, 0, 0.5) # Semi-transparent red
    mat.flags_unshaded = true
    mat.flags_use_point_size = true
    return mat

The custom material above ensures the gizmo elements are semi-transparent, unshaded and have a consistent point size.

Through these code examples, we’re creating a more intuitive and accessible experience within the Godot editor for developers. Not only does this provide immediate feedback and control, but it also embeds non-destructive editing practices into your workflow, which is essential for complex and dynamic game development.

At Zenva, we advocate for a practical approach to development, reinforcing learning with real-world applications. By adding these sophisticated features to your gizmos, you’re setting a foundation for producing polished, professional, and adaptable games. We encourage you to challenge yourself by incorporating these refined techniques into your game project and observing the significant impact it has on your design process.

Continuing Your Game Development Journey

Embarking on the journey of mastering Godot and game development can be both thrilling and challenging. With the skills you’ve gathered thus far, your potential to create engaging and interactive games is boundless. Yet the path to mastery doesn’t end here; it’s a continuous pursuit of knowledge and practice.

To further your learning and expand your expertise, we invite you to explore Zenva’s Godot Game Development Mini-Degree. This comprehensive collection of courses is designed to take you from the basics all the way to building cross-platform games using the latest features of Godot 4. You’ll dive into creating 2D and 3D games, understanding GDScript, and mastering gameplay mechanics for various game genres, thus bolstering your development toolkit and portfolio.

For an even broader range of topics and expertise, check out our full suite of Godot courses. These self-paced courses tailor to both beginners and experienced developers—it’s the ideal platform to learn at your own pace, whenever you choose. With the ability to learn coding, create games, and earn certificates, Zenva supports your growth from beginner to professional. Take the next step and harness the power of Godot to bring your imaginative worlds to life.

Conclusion

In this interactive tutorial series, we’ve journeyed through the creation and enhancing of custom gizmos in Godot 4, expanding your abilities in game development. With these new skills in hand, you’re well-equipped to take on the challenges of creating unique, responsive, and highly polished 3D games. We encourage you to practice what you’ve learned and remember that this is only the beginning of discovering Godot’s full potential.

At Zenva, we are committed to being part of your lifelong learning adventure. Whether you’re looking to refine your current skills or embark on new ones, our Godot Game Development Mini-Degree awaits to guide you through every step of the way. Take this moment to consolidate your knowledge and amplify your game development career. Join us at Zenva, where we bring learning to life.

FREE COURSES
Python Blog Image

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.