我有List<AbilityEffect> effects很多 AbilityEffect 的子類,例如 DamageEffect、HealEffect 等,它們都有[System.Serializable]屬性。如果我創建帶有諸如 DamageEffect 之類的欄位的類 - 默認編輯器將完美地繪制它!(還有其他效果!)
我在 AbilityData.cs 中為這個函式添加了一個 ContextMenu 屬性
[ContextMenu(Add/DamageEffect)]
public static void AddDamageEffect()
{
effects.Add(new DamageEffect());
}
但是默認的 Unity 編輯器會繪制它,如果它是一個AbilityEffect,而不是一個DamageEffect!
我已經為類撰寫了一些自定義編輯器,其中包含List<AbilityEffect> effects = new List<AbilitiEffect>()撰寫繪制自定義串列的代碼!但是我如何告訴編輯器DamageEffect專門畫一個 NOT AbilityEffect?
我將在下面放一些代碼:
能力資料類
using UnityEngine;
using System.Collections.Generic;
[CreateAssetMenu(fileName = "New Ability", menuName = "ScriptableObject/Ability")]
public class AbilityData : ScriptableObject
{
public int cooldown = 0;
public int range = 1;
public List<AbilityEffect> effects = new List<AbilityEffect>();
public bool showEffects = false;
[ContextMenu("Add/DamageEffect")]
public void AddDamageEffect()
{
effects.Add(new DamageEffect());
}
}
能力資料編輯類
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
[CustomEditor(typeof(AbilityData))]
public class AbilityEditor : Editor
{
public override void OnInspectorGUI()
{
var ability = (AbilityData)target;
DrawDetails(ability);
DrawEffects(ability);
}
private static void DrawEffects(AbilityData ability)
{
EditorGUILayout.Space();
ability.showEffects = EditorGUILayout.Foldout(ability.showEffects, "Effects", true);
if (ability.showEffects)
{
EditorGUI.indentLevel ;
List<AbilityEffect> effects = ability.effects;
int size = Mathf.Max(0, EditorGUILayout.IntField("Size", effects.Count));
while (size > effects.Count)
{
effects.Add(null);
}
while (size < effects.Count)
{
effects.RemoveAt(effects.Count - 1);
}
for (int i = 0; i < effects.Count; i )
{
DrawEffect(effects[i], i);
}
EditorGUI.indentLevel--;
}
}
private static void DrawDetails(AbilityData ability)
{
EditorGUILayout.LabelField("Details");
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Cooldown", GUILayout.MaxWidth(60));
ability.cooldown = EditorGUILayout.IntField(ability.cooldown);
EditorGUILayout.LabelField("Range", GUILayout.MaxWidth(40));
ability.range = EditorGUILayout.IntField(ability.range);
EditorGUILayout.EndHorizontal();
}
private static void DrawEffect(AbilityEffect effect, int index)
{
//if (effect is DamageEffect)
// effect = EditorGUILayout
// HOW??
}
}
能力效果類(非抽象)
[System.Serializable]
public class AbilityEffect
{
public virtual void Affect() { }
}
傷害效果等級
[System.Serializable]
public class DamageEffect : AbilityEffect
{
public int damageAmout = 1;
public override void Affect() { ... }
}
uj5u.com熱心網友回復:
由于序列化的作業方式,一旦你反序列化一些資料,Unity 將嘗試根據類定義中指定的型別填充物件實體。如果您有List<AbilityEffect>Unity 將無法區分您之前序列化的特定 AbilityEffect。確實有一個解決方案,將其更改AbilityEffect為 ScriptableObject,以便 Unity 實際上不會將它們序列化為原始資料,而是作為 GUID 參考,以便被參考的資產自己知道AbilityEffect它們是什么子型別。缺點是這樣你所有的效果都必須是你的資產檔案夾中的資產。
uj5u.com熱心網友回復:
首先:請注意,自 Unity 2021 以來,折疊是所有串列和陣列的內置默認設定,所以實際上我認為完全不需要自定義編輯器(至少對于串列部分);)
你的方法有幾個問題。
但是如果是AbilityEffect,而不是DamageEffect,則默認的Unity Editor會繪制它。
是的,因為它僅被序列化為AbilityEffect! 對于序列化器,該串列中的所有專案都是型別AbilityEffect,它不會再進一步??。
因此,即使您可以設法添加子類專案,它也只是暫時的!例如保存后,關閉 Unity 并重新打開所有子型別應轉換為,AbilityEffect因為這只是序列化程式實際看到的型別。
我的建議是讓你AbilityEffect也成為 type ScriptableObject。這樣,您甚至根本不必為它們使用自定義抽屜,并且可以擁有盡可能多的具有不同型別和配置的實體,重用它們等。
這現在說一個普遍的事情:不要直接通過target編輯器!(除非您確切地知道自己在做什么)
這不會將此物件標記為“臟”,不適用于撤消/重做,最糟糕的是 - 它不會持久保存這些更改!
總是寧愿通過 theserializedObject和SerializedPropertys。
[CustomEditor(typeof(AbilityData))]
public class AbilityEditor : Editor
{
SerializedProperty cooldown;
SerializedProperty range;
SerializedProperty effects;
SerializedProperty showEffects;
private void OnEnable ()
{
// Link up the serialized fields you will access
cooldown = serializedObject.FindProperty(nameof(AbilityData.cooldown));
range = serializedObject.FindProperty(nameof(AbilityData.range));
effects = serializedObject.FindProperty(nameof(AbilityData.effects));
showEffects = serializedObject.FindProperty(nameof(AbilityData.showEffects));
}
public override void OnInspectorGUI()
{
// refresh current actual values into the editor
serializedObject.Update();
DrawDetails();
DrawEffects();
// write back any changed values from the editor back to the actual object
// This handles all marking dirty, saving and handles Undo/Redo
serializedObject.ApplyModifiedProperties();
}
private void DrawEffects()
{
// Now always ever only read and set values via the SerializedPropertys
EditorGUILayout.Space();
showEffects.boolValue = EditorGUILayout.Foldout(showEffects.boolValue, effects.displayName, true);
if (showEffects.boolValue)
{
EditorGUI.indentLevel ;
// This already handles all the list drawing by default
EditorGUILayout.PropertyField(effects, GUIContent.none, true);
EditorGUI.indentLevel--;
}
}
private void DrawDetails()
{
EditorGUILayout.LabelField("Details");
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField(cooldown.displayName, GUILayout.MaxWidth(60));
cooldown.intValue = EditorGUILayout.IntField(cooldown.intValue);
EditorGUILayout.LabelField(range.displayName, GUILayout.MaxWidth(40));
range.intValue = EditorGUILayout.IntField(range.intValue);
EditorGUILayout.EndHorizontal();
}
}
現在,如果您真的想自定義串列繪圖的行為,您可以使用 a ReorderableList,然后可以為每個元素實作一個抽屜,您確實可以在那里執行型別檢查。
但正如我所說的,我根本不會這樣做,因為序列化器無論如何都不支持它。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/416068.html
標籤:
