Troubleshooting via UI in Unity – Deuta

As a visual learner, I’ve found that sometimes seeing something working is a better debugging tool for me than seeing an array of int values. With the array, I can still usually see where a pattern breaks down or unexpected values might be (at least when the use merits it), but it’s more difficult for me to wrap my brain around what needs to be changed.

I’ve continued work on Deuta, my project for parametric primitives in Unity. One of the things I’ve spent some time in is custom editor/inspector UIs for shape generation. I highlighted the vertex-index (vertidx) in a previous post, but I have a great example of how it helps me.

In the above video, I’m working on a parametric torus option. I have the vertices down how I want them, but as I was writing code for the tris, I clearly missed a step. I looked at the int[] of triangles I got. I could see where there was an issue. But being able to visualize it helped me understand precisely what I needed to change and how. I know how the triangles need to be ordered, but this also allowed me to see at what index it breaks down, if there’s a pattern to it or it’s just a single issue, and to see how numeric patterns played out.

I’m sure I’ll be posting more Deuta stuff over the next several weeks. I’ll likely show off some more of my troubleshooting/visual debugging steps as I work on some more complex shapes.

Unity Custom Attributes and Custom Editors

Shapes Editor 01
Custom editor/inspector view for Shapes.cs

While working on Programmatic Meshes, it became clear that I needed some custom gizmos to help me visualize things as I moved along. It really became a necessity as I was working on sphere generation because I was having some issues where certain sizes and segment counts were creating bad geometry. This was almost certainly due to ordering of the int[] array for triangles associated to the mesh. Since everything is generated by code, that means that once all vertices are created, the code needs to also sort/order those vertices the same every time to ensure that the facet tris are created correctly.

Sphere Vertices Indices
Sphere Vertices Indices

This was my first venture into gizmos aside from some very basic line drawings. I wanted the indices of each Vector3 in the mesh so that I could ensure they were getting properly ordered each time and would meet the requirements for tri calculation. So the above was born – and damn did it ever help me see where things were occasionally not working (sadly, I don’t have a screenshot of the bad sphere, but let’s just say that it was… not an ideal geometric shape).

After getting through that, I wanted to also change the inspector so that I could enable/disable vertex visualization. As shapes become more complex, the numbering is great, but I wanted a better visualization of the vertices in the scene view. As the screenshot above illustrates, unless you rotate the view around a bit, it’s easy to get lost with where vertices actually are in relation to one another.

Sphere Vertices Visualization
Sphere Vertices Visualization

In the above GIF, you can see how movement helps determine what indices you’re viewing, but the ROYGBIV and SIZE options for visualization also help. In the ROYGBIV mode, the closest vertices are red and the furthest are violet, and everything in between follows the ROYGBIV order. With the size option, the closest vertices are the largest and the furthest away are the smallest, and they are scaled to suit in between. In either case, they’ve updated in real time. I’m not yet sure how this performs on very high density meshes, and I’m sure some optimization will be necessary, but it’s good enough for my needs for now.

I wanted the collapsible Viewables area in the inspector for this, as well as for mesh data (Vertices[], Normals[], and Triangles[]). I also wanted to be able to select the Shape (each of which is a class inheriting from Primitive()), and which Generate() method should be used (each shape has different generate methods).

For this to work, I created a few custom classes, which I added to my external DLL:

SelectableList<T> is a custom List<> type collection that has an interactive indexer property called .Selected that references the index of the selected item in the list. This has turned out to be handy for selecting items in lists from dropdowns in the inspector.

MethodCaller<T> is a custom collection that contains a reference to a class (in this case, each shape class gets a method caller), and a SelectableList<MethodInfo> of the generation methods in that class.

And lastly, MethodDictionary<T> is a custom collection class that collects MethodCaller<T> objects. Its constructor takes a filter for classes (to remove the base class and secondary base classes where inheritance takes place on multiple levels), and a filter for methods based on the name of the methods to acquire.

The creation of the MethodDictionary also builds out the dictionary based on the filters provided, so there isn’t a lot of work needed to implement it. This is definitely a plus.

In the Unity code, I also created three custom attributes:

[Segmented] is tagged to generate methods that use the segment count value. This allows that slider to be enabled/disabled on constructor selection.

[DefaultGenerator] is tagged to generate methods that the default Generate() method passes to.

[GeneratorInfo] is tagged to all generate methods and provides inline help text in the inspector, typically what segment count actually accounts for if it’s used, and what the measure/size value indicates (e.g.: circle/diameter, quad/size, hexagon/apothem*2).

Using reflection, I do something like this in the ShapeEditor.cs script:

if (shape.shapeMethods.Callers.Selected. Methods.Selected. GetCustomAttributes().SingleOrDefault(s => s.GetType() == typeof(SegmentedAttribute)) != null)

It’s not terribly pretty, but it’s fairly quick – quick enough for inspector draws – and allows the inspector panel to change on the fly as selections are made.

It’s worth noting, if you haven’t worked with custom attributes before, that the attribute doesn’t need to have fields despite all examples I came across online containing them. Without fields, it’s basically a check to see if it exists or not – a boolean of sorts applied to a reflected method to change how the inspector is drawn. Some examples:

[AttributeUsage(AttributeTargets.Method)]
public class SegmentedAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
public class DefaultGeneratorAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
public class GeneratorInfoAttribute : Attribute
{
    public readonly string info;

    public GeneratorInfoAttribute(string info)
    {
        this.info = info;
    }
}

And usage on a class method:

[Segmented]
[DefaultGenerator]
[GeneratorInfo("Generates a circle based on the 'starburst' pattern.\n\nSize is the diameter of the circle.\n\nSegments is the number of segments _per quadrant_.")]
public Mesh GenerateStarburst() { /* code */ }

I will probably write some additional posts about this, maybe with more code, as this project continues. And I’m sure I’ll have a Part 2 of Programmatic Meshes in the next week or two.

Playing with ray-tracing, Pt. 1

Back to working on Labyrintheer. But it’s Unity 2020, and I’ve been interested in playing with ray-tracing (RTX), so I started a new project, brought in some old assets and started toying with it.

The first entry is the trusty Gel Cube (from InfinityPBR) with RTX materials applied. This one is only lit by the internal point light that dies off when it dies. This is both of the attack animations and the death animation without scene lighting:

The next is with scene lighting. This is where RTX really shines with colored shadows cast through the transparent material of the GelCube:

The video quality isn’t as good as I’d hoped – need to work on that a bit.

Really, working with RTX in Unity’s HDRP isn’t terribly difficult, but there are a variety of gotchas that make it a bit of a headache, and materials are set up significantly differently (as are lights and scene volumes and…) That said, I plan to work on a few creatures first, just to get a feel for it all, then move on to bringing in the dungeons under RTX. Should be fun!