Using IDrawGizmos without a MonoBehaviour (ScriptableObjects, statics)

  • ALINE version: 1.7.8
  • Unity version: Unity 6.3
  • Render pipeline: Standard

TLDR

Passing an IDrawGizmos to the DrawingManager does not ever fire the gizmos if the object is a ScriptableObject or a POCO.

Why

Hi, after reading some threads here in the forum and elsewhere I was trying to do something more advanced with my ALINE project. Basically, I am aware that you can pass in a custom IDrawGizmos to the DrawingManager and it will register it as a source of gizmo drawing. I was very excited about doing this, but spent a decent amount of time trying to figure out why nothing was working.

I was porting over some old code where I was using the DrawGizmos attribute to draw my gizmos. It’s super handy because it means I can separate my gizmo drawing from the assembly that contains my runtime. Otherwise, if I have somebody use my really simple data container (let’s say it’s a spawn point or a way point), they have to take a dependency on ALINE in order to properly load the type if it inherits from MonoBehaviourGizmos.

I like my dependency chains clean so I was hoping there was a way to use the DrawGizmosAttribute like I’ve always done to have this clean separation only to find myself hitting the exception many have bemoaned (you can’t use the Draw.* in this context one).

Example

So after discovering that the DrawingManager can just take in any old IDrawGizmos, I made one myself:

[InitializeOnLoad]
internal static class MyStaticGizmoClass
{
	private static readonly MyCustomDrawer _drawer = new MyCustomDrawer();

	static MyStaticGizmoClass()
	{
		DrawingManager.Init();
		DrawingManager.Register(_drawer);
	}
	
	private sealed class MyCustomDrawer : IDrawGizmos
	{
		public void DrawGizmos()
		{
			Debug.Log("This will never be called because I am not a MonoBehaviour :(");
		}
	}
}

The Reason

The Gizmo Drawer actually gets properly registered, it goes into a group with a drawer, but when RemoveDestroyedGizmoDrawers runs, it has a direct cast to MonoBehaviour. As I am not a MonoBehaviour, that just gets skipped out, and the rest of the code just assumes all of them are MonoBehaviours.

Alternatives? What do I do?

All I want is two things:

  1. To Draw A Gizmo for an Object for whom I don’t want its asmdef take a dependency on ALINE (Drawing editor-time gizmos feels separate from data storage).
  2. To do this without adding a MonoBehaviour to the scene. I get that I could do this with a bunch of trickery – make a monoBehavior and set its hide flags accordingly forcing it to actually just be the hook that draws objects, but that’s incredibly disgusting. I just wanted a static-time IDrawGizmos, like the docs seemed to promise, but instead I found I was actually tied to MonoBehaviour, IDrawGizmos instead of being tied to just the callback.

Ideally I would prefer even more an alternative to the oft-ignored but very powerful DrawGizmosAttribute but ALINE seems very unhappy if I try using that attribute.

public class GizmoFreeBehaviour : MonoBehaviour {}

internal static class MyStaticClass
{
	[DrawGizmo(GizmoType.Selected | GizmoType.InSelectionHierarchy)]
	public static void ADrawingMethod(GizmoFreeBehaviour behaviour, GizmoType type)
	{
		// Ideally I would Use Aline here
		Gizmos.DrawWireCube(behaviour.transform.position, Vector3.one);
	}
}

What are the best courses of action here?

This part makes me want to tag Aron on this to get his thoughts on it. I’m wondering if this could be changed (if it’s even possible) to accomodate a workflow like this. I’ll let him know about this. Unfortunately for the time being, I can’t think of any good workaround for you, sorry about that.

Seeing as it’s been two weeks – I’ll try a tag here: @aron_granberg – is there anything we can do to create an ‘external’ gizmo that lets us do the drawing separate to the MonoBehaviour that has the data in question?

An alternative to the IDrawGizmos support could be an attribute based approach that shoves things into the IDrawGizmos flow, but either way, the built-in assumption that you HAVE to be a MonoBehaviour makes things complicated, although I do understand that it’s sort of a shortcut for lifetime checks. You don’t have to keep track of the lifetime if you’re tied to a MonoBehaviour which has cleanly defined OnDestroy, but dealing with statics can be complicated especially if people have things persisting across domain reloads, etc. Same can be said of tying into a ScriptableObject, which can be ‘loaded’ in editor at non-deterministic times.

The frustrating thing is; this is why I bought ALINE. I thought it would give me better drawing tools for my gizmos, but I literally can’t use it right now without violating our asmdef dependency guidelines

Hi

I have investigated this.

`DrawGizmosAttribute`is sadly very tricky to use because Unity doesn’t really provide a performant way to get a list of objects of a specific type. Or events when such objects are added/removed. One would still have to call ALINE in e.g. the constructor or Awake method of such an object, which would invalidate the whole point.

What I could do is to have a method that registers drawing for a particular object with a custom callback. But then you would have to call such a method manually. The object would not be auto-discovered just because it has a specific type. Would that work for you?