698 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			698 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | namespace Fusion.Addons.KCC.Editor | ||
|  | { | ||
|  | 	using System; | ||
|  | 	using System.Collections; | ||
|  | 	using System.Collections.Generic; | ||
|  | 	using System.Collections.ObjectModel; | ||
|  | 	using UnityEngine; | ||
|  | 	using UnityEditor; | ||
|  | 
 | ||
|  | 	[InitializeOnLoad] | ||
|  | 	[CustomEditor(typeof(KCC), true)] | ||
|  | 	public class KCCEditor : Fusion.Editor.BehaviourEditor | ||
|  | 	{ | ||
|  | 		// CONSTANTS | ||
|  | 
 | ||
|  | 		private const string SETTINGS_PROPERTY_NAME                  = "_settings"; | ||
|  | 		private const string INTERPOLATION_DATA_SOURCE_PROPERTY_NAME = "_interpolationDataSource"; | ||
|  | 
 | ||
|  | 		// PRIVATE MEMBERS | ||
|  | 
 | ||
|  | 		private static string[]        _excludedProperties     = new string[] { SETTINGS_PROPERTY_NAME, INTERPOLATION_DATA_SOURCE_PROPERTY_NAME }; | ||
|  | 		private static bool            _localProcessorsFoldout = false; | ||
|  | 		private static bool            _modifiersFoldout       = false; | ||
|  | 		private static bool            _collisionsFoldout      = false; | ||
|  | 		private static bool            _ignoresFoldout         = false; | ||
|  | 		private static bool            _hitsFoldout            = false; | ||
|  | 		private static bool            _resetTools             = false; | ||
|  | 		private static GUIStyle        _stageButtonStyle; | ||
|  | 		private static GUIStyle        _buttonFoldoutStyle; | ||
|  | 		private static GUIStyle        _foldoutDotStyle; | ||
|  | 
 | ||
|  | 		// Initialization | ||
|  | 
 | ||
|  | 		[InitializeOnLoadMethod] | ||
|  | 		private static void Initialize() | ||
|  | 		{ | ||
|  | 			Selection.selectionChanged -= OnSelectionChanged; | ||
|  | 			Selection.selectionChanged += OnSelectionChanged; | ||
|  | 
 | ||
|  | 			return; | ||
|  | 
 | ||
|  | 			static void OnSelectionChanged() | ||
|  | 			{ | ||
|  | 				if (_resetTools == true) | ||
|  | 				{ | ||
|  | 					_resetTools = false; | ||
|  | 					Tools.hidden = false; | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// Editor INTERFACE | ||
|  | 
 | ||
|  | 		public override bool RequiresConstantRepaint() | ||
|  | 		{ | ||
|  | 			return true; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private void OnSceneGUI() | ||
|  | 		{ | ||
|  | 			_resetTools = true; | ||
|  | 
 | ||
|  | 			if (Application.isPlaying == false) | ||
|  | 				return; | ||
|  | 
 | ||
|  | 			KCC kcc = target as KCC; | ||
|  | 			if (kcc.Object == null) | ||
|  | 				return; | ||
|  | 
 | ||
|  | 			if (Tools.current == Tool.Move) | ||
|  | 			{ | ||
|  | 				Tools.hidden = true; | ||
|  | 
 | ||
|  | 				Vector3 currentPosition = kcc.Transform.position; | ||
|  | 				Vector3 newPosition     = Handles.PositionHandle(currentPosition, kcc.Transform.rotation); | ||
|  | 
 | ||
|  | 				if (newPosition.Equals(currentPosition) == false) | ||
|  | 				{ | ||
|  | 					kcc.FixedData.TargetPosition  = newPosition; | ||
|  | 					kcc.RenderData.TargetPosition = newPosition; | ||
|  | 
 | ||
|  | 					kcc.SynchronizeTransform(true, false, false); | ||
|  | 				} | ||
|  | 			} | ||
|  | 			else if (Tools.current == Tool.Rotate) | ||
|  | 			{ | ||
|  | 				Tools.hidden = true; | ||
|  | 
 | ||
|  | 				Quaternion currentRotation = kcc.Transform.rotation; | ||
|  | 				Quaternion newRotation     = Handles.RotationHandle(currentRotation, kcc.Transform.position); | ||
|  | 
 | ||
|  | 				if (newRotation.Equals(currentRotation) == false) | ||
|  | 				{ | ||
|  | 					Vector3 difference = (Quaternion.Inverse(currentRotation) * newRotation).eulerAngles; | ||
|  | 
 | ||
|  | 					if (difference.y != 0.0f) | ||
|  | 					{ | ||
|  | 						kcc.Data.AddLookRotation(0.0f, difference.y); | ||
|  | 
 | ||
|  | 						float lookYaw = kcc.Data.LookYaw; | ||
|  | 
 | ||
|  | 						kcc.FixedData.LookYaw  = lookYaw; | ||
|  | 						kcc.RenderData.LookYaw = lookYaw; | ||
|  | 
 | ||
|  | 						kcc.SynchronizeTransform(false, true, false); | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 			else | ||
|  | 			{ | ||
|  | 				Tools.hidden = false; | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		public override void OnInspectorGUI() | ||
|  | 		{ | ||
|  | 			PrepareOnInspectorGUI(); | ||
|  | 			DrawPropertiesExcluding(serializedObject, _excludedProperties); | ||
|  | 			DrawEditorButtons(); | ||
|  | 
 | ||
|  | 			SerializedProperty settingsProperty = serializedObject.FindProperty(SETTINGS_PROPERTY_NAME); | ||
|  | 			IEnumerator innerSettingsProperties = settingsProperty.GetEnumerator(); | ||
|  | 			while (innerSettingsProperties.MoveNext()) | ||
|  | 			{ | ||
|  | 				if (innerSettingsProperties.Current is SerializedProperty innerSettingsProperty) | ||
|  | 				{ | ||
|  | 					if (innerSettingsProperty.propertyPath.IndexOf('.') == innerSettingsProperty.propertyPath.LastIndexOf('.')) | ||
|  | 					{ | ||
|  | 						EditorGUILayout.PropertyField(innerSettingsProperty); | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 			serializedObject.ApplyModifiedProperties(); | ||
|  | 
 | ||
|  | 			if (Application.isPlaying == false) | ||
|  | 			{ | ||
|  | 				DrawLine(Color.gray); | ||
|  | 				EditorGUILayout.HelpBox("Additional information appears at runtime.", MessageType.Info); | ||
|  | 				DrawLine(Color.gray); | ||
|  | 				return; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			KCC kcc = target as KCC; | ||
|  | 			if (kcc.Object == null) | ||
|  | 				return; | ||
|  | 
 | ||
|  | 			KCCDebug kccDebug = kcc.Debug; | ||
|  | 
 | ||
|  | 			Color defaultColor             = GUI.color; | ||
|  | 			Color defaultContentColor      = GUI.contentColor; | ||
|  | 			Color defaultBackgroundColor   = GUI.backgroundColor; | ||
|  | 			Color enabledBackgroundColor   = Color.green; | ||
|  | 			Color disabledBackgroundColor  = defaultBackgroundColor; | ||
|  | 			Color colliderBackgroundColor  = defaultBackgroundColor; | ||
|  | 			Color processorBackgroundColor = Color.cyan; | ||
|  | 			float foldoutStaticOffset      = 8.0f; | ||
|  | 			float foldoutLevelOffset       = 20.0f; | ||
|  | 
 | ||
|  | 			DrawLine(Color.gray); | ||
|  | 
 | ||
|  | 			EditorGUILayout.BeginHorizontal(); | ||
|  | 			{ | ||
|  | 				EditorGUILayout.BeginVertical(); | ||
|  | 				{ | ||
|  | 					if (DrawButton("Input Authority",      kcc.HasInputAuthority       == true, enabledBackgroundColor,      disabledBackgroundColor) == true) {} | ||
|  | 					if (DrawButton("Draw Path",            kccDebug.ShowPath           == true, enabledBackgroundColor,      disabledBackgroundColor) == true) { kccDebug.ShowPath           = !kccDebug.ShowPath;           } | ||
|  | 					if (DrawButton("Draw Ground Normal",   kccDebug.ShowGroundNormal   == true, KCCDebug.GroundNormalColor,  disabledBackgroundColor) == true) { kccDebug.ShowGroundNormal   = !kccDebug.ShowGroundNormal;   } | ||
|  | 					if (DrawButton("Draw Ground Tangent",  kccDebug.ShowGroundTangent  == true, KCCDebug.GroundTangentColor, disabledBackgroundColor) == true) { kccDebug.ShowGroundTangent  = !kccDebug.ShowGroundTangent;  } | ||
|  | 					if (DrawButton("Draw Ground Snapping", kccDebug.ShowGroundSnapping == true, KCCDebug.GroundSnapingColor, disabledBackgroundColor) == true) { kccDebug.ShowGroundSnapping = !kccDebug.ShowGroundSnapping; } | ||
|  | 				} | ||
|  | 				EditorGUILayout.EndVertical(); | ||
|  | 				EditorGUILayout.BeginVertical(); | ||
|  | 				{ | ||
|  | 					if (DrawButton("State Authority",     kcc.HasStateAuthority      == true,  enabledBackgroundColor,      disabledBackgroundColor) == true) {} | ||
|  | 					if (DrawButton("Draw Speed",          kccDebug.ShowSpeed         == true,  KCCDebug.SpeedColor,         disabledBackgroundColor) == true) { kccDebug.ShowSpeed         = !kccDebug.ShowSpeed;         } | ||
|  | 					if (DrawButton("Draw Grounding",      kccDebug.ShowGrounding     == true,  KCCDebug.IsGroundedColor,    disabledBackgroundColor) == true) { kccDebug.ShowGrounding     = !kccDebug.ShowGrounding;     } | ||
|  | 					if (DrawButton("Draw Stepping Up",    kccDebug.ShowSteppingUp    == true,  KCCDebug.IsSteppingUpColor,  disabledBackgroundColor) == true) { kccDebug.ShowSteppingUp    = !kccDebug.ShowSteppingUp;    } | ||
|  | 					if (DrawButton("Draw Move Direction", kccDebug.ShowMoveDirection == true,  KCCDebug.MoveDirectionColor, disabledBackgroundColor) == true) { kccDebug.ShowMoveDirection = !kccDebug.ShowMoveDirection; } | ||
|  | 				} | ||
|  | 				EditorGUILayout.EndVertical(); | ||
|  | 			} | ||
|  | 			EditorGUILayout.EndHorizontal(); | ||
|  | 
 | ||
|  | 			if (DrawButton("Logs", kccDebug.LogsTime != default,  enabledBackgroundColor, disabledBackgroundColor) == true) | ||
|  | 			{ | ||
|  | 				kccDebug.LogsTime = -1.0f; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (kccDebug.ShowPath == true || kccDebug.ShowSpeed == true || kccDebug.ShowGrounding == true || kccDebug.ShowGroundSnapping == true || kccDebug.ShowGroundNormal == true || kccDebug.ShowMoveDirection == true || kccDebug.ShowSteppingUp == true || kccDebug.ShowGroundTangent == true) | ||
|  | 			{ | ||
|  | 				kccDebug.DisplayTime = EditorGUILayout.Slider("Display Time", kccDebug.DisplayTime, 1.0f, 60.0f); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (kccDebug.ShowPath == true) | ||
|  | 			{ | ||
|  | 				kccDebug.PointSize = EditorGUILayout.Slider("Point Size", kccDebug.PointSize, 0.001f, 0.1f); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (kccDebug.ShowSpeed == true) | ||
|  | 			{ | ||
|  | 				kccDebug.SpeedScale = EditorGUILayout.Slider("Speed Scale", kccDebug.SpeedScale, 0.01f, 1.0f); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			GUI.backgroundColor = defaultBackgroundColor; | ||
|  | 
 | ||
|  | 			DrawLine(Color.gray); | ||
|  | 
 | ||
|  | 			KCCData data = kcc.Object.IsInSimulation == true ? kcc.FixedData : kcc.RenderData; | ||
|  | 
 | ||
|  | 			EditorGUILayout.Toggle("Is In Simulation", kcc.Object.IsInSimulation); | ||
|  | 			EditorGUILayout.LabelField("Word Count", kcc.DynamicWordCount.Value.ToString()); | ||
|  | 			EditorGUILayout.LabelField("Shape", kcc.Settings.Shape.ToString()); | ||
|  | 			EditorGUILayout.Toggle("Is Active", data.IsActive); | ||
|  | 			EditorGUILayout.Toggle("Is Grounded", data.IsGrounded); | ||
|  | 			EditorGUILayout.Toggle("Is On Edge", data.IsOnEdge); | ||
|  | 			EditorGUILayout.Toggle("Is Stepping Up", data.IsSteppingUp); | ||
|  | 			EditorGUILayout.Toggle("Is Snapping To Ground", data.IsSnappingToGround); | ||
|  | 			EditorGUILayout.Toggle("Has Manual Update", kcc.HasManualUpdate); | ||
|  | 			EditorGUILayout.LabelField("Look Pitch", data.LookPitch.ToString("0.00°")); | ||
|  | 			EditorGUILayout.LabelField("Look Yaw", data.LookYaw.ToString("0.00°")); | ||
|  | 			EditorGUILayout.LabelField("Real Speed", data.RealSpeed.ToString("0.00")); | ||
|  | 			EditorGUILayout.LabelField("Ground Angle", data.GroundAngle.ToString("0.00°")); | ||
|  | 			EditorGUILayout.LabelField("Ground Distance", data.IsGrounded == true ? data.GroundDistance.ToString("F6") : "N/A"); | ||
|  | 			EditorGUILayout.LabelField("Collision Hits", data.Hits.Count.ToString()); | ||
|  | 			EditorGUILayout.LabelField("Prediction Error", kcc.PredictionError.magnitude.ToString("F6")); | ||
|  | 			EditorGUILayout.EnumFlagsField("Active Features", kcc.ActiveFeatures); | ||
|  | 
 | ||
|  | 			DrawLine(Color.gray); | ||
|  | 
 | ||
|  | 			if (_foldoutDotStyle == null) | ||
|  | 			{ | ||
|  | 				_foldoutDotStyle = new GUIStyle(GUI.skin.label); | ||
|  | 				_foldoutDotStyle.alignment = TextAnchor.MiddleCenter; | ||
|  | 				_foldoutDotStyle.fixedWidth = foldoutLevelOffset * 0.5f; | ||
|  | 				_foldoutDotStyle.stretchHeight = true; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			ReadOnlyCollection<IKCCProcessor> localProcessors = kcc.LocalProcessors; | ||
|  | 
 | ||
|  | 			if (DrawButtonFoldout($"Local Processors ({localProcessors.Count})", ref _localProcessorsFoldout, enabledBackgroundColor, disabledBackgroundColor) == true && localProcessors.Count > 0) | ||
|  | 			{ | ||
|  | 				for (int i = 0; i < localProcessors.Count; ++i) | ||
|  | 				{ | ||
|  | 					Component processor = localProcessors[i] as Component; | ||
|  | 					if (processor != null) | ||
|  | 					{ | ||
|  | 						DrawPingDotButton($"{processor.gameObject.name}\n({processor.GetType().Name})", processor.gameObject, foldoutStaticOffset, processorBackgroundColor); | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			List<KCCModifier> modifiers = data.Modifiers.All; | ||
|  | 
 | ||
|  | 			if (DrawButtonFoldout($"Networked Modifiers ({modifiers.Count})", ref _modifiersFoldout, enabledBackgroundColor, disabledBackgroundColor) == true && modifiers.Count > 0) | ||
|  | 			{ | ||
|  | 				for (int i = 0; i < modifiers.Count; ++i) | ||
|  | 				{ | ||
|  | 					Component processor = modifiers[i].Processor as Component; | ||
|  | 					Component provider  = modifiers[i].Provider  as Component; | ||
|  | 
 | ||
|  | 					if (processor != null) | ||
|  | 					{ | ||
|  | 						DrawPingDotButton($"{processor.gameObject.name}\n({processor.GetType().Name})", processor.gameObject, foldoutStaticOffset, processorBackgroundColor); | ||
|  | 					} | ||
|  | 					else if (provider != null) | ||
|  | 					{ | ||
|  | 						DrawPingDotButton($"{provider.gameObject.name}\n({provider.GetType().Name})", provider.gameObject, foldoutStaticOffset, processorBackgroundColor); | ||
|  | 					} | ||
|  | 					else | ||
|  | 					{ | ||
|  | 						DrawPingDotButton($"{modifiers[i].NetworkObject.gameObject.name}", modifiers[i].NetworkObject.gameObject, foldoutStaticOffset, processorBackgroundColor); | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			List<KCCCollision> collisions = data.Collisions.All; | ||
|  | 
 | ||
|  | 			if (DrawButtonFoldout($"Networked Collisions ({collisions.Count})", ref _collisionsFoldout, enabledBackgroundColor, disabledBackgroundColor) == true && collisions.Count > 0) | ||
|  | 			{ | ||
|  | 				for (int i = 0; i < collisions.Count; ++i) | ||
|  | 				{ | ||
|  | 					EditorGUILayout.BeginHorizontal(); | ||
|  | 					{ | ||
|  | 						GUILayout.Space(foldoutStaticOffset); | ||
|  | 						GUILayout.Label("•", _foldoutDotStyle); | ||
|  | 
 | ||
|  | 						KCCCollision collision = collisions[i]; | ||
|  | 
 | ||
|  | 						DrawPingButton($"{collision.Collider.name}\n{(collision.Collider.isTrigger ? "[Trigger] " : "")}({collision.Collider.GetType().Name})", collision.Collider.gameObject, colliderBackgroundColor); | ||
|  | 
 | ||
|  | 						Component processor = collision.Processor as Component; | ||
|  | 						Component provider  = collision.Provider  as Component; | ||
|  | 
 | ||
|  | 						if (processor != null) | ||
|  | 						{ | ||
|  | 							DrawPingButton($"{processor.gameObject.name}\n({processor.GetType().Name})", processor.gameObject, processorBackgroundColor); | ||
|  | 						} | ||
|  | 						else if (provider != null) | ||
|  | 						{ | ||
|  | 							DrawPingButton($"{provider.gameObject.name}\n({provider.GetType().Name})", provider.gameObject, processorBackgroundColor); | ||
|  | 						} | ||
|  | 					} | ||
|  | 					EditorGUILayout.EndHorizontal(); | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			List<KCCIgnore> ignores = data.Ignores.All; | ||
|  | 
 | ||
|  | 			if (DrawButtonFoldout($"Ignored Colliders ({ignores.Count})", ref _ignoresFoldout, enabledBackgroundColor, disabledBackgroundColor) == true && ignores.Count > 0) | ||
|  | 			{ | ||
|  | 				for (int i = 0; i < ignores.Count; ++i) | ||
|  | 				{ | ||
|  | 					KCCIgnore ignore = ignores[i]; | ||
|  | 
 | ||
|  | 					DrawPingDotButton($"{ignore.Collider.name}\n{(ignore.Collider.isTrigger ? "[Trigger] " : "")}({ignore.Collider.GetType().Name})", ignore.Collider.gameObject, foldoutStaticOffset, colliderBackgroundColor); | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			List<KCCHit> hits = data.Hits.All; | ||
|  | 
 | ||
|  | 			if (DrawButtonFoldout($"Collision Hits ({hits.Count})", ref _hitsFoldout, enabledBackgroundColor, disabledBackgroundColor) == true && hits.Count > 0) | ||
|  | 			{ | ||
|  | 				for (int i = 0; i < hits.Count; ++i) | ||
|  | 				{ | ||
|  | 					KCCHit hit = hits[i]; | ||
|  | 
 | ||
|  | 					string colliderInfo = hit.CollisionType != default ? $"[{hit.CollisionType}] " : "[---] "; | ||
|  | 
 | ||
|  | 					DrawPingDotButton($"{hit.Collider.name}\n{colliderInfo}({hit.Collider.GetType().Name})", hit.Collider.gameObject, foldoutStaticOffset); | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			bool traceExecution = kccDebug.TraceExecution; | ||
|  | 			if (traceExecution == false || kccDebug.TraceInfoCount <= 0) | ||
|  | 			{ | ||
|  | 				DrawButtonFoldout($"Execution Stack", ref traceExecution, enabledBackgroundColor, disabledBackgroundColor); | ||
|  | 			} | ||
|  | 			else | ||
|  | 			{ | ||
|  | 				GUILayout.BeginHorizontal(); | ||
|  | 
 | ||
|  | 				int stageTraces     = 0; | ||
|  | 				int processorTraces = 0; | ||
|  | 
 | ||
|  | 				for (int i = 0; i < kccDebug.TraceInfoCount; ++i) | ||
|  | 				{ | ||
|  | 					KCCTraceInfo traceInfo = kccDebug.TraceInfos[i]; | ||
|  | 					if (traceInfo.Trace == EKCCTrace.Stage) | ||
|  | 					{ | ||
|  | 						++stageTraces; | ||
|  | 					} | ||
|  | 					else if (traceInfo.Trace == EKCCTrace.Processor) | ||
|  | 					{ | ||
|  | 						++processorTraces; | ||
|  | 					} | ||
|  | 				} | ||
|  | 
 | ||
|  | 				DrawButtonFoldout($"Execution Stack ({stageTraces} + {processorTraces})", ref traceExecution, enabledBackgroundColor, disabledBackgroundColor); | ||
|  | 
 | ||
|  | 				if (GUILayout.Button("▼", GUILayout.Width(24.0f)) == true) | ||
|  | 				{ | ||
|  | 					traceExecution = true; | ||
|  | 
 | ||
|  | 					for (int i = 0; i < kccDebug.TraceInfoCount; ++i) | ||
|  | 					{ | ||
|  | 						KCCTraceInfo traceInfo = kccDebug.TraceInfos[i]; | ||
|  | 						traceInfo.IsVisible = true; | ||
|  | 					} | ||
|  | 				} | ||
|  | 				if (GUILayout.Button("▲", GUILayout.Width(24.0f)) == true) | ||
|  | 				{ | ||
|  | 					for (int i = 0; i < kccDebug.TraceInfoCount; ++i) | ||
|  | 					{ | ||
|  | 						KCCTraceInfo traceInfo = kccDebug.TraceInfos[i]; | ||
|  | 						traceInfo.IsVisible = false; | ||
|  | 					} | ||
|  | 				} | ||
|  | 
 | ||
|  | 				GUILayout.EndHorizontal(); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (kccDebug.TraceExecution != traceExecution) | ||
|  | 			{ | ||
|  | 				kccDebug.TraceExecution = traceExecution; | ||
|  | 				kccDebug.TraceInfoCount = default; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (traceExecution == true && kccDebug.TraceInfoCount > 0) | ||
|  | 			{ | ||
|  | 				if (_stageButtonStyle == null) | ||
|  | 				{ | ||
|  | 					_stageButtonStyle = new GUIStyle(GUI.skin.button); | ||
|  | 					_stageButtonStyle.alignment = TextAnchor.MiddleLeft; | ||
|  | 				} | ||
|  | 
 | ||
|  | 				/*kccDebug.TraceExecution = false; | ||
|  | 				for (int i = 0; i < kccDebug.TraceInfoCount; ++i) | ||
|  | 				{ | ||
|  | 					Debug.LogError($"{kccDebug.TraceInfos[i].Trace} {kccDebug.TraceInfos[i].Name} {kccDebug.TraceInfos[i].Level} {kccDebug.TraceInfos[i].Processor}"); | ||
|  | 				} | ||
|  | 				kccDebug.TraceInfoCount = 0;*/ | ||
|  | 
 | ||
|  | 				for (int i = 0; i < kccDebug.TraceInfoCount; ++i) | ||
|  | 				{ | ||
|  | 					KCCTraceInfo traceInfo = kccDebug.TraceInfos[i]; | ||
|  | 					if (traceInfo.IsStage == true && traceInfo.Level == default) | ||
|  | 					{ | ||
|  | 						DrawStageTrace(kccDebug.TraceInfos, kccDebug.TraceInfoCount, i, default); | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			DrawLine(Color.gray); | ||
|  | 
 | ||
|  | 			GUI.color           = defaultColor; | ||
|  | 			GUI.contentColor    = defaultContentColor; | ||
|  | 			GUI.backgroundColor = defaultBackgroundColor; | ||
|  | 
 | ||
|  | 			void DrawStageTrace(KCCTraceInfo[] traceInfos, int traceInfoCount, int traceInfoIndex, int indentLevel) | ||
|  | 			{ | ||
|  | 				KCCTraceInfo stageTraceInfo = traceInfos[traceInfoIndex]; | ||
|  | 				if (stageTraceInfo.IsStage == false) | ||
|  | 				{ | ||
|  | 					Debug.LogError($"Unexpected trace {stageTraceInfo.Trace}!"); | ||
|  | 					return; | ||
|  | 				} | ||
|  | 
 | ||
|  | 				int childCount           = 0; | ||
|  | 				int childStages          = 0; | ||
|  | 				int childProcessors      = 0; | ||
|  | 				int childStageLevel      = stageTraceInfo.Level + 1; | ||
|  | 				int childProcessorLevel  = stageTraceInfo.Level; | ||
|  | 
 | ||
|  | 				for (int i = traceInfoIndex + 1; i < traceInfoCount; ++i) | ||
|  | 				{ | ||
|  | 					KCCTraceInfo nextTraceInfo  = traceInfos[i]; | ||
|  | 					int          nextTraceLevel = nextTraceInfo.Level; | ||
|  | 
 | ||
|  | 					if (nextTraceInfo.IsStage == true) | ||
|  | 					{ | ||
|  | 						if (nextTraceLevel < childStageLevel) | ||
|  | 							break; | ||
|  | 						if (nextTraceLevel > childStageLevel) | ||
|  | 							continue; | ||
|  | 
 | ||
|  | 						++childStages; | ||
|  | 					} | ||
|  | 					else if (nextTraceInfo.IsProcessor == true) | ||
|  | 					{ | ||
|  | 						if (nextTraceLevel < childProcessorLevel) | ||
|  | 							break; | ||
|  | 						if (nextTraceLevel > childProcessorLevel) | ||
|  | 							continue; | ||
|  | 
 | ||
|  | 						++childProcessors; | ||
|  | 					} | ||
|  | 					else | ||
|  | 					{ | ||
|  | 						throw new NotImplementedException(nextTraceInfo.Trace.ToString()); | ||
|  | 					} | ||
|  | 
 | ||
|  | 					++childCount; | ||
|  | 				} | ||
|  | 
 | ||
|  | 				float indent = foldoutStaticOffset + indentLevel * foldoutLevelOffset; | ||
|  | 
 | ||
|  | 				GUILayout.BeginHorizontal(); | ||
|  | 				GUILayout.Space(indent); | ||
|  | 				GUILayout.Label("•", _foldoutDotStyle); | ||
|  | 				DrawButtonFoldout($"{stageTraceInfo.Name} ({childProcessors})", ref stageTraceInfo.IsVisible, Color.yellow, disabledBackgroundColor, _stageButtonStyle); | ||
|  | 				GUILayout.EndHorizontal(); | ||
|  | 
 | ||
|  | 				if (stageTraceInfo.IsVisible == true && childCount > 0) | ||
|  | 				{ | ||
|  | 					int nextIndentLevel = indentLevel + 1; | ||
|  | 
 | ||
|  | 					while (true) | ||
|  | 					{ | ||
|  | 						++traceInfoIndex; | ||
|  | 						if (traceInfoIndex >= traceInfoCount) | ||
|  | 							break; | ||
|  | 
 | ||
|  | 						KCCTraceInfo nextTraceInfo  = traceInfos[traceInfoIndex]; | ||
|  | 						int          nextTraceLevel = nextTraceInfo.Level; | ||
|  | 
 | ||
|  | 						if (nextTraceInfo.IsStage == true) | ||
|  | 						{ | ||
|  | 							if (nextTraceLevel < childStageLevel) | ||
|  | 								break; | ||
|  | 							if (nextTraceLevel > childStageLevel) | ||
|  | 								continue; | ||
|  | 
 | ||
|  | 							DrawStageTrace(traceInfos, traceInfoCount, traceInfoIndex, nextIndentLevel); | ||
|  | 						} | ||
|  | 						else if (nextTraceInfo.IsProcessor == true) | ||
|  | 						{ | ||
|  | 							if (nextTraceLevel < childProcessorLevel) | ||
|  | 								break; | ||
|  | 							if (nextTraceLevel > childProcessorLevel) | ||
|  | 								continue; | ||
|  | 
 | ||
|  | 							IKCCProcessor processor  = nextTraceInfo.Processor; | ||
|  | 							GameObject    gameObject = null; | ||
|  | 							string        name       = "N/A"; | ||
|  | 
 | ||
|  | 							if (processor is Component processorComponent) | ||
|  | 							{ | ||
|  | 								gameObject = processorComponent.gameObject; | ||
|  | 								name       = gameObject.name; | ||
|  | 							} | ||
|  | 
 | ||
|  | 							DrawPingDotButton($"{name}\n({processor.GetType().Name})", gameObject, indent + foldoutLevelOffset, processorBackgroundColor); | ||
|  | 
 | ||
|  | 							nextIndentLevel = indentLevel + 2; | ||
|  | 						} | ||
|  | 						else | ||
|  | 						{ | ||
|  | 							throw new NotImplementedException(nextTraceInfo.Trace.ToString()); | ||
|  | 						} | ||
|  | 					} | ||
|  | 				} | ||
|  | 			} | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// PRIVATE METHODS | ||
|  | 
 | ||
|  | 		public static void DrawLine(Color color, float thickness = 1.0f, float padding = 10.0f) | ||
|  | 		{ | ||
|  | 			Rect controlRect = EditorGUILayout.GetControlRect(GUILayout.Height(padding + thickness)); | ||
|  | 
 | ||
|  | 			controlRect.height = thickness; | ||
|  | 			controlRect.y += padding * 0.5f; | ||
|  | 
 | ||
|  | 			EditorGUI.DrawRect(controlRect, color); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private static bool DrawButton(string label, bool condition, Color enabledBackgroundColor, Color disabledBackgroundColor) | ||
|  | 		{ | ||
|  | 			Color backupBackgroundColor = GUI.backgroundColor; | ||
|  | 			GUI.backgroundColor = condition == true ? enabledBackgroundColor : disabledBackgroundColor; | ||
|  | 			bool result = GUILayout.Button(label); | ||
|  | 			GUI.backgroundColor = backupBackgroundColor; | ||
|  | 			return result; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private static bool DrawButton(string label, bool condition, Color enabledBackgroundColor, Color disabledBackgroundColor, GUIStyle style) | ||
|  | 		{ | ||
|  | 			Color backupBackgroundColor = GUI.backgroundColor; | ||
|  | 			GUI.backgroundColor = condition == true ? enabledBackgroundColor : disabledBackgroundColor; | ||
|  | 			bool result = GUILayout.Button(label, style); | ||
|  | 			GUI.backgroundColor = backupBackgroundColor; | ||
|  | 			return result; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private static bool DrawPingButton(string label, GameObject gameObject, Color backgroundColor) | ||
|  | 		{ | ||
|  | 			Color backupBackgroundColor = GUI.backgroundColor; | ||
|  | 			GUI.backgroundColor = backgroundColor; | ||
|  | 			bool result = GUILayout.Button(label); | ||
|  | 			GUI.backgroundColor = backupBackgroundColor; | ||
|  | 			if (result == true) | ||
|  | 			{ | ||
|  | 				EditorGUIUtility.PingObject(gameObject); | ||
|  | 			} | ||
|  | 			return result; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private static bool DrawPingButton(string label, GameObject gameObject, Color backgroundColor, GUIStyle style) | ||
|  | 		{ | ||
|  | 			Color backupBackgroundColor = GUI.backgroundColor; | ||
|  | 			GUI.backgroundColor = backgroundColor; | ||
|  | 			bool result = GUILayout.Button(label, style); | ||
|  | 			GUI.backgroundColor = backupBackgroundColor; | ||
|  | 			if (result == true) | ||
|  | 			{ | ||
|  | 				EditorGUIUtility.PingObject(gameObject); | ||
|  | 			} | ||
|  | 			return result; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private static bool DrawPingButton(string label, GameObject gameObject, bool condition, Color enabledBackgroundColor, Color disabledBackgroundColor) | ||
|  | 		{ | ||
|  | 			Color backupBackgroundColor = GUI.backgroundColor; | ||
|  | 			GUI.backgroundColor = condition == true ? enabledBackgroundColor : disabledBackgroundColor; | ||
|  | 			bool result = GUILayout.Button(label); | ||
|  | 			GUI.backgroundColor = backupBackgroundColor; | ||
|  | 			if (result == true) | ||
|  | 			{ | ||
|  | 				EditorGUIUtility.PingObject(gameObject); | ||
|  | 			} | ||
|  | 			return result; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private static bool DrawPingButton(string label, GameObject gameObject, bool condition, Color enabledBackgroundColor, Color disabledBackgroundColor, GUIStyle style) | ||
|  | 		{ | ||
|  | 			Color backupBackgroundColor = GUI.backgroundColor; | ||
|  | 			GUI.backgroundColor = condition == true ? enabledBackgroundColor : disabledBackgroundColor; | ||
|  | 			bool result = GUILayout.Button(label, style); | ||
|  | 			GUI.backgroundColor = backupBackgroundColor; | ||
|  | 			if (result == true) | ||
|  | 			{ | ||
|  | 				EditorGUIUtility.PingObject(gameObject); | ||
|  | 			} | ||
|  | 			return result; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private static bool DrawPingDotButton(string label, GameObject gameObject, float offset) | ||
|  | 		{ | ||
|  | 			GUILayout.BeginHorizontal(); | ||
|  | 			GUILayout.Space(offset); | ||
|  | 			GUILayout.Label("•", _foldoutDotStyle); | ||
|  | 
 | ||
|  | 			bool result = GUILayout.Button(label); | ||
|  | 			if (result == true) | ||
|  | 			{ | ||
|  | 				EditorGUIUtility.PingObject(gameObject); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			GUILayout.EndHorizontal(); | ||
|  | 
 | ||
|  | 			return result; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private static bool DrawPingDotButton(string label, GameObject gameObject, float offset, Color backgroundColor) | ||
|  | 		{ | ||
|  | 			Color backupBackgroundColor = GUI.backgroundColor; | ||
|  | 
 | ||
|  | 			GUILayout.BeginHorizontal(); | ||
|  | 			GUILayout.Space(offset); | ||
|  | 			GUILayout.Label("•", _foldoutDotStyle); | ||
|  | 
 | ||
|  | 			GUI.backgroundColor = backgroundColor; | ||
|  | 			bool result = GUILayout.Button(label); | ||
|  | 			GUI.backgroundColor = backupBackgroundColor; | ||
|  | 			if (result == true) | ||
|  | 			{ | ||
|  | 				EditorGUIUtility.PingObject(gameObject); | ||
|  | 			} | ||
|  | 
 | ||
|  | 			GUILayout.EndHorizontal(); | ||
|  | 
 | ||
|  | 			return result; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private static bool DrawButtonFoldout(string label, ref bool condition, Color enabledBackgroundColor, Color disabledBackgroundColor) | ||
|  | 		{ | ||
|  | 			return DrawButtonFoldout("▼", "▶", label, ref condition, enabledBackgroundColor, disabledBackgroundColor); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private static bool DrawButtonFoldout(string label, ref bool condition, Color enabledBackgroundColor, Color disabledBackgroundColor, GUIStyle style) | ||
|  | 		{ | ||
|  | 			return DrawButtonFoldout("▼", "▶", label, ref condition, enabledBackgroundColor, disabledBackgroundColor, style); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private static bool DrawButtonFoldout(string enabledPrefix, string disabledPrefix, string label, ref bool condition, Color enabledBackgroundColor, Color disabledBackgroundColor) | ||
|  | 		{ | ||
|  | 			if (_buttonFoldoutStyle == null) | ||
|  | 			{ | ||
|  | 				_buttonFoldoutStyle = new GUIStyle(GUI.skin.button); | ||
|  | 				_buttonFoldoutStyle.alignment = TextAnchor.MiddleLeft; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			return DrawButtonFoldout(enabledPrefix, disabledPrefix, label, ref condition, enabledBackgroundColor, disabledBackgroundColor, _buttonFoldoutStyle); | ||
|  | 		} | ||
|  | 
 | ||
|  | 		private static bool DrawButtonFoldout(string enabledPrefix, string disabledPrefix, string label, ref bool condition, Color enabledBackgroundColor, Color disabledBackgroundColor, GUIStyle style) | ||
|  | 		{ | ||
|  | 			Color backupBackgroundColor = GUI.backgroundColor; | ||
|  | 
 | ||
|  | 			if (condition == true) | ||
|  | 			{ | ||
|  | 				GUI.backgroundColor = enabledBackgroundColor; | ||
|  | 				if (string.IsNullOrEmpty(enabledPrefix) == false) | ||
|  | 				{ | ||
|  | 					label = $"{enabledPrefix} {label}"; | ||
|  | 				} | ||
|  | 			} | ||
|  | 			else | ||
|  | 			{ | ||
|  | 				GUI.backgroundColor = disabledBackgroundColor; | ||
|  | 				if (string.IsNullOrEmpty(disabledPrefix) == false) | ||
|  | 				{ | ||
|  | 					label = $"{disabledPrefix} {label}"; | ||
|  | 				} | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (GUILayout.Button(label, style) == true) | ||
|  | 			{ | ||
|  | 				condition = !condition; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			GUI.backgroundColor = backupBackgroundColor; | ||
|  | 
 | ||
|  | 			return condition; | ||
|  | 		} | ||
|  | 	} | ||
|  | } |