RandomMomentania

Godot Runtime 3D Gizmo Tutorial Part 2

Pre-tutorial note

This tutorial uses Markdeep! Markdeep renders to HTML locally on your web browser and this may take a second, so please be patient! 🙂



**Godot Runtime 3D Gizmo Tutorial Part 2**

# Part overview

In this part, we will be starting to make the 3D gizmos work in Godot, at runtime. Let's jump right in! # Making Gizmos ## Setup Before we start working on the gizmos, we need to do a few things so we are ready and know how everything is working. First let's look at how Editor_Viewport and its children nodes are setup.
* Editor_Viewport : The Viewport node that holds all of the gizmos. The plan is that anything we render in this viewport will be placed on top of what is rendered in the scene. See below for more details. * Gizmo_Camera : The Camera node that will render what it sees to the Editor_Viewport. * Gizmos : A Spatial node to hold all of the gizmos our editor has. This node is primarily used for organization purposes, and we'll also use it to position the gizmo at the selected object. * Translate : The translate/translation gizmo. This gizmo will move the selected object through 3D space when interacted with. * Handle_X : A StaticBody node that defines the X axis handle for the translation gizmo. The reason we are using StaticBody nodes is because we need something for the raycast to collide with. * Mesh : A MeshInstance node that will render the cube we are using for the handle. * CollisionShape : A CollisionShape node that defines the collision area for the handle. * Handle_Y : A StaticBody node that defines the Y axis handle for the translation gizmo. This has the same child node structure as Handle_X, just with a different material (green instead of red) and position. * Handle_Z : A StaticBody node that defines the Z axis handle for the translation gizmo. This has the same child node structure as Handle_X, just with a different material (blue instead of red) and position. * Body_Center : A StaticBody node that defines the center handle for the translation gizmo. The center handle will move the object relative to the camera, instead of on a single axis. This node has the same child layout as Handle_X, just with a different material (yellow instead of red) and position. * Rotate : The rotation gizmo. This gizmo will rotate the selected object when interacted with. It has a child node layout very similar to the Translate node. Feel free to take a look if you want, though there shouldn't be much, if any, difference in comparison to Translate. * Scale : The scale gizmo. This gizmo will scale the selected object when interacted with. It has a child node layout very similar to the Translate node. Feel free to take a look if you want, though there shouldn't be much, if any, difference in comparison to Translate. * Select : The select gizmo. This gizmo is purely visual and is just there to show which object is selected. * Mesh : A MeshInstance node to display the select cube. This uses the same yellow material as the center gizmo meshes. As you can see, this is quite a few nodes! There are a few things we should go over before we start writing the code we'll need to make the gizmos work. ### Viewport setup First I want to quickly mention some points about the Viewport node that tripped me up when working on this project. First let's take a look at the Editor_Viewport node's properties. Please ignore the Script Variables section, it is only there because I took the picture using the completed project, and will not be there until we write the script.
There are a few things we'll need to set for the viewport to work like we want it to, some of them obvious and some of them not. First, notice how we have defined the size of the viewport to be 1280x720. We will have the viewport change its size to always be the same size as the root viewport, but when the project is first initializing and we get try to get the render texture, if we do not set a viewport size by default there may be some shuttering as Godot tires to render into a 0x0 sized image. Just a bit lower in the properties list, you will find that the Transparent Bg property is enabled. The Transparent Bg property will make the viewport render the background with transparent pixels. If this property is not enabled, then the background will render solid black instead of transparent, which is not what we want. Another thing to note is the properties we have setup in the Render Target category. The only thing that is changed from the default Viewport settings is that we have enabled the V Flip property. In Godot, by default Viewports render upside down (for some reason) and so by enabling this property we can skip a step where we have to flip the viewport ourselves. This means that when we are using input events with a delta property, like mouse motion events, we need to flip the Y axis so the event acts like we would expect. Finally, and this one caught me by surprise, is how the World property works and how Godot physics work with Viewports. _____ First, let me quickly explain what a World does in Godot. A World object is responsible for controlling all of the physics and 3D environment settings for a Viewport. For example, the root viewport has its own world and when we use the Get_World function, we can access things like the direct physics space. Placing a WorldEnvironment world will override the default world environment of the viewport above it. So, I thought it would be a good idea to use a new World in the Editor_Viewport so we could use raycasting to detect which part of the gizmo we have selected without having to worry about colliding with other objects in the scene. Maybe it is a bug, or maybe I'm missing something, but I thought that by enabling the Own World property and supplying a new World object, that the Viewport would be using its own isolated world and then we could raycast into it without worrying about colliding with other physics objects in the scene. This is what the [documentation](https://docs.godotengine.org/en/3.0/classes/class_viewport.html) seems to imply as well. However, enabling the Own World property with a custom World object seems to make the physics not work at all for that Viewport. However, if we disable the Own World property but still have a custom World object, then physics within the World work as expected and will not interact with objects outside of the Viewport. Why/How this happens, I have no idea, but it took me a good couple hours of trying to figure out why the Own World property was not working to get to this point. So, because of this we now have a custom World, but we do not have Own World enabled, because with it enabled all physics within the viewport ceases to function. ______ Finally, the last thing thing I want to mention about Viewport nodes is something I actually briefly mentioned in part 1. Viewport nodes do not receive input events on their own by default, and have to have input events passed to them in order for them to work. This makes sense, because for the majority of Viewport uses, the Viewport will likely be of a different size and position in comparison to the root viewport. However, in our case it means we have to pass input events to the viewport if we want any of the child nodes in the viewport to receive any input events. We did this in Editor_Controller in part 1, but it is worth noting again. I spend quite a bit of time trying to figure out why the Viewport was not receiving input events before I hit upon why it was not working. As I said before, it makes sense, but because our viewport is the same size and position as the root viewport, we can just pass on input events without needing to do any additional processing. !!! NOTE: Note If you would like to see a tutorial on how to use Viewport nodes in multiple different ways, like how the Gui in 3D demo on the Godot demo repository works, let me know in the comments below! There is a lot of cool things you can do with Viewports once you know how they work and how to manipulate them to do what you want, and I'd love to share what I know! ### Gizmo setup That's all I have to say about the Viewport. Now let's look at the gizmos and go over how they are setup in more detail. We'll take a in depth look at the translate gizmo, and then a brief look at the other gizmos, as they more or less are the same. Because you cannot see nodes that are a child of a Viewport node that has its own world, in order to see the nodes we'll have to move them outside of the Editor_Viewport node temporarily so we can take a look. Go ahead and select the Gizmos node and re-parent it by dragging and dropping it on top of the Editor_Controller node. This will re-parent the Gizmos node and since it will no longer be a child of Editor_Viewport, we can see what we are working with. Now that we can actually see what we are working with, let's take a closer look! __________
As you can see, the translate gizmo is just a few cube shaped StaticBody nodes with MeshInstance and CollisionShape child nodes. Feel free to take a closer look if you want, but I am going to assume you can figure out, more or less, how these nodes are basically setup. If you need help, feel free to ask in the comments below! Let's quickly go over what each of the cube shaped nodes represent: * The big yellow cube in the center is for translating/moving the selected object relative to the view of the camera. This is invaluable for quickly moving a object to roughly where you want. * The red cube shape is for translating/moving the selected object on the global X axis. This means that when this handle is clicked and dragged, the selected object will only be moving on the X axis relative to the world. This is very useful for getting very precise positioning on the X axis. * The green cube shape is for translating/moving the selected object on the global Y axis. This means that when this handle is clicked and dragged, the selected object will only be moving on the Y axis relative to the world. * The blue cube shape is for translating/moving the selected object on the global Z axis. This means that when this handle is clicked and dragged, the selected object will only be moving on the Z axis relative to the world. Using these four elements together, we can position a object almost exactly where we want in 3D space. One thing to note specifically to the translate gizmo is that the three handles (the red, green, and blue cube shapes) are pointing towards the axis they move the object on, and they are pointing towards the positive side of said axis. While we are here, let's quickly setup the cube shaped nodes in the gizmo with a simple script that will allow us to know which axis the mouse is over. The code is really simple and is entirely self contained. Select the Handle_X child node in the Translate node and make a new script called Editor_Gizmo_Collider.gd. Save it somewhere and then add the following code:
This code is super simple and really is only there to make things a tad easier later. Let's quickly go through how this script works, starting with its class variables:

* gizmo_axis : A variable to hold the axis the StaticBody node represents. There are four choices: The X, Y, and Z axis, as well as ALL, which will move the object relative to the camera.
* collision_shape : A variable to hold the CollisionShape node for this StaticBody. We need this so we can disable and enable the collider as we activate/deactivate the gizmo itself.

!!! TIP: Tip
        In Godot, collision objects like StaticBody nodes are active in the physics world, even when they are not visible. This is different than some game engines that disable collision shapes when the node/component is disabled. All we have to do to disable a collision object is either move it to a collision layer that is empty (or zero), or we can simply disable all of the collision shapes, which will give the collision object no way to collide with anything.

### Going Through _ready:

All we're doing here is getting the CollisionShape node and assigning it to the collision_shape variable.

### Going Through activate

All we're doing is enabling collision_shape by setting its disabled property to false.

### Going Through deactivate

Inversely, all we're doing is disabling collision_shape by setting its disabled property to true.

_______

And that is all we need to do code wise! Now, select the Handle_X node and in it's script properties, set Gizmo Axis to X.

This will make that when we collide with that StaticBody, we will know it is for the X axis. Now all we need to do is setup the other three nodes. Select Handle_Y and set the Gizmo Axis property to Y, for Handle_Z set the Gizmo Axis property to Z, and for Body_Center set the Gizmo Axis property to ALL. That is all we need to do for the translation gizmo right now, so let's turn our attention next to the rotation gizmo! ______
As you can see, the rotation (Rotate) gizmo is almost exactly the same as the translation gizmo, with the only major difference being that the yellow cube in the center has been replaced with a yellow sphere. Let's quickly go over what each of the different shaped nodes represent: * The big yellow sphere in the center is for rotating the selected object relative to the view of the camera. This is invaluable for quickly getting the rough rotation you want. * The red cube shape is for rotating the selected object on the global X axis. This means that when this handle is clicked and dragged, the selected object will only be rotating on the X axis relative to the world. This is very useful for getting precise rotation on the X axis. * The green cube shape is for rotating the selected object on the global Y axis. This means that when this handle is clicked and dragged, the selected object will only be rotating on the Y axis relative to the world. * The blue cube shape is for rotating the selected object on the global Z axis. This means that when this handle is clicked and dragged, the selected object will only be rotating on the Z axis relative to the world. As you can see, functionally the rotation gizmo is very similar to the translation gizmo, just instead of moving the object through 3D space, the rotation gizmo instead rotates the object. !!! TIP: Tip I made a minor mistake in the setup of the rotation gizmo in the starter assets included in part 1. You'll find that the CollisionShape node under Handle_Y is not positioned correctly in relation to the gizmo handle. All you need to do to position it correctly is change its Translation to (0, 1, 0) and its Rotation Degrees to (-90, 0, 0). Sorry about that! Like with the Translate Gizmo, we'll need to apply the Editor_Gizmo_Collider.gd script to all of the StaticBody nodes under Rotate. Select all of the nodes (Handle_X, Handle_Y, Handle_Z, and Body_Center) and the in the inspector scroll down and assign the Editor_Gizmo_Collider.gd. Once all of the nodes have the script attached, configure the Gizmo Axis variable for each of the nodes to the following: * Gizmo Axis in Handle_X -> X * Gizmo Axis in Handle_Y -> Y * Gizmo Axis in Handle_Z -> Z * Gizmo Axis in Body_Center -> All And then we're done with the rotation gizmo and can move on to the scale gizmo next! _______
As with the other two gizmos we've looked at so far, the Scale gizmo is almost exactly the same. This time the only thing that has really changed is the shape of the handles coming out of the scale gizmo, as they have squares on the ends instead of just being cube shaped. Let's quickly go over what each of the different shaped nodes represent: * The big yellow cube in the center is for scale the selected object evenly across all axes. This is invaluable for quickly getting the rough scale you want. * The red cube handle is for scaling the selected object on the X axis. This means that when this handle is clicked and dragged, the selected object will only be scaled on the X axis. This is very useful for getting precise scaling on the X axis. * The green cube handle is for scaling the selected object on the global Y axis. This means that when this handle is clicked and dragged, the selected object will only be scaled on the Y axis. * The blue cube handle is for scaling the selected object on the global Z axis. This means that when this handle is clicked and dragged, the selected object will only be scaled on the Z axis. Like with the Translate Gizmo, we'll need to apply the Editor_Gizmo_Collider.gd script to all of the StaticBody nodes under Scale. Select all of the nodes (Handle_X, Handle_Y, Handle_Z, and Body_Center) and the in the inspector scroll down and assign the Editor_Gizmo_Collider.gd. Once all of the nodes have the script attached, configure the Gizmo Axis variable for each of the nodes to the following: * Gizmo Axis in Handle_X -> X * Gizmo Axis in Handle_Y -> Y * Gizmo Axis in Handle_Z -> Z * Gizmo Axis in Body_Center -> All And then we're done with the scale gizmo and can move on to the last gizmo, which is the simplest of the bunch! _______
The last gizmo is the Select gizmo, which is simply just a yellow cube. Unlike the other gizmos, the select gizmo is just a MeshInstance node. This gizmo is only used to visually show what is currently selected and serves no other purpose. # Programming the Gizmos Now that we have looked at all of the gizmos, let's move on to programming them next. First, move the Gizmos node back so that it is a child of the Editor_Viewport node again. ## Programming the Gizmo Viewport. First, let's work on the script that will oversee everything that happens within the editor viewport. Select the Editor_Viewport node and make a new script called Editor_Viewport_Controller.gd. Save it where you want, and then add the following code:
This is quite a bit of code to go through, but we'll take it step by step!

First, let's start with the class variables:

* editor_controller : A variable to hold a reference to the Editor_Controller node. We need this so we can tell when the editor mode has changed.
* GIZMO_COLLISOIN_LAYER : The collision layer that all of the gizmo StaticBody nodes are on. Check out this handy [Godot QA post](https://godotengine.org/qa/17896/collision-layer-and-masks-in-gdscript) for details on how to convert a collision layer into a integer!
* path_to_texture : A exported NodePath to the TextureRect node that we will use to display the contents of the Viewport on.
* path_to_editor_camera : A exported NodePath to the Editor_Camera_Controller node. We need this so we can get the currently selected object within the scene.
* editor_camera : A variable to hold the Editor_Camera_Controller node.
* gizmo_camera A variable to hold the camera used to render everything within Editor_Viewport.
* left_mouse_button_down : A variable to hold whether the left mouse button is down or not.
* selected_physics_object : A variable to hold the currently selected physics object, which can be any of the following node types: RigidBody, StaticBody, or KinematicBody.
* selected_object_physics_layer : A variable to hold the physics layer(s) the selected physics object was on prior to selection.
* gizmos_holder : A variable to hold the node that is holding all of the gizmos.
* gizmo_translate : A variable to hold the translate gizmo (translation -> translation -> movement on the X, Y, and/or Z axes).
* gizmo_rotate : A variable to hold the rotation gizmo.
* gizmo_scale : A variable to hold the scale gizmo.
* gizmo_select : A variable to hold the selection gizmo.
* current_gizmo : A variable to hold the currently active gizmo.

Phew! That is quite a few variables. Hopefully I explained what each one is going to be used for, though if you have any questions after you have completed the tutorial, feel free to ask in the comments below.

Now let's go through each of the functions and what they do!


### Going Through _ready

First we get the Editor_Controller node and assign it to the editor_controller variable.

!!! NOTE: Note
        using get_parent assumes that the parent of this node is Editor_Controller. Depending on your project, this may or may not be a safe assumption.

Next we make the editor viewport have the same size as the root viewport. We do this by getting the scene tree using the get_tree function, and then we set the editor viewport's size to the root viewport's size. This will make the editor viewport the same size as the viewport used to render the game window!

Next we let two render frames pass using yeild(get_tree(), "idle_frame). We do this because we need to make sure the Viewport has rendered and captured a image at least once.

Then we assign the texture property in the TextureRect node at path_to_texture to the texture stored within the Editor_Viewport. This will make the TextureRect display the viewport's contents.

Next we get the Editor_Camera_Controller node using path_to_editor_camera and assign it to the editor_camera variable. We then connect the physics_object_selected signal to the physics_object_selected function.

After that we get the Gizmo_Camera node and assign it to the gizmo_camera variable. We'll need the gizmo camera so we can move it to the same position as the view camera, and we'll also use it for raycasting with the gizmos themselves.

Next we connect the on_editor_mode_change signal in editor_controller to the on_editor_mode_change function so we can change the active gizmo when the editor mode has changed.

Then we get the Gizmos node and assign it to the gizmos_holder node. After that we get the four gizmos and assign them to the class variables intended to store them.

Finally, we set current_gizmo to the select gizmo by setting it to gizmo_select. This will make the select gizmo the default gizmo. We then call the update_gizmos function so the gizmo are setup correctly based on which gizmo is the current gizmo.

### Going Through on_editor_mode_change

All we're doing here is calling update_gizmos so when the editor mode changes, the proper gizmo is active and ready.

### Going Through update_gizmos

First we call a function called update on all four of the gizmos and pass in false. This will disable the gizmos and make them invisible.

!!! NOTE: Note
        Don't worry, we're going to make the gizmo scripts after this! For now just trust that it will work as intended by the time we reach the end. If you have questions once the gizmo scripts are written, let me know in the comments below and I'll do my best to answer!

Then based on the current editor mode, editor_controller.editor_mode, we set current_gizmo to the gizmo that corresponds to the to the editor mode. For example, when the editor mode is TRANSLATE, we set current_gizmo to gizmo_translate.

Finally, we check to see if current_gizmo is not null. If current_gizmo is not null, we call the update function and pass in true, which will make the gizmo active and ready.

### Going Through _process

First we check to see if gizmo_camera is not null. If it is not, then we set the global_transform of gizmo_camera to the global_transform of the view_camera in editor_camera. This will place the gizmo camera at the exact same position as the view camera, which will make it where the content we render in the Editor_Viewport will be positioned correctly.

!!! NOTE: Note
    If you are wondering why we are not just using a RemoteTransform node, it is because I couldn't the RemoteTransform node to work correctly with Gizmo_Camera and still render to the Editor_Viewport. Maybe it was something on my end, or maybe it is a bug with the RemoteTransform node, but I just couldn't find a way to get it to stay in sync with View_Camera while still rendering to Editor_Viewport.

Next we check to see if the size of the editor viewport is different than the size of the root viewport. If it is, then we set the size in Editor_Viewport to the same size as the root viewport.

Finally, we check to see if there is a selected physics object by checking to see if selected_physics_object is not null. If there is a selected physics object, then we set the position (global_transform.origin) of the gizmos_holder node to the position of the selected physics object.

### Going Through _physics_process

First we check to see if the left mouse button is pressed or held using is_mouse_button_pressed and passing in BUTTON_LEFT. If the left mouse button is pressed/held, we then check to see if the left mouse button was just pressed or not by checking to see if left_mouse_button_down is false.

If left_mouse_button_down is false, then we set left_mouse_button_down to true so the code below it is only called once. We then check to see if the editor_mode in editor_controller is NOT SELECT. If it is not SELECT, then we call the send_editor_raycast function so we send out a raycast that will interact with the current gizmo.

If is_mouse_button_pressed returns false instead of true, then we know the left mouse button is no longer pressed/held anymore. In that case, all we do is set left_mouse_button_down to false.

### Going Through send_editor_raycast

First we get the space state for the world within THIS viewport, within Editor_Viewport.

Then we get the starting and ending position of the raycast we want to create and assign the results to raycast_from and raycast_to. See [Ray-casting in Godot](https://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html) on the Godot documentation for more information on how to raycast using a Camera node.

Then we send out the raycast using intersect_ray and store the results within a new variable called result. Note that we are passing GIZMO_COLLISION_LAYER so the raycast will ONLY collide with physics objects on that layer.

Then we check to see if there is something stored within result by checking how many elements are stored within it using the size function.

If results does have something stored within it, then the raycast collided with something. In that case, we call a function called axis_set in current_gizmo and pass in the gizmo_axis variable from the collider the raycast collided with. If you recall, we set the gizmo_axis variables on all of the StaticBody nodes for the gizmos using Editor_Gizmo_Collider.gd, which we made earlier.

If results does not have anything stored within it, then we call axis_set in current_gizmo, but we pass in "NONE" since the raycast did not collide with the gizmo.

### Going Through physics_object_selected

First we check to see if there is already a physics object selected by checking to see if selected_physics_object is not null.

If there is already a physics object selected, we first set the collision/physics layer of the object stored in selected_physics_object to the collision/physics layer we stored in the selected_object_physics_layer variable.

Then we check to see if the selected_physics_object stored is a RigidBody node. If it is, then we apply a very, very small impulse to it. This will make it where it will not be sleeping and will make it interact with the physics world again. If we did not do this, it would just stay floating in the air until another physics object collided with it.

Regardless of whether there was a stored physics object already in selected_physics_object or not, we then set selected_physics_object to the passed in new_object.

!!! NOTE: Note
        Remember, this is passed in from the editor camera, and when no object is selected it will pass null. This means new_object will be null when no physics object was selected.

If selected_physics_object is not null, then we first store the collision_layer the physics object is on in the selected_object_physics_layer variable so we can later reassign it when the object is no longer selected. Then set the collision_layer in selected_physics_object to 0, so it is not on any collision layers and cannot interact/collide with anything.

If the newly selected_physics_object is a RigidBody node, then we set both the linear_velocity and angular_velocity to zero so the RigidBody will not move when selected. Then we put it to sleep by setting the sleeping variable to true, so it will not be effected by forces like gravity.

Finally, regardless of whether the newly selected_physics_object is a RigidBody or not, we tell the current gizmo to update by calling the update function.

_____

If the newly selected_physics_object is null, then we call update on the current gizmo, but we pass in false so the gizmo is disabled and inactive.


## Programming the gizmos themselves

Okay, now we're about halfway there. All that is left is to make each of the gizmos themselves. The gizmos will be responsible for changing the position, rotation, and scale of the selected object when they are active. Let's start with the Translate gizmo first.

### Programming the Translate gizmo.

Select the Translate gizmo (Example_Scene -> Editor_Controller -> Editor_Viewport -> Gizmos -> Translate) and make a new script called Gizmo_Translate.gd. Save it where you want, and then add the following code:

Let's go through how this script works, starting with its class variables:

* path_to_editor_viewport : A exported NodePath to the Editor_Viewport node.
* editor_viewport : A variable to hold the Editor_Viewport node.
* active_gizmo_axis : A variable to hold the currently active axis. *NOTE: We are making this a exported variable so we can check its value in the debugger.
* TRANSLATE_SPEED : A variable to store the speed the translate gizmo moves the selected object at. You may need to adjust this depending on the sensitivity of your mouse, and how fast you want the gizmo to move the object.
* left_button_down : A variable to store whether the left mouse button is down or not.

Next lets go through all of the functions, starting with _ready.

### Going Through _ready

First we get the Editor_Viewport node using path_to_editor_viewport and we assign it to editor_viewport.

Next we call the update function and pass in false so it is disabled by default.

Then we set the default gizmo axis to NONE by setting active_gizmo_axis to "NONE".

### Going Through _input

!!! NOTE: Note
    This is where the majority of the code unique to each gizmo is, and where the gizmo actually does something.

First, we check to make sure active_gizmo_axis is NOT set to NONE.

Then we check to see if the input event is a InputEventMouseButton event, a event created when a mouse button is pressed/held/released. We then check to see if the button_index in the event is equal to BUTTON_LEFT, meaning the left button was pressed/held/released. If it is the left mouse button, then we set left_button_down to event.is_pressed(). The is_pressed function will return true if the button is pressed *and* when the button is held, which is exactly what we want.

____

If the input event is not a InputEventMouseButton event, but is rather a InputEventMouseMotion event, then we check to see if left_button_down is true.

If it is, then we check to make sure there is a selected physics object by checking to see if selected_physics_object in editor_viewport is not null.

If there is a select physics object, we then store the position of said object in a new variable called prior_position. We then make another new variable called current_position and we assign it to prior_position.

Then we get the relative position of the mouse event (event.relative) multiplied by TRANSLATE_SPEED to a new variable called mouse_delta.

Next we add the gizmo camera's relative Y axis (global_transform.basis.y) multiplied by mouse_delta on the negative y axis. This will move the object up/down relative to the gizmo camera. We then do the same thing for the gizmo camera's relative X axis (global_transform.basis.x) and we multiply it by mouse_delta on the x axis instead of the Y. This will move the object right/left relative to the gizmo camera.

Then we check to see if active_gizmo_axis is equal to X. If it is, we set current_position on the y and z axes to the stored position in prior_position. This will make it where current_position only has changed on the x axis.

If instead active_gizmo_axis is equal to Y. If it is we set current_position on the x and z axes to the stored position in prior_position. If active_gizmo_axis is set to Z, then we set current_position on the x and y axes to the stored position in prior_position.

Finally, we change the selected physics object's position to current_position, applying the changes we made through the gizmo.

### Going Through update

First we go through all of the children in the gizmo.

!!! NOTE: Note
        We are going to assume that all children of the gizmos have the Editor_Gizmo_Collider script attached to them.

If the gizmo is supposed to be active, is_active is equal to true, then we tell the gizmo collider to become active by calling the activate function on the child node. If the gizmo is not supposed to be active, is_active is equal to false, then we call the deactivate function on the child node.

Finally, based on the value passed in is_active, we change the visibility (visible) of the gizmo.

### Going Through axis_set

All we are doing here is setting active_gizmo_axis to the passed in axis, new_axis.


### Programming the Rotate gizmo.

That's all we need to do for the Translate gizmo, so let's move on to the Rotate gizmo next!

Select the Rotate node and make a new script called Gizmo_Rotate.gd. Save it where you want, and then add the following code:

As you can see, all that has really changed is the class variables and the code within the _input function, so **that is what we are going to be focusing on**. First, let's start with the new class variables:

* LOOK_AT_ROTATION_SPEED : The speed the rotation gizmo changes the rotation of the selected object at when using the look_at function.
* ROTATION_SPEED : The speed the rotation gizmo changes the rotation of the selected object at when constrained to a single axis. You may need to change this depending on the sensitivity of your mouse and how fast you want the gizmo to rotate the object.

### Going Through _input

As before, we check to see if the input event is a InputEventMouseButton, and update left_button_down accordingly. Likewise, if the event is a InputEventMouseMotion event, we check to see if the left mouse button is down and if there is a physics object selected. This is exactly the same as the Translate gizmo.

First we store the current rotation of the selected object in a new variable called prior_rotation. We then make a new variable called current_rotation and assign it to prior_rotation.

_______

Next we check to see if active_gizmo_axis is set to ALL.

!!! NOTE: Note
        I have to admit, the rotation gizmo does not work like a 'normal' free rotation gizmo. This is because I could not find a way to get it working like the rotation gizmo in programs like Blender, mainly because of a lack of resources I could find (I tried!), so instead we're going to make a workable approximation using look_at instead!

First we assign the relative position of the mouse (event.relative) multiplied by LOOK_AT_ROTATION_SPEED to a new variable called mouse_delta.

Then we get the origin of a raycast using the mouse position, just like in the Godot documentation on ray-casting.

Next we calculate the distance from the gizmo camera to the selected physics object by subtracting the position of the selected physics object from the position of the camera and getting the length of the resulting vector. We store this distance in a variable called obj_distance.

Then we calculate the end point of a 'raycast' using the same method we used for ray-casting prior, but this time we multiply it by obj_distance so it is on the same relative plan as the selected physics object.

Finally, we make the selected physics object look at the position stored within raycast_to using the look_at function. This will make the object rotate to look at the mouse.

_____

If the active_gizmo_axis is NOT set to ALL, then we need to handle rotation differently.

First, we assign the relative position of the mouse (event.relative) multiplied by ROTATION_SPEED to a new variable called mouse_delta.

Then we change current_rotation by adding the gizmo camera's relative y axis (global_transform.basis.y) multiplied by mouse_delta on the x axis. This will (sort of) rotate the object around the Y axis, but relative to the camera.

Next we do the same thing but on the X axis. We add the gizmo camera's relative x axis (global_transform.x) multiplied by mouse_delta on the negative y axis to current_rotation. This will (sort of) rotate the object around the X axis, but relative to the camera.

After that we check to see if active_gizmo_axis is either X, Y, or Z in separate if checks. Based on whether active_gizmo_axis is set to one of those values, we assign some of the axes in current_rotation to the axes stored in prior_rotation so the rotation only happens on one axis.

Finally, we set the rotation on the selected physics object to current_rotation.

_____

Regardless of which method we used to rotate the object, we then set the rotation gizmo's rotation to the same rotation as the selected object by setting the rotation_degrees in the rotation gizmo to the rotation_degrees of selected_physics_object.


### Programming the Scale gizmo

That is all we need to do for the rotation gizmo, so let's move on to the scale gizmo next!

Select the Scale node and make a new script called Gizmo_Scale.gd. Save it where you want, and then add the following code:

As you can see, all that has really changed is the class variables and the code within the _input function, so **that is what we are going to be focusing on**. First, let's start with the new class variables:

* SCALE_SPEED : The speed the scale gizmo changes the scale of the selected object at. Depending on the sensitivity of your mouse and/or how fast you want the object to be scaled, you may need to change this.

### Going Through _input

As before, we check to see if the input event is a InputEventMouseButton, and update left_button_down accordingly. Likewise, if the event is a InputEventMouseMotion event, we check to see if the left mouse button is down and if there is a physics object selected. This is exactly the same as the Translate gizmo.

First we store the current scale of the selected object in a new variable called prior_scale. We then make a new variable called current_scale and assign it to prior_scale.

_______

Next we check to see if active_gizmo_axis is set to ALL.

If it is, we then check to see if the relative mouse motion is less than zero on either axis by checking to see if event.relative.x is less than zero or event.relative.y is less than zero.

If the relative mouse motion is less than zero, then we subtract Vector3(1,1,1) multiplied by the length of mouse_delta. This will scale the object down evenly on all axes. If the relative mouse motion is more than zero, then we add Vector3(1,1,1) multiplied by the length of mouse_delta, which will scale the object up evenly on all axes.

_____

If the active_gizmo_axis is NOT set to ALL, we instead add the gizmo camera's relative Y axis (global_transform.basis.y) multiplied by mouse_delta on the negative y axis. This will scale the object up/down relative to the gizmo camera.

Likewise, we then add the gizmo camera's relative X axis (global_transform.basis.x) multiplied by mouse_delta on the x axis. This will scale the object left/right relative to the gizmo camera.

Regardless of how we changed current_scale, we then check to see if active_gizmo_axis is either X, Y, or Z in separate if checks. Based on whether active_gizmo_axis is set to one of those values, we assign some of the axes in current_scale to the axes stored in prior_scale so the scaling only happens on one axis, if the active_gizmo_axis is X, Y, or Z.

Finally, we set the selected object's scale to current_scale.


### Programming The Select Gizmo

Alright, last one!

Select the Select node and make a new script called Gizmo_Select. Save it somewhere and then add the following code:

As you can see, because the Select gizmo does not do anything, it's code is really tiny!

All we're doing in the _ready function is making the gizmo invisible by default by calling the update function and passing false. In the update function all we are doing is setting visible to the passed in is_active variable, and for the axis_set function we just call pass because there is nothing to do so we can ignore the function!

Phew! Only one minor thing left to do, and then we're done!


## Setting everything up

All that is left to do now is setup the NodePath variables we've exported in our scripts. For the Translate, Rotate, and Scale nodes, set the Path To Editor Viewport properties so they point to the Editor_Viewport node.

Then select the Editor_Viewport node and assign Path To Texture property to the Gizmo_Viewport TextRect node stored in the Editor_UI node. For the Path To Editor Camera property in Editor_Viewport, assign it to the Editor_Camera node.


# Final Notes

Phew! With all that done, go ahead and give the project a test! Now when you are in select mode, try selecting one of the objects. A yellow cube should appear. Then change to another mode to change it's position, rotation, or scale. You can limit the axis affected by the gizmo by dragging one of the colored handles instead of the yellow center. That concludes it for this tutorial! There are still loads of improvements that could be made. For example: * Add snapping to keep the position/rotation/scale on a grid. * Redo the rotation gizmo so it functions akin to the rotation gizmo in other programs. * Add a save/load system so changes made with the gizmos are saved. * Add support for more nodes! Using the AABB system, you maybe be able to select MeshInstance nodes and other nodes that have a AABB box. Hopefully this two part tutorial series will be helpful for you guys. I am using a similar system to this for my own game, and while I have changed several things, the gist of the gizmo system is almost exactly the same as what the tutorial here shows. If you have any questions, comments, or other feedback, let me know in the comments below! !!! WARNING: Warning If you ever get lost, be sure to read over the code again! If you have any questions, feel free to ask in the comments below! You can access the finished project for this tutorial series [right here](https://drive.google.com/open?id=1dtGVHF54I-Np7R-s40K_D-boFZ5CIMz0)! Please read LICENSE.html or LICENSE.pdf for details on attribution. **You must include attributions to use some/all of the assets provided**. See the LICENSE files for details. !!! TIP: Thanks
Thanks for going through this tutorial! If you like what we do, **please consider buying one of [our paid games](https://randommomentania.com/paid-games/) or [products](https://randommomentania.com/other-products/) to help support RandomMomentania!** Have a suggestion for a future tutorial? Let us know either in the comments section below, or by following the instructions on our [Contact and FAQ](https://randommomentania.com/contact/) page!
© RandomMomentania 2019



Leave a Reply

Your email address will not be published. Required fields are marked *