RandomMomentania

Godot Runtime 3D Gizmo Tutorial Part 1

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 1**

# Godot Runtime 3D Gizmo Tutorial Overview

In this tutorial series, we are going to be making a set of gizmos you can use in 3D, at run time in Godot. We will not be tackling 2D gizmos in this tutorial series, though if you are interested let me know in the comments below! I'd love to make a 2D gizmo tutorial series as well if there is interest. !!! NOTE: Note While this tutorial can be completed by beginners, it is recommended to have some Godot experience before tackling this tutorial as it is fairly complex. This tutorial was made using **Godot 3.0.6!** ## What is gizmo? If you are wondering what a gizmo is, I'll do my best to explain what we will be making. We will be making a set of objects, gizmos, that then the mouse interacts with them, they will perform a certain function. For example, in the picture above there is a yellow square with some colored handles coming out of it. This is the translation gizmo (like the math function). By clicking and dragging on one of the colored handles, you can move the object around on that single axis. For example, clicking and dragging on the green handle will move the object up and down. Another example is clicking and dragging on the yellow cube in the center, which will move the object relative to the camera's view. The idea is that by using multiple gizmos, you can position, rotate, and scale the object until it is in the correct position, with the correct rotation, and the correct scale. ## Why would we want a gizmo? You may be thinking, why would we want 3D gizmos during runtime, especially since we can already position, rotate, and scale nodes in Godot using Godot's built in gizmos. There are several use cases where having gizmos that work in runtime could be very useful. Here are a few ideas: * The reason I got inspired to make this project to begin with is the need in a level editor I am making. Levels in the game are stored in a custom file format and are saved/loaded at runtime, so I needed a gizmo that would allow me to manipulate objects within my level at runtime. * You could add 3D gizmos in a debug mode or sorts so you can easily manipulate objects for testing purposes. For example, you may want to test whether a AI agent can navigate its way out of a maze from any point. By implementing a 3D gizmo, you can move the agent to the position you want within the maze without having to restart the project, go into the scene, move the object, and then launch the game again. * If you want to learn how to place objects from one viewport into another. This could be useful to know for all sorts of projects, not just 3D gizmos. Another reason you may want to make 3D gizmos that work during runtime is simply to know how to make 3D gizmos. I found writing the 3D gizmos incredibly interesting and it made me realize how complicated something as simple seeming as 3D gizmos can be! A added bonus is that we will also cover how to work with multiple viewports, learning how to move/rotate/scale objects relative to the camera, and how to make a free look camera that can select objects in the scene! !!! NOTE: Note if you are wondering why we don't just reuse Godot's gizmos during runtime, there are a few reasons: * For the project I'm making that needs gizmos, this was not a feasible option. * We can learn a lot by making our own version, and as a bonus, if we need to change something we can easily make adjustments. * I have no idea how hard it would be to reuse Godot's gizmos and I do not want to edit the C++ source code or use GDNative for this tutorial. So, let's jump right in and get this tutorial going! # Getting Setup First we need to get everything setup. I have included a starter project that has a prebuilt scene we will be using in this tutorial. Go ahead and [download it HERE](https://drive.google.com/open?id=1LF6RGZYm70FvCw6e9BC2_dH6MVI40O1p) and extract it somewhere on your computer. Open the project up in Godot and let's take a look at what is included. _____ Before we start poking around the scene itself, let's take a look at the project settings.
!!! NOTE: Note: If you are wondering why everything looks a tad smaller, it is because I'm using a borrowed Mac computer, and I do not have Godot setup quite right just yet! Hopefully in the future I'll be able to fix it, but for this tutorial the pictures will all be a tad smaller than normal. Sorry! Once you have the project settings open, navigate to the input map tab and scroll down to the bottom. You should find that there are a few input actions already setup and ready.
These input actions are what we will be using to move the camera around the scene. Feel free to change them if you want/need. Other than that, the rest of the project settings are the same as the defaults Godot creates. _________ Let's take a look at the included scene and how it is setup next.
You may have noticed some of the nodes in the above picture are not fully expanded. This is on purpose and we'll look at these nodes in more details as we use them. First though, let's go over the majority of the nodes in the scene: * Example_Scene: The root node of the scene. * Normal_Objects A scene to hold all of the normal objects in the scene. In this case, 'normal' are just nodes that are nodes we will not be directly using in this tutorial. Instead, these nodes are the nodes we will be moving with the gizmos. * Floor : A StaticBody node that will act as the floor for this example scene. * MeshInstance : A MeshInstance node that holds the mesh used for the floor. * CollisionShape : A CollisionShape node that holds the collision shape surrounding the floor. * Wall : A StaticBody node that will act as a wall in this example scene. * MeshInstance : A MeshInstance node that holds the mesh used for the wall. * CollisionShape : A CollisionShape node that holds the collision shape surrounding the wall. * Wall2 : Another StaticBody node that will act as a wall. The node structure is exactly the same as Wall. * RigidBody_Box : A RigidBody node that is a simple red box. * MeshInstance : A MeshInstance node that holds the mesh used for the RigidBody. * CollisionShape : A CollisionShape node that holds the collision shape surrounding the RigidBody. * RigidBody_Prism : A RigidBody node that is a simple green prism. Outside of the different mesh, material, and collision shape, everything is more or less the same as RigidBody_Box. * RigidBody_Cylinder : A RigidBody node that is a simple blue cylinder. Outside of the different mesh, material, and collision shape, everything is more or less the same as RigidBody_Box. * DirectionalLight : A DirectionalLight node to help illuminate the scene. * WorldEnvironment : A WorldEnvironment node to add tweak the visuals slightly to make them look a little better. Feel free to delete this node if you want/need, as it is completely unnecessary and is only used for visuals. * Editor_Controller : A Spatial node to hold all of the nodes we will need for our 3D gizmo editor of sorts. * Editor_UI : A Control node to hold all of the UI we will need for our editor. * Editor_Viewport : A Viewport node that we will use to render objects on top of the current scene. This is especially useful for the 3D gizmos, as we want to make them render on top of whatever object is selected. * Editor_Camera_Controller : A Spatial node that holds all of the nodes we will need to make the camera that will navigate through the scene. As you can see, this is quite a few nodes and we haven't even seen them all yet! Thankfully all of the nodes under the Normal_Objects node we will not be using at all in this tutorial expect at runtime, and we will not need to do anything to those nodes to make them work during runtime. # Creating The Camera Let's work on the editor camera first, as from there we can continually build on top of it as we go through the tutorial series. But before we start working on anything, let's quickly write all of the code we will need in the Editor_Controller node, as it is really small, straightforward, and we'll need it for the editor camera. ## Making The Editor Controller Select the Editor_Controller node and assign a new script called Editor_Controller.gd. Save the file where ever you want, I'll just be placing it in the res:// folder, and then add the following code:
Let's go through this script, starting with the class variables. For this tutorial, we will be defining **class variables as variables outside of any/all functions**. With that out of the way, let's take a look:

