浏览代码
Feature/check window (#21)
Feature/check window (#21)
* Base Files * Check Results display + First implementation * Added Selection of Object in the list * Added Result Sorting * Added Filtering for Info/Warn/Error * Added Check for external references * Moved Search SceneObjects to Check Window so it's shared among all Checks * Work on Ignored Results * Added checking to UnityEvent fields / Resolve now ignores corrrectly * Use ShowIgnored as Preference + Ensure correct scene change. * Recheck after resolve / Null Material Check / Empty Game Object Check * Added Icon * Updated Changelog/main
GitHub
4 年前
当前提交
9c54b406
共有 22 个文件被更改,包括 1044 次插入 和 3 次删除
-
1CHANGELOG.md
-
14Editor/SceneViewToolbar.cs
-
8Editor/CheckWindow.meta
-
8Runtime/CheckWIndow.meta
-
91Editor/CheckWindow/Check.cs
-
11Editor/CheckWindow/Check.cs.meta
-
116Editor/CheckWindow/CheckResult.cs
-
11Editor/CheckWindow/CheckResult.cs.meta
-
511Editor/CheckWindow/CheckWindow.cs
-
11Editor/CheckWindow/CheckWindow.cs.meta
-
8Editor/CheckWindow/Implementations.meta
-
82Editor/CheckWindow/Implementations/EmptyGameObjectCheck.cs
-
11Editor/CheckWindow/Implementations/EmptyGameObjectCheck.cs.meta
-
55Editor/CheckWindow/Implementations/InvalidTransformCheck.cs
-
11Editor/CheckWindow/Implementations/InvalidTransformCheck.cs.meta
-
61Editor/CheckWindow/Implementations/NullMaterialCheck.cs
-
11Editor/CheckWindow/Implementations/NullMaterialCheck.cs.meta
-
15Runtime/CheckWIndow/IgnoredCheckResults.cs
-
11Runtime/CheckWIndow/IgnoredCheckResults.cs.meta
|
|||
fileFormatVersion: 2 |
|||
guid: 288b8d771acd0ca4c844250d148bd1e6 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: d39bd230a7fe2ea46a39fd939b52e5d5 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
using UnityEditor; |
|||
using System.Linq; |
|||
|
|||
namespace GameplayIngredients.Editor |
|||
{ |
|||
public abstract class Check |
|||
{ |
|||
public abstract string name { get; } |
|||
|
|||
public abstract bool defaultEnabled { get; } |
|||
public abstract IEnumerable<CheckResult> GetResults(SceneObjects sceneObjects); |
|||
public abstract void Resolve(CheckResult result); |
|||
public abstract string[] ResolutionActions { get; } |
|||
public abstract int defaultResolutionActionIndex { get; } |
|||
|
|||
#region STATIC
|
|||
|
|||
public static List<Check> allChecks { |
|||
get { |
|||
if (s_Checks == null) |
|||
Initialize(); |
|||
return s_Checks.Values.ToList(); |
|||
} |
|||
} |
|||
static Dictionary<Type, Check> s_Checks; |
|||
|
|||
[InitializeOnLoadMethod] |
|||
static void Initialize() |
|||
{ |
|||
s_Checks = new Dictionary<Type, Check>(); |
|||
var types = GetAllTypes(); |
|||
foreach(var type in types) |
|||
{ |
|||
Check check = (Check)Activator.CreateInstance(type); |
|||
s_Checks.Add(type, check); |
|||
} |
|||
} |
|||
|
|||
public static T Get<T>() where T : Check |
|||
{ |
|||
if (s_Checks.ContainsKey(typeof(T))) |
|||
return (T)s_Checks[typeof(T)]; |
|||
else |
|||
{ |
|||
Debug.LogError($"Check of type '{typeof(T)}' could not be accessed."); |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
public static bool Has<T>() where T : Check |
|||
{ |
|||
return (s_Checks.ContainsKey(typeof(T))); |
|||
} |
|||
|
|||
static Type[] GetAllTypes() |
|||
{ |
|||
List<Type> types = new List<Type>(); |
|||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) |
|||
{ |
|||
Type[] assemblyTypes = null; |
|||
|
|||
try |
|||
{ |
|||
assemblyTypes = assembly.GetTypes(); |
|||
} |
|||
catch |
|||
{ |
|||
Debug.LogError($"Could not load types from assembly : {assembly.FullName}"); |
|||
} |
|||
|
|||
if (assemblyTypes != null) |
|||
{ |
|||
foreach (Type t in assemblyTypes) |
|||
{ |
|||
if (typeof(Check).IsAssignableFrom(t) && !t.IsAbstract) |
|||
{ |
|||
types.Add(t); |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
return types.ToArray(); |
|||
} |
|||
#endregion
|
|||
} |
|||
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: f8d1edf29462baa488e148ff457a806e |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
using UnityEditor; |
|||
using UnityEngine.SceneManagement; |
|||
using System.Linq; |
|||
|
|||
namespace GameplayIngredients.Editor |
|||
{ |
|||
public class CheckResult |
|||
{ |
|||
public enum Result |
|||
{ |
|||
Notice, |
|||
Warning, |
|||
Failed |
|||
} |
|||
|
|||
public Check check { get; private set; } |
|||
public Result result { get; private set; } |
|||
public Object mainObject { get { if (objects != null && objects.Length > 0) return objects[0]; else return null; } } |
|||
Object[] objects; |
|||
public GUIContent message; |
|||
|
|||
|
|||
public int resolutionActionIndex; |
|||
public string action { get { return check.ResolutionActions[resolutionActionIndex]; } } |
|||
public CheckResult(Check check, Result result, string message, params Object[] objects) |
|||
{ |
|||
this.check = check; |
|||
this.result = result; |
|||
this.message = new GUIContent(message, GetIcon(result)); |
|||
this.objects = objects; |
|||
} |
|||
|
|||
public static Texture GetIcon(Result r) |
|||
{ |
|||
switch (r) |
|||
{ |
|||
default: |
|||
case Result.Notice: |
|||
return Contents.notice; |
|||
case Result.Warning: |
|||
return Contents.warning; |
|||
case Result.Failed: |
|||
return Contents.failed; |
|||
|
|||
} |
|||
} |
|||
|
|||
static class Contents |
|||
{ |
|||
public static Texture notice; |
|||
public static Texture warning; |
|||
public static Texture failed; |
|||
|
|||
static Contents() |
|||
{ |
|||
notice = EditorGUIUtility.IconContent("console.infoicon.sml").image; |
|||
warning = EditorGUIUtility.IconContent("console.warnicon.sml").image; |
|||
failed = EditorGUIUtility.IconContent("console.erroricon.sml").image; |
|||
|
|||
} |
|||
} |
|||
public void SetIgnored(bool ignored = true) |
|||
{ |
|||
if (!(mainObject is GameObject)) |
|||
return; |
|||
|
|||
GameObject go = mainObject as GameObject; |
|||
Scene scene = go.scene; |
|||
var ignoredResults = Object.FindObjectsOfType<IgnoredCheckResults>(); |
|||
IgnoredCheckResults targetResults = null; |
|||
|
|||
foreach(var result in ignoredResults) |
|||
{ |
|||
if(result.gameObject.scene == scene) |
|||
{ |
|||
targetResults = result; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if(targetResults == null) |
|||
{ |
|||
var newGo = new GameObject("__IGNORED_CHECK_RESULTS__"); |
|||
newGo.hideFlags = HideFlags.HideInHierarchy; |
|||
SceneManager.MoveGameObjectToScene(newGo,scene); |
|||
targetResults = newGo.AddComponent<IgnoredCheckResults>(); |
|||
} |
|||
|
|||
if (targetResults.ignoredCheckResults == null) |
|||
targetResults.ignoredCheckResults = new List<IgnoredCheckResults.IgnoredCheckResult>(); |
|||
|
|||
if(ignored) |
|||
{ |
|||
if(!targetResults.ignoredCheckResults.Any(o => o.gameObject == go && o.check == check.GetType().ToString())) |
|||
{ |
|||
IgnoredCheckResults.IgnoredCheckResult r = new IgnoredCheckResults.IgnoredCheckResult() |
|||
{ |
|||
check = check.GetType().ToString(), |
|||
gameObject = go |
|||
}; |
|||
targetResults.ignoredCheckResults.Add(r); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (targetResults.ignoredCheckResults.Any(o => o.gameObject == go && o.check == check.GetType().ToString())) |
|||
{ |
|||
targetResults.ignoredCheckResults.Remove(targetResults.ignoredCheckResults.FirstOrDefault(o => o.gameObject == go && o.check == check.GetType().ToString())); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: eda144a76a8766c4eafbd6c68392adbc |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
using UnityEditor; |
|||
using System.Linq; |
|||
using System.Reflection; |
|||
using System; |
|||
using UnityEngine.SceneManagement; |
|||
using GameplayIngredients.Rigs; |
|||
using UnityEngine.Events; |
|||
using UnityEditor.SceneManagement; |
|||
|
|||
namespace GameplayIngredients.Editor |
|||
{ |
|||
public class CheckWindow : EditorWindow |
|||
{ |
|||
[MenuItem("Window/Gameplay Ingredients/Check and Resolve")] |
|||
static void OpenWindow() |
|||
{ |
|||
GetWindow<CheckWindow>(false); |
|||
} |
|||
|
|||
|
|||
private void OnEnable() |
|||
{ |
|||
titleContent = new GUIContent(EditorGUIUtility.IconContent("Valid")); |
|||
titleContent.text = "Check/Resolve"; |
|||
minSize = new Vector2(640, 180); |
|||
InitializeCheckStates(); |
|||
} |
|||
|
|||
void InitializeCheckStates() |
|||
{ |
|||
s_CheckStates = new Dictionary<Check, bool>(); |
|||
foreach (var check in Check.allChecks) |
|||
{ |
|||
s_CheckStates.Add(check, EditorPrefs.GetBool(kPreferencePrefix + check.name, check.defaultEnabled)); |
|||
} |
|||
} |
|||
|
|||
void SetCheckState(Check check, bool value) |
|||
{ |
|||
s_CheckStates[check] = value; |
|||
EditorPrefs.SetBool(kPreferencePrefix + check.name, value); |
|||
} |
|||
|
|||
const string kPreferencePrefix = "GameplayIngredients.Check."; |
|||
const string kShowIgnoredPreference = "GameplayIngredients.ShowIgnored"; |
|||
|
|||
bool showIgnored |
|||
{ |
|||
get { |
|||
return EditorPrefs.GetBool(kShowIgnoredPreference, true); |
|||
} |
|||
set { |
|||
EditorPrefs.SetBool(kShowIgnoredPreference, value); |
|||
} |
|||
} |
|||
Vector2 Scroll = new Vector2(); |
|||
Dictionary<Check, bool> s_CheckStates; |
|||
|
|||
enum SortMode |
|||
{ |
|||
None, |
|||
CheckType, |
|||
ObjectName, |
|||
Message, |
|||
Resolution, |
|||
Ignored |
|||
} |
|||
|
|||
SortMode sortMode = SortMode.None; |
|||
bool invertSort = false; |
|||
|
|||
string filterString = ""; |
|||
|
|||
bool showNotice |
|||
{ |
|||
get |
|||
{ |
|||
return EditorPrefs.GetBool(kPreference + "showNotice", true); |
|||
} |
|||
set |
|||
{ |
|||
if (value != showNotice) |
|||
EditorPrefs.SetBool(kPreference + "showNotice", value); |
|||
} |
|||
} |
|||
bool showWarning |
|||
{ |
|||
get |
|||
{ |
|||
return EditorPrefs.GetBool(kPreference + "showWarning", true); |
|||
} |
|||
set |
|||
{ |
|||
if (value != showWarning) |
|||
EditorPrefs.SetBool(kPreference + "showWarning", value); |
|||
} |
|||
} |
|||
|
|||
bool showError |
|||
{ |
|||
get |
|||
{ |
|||
return EditorPrefs.GetBool(kPreference + "showError", true); |
|||
} |
|||
set |
|||
{ |
|||
if (value != showError) |
|||
EditorPrefs.SetBool(kPreference + "showError", value); |
|||
} |
|||
} |
|||
|
|||
string kPreference = "GameplayIngredients.CheckResolve."; |
|||
|
|||
private void SortButton(string label, SortMode sortMode, params GUILayoutOption[] options) |
|||
{ |
|||
if (GUILayout.Button(label, this.sortMode == sortMode ? Styles.sortHeader : Styles.header, options)) |
|||
{ |
|||
|
|||
if (this.sortMode == sortMode) |
|||
invertSort = !invertSort; |
|||
else |
|||
{ |
|||
this.sortMode = sortMode; |
|||
invertSort = false; |
|||
} |
|||
|
|||
if(m_Results != null) |
|||
{ |
|||
switch (sortMode) |
|||
{ |
|||
default: |
|||
case SortMode.None: |
|||
break; |
|||
case SortMode.CheckType: |
|||
m_Results = m_Results.OrderBy((a) => a.check.name).ToList(); |
|||
break; |
|||
case SortMode.ObjectName: |
|||
m_Results = m_Results.OrderBy((a) => a.mainObject == null? "" : a.mainObject.name).ToList(); |
|||
break; |
|||
case SortMode.Message: |
|||
m_Results = m_Results.OrderBy((a) => a.message.text).OrderBy((a) => a.result).ToList(); |
|||
break; |
|||
case SortMode.Resolution: |
|||
m_Results = m_Results.OrderBy((a) => a.check.ResolutionActions[a.resolutionActionIndex]).ToList(); |
|||
break; |
|||
case SortMode.Ignored: |
|||
m_Results = m_Results.OrderBy((a) => IsIgnored(a)).ToList(); |
|||
break; |
|||
} |
|||
if(invertSort) |
|||
{ |
|||
m_Results.Reverse(); |
|||
} |
|||
|
|||
Repaint(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void OnGUI() |
|||
{ |
|||
using(new GUILayout.HorizontalScope(EditorStyles.toolbar, GUILayout.Height(22))) |
|||
{ |
|||
if (GUILayout.Button("Check", EditorStyles.toolbarButton)) |
|||
PerformChecks(); |
|||
if(GUILayout.Button("", EditorStyles.toolbarPopup)) |
|||
{ |
|||
Rect r = new Rect(0, 0, 16, 20); |
|||
GenericMenu menu = new GenericMenu(); |
|||
foreach (Check check in s_CheckStates.Keys) |
|||
{ |
|||
menu.AddItem(new GUIContent(check.name), s_CheckStates[check], () => SetCheckState(check, !s_CheckStates[check])); |
|||
} |
|||
menu.DropDown(r); |
|||
} |
|||
|
|||
if (GUILayout.Button("Resolve", EditorStyles.toolbarButton)) |
|||
Resolve(); |
|||
|
|||
GUILayout.FlexibleSpace(); |
|||
|
|||
filterString = EditorGUILayout.DelayedTextField(filterString, EditorStyles.toolbarSearchField, GUILayout.Width(180)); |
|||
|
|||
showIgnored = GUILayout.Toggle(showIgnored, "Ignored", EditorStyles.toolbarButton); |
|||
showNotice = GUILayout.Toggle(showNotice, new GUIContent(m_Results.Where(o => o.result == CheckResult.Result.Notice).Count().ToString(), CheckResult.GetIcon(CheckResult.Result.Notice)), EditorStyles.toolbarButton); |
|||
showWarning = GUILayout.Toggle(showWarning, new GUIContent(m_Results.Where(o => o.result == CheckResult.Result.Warning).Count().ToString(), CheckResult.GetIcon(CheckResult.Result.Warning)), EditorStyles.toolbarButton); |
|||
showError = GUILayout.Toggle(showError, new GUIContent(m_Results.Where(o => o.result == CheckResult.Result.Failed).Count().ToString(), CheckResult.GetIcon(CheckResult.Result.Failed)), EditorStyles.toolbarButton); |
|||
|
|||
} |
|||
using(new GUILayout.VerticalScope()) |
|||
{ |
|||
GUI.backgroundColor = Color.white * 1.3f; |
|||
using (new GUILayout.HorizontalScope(EditorStyles.toolbar)) |
|||
{ |
|||
SortButton("Check Type", SortMode.CheckType, GUILayout.Width(128)); |
|||
SortButton("Object", SortMode.ObjectName, GUILayout.Width(128)); |
|||
SortButton("Message", SortMode.Message, GUILayout.ExpandWidth(true)); |
|||
SortButton("Resolution", SortMode.Resolution, GUILayout.Width(128)); |
|||
if(showIgnored) |
|||
SortButton("Ignored", SortMode.Ignored, GUILayout.Width(64)); |
|||
GUILayout.Space(12); |
|||
} |
|||
|
|||
Scroll = GUILayout.BeginScrollView(Scroll, false,true); |
|||
|
|||
if (m_Results != null && m_Results.Count > 0) |
|||
{ |
|||
bool odd = true; |
|||
foreach (var result in m_Results) |
|||
{ |
|||
|
|||
if (!showIgnored && IsIgnored(result) || result.mainObject == null) |
|||
continue; |
|||
|
|||
if (result.result == CheckResult.Result.Notice && !showNotice) |
|||
continue; |
|||
|
|||
if (result.result == CheckResult.Result.Warning && !showWarning) |
|||
continue; |
|||
|
|||
if (result.result == CheckResult.Result.Failed && !showError) |
|||
continue; |
|||
|
|||
if (!string.IsNullOrEmpty(filterString) && !result.message.text.Contains(filterString)) |
|||
continue; |
|||
|
|||
GUI.backgroundColor = Color.white * (odd ? 0.9f : 0.8f); |
|||
odd = !odd; |
|||
|
|||
using (new GUILayout.HorizontalScope(EditorStyles.toolbar)) |
|||
{ |
|||
EditorGUI.BeginDisabledGroup(showIgnored && IsIgnored(result)); |
|||
|
|||
GUILayout.Label(result.check.name, Styles.line, GUILayout.Width(128)); |
|||
if(GUILayout.Button(result.mainObject != null? result.mainObject.name : "Null", Styles.line, GUILayout.Width(128))) |
|||
{ |
|||
Selection.activeObject = result.mainObject; |
|||
} |
|||
GUILayout.Label(result.message, Styles.line, GUILayout.ExpandWidth(true)); |
|||
ShowMenu(result); |
|||
|
|||
EditorGUI.EndDisabledGroup(); |
|||
|
|||
if(showIgnored) |
|||
{ |
|||
GUILayout.Space(18); |
|||
EditorGUI.BeginChangeCheck(); |
|||
var ignored = GUILayout.Toggle(IsIgnored(result),"", EditorStyles.toggle ,GUILayout.Width(24)); |
|||
if (EditorGUI.EndChangeCheck()) |
|||
{ |
|||
SetIgnored(result, ignored); |
|||
} |
|||
GUILayout.Space(18); |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
GUILayout.Label("No Results"); |
|||
} |
|||
GUI.backgroundColor = Color.white; |
|||
|
|||
GUILayout.FlexibleSpace(); |
|||
GUILayout.EndScrollView(); |
|||
} |
|||
} |
|||
|
|||
Dictionary<Scene,IgnoredCheckResults> m_IgnoredLists; |
|||
|
|||
void BuildIgnoredList() |
|||
{ |
|||
if (m_IgnoredLists == null) |
|||
m_IgnoredLists = new Dictionary<Scene, IgnoredCheckResults>(); |
|||
else |
|||
m_IgnoredLists.Clear(); |
|||
|
|||
var all = FindObjectsOfType<IgnoredCheckResults>().ToList(); |
|||
foreach(var one in all) |
|||
{ |
|||
if (!m_IgnoredLists.ContainsKey(one.gameObject.scene)) |
|||
m_IgnoredLists.Add(one.gameObject.scene, one); |
|||
else |
|||
Debug.LogWarning($"Found at least two IgnoredCheckResults objects in scene {one.gameObject.scene.name}"); |
|||
} |
|||
} |
|||
|
|||
bool IsIgnored(CheckResult result) |
|||
{ |
|||
if (result.mainObject == null || !(result.mainObject is GameObject)) |
|||
return false; |
|||
|
|||
GameObject go = result.mainObject as GameObject; |
|||
if (!m_IgnoredLists.ContainsKey(go.scene)) |
|||
return false; |
|||
|
|||
var igl = m_IgnoredLists[go.scene]; |
|||
return igl.ignoredCheckResults.Any(o => (o.check == result.check.GetType().ToString()) && (o.gameObject == go)); |
|||
} |
|||
|
|||
void SetIgnored(CheckResult result, bool ignored) |
|||
{ |
|||
result.SetIgnored(ignored); |
|||
if(result.mainObject is GameObject) |
|||
{ |
|||
var go = result.mainObject as GameObject; |
|||
EditorSceneManager.MarkSceneDirty(go.scene); |
|||
} |
|||
BuildIgnoredList(); |
|||
} |
|||
|
|||
void ShowMenu(CheckResult result) |
|||
{ |
|||
if (s_IntValues == null) |
|||
s_IntValues = new Dictionary<Check, int[]>(); |
|||
|
|||
if(!s_IntValues.ContainsKey(result.check)) |
|||
{ |
|||
int count = result.check.ResolutionActions.Length; |
|||
int[] values = new int[count]; |
|||
for(int i = 0; i < count; i++) |
|||
{ |
|||
values[i] = i; |
|||
} |
|||
s_IntValues.Add(result.check, values); |
|||
} |
|||
|
|||
result.resolutionActionIndex = EditorGUILayout.IntPopup(result.resolutionActionIndex, result.check.ResolutionActions, s_IntValues[result.check], EditorStyles.toolbarDropDown, GUILayout.Width(128)); |
|||
|
|||
} |
|||
|
|||
static Dictionary<Check, int[]> s_IntValues; |
|||
|
|||
List<CheckResult> m_Results = new List<CheckResult>(); |
|||
|
|||
void Resolve() |
|||
{ |
|||
foreach(var result in m_Results) |
|||
{ |
|||
if(!IsIgnored(result)) |
|||
result.check.Resolve(result); |
|||
} |
|||
PerformChecks(); |
|||
} |
|||
|
|||
void PerformChecks() |
|||
{ |
|||
List<CheckResult> results = new List<CheckResult>(); |
|||
bool canceled = false; |
|||
try |
|||
{ |
|||
var so = new SceneObjects(); |
|||
|
|||
int count = s_CheckStates.Count; |
|||
int i = 0; |
|||
foreach (var kvp in s_CheckStates) |
|||
{ |
|||
float t = (float)i / count; |
|||
if (EditorUtility.DisplayCancelableProgressBar("Performing Checks", kvp.Key.name, t)) |
|||
{ |
|||
canceled = true; |
|||
break; |
|||
} |
|||
|
|||
if (kvp.Value) |
|||
results.AddRange(kvp.Key.GetResults(so)); |
|||
i++; |
|||
} |
|||
} |
|||
finally |
|||
{ |
|||
EditorUtility.ClearProgressBar(); |
|||
} |
|||
|
|||
if(!canceled) |
|||
m_Results = results; |
|||
|
|||
sortMode = SortMode.None; |
|||
BuildIgnoredList(); |
|||
Repaint(); |
|||
} |
|||
|
|||
static class Styles |
|||
{ |
|||
public static GUIStyle header; |
|||
public static GUIStyle sortHeader; |
|||
public static GUIStyle line; |
|||
|
|||
static Styles() |
|||
{ |
|||
header = new GUIStyle(EditorStyles.toolbarButton); |
|||
header.alignment = TextAnchor.MiddleLeft; |
|||
header.fontStyle = FontStyle.Bold; |
|||
|
|||
sortHeader = new GUIStyle(EditorStyles.toolbarDropDown); |
|||
sortHeader.alignment = TextAnchor.MiddleLeft; |
|||
sortHeader.fontStyle = FontStyle.Bold; |
|||
|
|||
line = new GUIStyle(EditorStyles.toolbarButton); |
|||
line.alignment = TextAnchor.MiddleLeft; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
public class SceneObjects |
|||
{ |
|||
public GameObject[] sceneObjects; |
|||
public List<GameObject> referencedGameObjects; |
|||
public List<Component> referencedComponents; |
|||
public List<UnityEngine.Object> referencedObjects; |
|||
|
|||
public SceneObjects() |
|||
{ |
|||
sceneObjects = GameObject.FindObjectsOfType<GameObject>(); |
|||
referencedGameObjects = new List<GameObject>(); |
|||
referencedComponents = new List<Component>(); |
|||
referencedObjects = new List<UnityEngine.Object>(); |
|||
|
|||
if (sceneObjects == null || sceneObjects.Length == 0) |
|||
return; |
|||
|
|||
try |
|||
{ |
|||
int count = sceneObjects.Length; |
|||
int i = 0; |
|||
|
|||
foreach (var sceneObject in sceneObjects) |
|||
{ |
|||
float progress = ++i / (float)count; |
|||
if (EditorUtility.DisplayCancelableProgressBar("Building Reference Table...", $"{sceneObject.name}", progress)) |
|||
{ |
|||
referencedComponents.Clear(); |
|||
referencedGameObjects.Clear(); |
|||
referencedObjects.Clear(); |
|||
break; |
|||
} |
|||
|
|||
var components = sceneObject.GetComponents<Component>(); |
|||
foreach (var component in components) |
|||
{ |
|||
if (!(component is Transform)) |
|||
{ |
|||
Type t = component.GetType(); |
|||
FieldInfo[] fields = t.GetFields(BindingFlags.Public |
|||
| BindingFlags.Instance |
|||
| BindingFlags.NonPublic); |
|||
|
|||
foreach (var f in fields) |
|||
{ |
|||
if (f.FieldType == typeof(GameObject)) |
|||
{ |
|||
var go = f.GetValue(component) as GameObject; |
|||
if (go != null && go != component.gameObject) |
|||
{ |
|||
referencedGameObjects.Add(go); |
|||
} |
|||
} |
|||
else if (f.FieldType == typeof(Transform)) |
|||
{ |
|||
var tr = f.GetValue(component) as Transform; |
|||
if (tr != null && tr.gameObject != component.gameObject) |
|||
{ |
|||
referencedGameObjects.Add(tr.gameObject); |
|||
} |
|||
} |
|||
else if (f.FieldType.IsSubclassOf(typeof(Component))) |
|||
{ |
|||
var comp = f.GetValue(component) as Component; |
|||
if (comp != null && comp.gameObject != component.gameObject) |
|||
{ |
|||
referencedComponents.Add(comp); |
|||
} |
|||
} |
|||
else if (f.FieldType == typeof(UnityEvent)) |
|||
{ |
|||
var ue = f.GetValue(component) as UnityEvent; |
|||
int evtCount = ue.GetPersistentEventCount(); |
|||
for(int k = 0; k< evtCount; k++) |
|||
{ |
|||
var target = ue.GetPersistentTarget(k); |
|||
if (target is GameObject) |
|||
referencedGameObjects.Add(target as GameObject); |
|||
else if(target.GetType().IsSubclassOf(typeof(Component))) |
|||
referencedGameObjects.Add((target as Component).gameObject); |
|||
} |
|||
} |
|||
else if (f.FieldType.IsSubclassOf(typeof(UnityEngine.Object))) |
|||
{ |
|||
var obj = f.GetValue(component) as UnityEngine.Object; |
|||
referencedObjects.Add(obj); |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
finally |
|||
{ |
|||
EditorUtility.ClearProgressBar(); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: c176a67ddfd8cf741b242b56de664c83 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 35f64cba36fa7f648aabe0906a871c71 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
using UnityEditor; |
|||
using Object = UnityEngine.Object; |
|||
|
|||
namespace GameplayIngredients.Editor |
|||
{ |
|||
public class EmptyGameObjectCheck : Check |
|||
{ |
|||
public override string name => "GameObject/Empty"; |
|||
|
|||
public override bool defaultEnabled => true; |
|||
|
|||
public override string[] ResolutionActions => new string[] { "Set Static", "Delete Object" }; |
|||
|
|||
public override int defaultResolutionActionIndex => 0; |
|||
|
|||
public override IEnumerable<CheckResult> GetResults(SceneObjects so) |
|||
{ |
|||
try |
|||
{ |
|||
int count = so.sceneObjects.Length; |
|||
int i = 0; |
|||
|
|||
foreach (var go in so.sceneObjects) |
|||
{ |
|||
float progress = ++i / count; |
|||
if (EditorUtility.DisplayCancelableProgressBar("Finding Empty Game Objects...", $"{go.name}", progress)) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
var allComps = go.GetComponents<Component>(); |
|||
if (allComps.Length == 1) |
|||
{ |
|||
if (!go.isStatic) |
|||
{ |
|||
if(!so.referencedGameObjects.Contains(go)) |
|||
{ |
|||
var result = new CheckResult(this, CheckResult.Result.Warning, $"Empty Game Object {go.name} is not static", go); |
|||
result.resolutionActionIndex = 0; |
|||
yield return result; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (go.transform.childCount == 0) |
|||
{ |
|||
if (!so.referencedGameObjects.Contains(go) && !so.referencedComponents.Contains(go.transform)) |
|||
{ |
|||
var result = new CheckResult(this, CheckResult.Result.Notice, "Empty Static Game Object has no children and could be deleted if unused.", go); |
|||
result.resolutionActionIndex = 1; |
|||
yield return result; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
finally |
|||
{ |
|||
EditorUtility.ClearProgressBar(); |
|||
} |
|||
} |
|||
|
|||
public override void Resolve(CheckResult result) |
|||
{ |
|||
switch (result.resolutionActionIndex) |
|||
{ |
|||
case 0: |
|||
default: |
|||
(result.mainObject as GameObject).isStatic = true; |
|||
break; |
|||
case 1: |
|||
Object.DestroyImmediate(result.mainObject as GameObject); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|
|||
fileFormatVersion: 2 |
|||
guid: 14cd4032eb6693645968d03dc1318cf4 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
using UnityEditor; |
|||
using Object = UnityEngine.Object; |
|||
using UnityEngine.Events; |
|||
|
|||
namespace GameplayIngredients.Editor |
|||
{ |
|||
public class InvalidTransformCheck : Check |
|||
{ |
|||
public override string name => "GameObject/Invalid Transform"; |
|||
|
|||
public override bool defaultEnabled => true; |
|||
|
|||
public override string[] ResolutionActions => new string[] { "Do Nothing", "Reset", "Delete Object" }; |
|||
|
|||
public override int defaultResolutionActionIndex => 0; |
|||
|
|||
public override IEnumerable<CheckResult> GetResults(SceneObjects so) |
|||
{ |
|||
try |
|||
{ |
|||
int count = so.sceneObjects.Length; |
|||
int i = 0; |
|||
|
|||
foreach (var go in so.sceneObjects) |
|||
{ |
|||
float progress = ++i / count; |
|||
if (EditorUtility.DisplayCancelableProgressBar("Finding Transforms...", $"{go.name}", progress)) |
|||
{ |
|||
break; |
|||
} |
|||
|
|||
Transform t = go.transform; |
|||
if(t.localScale.sqrMagnitude == 0) |
|||
{ |
|||
yield return new CheckResult(this, CheckResult.Result.Warning, $"Transform of Game Object \"{go.name}\" has zero Scale.", go); |
|||
} |
|||
} |
|||
|
|||
} |
|||
finally |
|||
{ |
|||
EditorUtility.ClearProgressBar(); |
|||
} |
|||
} |
|||
|
|||
public override void Resolve(CheckResult result) |
|||
{ |
|||
throw new System.NotImplementedException(); |
|||
} |
|||
} |
|||
} |
|||
|
|
|||
fileFormatVersion: 2 |
|||
guid: 90282888ae2e5d44db5119f5cd3a5de1 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
namespace GameplayIngredients.Editor |
|||
{ |
|||
public class NullMaterialCheck : Check |
|||
{ |
|||
public override string name => "Renderer/Null Material"; |
|||
|
|||
public override bool defaultEnabled => true; |
|||
|
|||
public override string[] ResolutionActions => new string[] { "Assign Default Material" }; |
|||
|
|||
public override int defaultResolutionActionIndex => 0; |
|||
|
|||
public override IEnumerable<CheckResult> GetResults(SceneObjects sceneObjects) |
|||
{ |
|||
foreach(var obj in sceneObjects.sceneObjects) |
|||
{ |
|||
if(obj.TryGetComponent(out Renderer r)) |
|||
{ |
|||
foreach(var mat in r.sharedMaterials) |
|||
{ |
|||
if(mat == null) |
|||
{ |
|||
yield return new CheckResult(this, CheckResult.Result.Failed, $"Missing Material for Object {obj.name}", obj); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
static Material s_DefaultMaterial; |
|||
|
|||
public override void Resolve(CheckResult result) |
|||
{ |
|||
if(s_DefaultMaterial == null) |
|||
{ |
|||
var cube = GameObject.CreatePrimitive(PrimitiveType.Cube); |
|||
s_DefaultMaterial = cube.GetComponent<Renderer>().sharedMaterial; |
|||
Object.DestroyImmediate(cube); |
|||
} |
|||
|
|||
switch (result.resolutionActionIndex) |
|||
{ |
|||
default: |
|||
var renderer = (result.mainObject as GameObject).GetComponent<Renderer>(); |
|||
|
|||
for(int i = 0; i<renderer.sharedMaterials.Length; i++) |
|||
{ |
|||
if(renderer.sharedMaterials[i] == null) |
|||
{ |
|||
renderer.materials[i] = s_DefaultMaterial; |
|||
} |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 512974ea9d269c8429756e4b07a4386e |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
public class IgnoredCheckResults : MonoBehaviour |
|||
{ |
|||
[System.Serializable] |
|||
public struct IgnoredCheckResult |
|||
{ |
|||
public string check; |
|||
public GameObject gameObject; |
|||
} |
|||
|
|||
public List<IgnoredCheckResult> ignoredCheckResults; |
|||
|
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d6d90fb7f3ef6bc4e84c5c6bc7a7e3f8 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue