公共字段在Inspector中重叠显示(自定义Struct Class 数组 列表)

文章介绍了Unity中自定义结构体或类在Inspector显示时属性重叠的问题,并提供了解决方案:使用[NonReorderable]标签防止数组或列表重排,以改善数据可视化效果。

自定义公共字段在Inspector相互重叠

一、什么问题

当用到自定义的结构体或类的时候,在Unity面板中展开该属性,会出现属性之间相互重叠的问题。如下图所示。
在这里插入图片描述

二、如何解决

方法:对数据(数组 // 列表)定义之前添加[NonReorderable]字段。
含义:在inspector中,禁止对数组或列表重排。
【官方文档】

//定义公共类
 [Serializable]  //在面板中可视公共类Obj
    public class Obj
    {
   
   
        public Transform Object
将以下代码改成不通过在commandBuffer绘制来使outlines里的物体可以实现描边效果: using UnityEngine; using System.Collections.Generic; using UnityEngine.Rendering; namespace cakeslice { [DisallowMultipleComponent] [RequireComponent(typeof(Camera))] [ExecuteInEditMode] public class OutlineEffect : MonoBehaviour { private static OutlineEffect m_instance; public static OutlineEffect Instance { get { if (Equals(m_instance, null)) { return m_instance = FindObjectOfType(typeof(OutlineEffect)) as OutlineEffect; } return m_instance; } } private OutlineEffect() { } private readonly LinkedSet<Outline> outlines = new LinkedSet<Outline>(); [Range(0.0f, 6.0f)] public float lineThickness = 1.25f; [Range(0, 10)] public float lineIntensity = .5f; [Range(0, 1)] public float fillAmount = 0.2f; public Color lineColor0 = Color.red; public Color lineColor1 = Color.green; public Color lineColor2 = Color.blue; public bool additiveRendering = false; public bool backfaceCulling = true; [Header("These settings can affect performance!")] public bool cornerOutlines = false; public bool addLinesBetweenColors = false; [Header("Advanced settings")] public bool scaleWithScreenSize = true; [Range(0.1f, .9f)] public float alphaCutoff = .5f; public bool flipY = false; public Camera sourceCamera; [HideInInspector] public Camera outlineCamera; Material outline1Material; Material outline2Material; Material outline3Material; Material outlineEraseMaterial; Shader outlineShader; Shader outlineBufferShader; [HideInInspector] public Material outlineShaderMaterial; [HideInInspector] public RenderTexture renderTexture; [HideInInspector] public RenderTexture extraRenderTexture; CommandBuffer commandBuffer; Material GetMaterialFromID(int ID) { if (ID == 0) return outline1Material; else if (ID == 1) return outline2Material; else return outline3Material; } List<Material> materialBuffer = new List<Material>(); Material CreateMaterial(Color emissionColor) { Material m = new Material(outlineBufferShader); m.SetColor("_Color", emissionColor); m.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha); m.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); m.SetInt("_ZWrite", 1); m.DisableKeyword("_ALPHATEST_ON"); m.EnableKeyword("_ALPHABLEND_ON"); m.DisableKeyword("_ALPHAPREMULTIPLY_ON"); m.renderQueue = 3000; m.enableInstancing = true; return m; } private void Awake() { m_instance = this; } void Start() { CreateMaterialsIfNeeded(); UpdateMaterialsPublicProperties(); if (sourceCamera == null) { sourceCamera = GetComponent<Camera>(); if (sourceCamera == null) sourceCamera = Camera.main; } if (outlineCamera == null) { GameObject cameraGameObject = new GameObject("Outline Camera"); cameraGameObject.transform.parent = sourceCamera.transform; outlineCamera = cameraGameObject.AddComponent<Camera>(); outlineCamera.enabled = false; } renderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default); extraRenderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default); UpdateOutlineCameraFromSource(); commandBuffer = new CommandBuffer(); outlineCamera.AddCommandBuffer(CameraEvent.BeforeImageEffects, commandBuffer); } public void OnPreRender() { if (commandBuffer == null) return; CreateMaterialsIfNeeded(); if (renderTexture == null || renderTexture.width != sourceCamera.pixelWidth || renderTexture.height != sourceCamera.pixelHeight) { renderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default); extraRenderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default); outlineCamera.targetTexture = renderTexture; } UpdateMaterialsPublicProperties(); UpdateOutlineCameraFromSource(); outlineCamera.targetTexture = renderTexture; commandBuffer.SetRenderTarget(renderTexture); commandBuffer.Clear(); commandBuffer.ClearRenderTarget(true, true, Color.clear); if (outlines != null) { var sortedOutlines = new List<Outline>(outlines); sortedOutlines.Sort((a, b) => { if (a == null || b == null) return 0; float distA = Vector3.SqrMagnitude(sourceCamera.transform.position - a.transform.position); float distB = Vector3.SqrMagnitude(sourceCamera.transform.position - b.transform.position); return distA.CompareTo(distB); }); foreach (Outline outline in sortedOutlines) { if (outline == null) continue; LayerMask l = sourceCamera.cullingMask; if ((l & (1 << outline.gameObject.layer)) == 0) continue; Renderer renderer = outline.Renderer; if (renderer == null) continue; for (int v = 0; v < renderer.sharedMaterials.Length; v++) { Material m = null; if (renderer.sharedMaterials[v]?.mainTexture != null) { foreach (Material g in materialBuffer) { if (g.mainTexture == renderer.sharedMaterials[v].mainTexture) { if (outline.eraseRenderer && g.color == outlineEraseMaterial.color) m = g; else if (g.color == GetMaterialFromID(outline.color).color) m = g; } } if (m == null) { if (outline.eraseRenderer) m = new Material(outlineEraseMaterial); else m = new Material(GetMaterialFromID(outline.color)); m.mainTexture = renderer.sharedMaterials[v].mainTexture; materialBuffer.Add(m); } } else { if (outline.eraseRenderer) m = outlineEraseMaterial; else m = GetMaterialFromID(outline.color); } if (backfaceCulling) m.SetInt("_Culling", (int)UnityEngine.Rendering.CullMode.Back); else m.SetInt("_Culling", (int)UnityEngine.Rendering.CullMode.Off); commandBuffer.DrawRenderer(renderer, m, 0, 0); if (renderer is MeshRenderer meshRenderer) { MeshFilter meshFilter = outline.GetComponent<MeshFilter>(); if (meshFilter != null && meshFilter.sharedMesh != null) { for (int i = 1; i < meshFilter.sharedMesh.subMeshCount; i++) commandBuffer.DrawRenderer(renderer, m, i, 0); } } else if (renderer is SkinnedMeshRenderer skinnedMeshRenderer) { if (skinnedMeshRenderer.sharedMesh != null) { for (int i = 1; i < skinnedMeshRenderer.sharedMesh.subMeshCount; i++) commandBuffer.DrawRenderer(renderer, m, i, 0); } } } } } outlineCamera.Render(); } private void OnEnable() { Outline[] o = FindObjectsOfType<Outline>(); foreach (Outline oL in o) { oL.enabled = false; oL.enabled = true; } } void OnDestroy() { if (renderTexture != null) renderTexture.Release(); if (extraRenderTexture != null) extraRenderTexture.Release(); DestroyMaterials(); } void OnRenderImage(RenderTexture source, RenderTexture destination) { outlineShaderMaterial.SetTexture("_OutlineSource", renderTexture); if (addLinesBetweenColors) { Graphics.Blit(source, extraRenderTexture, outlineShaderMaterial, 0); outlineShaderMaterial.SetTexture("_OutlineSource", extraRenderTexture); } Graphics.Blit(source, destination, outlineShaderMaterial, 1); } private void CreateMaterialsIfNeeded() { if (outlineShader == null) outlineShader = Resources.Load<Shader>("OutlineShader"); if (outlineBufferShader == null) { outlineBufferShader = Resources.Load<Shader>("OutlineBufferShader"); } if (outlineShaderMaterial == null) { outlineShaderMaterial = new Material(outlineShader); outlineShaderMaterial.hideFlags = HideFlags.HideAndDontSave; UpdateMaterialsPublicProperties(); } if (outlineEraseMaterial == null) outlineEraseMaterial = CreateMaterial(new Color(0, 0, 0, 0)); if (outline1Material == null) outline1Material = CreateMaterial(new Color(1, 0, 0, 0)); if (outline2Material == null) outline2Material = CreateMaterial(new Color(0, 1, 0, 0)); if (outline3Material == null) outline3Material = CreateMaterial(new Color(0, 0, 1, 0)); } private void DestroyMaterials() { foreach (Material m in materialBuffer) DestroyImmediate(m); materialBuffer.Clear(); DestroyImmediate(outlineShaderMaterial); DestroyImmediate(outlineEraseMaterial); DestroyImmediate(outline1Material); DestroyImmediate(outline2Material); DestroyImmediate(outline3Material); outlineShader = null; outlineBufferShader = null; outlineShaderMaterial = null; outlineEraseMaterial = null; outline1Material = null; outline2Material = null; outline3Material = null; } public void UpdateMaterialsPublicProperties() { if (outlineShaderMaterial) { float scalingFactor = 1; if (scaleWithScreenSize) { // If Screen.height gets bigger, outlines gets thicker scalingFactor = Screen.height / 360.0f; } // If scaling is too small (height less than 360 pixels), make sure you still render the outlines, but render them with 1 thickness if (scaleWithScreenSize && scalingFactor < 1) { if (UnityEngine.XR.XRSettings.isDeviceActive && sourceCamera.stereoTargetEye != StereoTargetEyeMask.None) { outlineShaderMaterial.SetFloat("_LineThicknessX", (1 / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureWidth) * 1000.0f); outlineShaderMaterial.SetFloat("_LineThicknessY", (1 / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureHeight) * 1000.0f); } else { outlineShaderMaterial.SetFloat("_LineThicknessX", (1 / 1000.0f) * (1.0f / Screen.width) * 1000.0f); outlineShaderMaterial.SetFloat("_LineThicknessY", (1 / 1000.0f) * (1.0f / Screen.height) * 1000.0f); } } else { if (UnityEngine.XR.XRSettings.isDeviceActive && sourceCamera.stereoTargetEye != StereoTargetEyeMask.None) { outlineShaderMaterial.SetFloat("_LineThicknessX", scalingFactor * (lineThickness / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureWidth) * 1000.0f); outlineShaderMaterial.SetFloat("_LineThicknessY", scalingFactor * (lineThickness / 1000.0f) * (1.0f / UnityEngine.XR.XRSettings.eyeTextureHeight) * 1000.0f); } else { outlineShaderMaterial.SetFloat("_LineThicknessX", scalingFactor * (lineThickness / 1000.0f) * (1.0f / Screen.width) * 1000.0f); outlineShaderMaterial.SetFloat("_LineThicknessY", scalingFactor * (lineThickness / 1000.0f) * (1.0f / Screen.height) * 1000.0f); } } outlineShaderMaterial.SetFloat("_LineIntensity", lineIntensity); outlineShaderMaterial.SetFloat("_FillAmount", fillAmount); outlineShaderMaterial.SetColor("_LineColor1", lineColor0 * lineColor0); outlineShaderMaterial.SetColor("_LineColor2", lineColor1 * lineColor1); outlineShaderMaterial.SetColor("_LineColor3", lineColor2 * lineColor2); if (flipY) outlineShaderMaterial.SetInt("_FlipY", 1); else outlineShaderMaterial.SetInt("_FlipY", 0); if (!additiveRendering) outlineShaderMaterial.SetInt("_Dark", 1); else outlineShaderMaterial.SetInt("_Dark", 0); if (cornerOutlines) outlineShaderMaterial.SetInt("_CornerOutlines", 1); else outlineShaderMaterial.SetInt("_CornerOutlines", 0); Shader.SetGlobalFloat("_OutlineAlphaCutoff", alphaCutoff); } } void UpdateOutlineCameraFromSource() { outlineCamera.CopyFrom(sourceCamera); outlineCamera.renderingPath = RenderingPath.Forward; outlineCamera.backgroundColor = new Color(0.0f, 0.0f, 0.0f, 0.0f); outlineCamera.clearFlags = CameraClearFlags.SolidColor; outlineCamera.rect = new Rect(0, 0, 1, 1); outlineCamera.cullingMask = 0; outlineCamera.targetTexture = renderTexture; outlineCamera.enabled = false; #if UNITY_5_6_OR_NEWER outlineCamera.allowHDR = false; #else outlineCamera.hdr = false; #endif } public void AddOutline(Outline outline) { if (!outlines.Contains(outline)) outlines.Add(outline); } public void RemoveOutline(Outline outline) { if (outlines.Contains(outline)) outlines.Remove(outline); } } }
最新发布
09-18
<think>我们正在讨论Unity显示自定义数组字段的方法。根据用户提供的引用和问题,用户希望能在Unity编辑器的Inspector窗口中显示自定义数组中的字段。引用[1]提到,自定义的结构体或类默认不会显示出来,除非加上[Serializable]序列化标记。用户已经为结构体People添加了[Serializable]属性,这样它就可以在Inspector显示。引用[3]展示了如何通过自定义属性(Attribute)来改变Inspector字段显示名称。用户创建了一个CustomLabelAttribute特性,用于指定字段Inspector显示自定义名称。引用[4]提到了使用CustomPropertyDrawer来自定义显示属性。当需要更复杂的显示方式时,可以创建自定义的PropertyDrawer。用户的问题是:如何在Unity编译器中查看或显示自定义数组中的字段?解决方案:1.确保自定义类或结构体标记为[Serializable]。2.如果需要在数组(或List)中显示,则将该数组或List声明为public(或[SerializeField]private)。3.如果需要自定义显示字段的名称,可以使用类似引用[3]中的CustomLabelAttribute,然后为这个Attribute编写相应的PropertyDrawer(参考引用[4])。4.如果需要更复杂的布局,可以编写自定义的PropertyDrawer。具体步骤:步骤1:序列化自定义类例如,有一个自定义类:```csharp[Serializable]publicclassMyCustomClass{publicintnumber;publicstringtext;}```步骤2:在MonoBehaviour中使用这个类的数组```csharppublicclassMyBehaviour:MonoBehaviour{publicMyCustomClass[]myArray;}```这样,在Inspector中就会显示一个数组,每个元素都可以展开并显示其中的字段。但是,如果用户希望修改字段Inspector中的显示名称,或者对字段显示进行更复杂的控制,就需要使用自定义Attribute和PropertyDrawer。例如,使用CustomLabelAttribute(如引用[3]):首先,定义CustomLabelAttribute:```csharpusingUnityEngine;publicclassCustomLabelAttribute:PropertyAttribute{publicstringname;publicCustomLabelAttribute(stringname){this.name=name;}}```然后,为这个Attribute编写PropertyDrawer(需要放在Editor文件夹下):```csharpusingUnityEditor;usingUnityEngine;[CustomPropertyDrawer(typeof(CustomLabelAttribute))]publicclassCustomLabelDrawer:PropertyDrawer{publicoverridevoidOnGUI(Rectposition,SerializedPropertyproperty,GUIContentlabel){//获取我们自定义的attributeCustomLabelAttributecustomLabel=attributeasCustomLabelAttribute;//使用自定义的名称作为标签EditorGUI.PropertyField(position,property,newGUIContent(customLabel.name),true);}}```在自定义类中使用这个Attribute:```csharp[Serializable]publicclassMyCustomClass{[CustomLabel("数字")]publicintnumber;[CustomLabel("文本")]publicstringtext;}```这样,在Inspector中,字段number将显示为“数字”,字段text将显示为“文本”。另外,对于数组中的每个元素,Unity默认会显示为“Element0”、“Element1”等,如果想自定义数组元素的显示,可以使用自定义PropertyDrawer来绘制整个数组,但这样比较复杂。通常,我们通过自定义数组元素的类(即MyCustomClass)的显示方式,来达到目的。如果用户希望自定义数组元素的显示方式(例如,将每个元素的字段水平排列),可以为MyCustomClass编写一个自定义的PropertyDrawer。例如,为MyCustomClass编写PropertyDrawer(同样放在Editor文件夹):```csharp[CustomPropertyDrawer(typeof(MyCustomClass))]publicclassMyCustomClassDrawer:PropertyDrawer{publicoverridevoidOnGUI(Rectposition,SerializedPropertyproperty,GUIContentlabel){//使用EditorGUI.PropertyField绘制默认的,或者自定义布局//例如,将两个字段水平排列Rectrect1=newRect(position.x,position.y,position.width/2,position.height);Rectrect2=newRect(position.x+position.width/2,position.y,position.width/2,position.height);//分别绘制两个属性EditorGUI.PropertyField(rect1,property.FindPropertyRelative("number"));EditorGUI.PropertyField(rect2,property.FindPropertyRelative("text"));}}```这样,在数组中每个元素就会以自定义的方式绘制。注意:如果数组元素是自定义类,并且我们为该类编写了PropertyDrawer,那么数组中的每个元素都会按照这个PropertyDrawer绘制。总结:1.序列化自定义类和数组。2.使用Attribute和PropertyDrawer自定义字段的标签和显示方式。3.对于数组中的自定义类,可以通过为该类编写PropertyDrawer来自定义每个元素的显示。用户可以根据需要选择合适的方法。相关问题:1.Unity中如何序列化自定义类?2.如何为UnityInspector中的字段自定义显示名称?3.如何为自定义类编写PropertyDrawer?4.如何自定义数组Inspector中的显示?5.如何让私有字段Inspector显示?</think>### 在Unity编辑器中显示自定义数组字段的方法 要在Unity编辑器的Inspector窗口中显示自定义数组字段,需遵循以下步骤: #### 1. **序列化自定义类和数组** 确保自定义类标记 `[Serializable]`,数组字段需声明为 `public` 或使用 `[SerializeField]`: ```csharp [System.Serializable] // 关键序列化标记 public class People { public string Name; public int Age; public string Sex; } public class Example : MonoBehaviour { public People[] peopleArray; // 公有数组自动显示 // 或 [SerializeField] private People[] privateArray; // 私有数组需SerializeField } ``` #### 2. **自定义字段显示名称(可选)** 使用 `CustomLabelAttribute` 修改Inspector中的字段名[^3]: ```csharp // 定义特性 public class CustomLabelAttribute : PropertyAttribute { public string name; public CustomLabelAttribute(string name) => this.name = name; } // 应用特性 [System.Serializable] public class People { [CustomLabel("姓名")] public string Name; [CustomLabel("年龄")] public int Age; [CustomLabel("性别")] public string Sex; } ``` #### 3. **创建PropertyDrawer(高级定制)** 如需深度定制显示样式,需在 **Editor文件夹** 下创建 `PropertyDrawer`[^4]: ```csharp using UnityEditor; using UnityEngine; // 为CustomLabel特性创建绘制器 [CustomPropertyDrawer(typeof(CustomLabelAttribute))] public class CustomLabelDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { var attr = (CustomLabelAttribute)attribute; EditorGUI.PropertyField(position, property, new GUIContent(attr.name), true); } } // 为People类创建整体绘制器(可选) [CustomPropertyDrawer(typeof(People))] public class PeopleDrawer : PropertyDrawer { public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { EditorGUI.PropertyField(position, property, label, true); } } ``` #### 4. **使用效果** 完成上述步骤后,自定义数组Inspector中将显示为: - 可折叠的数组列表 - 每个元素可展开显示详细字段 - 字段名称按定制显示(如使用CustomLabel) ![Inspector显示效果](https://docs.unity3d.com/uploads/Main/Inspector-ArrayExample.png) > ⚠️ 注意事项: > 1. 所有自定义类必须标记 `[Serializable]` > 2. PropertyDrawer脚本必须放在 **Editor文件夹** 内 > 3. 修改脚本后需重新编译才能生效 > 4. 数组元素修改需在Play模式外进行(序列化限制) --- ### 常见问题解决方案 | 问题现象 | 解决方法 | |---------|----------| | 数组显示 | 检查类是否标记 `[Serializable]`,字段是否为public或`[SerializeField]` | | 字段名未改变 | 确保PropertyDrawer在Editor文件夹,且特性名称匹配 | | 嵌套类不显示 | 在内嵌类上也添加 `[Serializable]` 标记 | | 修改不生效 | 关闭脚本后重新编译,或重启Unity | 通过以上方法,您可以在Unity编辑器中完整显示控制自定义数组的所有字段。如需进一步定制UI布局,可扩展 `PropertyDrawer` 的 `OnGUI` 方法实现更复杂的绘制逻辑[^2][^4]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值