Goal: create an Editor Scriptable Tool that allows the user to edit variable-defined connections between Actors in a level by click-dragging on them in the editor viewport, and visualizes these connections.
Requires Unreal Engine 5.2 or newer.
Visualizing arbitrary connections between actors in editor mode has not been a simple task in Unreal, especially if you don’t want to write C++ code. Scriptable Tool system provides a way to do this in a modal way, while being completely separate from gameplay code and exposed to Blueprint.
1. Enable Plugins
Plugins Scriptable Tools Framework
, Scriptable Tools Editor Mode
and Geometry Script
(we’ll need this one to generate temporary collision geometry for object selection) have to be enabled in the project.
2. Create Actor Blueprint
For this example, I created a simple Actor Blueprint Class called BP_Waypoint
. It has one Instance Editable
variable called NextWaypoint
of type BP_Waypoint Object Reference
.
We are going to create a viewpoort tool for visualizing and editing connections defined by this variable.
Drop a few instances of this Actor into a level.
3. Create Scriptable Tool Blueprint
Next, I created an Editor Utility Blueprint (under Editor Utilities group in the asset creation menu) called BP_WaypointScriptableTool
. It extends EditorScriptableClickDragTool
, which fires events when user clicks and drags the cursor over viewport while using the tool.
In its Class Defaults
I filled out some basic information about the tool that will be displayed in the toolbar.
4. Tool Setup Event
The tool functionality is implemented by overriding base class’ functions and events. When the tool is activated, the OnScriptSetup
event is fired.
In my override for this event, I am getting a list of all BP_Waypoint
actors in the level and storing it in a variable called WaypointList
. I also call a custom function to build a “selection mesh”, but we will talk about it a bit later.
5. Visualizing connections
The function that handles in-world visualization is called OnScriptRender
. Here I iterate through WaypointList
, and if a Waypoint has a defined NextWaypoint
, I draw a line between the two actors.
Since the connection is directional, I also made a function DrawChevronPath
that draws chevrons along the line.
My OnScriptRender
override has a second part - it draws a chevron path from an actor referenced in EditedWaypoint
variable (if valid) to a location vector stored in DragTempTarget
variable. This is drawn when the user is in the middle of creating a new connection and is dragging the mouse cursor from one waypoint to another. These variables will be defined in other events.
Compile the blueprint. At this point, if you manually define some NextWaypoint
references on the waypoints in your level, you can go to Scriptable Tools Mode
(Shift+F9)
in the main editor window, selected your tool on the left, and see the connections visualized in the viewport!
6. Selecting Actors
The events in EditorScriptableClickDragTool
provide us with a world-space ray that we can use to get objects the user clicked on. If the actor that you want to edit has collision geometry, you can get it by just doing a line trace against this collision geo, like you would normally do in gameplay code.
But our BP_Waypoint
actors have no collision - and the Scriptable Tools do not expose editor’s normal viewport mouse selection logic, so we need to use a workaround.
One simple option is to just add an editor-only sphere collision component to BP_Waypoint
. This will result in unnecessary components that are only intended for editor use, which might cause unintended behaviour in play mode if you’re not careful. Additionally, if you wish to later extend the tool to support more types of objects, you will need to add such collision components to each of them.
Another option that keeps the selection logic completely inside our tool, is to create a temporary mesh using Geometry Script and cast the ray against that.
I made a function for building this mesh, and a macro for casting a ray against it and getting the associated Waypoint from the list. I chose a macro instead of a function to have a second exec output to handle the case when no Waypoint was found.
- The function
BuildSelectionMesh
constructs a newDynamicMesh
object, creates a sphere on each Waypoint position, stores the Waypoint’s index in the list as a Poly Group ID, and builds a BVH for the resulting mesh. The mesh object itself and the BVH are stored in the variables calledSelectionMesh
andSelectionMeshBVH
. This is the function that is called in theOnScriptSetup
event. - The macro
GetWaypointFromSelectionMeshRayCast
takes a ray as an input, casts it against the mesh using BVH, reads the Poly Group ID, and gets the Waypoint from the array. It also exposes the location of the hit.
7. Set NextWaypoint Property Transaction
This is the function that will be called to actually edit the property on the waypoint Actor. I use the Transaction system to make the action undo-able.
8. Mouse Interaction
Now we just need to overide the events responsible for mouse interactions.
TestIfCanBeginClickDrag
makes a cast against our selection mesh and reports if a hit was detected.
OnDragBegin
makes a cast against the selection mesh and stores the reference to the clicked Waypoint in theEditedWaypoint
variable. It also resetsDragTempTarget
to the location of the detected hit. If no hit was detected, it resetsEditedWaypoint
to Null.
OnDragUpdatePosition
projects a given ray into the world at the same distance as theEditedWaypoint
is from the origin point, and stores the resulting location in theDragTempTarget
variable. This is used to provide visual feedback for dragging the cursor around the viewport inOnScriptRender
.
OnDragEnd
makes a cast against the selection mesh, and calls theSetNextWaypointTransaction
function, setting the found Waypoint as theEditedWaypoint
’sNextWaypoint
. If no hit was detected,NextWaypoint
is set to Null instead. Then we finalize the action by resettingEditedWaypoint
to Null.
And that’s it!
This can obviously be further improved, for example:
- Handle creation, removal and movement of Waypoint Actors in the level while the tool is active to rebuild the
WaypointList
and selection geometry. - Highlight unconnected Waypoints in the level when the tool is active.
- Allow the user to customize Waypoint and path colors.