Saturday, October 11, 2014

Level construction and keeping focus in the Unity editor

Up until now, creating the geometry for a level in Hellenica was controlled entirely through keyboard input. Here's what it looked like:


It's a cinch once you pick up the hotkeys, and now that I've been using it for a few months, constructing the geometry for a level is one of the quickest parts of the process.

Recently, we've been thinking about how to layer on Valery's art to make our environments more appealing. I decided it was going to be cumbersome and complicated to paint specific faces of the level blocks using keyboard hotkeys. The time had come to add mouse picking to the editor interface.

I immediately ran into issues dealing with Unity's default mouse picking behavior, though. Whenever you click on a GameObject in the scene view, Unity changes its focus to that new object! Generally, this is exactly what the user wants. In our case, however, painting tiles becomes very difficult when every click changes the user's selection!

After some internet research and experimentation on my own, I ended up with a solution that works well for us. It comes down to overriding Unity's GUI focus for the duration of the painting operation. Here's a quick sketch of how this is done:

 ...
else if (Event.current.isMouse)
{
process_mouse_input(Event.current);

// if we detect a left click while the mesh editor is selected,
//   do not allow Unity to change our selection. assume we're editing
//   until we release the left mouse button.
if (Event.current.type == EventType.MouseDown && Event.current.button == 0)
{
    // hotControl is a static variable that Unity uses to track the
    //  active GUI control
GUIUtility.hotControl = m_editor_control_id;
}
else if (GUIUtility.hotControl == m_editor_control_id &&
Event.current.type == EventType.MouseUp && Event.current.button == 0)
{
GUIUtility.hotControl = 0;
}
}
 ...

I begin overriding GUIUtility.hotControl on the MouseDown event and hang on to control until a corresponding MouseUp event to guarantee that the editor captures clicks as well as sustained drags.

m_editor_control_id is generated as follows:

m_editor_control_id = GUIUtility.GetControlID(this.GetHashCode(), FocusType.Passive);

As you may be able to tell, this solution isn't perfect. Any gizmos that end up in front of the level mesh will be unresponsive to mouse clicks so long as the mesh remains behind them, as the mesh editor is now all-consuming. (Muahaha.) But, it works well enough for us to get started painting levels.

Here's a quick gif of a paint job I did with some test tiles:


I have no doubt that this system will keep evolving as we go. If anyone out there has any ideas for improvement, let me know!

No comments:

Post a Comment