* on_editor_mode_change : Technically this is not a variable but instead is a custom signal that we will be emitting when the UI has changed the editor mode. We'll not be using this until we get to the editor UI.
* editor_mode : A variable to hold the current mode the editor is in, which we will be storing in a string.
  * If you are wondering, we are exporting the variable so we can see its value in the remote debugger, not so it can be set from the editor, which is an added bonus.
  * Also, we could make this a integer, a enumerator, or something else, but for simplicity we will be using a string. So long as we are consistent with how we set editor_mode, using a string should work fine.
* is_in_freelook_mode : A variable to hold whether the camera is in free look mode. This is we we know whether the camera is moving and so we need to ignore mouse clicks due to the camera's movement.

The only variable we really need of this bunch for the player camera is is_in_freelook_mode. We need is_in_freelook_mode so we the camera only moves in free look mode and not all the time.

Let's quickly go through the functions too, since they are small and easy to quickly blaze through.

### Going Through change_editor_mode

First we assign editor_mode to the new passed in editor mode, new_mode. Finally, we emit the on_editor_mode_change signal using the emit_signal function. We do not need to pass any arguments along with the signal, so that's all we need to do.

### Going Through _input

All we are doing here is passing whatever input event we have to the Editor_Viewport node. We need to do this because in Godot Viewport nodes do not receive input events on their own, they have to have input events passed to them.

So, all we are doing is for each and every event the Editor_Controller node receives, we just pass it straight to the Editor_Viewport node using the input function, so all of the children nodes in Editor_Viewport will receive input events.

