
一键复制InstaceId和Id
一键索引到EntityRef对应的Entity的ViewGo
EntityData的显示
#if ENABLE_VIEW && UNITY_EDITOR
using System.Reflection;
using Sirenix.OdinInspector;
using UnityEngine;
namespace ET
{
public class ComponentView : MonoBehaviour
{
#if ODIN_INSPECTOR
// 你的 Entity 对象
[HideInInspector]
#endif
public Entity Component
{
get;
set;
}
#if ODIN_INSPECTOR
[StaticField]
private static readonly PropertyInfo instanceIdProp =
typeof(Entity).GetProperty(nameof(Entity.InstanceId), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
[StaticField]
private static readonly PropertyInfo idProp =
typeof(Entity).GetProperty(nameof(Entity.Id), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
// Base Info 折叠组
[BoxGroup("Base Info"), ShowInInspector, ReadOnly, LabelText("Instance Id")]
public long InstanceId => Component != null ? (long)instanceIdProp.GetValue(Component) : 0;
[BoxGroup("Base Info"), Button(ButtonSizes.Small), GUIColor(0.7f, 0.9f, 1f)]
private void CopyInstanceId()
{
if (Component != null)
{
GUIUtility.systemCopyBuffer = InstanceId.ToString();
Debug.Log($"[ComponentView] 已复制 InstanceId: {InstanceId}");
}
}
[BoxGroup("Base Info"), ShowInInspector, ReadOnly, LabelText("Entity Id")]
public long Id => Component != null ? (long)idProp.GetValue(Component) : 0;
[BoxGroup("Base Info"), Button(ButtonSizes.Small), GUIColor(0.7f, 0.9f, 1f)]
private void CopyEntityId()
{
if (Component != null)
{
GUIUtility.systemCopyBuffer = Id.ToString();
Debug.Log($"[ComponentView] 已复制 EntityId: {Id}");
}
}
// Entity Data 折叠组(可编辑)
[BoxGroup("Entity Data"), ShowInInspector, InlineProperty, HideLabel]
[PropertyOrder(100)]
private Entity EditableEntityData
{
get => Component;
set => Component = value;
}
#endif
}
}
#endif
选择EntityRef对应的Entity ViewGo
using System;
#if ODIN_INSPECTOR
using Sirenix.OdinInspector;
#endif
namespace ET
{
public struct EntityRef<T> where T : Entity
{
private readonly long instanceId;
private T entity;
private EntityRef(T t)
{
if (t == null)
{
this.instanceId = 0;
this.entity = null;
return;
}
this.instanceId = t.InstanceId;
this.entity = t;
}
private T UnWrap
{
get
{
if (this.entity == null)
{
return null;
}
if (this.entity.InstanceId != this.instanceId)
{
// 这里instanceId变化了,设置为null,解除引用,好让runtime去gc
this.entity = null;
}
return this.entity;
}
}
public static implicit operator EntityRef<T>(T t)
{
return new EntityRef<T>(t);
}
public static implicit operator T(EntityRef<T> v)
{
return v.UnWrap;
}
#if UNITY_EDITOR && ODIN_INSPECTOR
[ShowInInspector, ReadOnly, LabelText("Target Entity")]
private T DebugEntity => UnWrap;
[Button("Select Entity"), ShowIf("@DebugEntity != null")]
private void SelectEntityInHierarchy()
{
var target = DebugEntity;
if (target != null && target.ViewGO != null)
{
UnityEditor.Selection.activeGameObject = target.ViewGO;
}
else
{
Log.Warning("[EntityRef] 无法选中,ViewGO 为空");
}
}
#endif
}
public struct EntityWeakRef<T> where T : Entity
{
private long instanceId;
// 使用WeakReference,这样不会导致entity dispose了却无法gc的问题
// 不过暂时没有测试WeakReference的性能
private readonly WeakReference<T> weakRef;
private EntityWeakRef(T t)
{
if (t == null)
{
this.instanceId = 0;
this.weakRef = null;
return;
}
this.instanceId = t.InstanceId;
this.weakRef = new WeakReference<T>(t);
}
private T UnWrap
{
get
{
if (this.instanceId == 0)
{
return null;
}
if (!this.weakRef.TryGetTarget(out T entity))
{
this.instanceId = 0;
return null;
}
if (entity.InstanceId != this.instanceId)
{
this.instanceId = 0;
return null;
}
return entity;
}
}
public static implicit operator EntityWeakRef<T>(T t)
{
return new EntityWeakRef<T>(t);
}
public static implicit operator T(EntityWeakRef<T> v)
{
return v.UnWrap;
}
#if UNITY_EDITOR && ODIN_INSPECTOR
[ShowInInspector, ReadOnly, LabelText("Target Entity")]
private T DebugEntity => UnWrap;
[Button("Select Entity"), ShowIf("@DebugEntity != null")]
private void SelectEntityInHierarchy()
{
var target = DebugEntity;
if (target != null && target.ViewGO != null)
{
UnityEditor.Selection.activeGameObject = target.ViewGO;
}
else
{
Log.Warning("[EntityWeakRef] 无法选中,ViewGO 为空");
}
}
#endif
}
}
ComponentViewEditor注释掉或者替换成下面的代码 (用Odin的宏包一下)
#if ENABLE_VIEW
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace ET
{
#if ODIN_INSPECTOR
#else
[CustomEditor(typeof(ComponentView))]
public class ComponentViewEditor : Editor
{
public override void OnInspectorGUI()
{
ComponentView componentView = (ComponentView)target;
Entity component = componentView.Component;
ComponentViewHelper.Draw(component);
}
}
public static class ComponentViewHelper
{
private static readonly List<ITypeDrawer> typeDrawers = new List<ITypeDrawer>();
static ComponentViewHelper()
{
Assembly assembly = typeof(ComponentViewHelper).Assembly;
foreach (Type type in assembly.GetTypes())
{
if (!type.IsDefined(typeof(TypeDrawerAttribute)))
{
continue;
}
ITypeDrawer iTypeDrawer = (ITypeDrawer)Activator.CreateInstance(type);
typeDrawers.Add(iTypeDrawer);
}
}
public static void Draw(Entity entity)
{
try
{
FieldInfo[] fields = entity.GetType()
.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
EditorGUILayout.BeginVertical();
EditorGUILayout.LongField("InstanceId: ", entity.InstanceId);
EditorGUILayout.LongField("Id: ", entity.Id);
foreach (FieldInfo fieldInfo in fields)
{
Type type = fieldInfo.FieldType;
if (type.IsDefined(typeof(HideInInspector), false))
{
continue;
}
if (fieldInfo.IsDefined(typeof(HideInInspector), false))
{
continue;
}
object value = fieldInfo.GetValue(entity);
foreach (ITypeDrawer typeDrawer in typeDrawers)
{
if (!typeDrawer.HandlesType(type))
{
continue;
}
string fieldName = fieldInfo.Name;
if (fieldName.Length > 17 && fieldName.Contains("k__BackingField"))
{
fieldName = fieldName.Substring(1, fieldName.Length - 17);
}
try
{
value = typeDrawer.DrawAndGetNewValue(type, fieldName, value, null);
}
catch (Exception e)
{
Debug.LogError(e);
}
fieldInfo.SetValue(entity, value);
break;
}
}
EditorGUILayout.EndVertical();
}
catch (Exception e)
{
UnityEngine.Debug.Log($"component view error: {entity.GetType().FullName} {e}");
}
}
}
#endif
}
#endif