I have a special struct to represent hex grid coordinates. It basically looks like a float3 but uses int values and there are some constraints on the validity of combinations:
public struct TileCoordinates : IEquatable<TileCoordinates>
{
public int X { get; }
public int Y { get; }
public int Z { get; }
//...
}
I use this struct inside of a component:
/// <summary>
/// Coordinates of a thing inside the hex world.
/// </summary>
public struct Coordinates: IComponentData
{
public TileCoordinates Value;
}
This works all fine and dandy, however in the entity inspector, the contents of the Coordinates struct are not rendering:
BecauseTileCoordinates is a struct and not a UnityEngine.Object I cannot write a custom inspector for it the classic way. Is there any way i can render the contents of this struct properly in the inspector?
Nope, I added a very primitive one just outputting a Debug.Log, but it doesn’t even get called. There seems to be some special magic in place for the entity debugger.
So [Serializable] didn’t help, but changing the properties to public member variables made the struct render properly. I’m not really a huge fan of this, but looks like the Unity.Mathematics types are implemented the same way. They have some logic in the struct but ultimately you can throw all of this out of the window by directly writing the members. Thank you very much
It requires a little bit of hacking (accessing private field of EntitySelectionProxyEditor).
How normal entity inspector works:
Entities cannot be selected because they’re not UnityEngine.Object
there’s EntitySelectionProxy which is ScriptableObject, it stores selected Entity
EntitySelectionProxyEditor draws inspector for EntitySelectionProxy
PropertyVisitor class from com.unity.properties is used to walk component field hierarchy
visitor adapters are used, they call EditorGUILayout.anything to draw inspector
How this code works
it finds all types decorated with EntityInspectorAdapterAttribute
creates instances of those and stores them in list
when EntitySelectionProxy is selected, it adds adapters to it’s editor
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Unity.Entities.Editor;
using Unity.Properties;
using UnityEditor;
using UnityEngine;
[AttributeUsage(AttributeTargets.Class)]
public class EntityInspectorAdapterAttribute : Attribute
{
}
public static class EntityInspectorExtension
{
static Type m_proxyEditor = typeof(EntitySelectionProxy).Assembly.GetType("Unity.Entities.Editor.EntitySelectionProxyEditor");
static FieldInfo m_visitorField = m_proxyEditor.GetField("visitor", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
public static readonly List<IPropertyVisitorAdapter> Adapters = new List<IPropertyVisitorAdapter>();
[InitializeOnLoadMethod]
static void Init()
{
Selection.selectionChanged += OnSelectionChanged;
foreach (var type in TypeCache.GetTypesWithAttribute<EntityInspectorAdapterAttribute>())
{
Adapters.Add((IPropertyVisitorAdapter)Activator.CreateInstance(type));
}
}
private static void OnSelectionChanged()
{
if (Selection.activeObject.GetType() == typeof(EntitySelectionProxy))
{
var editor = Resources.FindObjectsOfTypeAll(m_proxyEditor)[0];
var visitor = (PropertyVisitor)m_visitorField.GetValue(editor);
foreach (var adapter in Adapters)
{
visitor.AddAdapter(adapter);
}
}
}
}
This is custom inspector for my component DebugName
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Unity.Properties;
using UnityEditor;
[EntityInspectorAdapter]
public class EntityInspectorDebugName : IPropertyVisitorAdapter, IVisitAdapter<DebugName>
{
public VisitStatus Visit<TProperty, TContainer>(IPropertyVisitor visitor, TProperty property, ref TContainer container, ref DebugName value, ref ChangeTracker changeTracker)
where TProperty : IProperty<TContainer, DebugName>
{
EditorGUI.BeginChangeCheck();
value = new DebugName(EditorGUILayout.TextField(property.GetName(), value.Value.ToString()));
if (EditorGUI.EndChangeCheck())
{
changeTracker.MarkChanged();
}
return VisitStatus.Handled;
}
}
You can install it into Unity as package by adding this into manifest: "entity-inspector-extension": "https://github.com/OndrejPetrzilka/EntityComponentInspector.git"