!!! TIP: Tip
    If you are wondering why Viewport nodes do not receive input events by default is for a very good reason, though for this project it kinda gets in the way, unfortunately. Viewport nodes do not receive input events by default is because a input event is tailored for the root viewport, the root node of the entire Godot game. For example, this makes things like the mouse by positioned according to the root viewport.

    Now for our project, this is not a issue because we are going to make the Editor_Viewport have the exact same size as the root viewport, so in our case all of the elements will line up perfectly with the root viewport. However, if you are doing something like in the [Godot Viewport demos](https://github.com/godotengine/godot-demo-projects/tree/master/viewport), you have Viewports with differing sizes and positions. In this case, you will need to do additional processing so the input events are positioned correctly within the differently sized and positioned Viewport nodes, as they do not have the same size and/or position as the root viewport.

  However, for this tutorial, the Editor_Viewport node will have the exact same size and position, so all we need to do is just pass the input events along.



## Making The Editor Camera Controller

Now that we have written all of the code we will need in the Editor_Controller node, we can move on to making the editor camera itself. This code will move the camera through 3D space so we can see what we are doing, and it will also select physics objects within the scene so we can use the 3D gizmos to edit their position, rotation, and/or scale.


First, let's take take a look at how the nodes in Editor_Camera_Controller are setup

* Editor_Camera_Controller : A Spatial node to hold the Camera node that will render the scene. We will be moving this node through 3D space to move the camera, and we will also be rotating this node on the Y axis (left and right) when the mouse moves in free look mode. * View_Camera : The Camera node that will be rendering the scene. We will be rotating the Camera on the X axis (up and down) when the mouse moves in free look mode. !!! NOTE: note Remember, Camera nodes in Godot render face the negative Z axis. You can check this by clicking a camera node and having the Godot editor set to Local Space Mode. You can enable local space mode by pressing a little cube icon in the top right row of buttons in the center scene view.
As you can see, we will only need a couple nodes for the camera system we will use to look around the scene. Let's write the code needed for the camera system next! Select Editor_Camera_Controller and assign a new script called Editor_Camera_Controller.gd. Save it where ever you want, I'll just be placing it in res://, and then add the following code:
This is quite a bit of code, but we'll go through it piece by piece. First, let's go over the class variables:

  * editor_controller : A variable to hold the Editor_Controller node.
  * physics_object_selected : A custom signal that we will emit when a new physics object has been selected by the camera. The camera can select the following node types: StaticBody, RigidBody, and KinematicBody nodes.
  * NORMAL_COLLISION_LAYER : A variable to hold the collision layer that all of the 'normal' nodes we want to be able to select are on. Anything on this layer will be able to be selected. Check out the tip below for how to calculate this number!
  * CONTROL_SPEED : A variable to hold the speed the camera moves at when the control key is held down. Generally in most programs, when the control key is held down the camera will move slower.
  * MOVE_SPEED : A variable to hold the speed the camera moves at normally.
  * SHIFT_SPEED : A variable to hold the speed the camera moves at when the shift key is held down. Generally in most programs, when the shift key is held down the camera will move faster.
  * MOUSE_SENSITIVITY : A variable to store how sensitive the mouse is. You may need to adjust this variable based on the sensitivity of your mouse.
  * view_camera : A variable to hold the camera that will render the scene. We need this to get directions based on the camera's rotation.
  * CAMERA_MAX_ROTATION_ANGLE : A variable to define how far the camera can move on the X axis when in free look mode. We need this to ensure the camera cannot rotate soo far that it flips upside down.
  * send_raycast : A variable for storing whether the camera needs to send out a raycast on the next _physics_process call.

!!! TIP: Tip
    Check out [this answer on the Godot QA](https://godotengine.org/qa/17896/collision-layer-and-masks-in-gdscript) site for how to convert a physics layer to a integer!

If you have gone through some of my other tutorials with free looking cameras, then most of these variables will look similar.

There is only a few variables specific to the 3D gizmo part of this tutorial series: editor_controller, physics_object_selected, and NORMAL_COLLISION_LAYER. All of the other variables will be used almost exclusively for moving the camera.

In fact, most of this code you can reuse in other projects if you want a simple, relatively compact 3D free look camera.

!!! NOTE: Note
    If you are wondering, by free look I must mean a FPS like camera where you can look around the scene with the mouse, and move around the scene with the WASD and arrow keys.

Let's look at the functions next, starting with _ready:

### Going Through _ready

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

!!! WARNING: Warning
    We are using get_parent because in this tutorial, the Editor_Controller node is the parent node in this scene. However, this is assuming that Editor_Controller will always be the parent node, which depending on your project, this may or may not be a safe assumption.

Then we get the View_Camera node and assign it to the view_camera variable.

### Going Through _process

First we check to see if the right mouse button is pressed or held. We do this by checking to see if Input.is_mouse_button_pressed(BUTTON_RIGHT) is equal to true.

If the right mouse button is pressed/held, then we check to see if the current mouse mode (Input.get_mouse_mode) is not equal to MOUSE_MODE_CAPTURED. If it is, then we set the mouse mode to MOUSE_MODE_CAPTURED using the Input.set_mouse_mode function.

Then we set is_in_freelook_mode in editor_controller to true so we can move the camera around when the right mouse button is pressed/held.

If the right mouse button is NOT pressed/held, then we check to see if the current mouse mode is not equal to MOUSE_MODE_VISIBLE. If it is, then we set the mouse mode to MOUSE_MODE_VISIBLE using the Input.set_mouse_mode function.

Then we set is_in_freelook_mode in editor_controller to false so we can no longer move the camera around, as the right mouse button has been released.

!!! NOTE: Note
    This will make the mouse captured when the right mouse button is pressed, but when the right mouse button is released then the mouse will be released.

The last thing we do is check to see if is_in_freelook_mode in editor_controller is true. If it is, then call the process_movement function so the camera can move around the scene.


### Going Through process_movement

First we make two new variables. movement_vector will store the direction the player intends to go, while movement_speed will store the speed the camera will be moving at.

We then check to see if the editor_move_forward action is pressed/down using the is_action_pressed function in Input. If the action is pressed/held, then we set movement_vector.z to -1. If the editor_move_backwards action is pressed/held instead, then we set movement_vector.z to 1 instead.

!!! NOTE: Note
    Remember how I said Camera nodes face the negative Z axis? That is why we are setting movement_vector.z to -1 when the player wants to move forward, and 1 when the player wants to move backwards.

Then we do similar checks for editor_move_right and editor_move_left. If editor_move_right is pressed/held we set movement_vector.x to 1, while if editor_move_left is pressed/held we set movement_vector.x to -1.

Next we check to see if the shift key is pressed/held down by checking to see if is_key_pressed(KEY_SHIFT) returns true. If the shift key is pressed/held, then we set movement_speed to SHIFT_SPEED. Likewise, if is_key_pressed(KEY_CONTROL) returns true, then we set move_speed to CONTROL_SPEED instead.

Finally, we need to move everything in the direction the player intends to go.

To do this, we first add the direction the camera is facing on the negative Z axis (where the camera is looking -> -view_camera.global_transform.basis.z) multiplied by movement_vector.z, delta, and speed to the camera's global position, global_transform.origin. This will move the camera forwards/backwards according to where the camera is facing.

Then we add the direction the camera is facing on the X axis (the right of the camera -> view_camera.global_transform.basis.x) multiplied by movement_vector.x, delta and move_speed to the camera's global position, global_transform.origin. This will move the camera left/right according to where the camera is facing. 


### Going Through unhandled_input

First we check to see if is_in_freelook_mode in editor_controller is true.

If it is, we then check to see if the input event is a InputEventMouseMotion event. A InputEventMouseMotion event only happens when the mouse moves on the screen.

If the event is a InputEventMouseMotion event, then we we need to rotate the camera based on the mouse movement.

First, we get the camera's current rotation and store in a new variable called camera_rotation.

We than add the mouse motion (event.relative) multiplied by MOUSE_SENSITIVITY to the X axis of camera_rotation. To ensure the rotation on the X axis cannot go so far that the camera flips upside down, we use the Clamp function and pass in -CAMERA_MAX_ROTATION_ANGLE as the minimum value, and CAMERA_MAX_ROTATION_ANGLE as the maximum value. This keeps camera_rotation.x within the range set in CAMERA_MAX_ROTATION_ANGLE.

Next we rotate the Editor_Camera_Controller node on the Y axis by adding -event.relative.x multiplied by MOUSE_SENSITIVITY to the Y axis in rotation_degrees.

Finally, we apply the rotation on the X axis, current_rotation to view_camera.

______

If is_in_freelook_mode in editor_controller is NOT true, then we check to see if the input event is InputEventMouseButton.

InputEventMouseButton event only happens with one of the mouse buttons is pressed, released, or held.

If event is a InputEventMouseButton event, we then check to see if the button is the left mouse button by checking to see if button_index equals BUTTON_LEFT, and we check if event.pressed is true, meaning the left mouse button was just pressed.

Then we check if the editor_mode in editor_controller is SELECT. If it is, then we set send_raycast to true.


### _physics_process

First we check to see if send_raycast is true.

If it is, we set send_raycast to false so we only send out a single raycast per mouse click.

Then we make a new variable called selected_node and assign its default value to null.

Next we get the direct space state from the physics world using get_world().direct_space_state. Then we get the starting and ending position the raycast we want to create.

!!! NOTE: Note
    See the [page on ray-casting](https://docs.godotengine.org/en/3.0/tutorials/physics/ray-casting.html) on the Godot documentation for more information!

We then send out a raycast using the intersect_ray function in space_state and store the results in a new variable called result. Note how we are passing in NORMAL_COLLISION_LAYER in so the raycast will only collide with objects on that layer!

We then check to see if something is stored within result by checking to see if there is at least a single element stored within the dictionary. If there is at least something stored within result, then we set selected_node to the collider the raycast collided with, result.collider.

Finally, we emit the physics_object_selected signal and pass selected_node along with the signal. This will make that when no nodes are found by the raycast null will be emitted with the signal, while when the raycast collides with a node then the node the raycast collided with will be passed along.




# Creating the UI

Now that we have the camera setup, let's really quickly get the UI setup before we end this part.

First, let's take a look at how the UI in the scene is setup:

* Editor_UI : The main Control node that holds all of the UI nodes we will need for the editor. See the note below for a important detail! * Gizmo_Viewport : A TextureRect to display the results of the Gizmo_Viewport node. We will be using this in the next part to display the contents within the gizmo viewport! * Gizmo_Selection : A Panel node to act as a backdrop for the buttons that will change the editor mode. * Button_Select : A button that, when pressed, will change the editor mode to SELECT. * Button_Translate : A button that, when pressed, will change the editor mode to TRANSLATE. Side note: *Translate in this case means the math term for moving a object along a axis.* * Button_Rotate : A button that, when pressed, will change the editor mode to ROTATE. * Button_Scale : A button that, when pressed, will change the editor mode to SCALE. * TextureRect : A TextureRect to hold the texture for that button. All of the buttons have this node, and the texture is just to show what the button does. !!! NOTE: Note Because we want the mouse to be able to interact with the buttons and other elements in the scene, we have the Mouse filter property in Editor_UI set to ignore so it will ignore mouse events and allow them to pass through! Now that we have seen how the UI is setup in the scene, let's quickly write the code for it. Select Editor_UI and make a new script called Editor_UI.gd. Save it where ever you want, I'll be using res://, and then add the following code:
Let's quickly go through this script, starting with the single class variable:

* editor_controller : A variable to hold the Editor_Controller node.

## Going Through _ready

First we get the parent node of Editor_UI, which we assume will be Editor_Controller and assign it to the editor_controller variable. As before, this assumption may or may not work for your project.

Then we get each of the four buttons and connect their pressed signals to the on_mode_button_pressed function. We pass in a additional argument, which is the mode that the button will change the editor to.

## Going Through on_mode_button_pressed

All we are doing here is calling change_editor_mode and passing in new_mode as the argument so editor_mode is changed to the passed in mode.


# Final Notes

With all that done, we are ready to make the 3D gizmos in the next part of this tutorial series! But for now, go ahead and try running the project. You should find that if you hold the right mouse button down, you can look around using the mouse and move around using the WASD and arrow keys. Right now the UI does not seem to be doing anything, but if you run the remote debugger and look at Editor_Controller after pushing one of the buttons, you should find that the editor_mode variable has changed according to which button you have pressed!
In the next part, we'll start working on the 3D gizmos! **You can find Part 2 right here:** ***Coming soon!*** !!! 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 part here](https://drive.google.com/open?id=1gHZEhkqe2kghDJznJx5UTb44LMw3hXS1)! 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.
Β© RandomMomentania 2019



2 thoughts on “Godot Runtime 3D Gizmo Tutorial Part 1

    1. Thanks εˆ˜εΊ†ζ–‡!

      The second code block was written incorrectly in the MarkDeep file so it rendered as HTML text, like you guessed. I must have missed it when I was writing the tutorial… πŸ˜…
      It is fixed now, though I’m glad you were able to understand it regardless through the explanation. Thanks for letting me know about the issue so I could fix it!

      Thanks again! πŸ™‚

Leave a Reply

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