嘿,我有一個簡單的類UiManager
,我想驗證所有變數,所以它們不應該為空
[SerializeField] private SceneLoaderManager.SceneName gamePlaySceneName;
[SerializeField] private TextMeshProUGUI coinCountUI;
[SerializeField] private TextMeshProUGUI energyCountUI;
目前,我只有 3 個變數,但將來可能超過 1000 個,我可能會通過檢查 null 來驗證它們,但還有很多作業要做
if (coinCountUi == null) Debug.Log ("you may forget to assign `CoinCountUi` ");
如何一次驗證所有變數并向用戶發送某種訊息?我有一個想法,但不知道如何執行它,[WarnOnNull] [SerializeField] private TextMeshProUGUI energyCountUI;
請指導我如何創建Attribute
這樣可以驗證這些東西的方法
第2部分:
namespace Randoms.Reflection
{
[AttributeUsage (AttributeTargets.All)]
public class WarnOnNullAttribute : Attribute {}
public static class TypeChecker
{
public static void WarnOnNull (this MonoBehaviour other)
{
Type type = other.GetType ();
BindingFlags bindingFlags =
BindingFlags.Instance |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.FlattenHierarchy;
FieldInfo[] fieldInfos = type
.GetFields (bindingFlags)
.Where (_ => _.IsDefined (typeof (WarnOnNullAttribute),true))
.ToArray();
foreach (FieldInfo info in fieldInfos)
{
var val = info.GetValue (other);
if (val == null)
Debug.LogWarning($"{info.Name} is not referenced!");
}
}
}
}
回答后
DerHugo
我有這段代碼到目前為止這段代碼正在作業,但我仍然必須撰寫一個附加行。
private void Awake ()
{
this.WarnOnNull ();
}
我如何才能使這項作業只使用這樣的屬性
[WarnOnNull] [SerializeField] private TextMeshProUGUI energyCountUI;
uj5u.com熱心網友回復:
How can I validate all variables at once
具體什么時候?在編譯時,這些很可能總是為空。
您當然可以進行反思并使用某個屬性 - 或者簡單地使用現有的屬性SerializeField
并只檢查諸如
var type = GetType();
var serializedFields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).Where(field => (field.FieldType.IsSubclassOf(typeof(UnityEngine.Object)) || field.FieldType == typeof(UnityEngine.Object)) && (field.IsPublic || field.IsDefined(typeof(SerializeField)))).ToArray();
foreach (var field in serializedFields)
{
var value = field.GetValue(this);
if (value == null)
{
Debug.LogError($"{field.Name} is not referenced in {type.Name} on {this}!", this);
}
}
那么這是做什么的:
GetType()
: 給我們這個組件型別GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)
:為我們提供所有實體欄位,包括由父型別實作的欄位(field.FieldType.IsSubclassOf(typeof(UnityEngine.Object)) || field.FieldType == typeof(UnityEngine.Object))
: 因為我們只對UnityEngine.Object
參考欄位感興趣。我們不關心“普通”Serializable
類、字串等(field.IsPublic || field.IsDefined(typeof(SerializeField)))
: 序列化到 Inspector 中的欄位要么是要么public
具有屬性[SerializeField]
更新
由于您只想使用屬性,因此您需要一種更集中的方式來呼叫 check 方法。
你當然可以投入更多的反思來簡單地完成你所有的課程。
可能有更好的方法可以做到這一點,但這是我在手機上想出的^^
[AttributeUsage(AttributeTargets.Field)]
public class WarnOnNullAttribute : PropertyAttribute { }
#if UNITY_EDITOR
// Just a custom drawer to display an error if used on fields
// that are not of type UnityEngine.Object
[CustomPropertyDrawer(typeof(WarnOnNullAttribute))]
public class WarnOnNullAttributeDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
WarnOnNullAttribute warn = attribute as WarnOnNullAttribute;
if (property.propertyType != SerializedPropertyType.ObjectReference)
{
EditorGUI.HelpBox(position, label.text, "[WarnOnNull] is only valid on UnityEngine.Object references!", MessageType.Error);
}
else
{
EditorGUI.PropertyField(position, property);
}
}
}
public static class WarnOnNullAttributeCheck
{
// This is called as soon as you open the project in Unity
// and after script compilations
[InitializeOnLoadMethod]
private static void Init()
{
// Attach a listener to get notified whenever the play mode is changed
EditorApplication.playModeStateChanged -= OnEnterPlayMode;
EditorApplication.playModeStateChanged = OnEnterPlayMode;
}
private static void OnEnterPlayMode(PlayModeStateChange state)
{
// only run checks once when leaving edit mode before entering play mode
if(state != PlayModeStateChange.ExitingEditMode) return;
// Get all types that inherit from ScriptableObject or Component
// as these are basically all that can have an Inspector and serialized fields
var componentTypes = GetAllDerivedFrom(typeof(Component));
var scriptabeObjectTypes = GetAllDerivedFrom(typeof(ScriptableObject));
// Now we can check both lists
// For the components we check the instances from the scene
CheckComponents(componentTypes);
// For the ScriptableObjects we check the assets
CheckScriptableObjects(scriptableObjecttypes
}
private void CheckComponents (Type[] componentTypes)
{
foreach(var type in componentTypes)
{
// Luckily in newer Unity versions there is finally
// a simple call to get all instances of a type from the scene
// including even disabled/inactive ones
var instances = UnityEngine.Object.FindObjectsOfType(type, true);
foreach(var instance in instances)
{
ValidateInstance (instance, type);
}
}
}
private static void CheckScriptableObjects (Type[] types)
{
foreach(var type in types)
{
var instances = GetAllInstaces(type);
foreach(var instance in instances)
{
ValidateInstance (instance, type);
}
}
}
private static UnityEngine.Object[] GetAllInstances(Type type)
{
// For ScriptableObjects we need a different approach
// since these are assets and we need to actually load them
var guids = AssetDatabase.FindAssets("t:" typeof(T).Name);
var instances = new UnityEngine.Object[guids.Length];
for(int i =0;i<guids.Length;i )
{
var path = AssetDatabase.GUIDToAssetPath(guids[i]);
instances[i] = AssetDatabase.LoadAssetAtPath(path);
}
return instances;
}
private static void ValidateInstance(UnityEngine.Object instance, Type type)
{
// Basically a slightly adjusted version of above
var serializedFields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).Where(field => (field.FieldType.IsSubclassOf(typeof(UnityEngine.Object)) || field.FieldType == typeof(UnityEngine.Object)) && field.IsDefined(typeof(WarnOnNullAttribute)))).ToArray();
foreach (var field in serializedFields)
{
var value = field.GetValue(instance);
if (value == null)
{
Debug.LogError($"{field.Name} is not referenced for {instance}!", instance);
}
}
}
private static Type[] GetAllDerivedFrom(Type baseType)
{
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.Where(type => type.IsSubclassOf(baseType)).ToArray();
}
}
#endif
也可以使用Resources.FindObjectsOfTypeAll
它甚至包括預制件等,但它會變得更復雜,也更昂貴;)
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/508677.html
標籤:C#unity3d属性