Showing
44 changed files
with
3782 additions
and
0 deletions
Too many changes to show.
To preserve performance only 44 of 44+ files are displayed.
@@ -758,6 +758,14 @@ | @@ -758,6 +758,14 @@ | ||
758 | </Reference> | 758 | </Reference> |
759 | </ItemGroup> | 759 | </ItemGroup> |
760 | <ItemGroup> | 760 | <ItemGroup> |
761 | + <ProjectReference Include="IngameDebugConsole.Editor.csproj"> | ||
762 | + <Project>{9b47dfc7-464c-e716-7c49-c01c25245e7d}</Project> | ||
763 | + <Name>IngameDebugConsole.Editor</Name> | ||
764 | + </ProjectReference> | ||
765 | + <ProjectReference Include="IngameDebugConsole.Runtime.csproj"> | ||
766 | + <Project>{f455341b-6302-350b-c7b4-f9a38305d0ae}</Project> | ||
767 | + <Name>IngameDebugConsole.Runtime</Name> | ||
768 | + </ProjectReference> | ||
761 | </ItemGroup> | 769 | </ItemGroup> |
762 | <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | 770 | <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
763 | <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | 771 | <!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
@@ -763,6 +763,14 @@ | @@ -763,6 +763,14 @@ | ||
763 | <Project>{0fb0da77-85a4-96d4-1dc2-8ddaab877745}</Project> | 763 | <Project>{0fb0da77-85a4-96d4-1dc2-8ddaab877745}</Project> |
764 | <Name>Assembly-CSharp-firstpass</Name> | 764 | <Name>Assembly-CSharp-firstpass</Name> |
765 | </ProjectReference> | 765 | </ProjectReference> |
766 | + <ProjectReference Include="IngameDebugConsole.Editor.csproj"> | ||
767 | + <Project>{9b47dfc7-464c-e716-7c49-c01c25245e7d}</Project> | ||
768 | + <Name>IngameDebugConsole.Editor</Name> | ||
769 | + </ProjectReference> | ||
770 | + <ProjectReference Include="IngameDebugConsole.Runtime.csproj"> | ||
771 | + <Project>{f455341b-6302-350b-c7b4-f9a38305d0ae}</Project> | ||
772 | + <Name>IngameDebugConsole.Runtime</Name> | ||
773 | + </ProjectReference> | ||
766 | </ItemGroup> | 774 | </ItemGroup> |
767 | <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | 775 | <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> |
768 | <!-- To modify your build process, add your task inside one of the targets below and uncomment it. | 776 | <!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
Assets/Plugins/IngameDebugConsole.meta
0 → 100644
1 | +#if UNITY_EDITOR || UNITY_ANDROID | ||
2 | +using System.Collections.Generic; | ||
3 | +using UnityEngine; | ||
4 | + | ||
5 | +// Credit: https://stackoverflow.com/a/41018028/2373034 | ||
6 | +namespace IngameDebugConsole | ||
7 | +{ | ||
8 | + public class DebugLogLogcatListener : AndroidJavaProxy | ||
9 | + { | ||
10 | + private Queue<string> queuedLogs; | ||
11 | + private AndroidJavaObject nativeObject; | ||
12 | + | ||
13 | + public DebugLogLogcatListener() : base( "com.yasirkula.unity.DebugConsoleLogcatLogReceiver" ) | ||
14 | + { | ||
15 | + queuedLogs = new Queue<string>( 16 ); | ||
16 | + } | ||
17 | + | ||
18 | + ~DebugLogLogcatListener() | ||
19 | + { | ||
20 | + Stop(); | ||
21 | + | ||
22 | + if( nativeObject != null ) | ||
23 | + nativeObject.Dispose(); | ||
24 | + } | ||
25 | + | ||
26 | + public void Start( string arguments ) | ||
27 | + { | ||
28 | + if( nativeObject == null ) | ||
29 | + nativeObject = new AndroidJavaObject( "com.yasirkula.unity.DebugConsoleLogcatLogger" ); | ||
30 | + | ||
31 | + nativeObject.Call( "Start", this, arguments ); | ||
32 | + } | ||
33 | + | ||
34 | + public void Stop() | ||
35 | + { | ||
36 | + if( nativeObject != null ) | ||
37 | + nativeObject.Call( "Stop" ); | ||
38 | + } | ||
39 | + | ||
40 | + [UnityEngine.Scripting.Preserve] | ||
41 | + public void OnLogReceived( string log ) | ||
42 | + { | ||
43 | + queuedLogs.Enqueue( log ); | ||
44 | + } | ||
45 | + | ||
46 | + public string GetLog() | ||
47 | + { | ||
48 | + if( queuedLogs.Count > 0 ) | ||
49 | + return queuedLogs.Dequeue(); | ||
50 | + | ||
51 | + return null; | ||
52 | + } | ||
53 | + } | ||
54 | +} | ||
55 | +#endif |
No preview for this file type
1 | +fileFormatVersion: 2 | ||
2 | +guid: bf909fab1c14af446b0a854de42289b2 | ||
3 | +timeCreated: 1510086220 | ||
4 | +licenseType: Store | ||
5 | +PluginImporter: | ||
6 | + serializedVersion: 2 | ||
7 | + iconMap: {} | ||
8 | + executionOrder: {} | ||
9 | + isPreloaded: 0 | ||
10 | + isOverridable: 0 | ||
11 | + platformData: | ||
12 | + data: | ||
13 | + first: | ||
14 | + Android: Android | ||
15 | + second: | ||
16 | + enabled: 1 | ||
17 | + settings: {} | ||
18 | + data: | ||
19 | + first: | ||
20 | + Any: | ||
21 | + second: | ||
22 | + enabled: 0 | ||
23 | + settings: {} | ||
24 | + data: | ||
25 | + first: | ||
26 | + Editor: Editor | ||
27 | + second: | ||
28 | + enabled: 0 | ||
29 | + settings: | ||
30 | + DefaultValueInitialized: true | ||
31 | + userData: | ||
32 | + assetBundleName: | ||
33 | + assetBundleVariant: |
1 | +using UnityEditor; | ||
2 | +using UnityEngine; | ||
3 | + | ||
4 | +namespace IngameDebugConsole | ||
5 | +{ | ||
6 | + [CustomEditor( typeof( DebugLogManager ) )] | ||
7 | + public class DebugLogManagerEditor : Editor | ||
8 | + { | ||
9 | + private SerializedProperty singleton; | ||
10 | + private SerializedProperty minimumHeight; | ||
11 | + private SerializedProperty enableHorizontalResizing; | ||
12 | + private SerializedProperty resizeFromRight; | ||
13 | + private SerializedProperty minimumWidth; | ||
14 | + private SerializedProperty logWindowOpacity; | ||
15 | + private SerializedProperty popupOpacity; | ||
16 | + private SerializedProperty popupVisibility; | ||
17 | + private SerializedProperty popupVisibilityLogFilter; | ||
18 | + private SerializedProperty startMinimized; | ||
19 | + private SerializedProperty toggleWithKey; | ||
20 | + private SerializedProperty toggleKey; | ||
21 | + private SerializedProperty enableSearchbar; | ||
22 | + private SerializedProperty topSearchbarMinWidth; | ||
23 | + private SerializedProperty receiveLogsWhileInactive; | ||
24 | + private SerializedProperty receiveInfoLogs; | ||
25 | + private SerializedProperty receiveWarningLogs; | ||
26 | + private SerializedProperty receiveErrorLogs; | ||
27 | + private SerializedProperty receiveExceptionLogs; | ||
28 | + private SerializedProperty captureLogTimestamps; | ||
29 | + private SerializedProperty alwaysDisplayTimestamps; | ||
30 | + private SerializedProperty maxLogCount; | ||
31 | + private SerializedProperty logsToRemoveAfterMaxLogCount; | ||
32 | + private SerializedProperty queuedLogLimit; | ||
33 | + private SerializedProperty clearCommandAfterExecution; | ||
34 | + private SerializedProperty commandHistorySize; | ||
35 | + private SerializedProperty showCommandSuggestions; | ||
36 | + private SerializedProperty receiveLogcatLogsInAndroid; | ||
37 | + private SerializedProperty logcatArguments; | ||
38 | + private SerializedProperty avoidScreenCutout; | ||
39 | + private SerializedProperty popupAvoidsScreenCutout; | ||
40 | + private SerializedProperty autoFocusOnCommandInputField; | ||
41 | + | ||
42 | +#if UNITY_2017_3_OR_NEWER | ||
43 | + private readonly GUIContent popupVisibilityLogFilterLabel = new GUIContent( "Log Filter", "Determines which log types will show the popup on screen" ); | ||
44 | +#endif | ||
45 | + private readonly GUIContent receivedLogTypesLabel = new GUIContent( "Received Log Types", "Only these logs will be received by the console window, other logs will simply be skipped" ); | ||
46 | + private readonly GUIContent receiveInfoLogsLabel = new GUIContent( "Info" ); | ||
47 | + private readonly GUIContent receiveWarningLogsLabel = new GUIContent( "Warning" ); | ||
48 | + private readonly GUIContent receiveErrorLogsLabel = new GUIContent( "Error" ); | ||
49 | + private readonly GUIContent receiveExceptionLogsLabel = new GUIContent( "Exception" ); | ||
50 | + | ||
51 | + private void OnEnable() | ||
52 | + { | ||
53 | + singleton = serializedObject.FindProperty( "singleton" ); | ||
54 | + minimumHeight = serializedObject.FindProperty( "minimumHeight" ); | ||
55 | + enableHorizontalResizing = serializedObject.FindProperty( "enableHorizontalResizing" ); | ||
56 | + resizeFromRight = serializedObject.FindProperty( "resizeFromRight" ); | ||
57 | + minimumWidth = serializedObject.FindProperty( "minimumWidth" ); | ||
58 | + logWindowOpacity = serializedObject.FindProperty( "logWindowOpacity" ); | ||
59 | + popupOpacity = serializedObject.FindProperty( "popupOpacity" ); | ||
60 | + popupVisibility = serializedObject.FindProperty( "popupVisibility" ); | ||
61 | + popupVisibilityLogFilter = serializedObject.FindProperty( "popupVisibilityLogFilter" ); | ||
62 | + startMinimized = serializedObject.FindProperty( "startMinimized" ); | ||
63 | + toggleWithKey = serializedObject.FindProperty( "toggleWithKey" ); | ||
64 | +#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER | ||
65 | + toggleKey = serializedObject.FindProperty( "toggleBinding" ); | ||
66 | +#else | ||
67 | + toggleKey = serializedObject.FindProperty( "toggleKey" ); | ||
68 | +#endif | ||
69 | + enableSearchbar = serializedObject.FindProperty( "enableSearchbar" ); | ||
70 | + topSearchbarMinWidth = serializedObject.FindProperty( "topSearchbarMinWidth" ); | ||
71 | + receiveLogsWhileInactive = serializedObject.FindProperty( "receiveLogsWhileInactive" ); | ||
72 | + receiveInfoLogs = serializedObject.FindProperty( "receiveInfoLogs" ); | ||
73 | + receiveWarningLogs = serializedObject.FindProperty( "receiveWarningLogs" ); | ||
74 | + receiveErrorLogs = serializedObject.FindProperty( "receiveErrorLogs" ); | ||
75 | + receiveExceptionLogs = serializedObject.FindProperty( "receiveExceptionLogs" ); | ||
76 | + captureLogTimestamps = serializedObject.FindProperty( "captureLogTimestamps" ); | ||
77 | + alwaysDisplayTimestamps = serializedObject.FindProperty( "alwaysDisplayTimestamps" ); | ||
78 | + maxLogCount = serializedObject.FindProperty( "maxLogCount" ); | ||
79 | + logsToRemoveAfterMaxLogCount = serializedObject.FindProperty( "logsToRemoveAfterMaxLogCount" ); | ||
80 | + queuedLogLimit = serializedObject.FindProperty( "queuedLogLimit" ); | ||
81 | + clearCommandAfterExecution = serializedObject.FindProperty( "clearCommandAfterExecution" ); | ||
82 | + commandHistorySize = serializedObject.FindProperty( "commandHistorySize" ); | ||
83 | + showCommandSuggestions = serializedObject.FindProperty( "showCommandSuggestions" ); | ||
84 | + receiveLogcatLogsInAndroid = serializedObject.FindProperty( "receiveLogcatLogsInAndroid" ); | ||
85 | + logcatArguments = serializedObject.FindProperty( "logcatArguments" ); | ||
86 | + avoidScreenCutout = serializedObject.FindProperty( "avoidScreenCutout" ); | ||
87 | + popupAvoidsScreenCutout = serializedObject.FindProperty( "popupAvoidsScreenCutout" ); | ||
88 | + autoFocusOnCommandInputField = serializedObject.FindProperty( "autoFocusOnCommandInputField" ); | ||
89 | + } | ||
90 | + | ||
91 | + public override void OnInspectorGUI() | ||
92 | + { | ||
93 | + serializedObject.Update(); | ||
94 | + | ||
95 | + EditorGUILayout.PropertyField( singleton ); | ||
96 | + | ||
97 | + EditorGUILayout.Space(); | ||
98 | + | ||
99 | + EditorGUILayout.PropertyField( minimumHeight ); | ||
100 | + | ||
101 | + EditorGUILayout.PropertyField( enableHorizontalResizing ); | ||
102 | + if( enableHorizontalResizing.boolValue ) | ||
103 | + { | ||
104 | + DrawSubProperty( resizeFromRight ); | ||
105 | + DrawSubProperty( minimumWidth ); | ||
106 | + } | ||
107 | + | ||
108 | + EditorGUILayout.PropertyField( avoidScreenCutout ); | ||
109 | + DrawSubProperty( popupAvoidsScreenCutout ); | ||
110 | + | ||
111 | + EditorGUILayout.Space(); | ||
112 | + | ||
113 | + EditorGUILayout.PropertyField( startMinimized ); | ||
114 | + EditorGUILayout.PropertyField( logWindowOpacity ); | ||
115 | + EditorGUILayout.PropertyField( popupOpacity ); | ||
116 | + | ||
117 | + EditorGUILayout.PropertyField( popupVisibility ); | ||
118 | + if( popupVisibility.intValue == (int) PopupVisibility.WhenLogReceived ) | ||
119 | + { | ||
120 | + EditorGUI.indentLevel++; | ||
121 | +#if UNITY_2017_3_OR_NEWER | ||
122 | + Rect rect = EditorGUILayout.GetControlRect(); | ||
123 | + EditorGUI.BeginProperty( rect, GUIContent.none, popupVisibilityLogFilter ); | ||
124 | + popupVisibilityLogFilter.intValue = (int) (DebugLogFilter) EditorGUI.EnumFlagsField( rect, popupVisibilityLogFilterLabel, (DebugLogFilter) popupVisibilityLogFilter.intValue ); | ||
125 | +#else | ||
126 | + EditorGUI.BeginProperty( new Rect(), GUIContent.none, popupVisibilityLogFilter ); | ||
127 | + EditorGUI.BeginChangeCheck(); | ||
128 | + | ||
129 | + bool infoLog = EditorGUILayout.Toggle( "Info", ( (DebugLogFilter) popupVisibilityLogFilter.intValue & DebugLogFilter.Info ) == DebugLogFilter.Info ); | ||
130 | + bool warningLog = EditorGUILayout.Toggle( "Warning", ( (DebugLogFilter) popupVisibilityLogFilter.intValue & DebugLogFilter.Warning ) == DebugLogFilter.Warning ); | ||
131 | + bool errorLog = EditorGUILayout.Toggle( "Error", ( (DebugLogFilter) popupVisibilityLogFilter.intValue & DebugLogFilter.Error ) == DebugLogFilter.Error ); | ||
132 | + | ||
133 | + if( EditorGUI.EndChangeCheck() ) | ||
134 | + popupVisibilityLogFilter.intValue = ( infoLog ? (int) DebugLogFilter.Info : 0 ) | ( warningLog ? (int) DebugLogFilter.Warning : 0 ) | ( errorLog ? (int) DebugLogFilter.Error : 0 ); | ||
135 | +#endif | ||
136 | + EditorGUI.EndProperty(); | ||
137 | + EditorGUI.indentLevel--; | ||
138 | + } | ||
139 | + | ||
140 | + EditorGUILayout.PropertyField( toggleWithKey ); | ||
141 | + if( toggleWithKey.boolValue ) | ||
142 | + DrawSubProperty( toggleKey ); | ||
143 | + | ||
144 | + EditorGUILayout.Space(); | ||
145 | + | ||
146 | + EditorGUILayout.PropertyField( enableSearchbar ); | ||
147 | + if( enableSearchbar.boolValue ) | ||
148 | + DrawSubProperty( topSearchbarMinWidth ); | ||
149 | + | ||
150 | + EditorGUILayout.Space(); | ||
151 | + | ||
152 | + EditorGUILayout.PropertyField( receiveLogsWhileInactive ); | ||
153 | + | ||
154 | + EditorGUILayout.PrefixLabel( receivedLogTypesLabel ); | ||
155 | + EditorGUI.indentLevel++; | ||
156 | + EditorGUILayout.PropertyField( receiveInfoLogs, receiveInfoLogsLabel ); | ||
157 | + EditorGUILayout.PropertyField( receiveWarningLogs, receiveWarningLogsLabel ); | ||
158 | + EditorGUILayout.PropertyField( receiveErrorLogs, receiveErrorLogsLabel ); | ||
159 | + EditorGUILayout.PropertyField( receiveExceptionLogs, receiveExceptionLogsLabel ); | ||
160 | + EditorGUI.indentLevel--; | ||
161 | + | ||
162 | + EditorGUILayout.PropertyField( receiveLogcatLogsInAndroid ); | ||
163 | + if( receiveLogcatLogsInAndroid.boolValue ) | ||
164 | + DrawSubProperty( logcatArguments ); | ||
165 | + | ||
166 | + EditorGUILayout.PropertyField( captureLogTimestamps ); | ||
167 | + if( captureLogTimestamps.boolValue ) | ||
168 | + DrawSubProperty( alwaysDisplayTimestamps ); | ||
169 | + | ||
170 | + EditorGUILayout.PropertyField( maxLogCount ); | ||
171 | + DrawSubProperty( logsToRemoveAfterMaxLogCount ); | ||
172 | + | ||
173 | + EditorGUILayout.PropertyField( queuedLogLimit ); | ||
174 | + | ||
175 | + EditorGUILayout.Space(); | ||
176 | + | ||
177 | + EditorGUILayout.PropertyField( clearCommandAfterExecution ); | ||
178 | + EditorGUILayout.PropertyField( commandHistorySize ); | ||
179 | + EditorGUILayout.PropertyField( showCommandSuggestions ); | ||
180 | + EditorGUILayout.PropertyField( autoFocusOnCommandInputField ); | ||
181 | + | ||
182 | + EditorGUILayout.Space(); | ||
183 | + | ||
184 | + DrawPropertiesExcluding( serializedObject, "m_Script" ); | ||
185 | + serializedObject.ApplyModifiedProperties(); | ||
186 | + } | ||
187 | + | ||
188 | + private void DrawSubProperty( SerializedProperty property ) | ||
189 | + { | ||
190 | + EditorGUI.indentLevel++; | ||
191 | + EditorGUILayout.PropertyField( property ); | ||
192 | + EditorGUI.indentLevel--; | ||
193 | + } | ||
194 | + } | ||
195 | +} |
1 | +{ | ||
2 | + "name": "IngameDebugConsole.Editor", | ||
3 | + "references": [ | ||
4 | + "IngameDebugConsole.Runtime" | ||
5 | + ], | ||
6 | + "includePlatforms": [ | ||
7 | + "Editor" | ||
8 | + ], | ||
9 | + "excludePlatforms": [], | ||
10 | + "allowUnsafeCode": false, | ||
11 | + "overrideReferences": false, | ||
12 | + "precompiledReferences": [], | ||
13 | + "autoReferenced": true, | ||
14 | + "defineConstraints": [], | ||
15 | + "versionDefines": [], | ||
16 | + "noEngineReferences": false | ||
17 | +} |
This diff could not be displayed because it is too large.
1 | +%YAML 1.1 | ||
2 | +%TAG !u! tag:unity3d.com,2011: | ||
3 | +--- !u!1001 &100100000 | ||
4 | +Prefab: | ||
5 | + m_ObjectHideFlags: 1 | ||
6 | + serializedVersion: 2 | ||
7 | + m_Modification: | ||
8 | + m_TransformParent: {fileID: 0} | ||
9 | + m_Modifications: [] | ||
10 | + m_RemovedComponents: [] | ||
11 | + m_ParentPrefab: {fileID: 0} | ||
12 | + m_RootGameObject: {fileID: 1386426139070838} | ||
13 | + m_IsPrefabParent: 1 | ||
14 | +--- !u!1 &1386426139070838 | ||
15 | +GameObject: | ||
16 | + m_ObjectHideFlags: 0 | ||
17 | + m_PrefabParentObject: {fileID: 0} | ||
18 | + m_PrefabInternal: {fileID: 100100000} | ||
19 | + serializedVersion: 5 | ||
20 | + m_Component: | ||
21 | + - component: {fileID: 224955737853170496} | ||
22 | + - component: {fileID: 222541766812100524} | ||
23 | + - component: {fileID: 114169395487023046} | ||
24 | + m_Layer: 5 | ||
25 | + m_Name: CommandSuggestion | ||
26 | + m_TagString: Untagged | ||
27 | + m_Icon: {fileID: 0} | ||
28 | + m_NavMeshLayer: 0 | ||
29 | + m_StaticEditorFlags: 0 | ||
30 | + m_IsActive: 1 | ||
31 | +--- !u!114 &114169395487023046 | ||
32 | +MonoBehaviour: | ||
33 | + m_ObjectHideFlags: 1 | ||
34 | + m_PrefabParentObject: {fileID: 0} | ||
35 | + m_PrefabInternal: {fileID: 100100000} | ||
36 | + m_GameObject: {fileID: 1386426139070838} | ||
37 | + m_Enabled: 1 | ||
38 | + m_EditorHideFlags: 0 | ||
39 | + m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} | ||
40 | + m_Name: | ||
41 | + m_EditorClassIdentifier: | ||
42 | + m_Material: {fileID: 0} | ||
43 | + m_Color: {r: 0.83823526, g: 0.84439874, b: 0.84439874, a: 1} | ||
44 | + m_RaycastTarget: 0 | ||
45 | + m_OnCullStateChanged: | ||
46 | + m_PersistentCalls: | ||
47 | + m_Calls: [] | ||
48 | + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, | ||
49 | + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null | ||
50 | + m_FontData: | ||
51 | + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} | ||
52 | + m_FontSize: 16 | ||
53 | + m_FontStyle: 0 | ||
54 | + m_BestFit: 0 | ||
55 | + m_MinSize: 1 | ||
56 | + m_MaxSize: 40 | ||
57 | + m_Alignment: 3 | ||
58 | + m_AlignByGeometry: 0 | ||
59 | + m_RichText: 1 | ||
60 | + m_HorizontalOverflow: 0 | ||
61 | + m_VerticalOverflow: 0 | ||
62 | + m_LineSpacing: 1 | ||
63 | + m_Text: help | ||
64 | +--- !u!222 &222541766812100524 | ||
65 | +CanvasRenderer: | ||
66 | + m_ObjectHideFlags: 1 | ||
67 | + m_PrefabParentObject: {fileID: 0} | ||
68 | + m_PrefabInternal: {fileID: 100100000} | ||
69 | + m_GameObject: {fileID: 1386426139070838} | ||
70 | +--- !u!224 &224955737853170496 | ||
71 | +RectTransform: | ||
72 | + m_ObjectHideFlags: 1 | ||
73 | + m_PrefabParentObject: {fileID: 0} | ||
74 | + m_PrefabInternal: {fileID: 100100000} | ||
75 | + m_GameObject: {fileID: 1386426139070838} | ||
76 | + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | ||
77 | + m_LocalPosition: {x: 0, y: 0, z: 0} | ||
78 | + m_LocalScale: {x: 1, y: 1, z: 1} | ||
79 | + m_Children: [] | ||
80 | + m_Father: {fileID: 0} | ||
81 | + m_RootOrder: 0 | ||
82 | + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} | ||
83 | + m_AnchorMin: {x: 0, y: 0} | ||
84 | + m_AnchorMax: {x: 0, y: 0} | ||
85 | + m_AnchoredPosition: {x: 0, y: 0} | ||
86 | + m_SizeDelta: {x: 0, y: 0} | ||
87 | + m_Pivot: {x: 0.5, y: 0.5} |
1 | +%YAML 1.1 | ||
2 | +%TAG !u! tag:unity3d.com,2011: | ||
3 | +--- !u!1 &104862 | ||
4 | +GameObject: | ||
5 | + m_ObjectHideFlags: 0 | ||
6 | + m_PrefabParentObject: {fileID: 0} | ||
7 | + m_PrefabInternal: {fileID: 100100000} | ||
8 | + serializedVersion: 5 | ||
9 | + m_Component: | ||
10 | + - component: {fileID: 22461494} | ||
11 | + - component: {fileID: 22233942} | ||
12 | + - component: {fileID: 11411806} | ||
13 | + m_Layer: 5 | ||
14 | + m_Name: LogCount | ||
15 | + m_TagString: Untagged | ||
16 | + m_Icon: {fileID: 0} | ||
17 | + m_NavMeshLayer: 0 | ||
18 | + m_StaticEditorFlags: 0 | ||
19 | + m_IsActive: 0 | ||
20 | +--- !u!1 &151462 | ||
21 | +GameObject: | ||
22 | + m_ObjectHideFlags: 1 | ||
23 | + m_PrefabParentObject: {fileID: 0} | ||
24 | + m_PrefabInternal: {fileID: 100100000} | ||
25 | + serializedVersion: 5 | ||
26 | + m_Component: | ||
27 | + - component: {fileID: 22420350} | ||
28 | + - component: {fileID: 22200920} | ||
29 | + - component: {fileID: 11432936} | ||
30 | + m_Layer: 5 | ||
31 | + m_Name: LogCountText | ||
32 | + m_TagString: Untagged | ||
33 | + m_Icon: {fileID: 0} | ||
34 | + m_NavMeshLayer: 0 | ||
35 | + m_StaticEditorFlags: 0 | ||
36 | + m_IsActive: 1 | ||
37 | +--- !u!1 &152362 | ||
38 | +GameObject: | ||
39 | + m_ObjectHideFlags: 0 | ||
40 | + m_PrefabParentObject: {fileID: 0} | ||
41 | + m_PrefabInternal: {fileID: 100100000} | ||
42 | + serializedVersion: 5 | ||
43 | + m_Component: | ||
44 | + - component: {fileID: 22427300} | ||
45 | + - component: {fileID: 22262284} | ||
46 | + - component: {fileID: 11404142} | ||
47 | + m_Layer: 5 | ||
48 | + m_Name: LogType | ||
49 | + m_TagString: Untagged | ||
50 | + m_Icon: {fileID: 0} | ||
51 | + m_NavMeshLayer: 0 | ||
52 | + m_StaticEditorFlags: 0 | ||
53 | + m_IsActive: 1 | ||
54 | +--- !u!1 &166880 | ||
55 | +GameObject: | ||
56 | + m_ObjectHideFlags: 0 | ||
57 | + m_PrefabParentObject: {fileID: 0} | ||
58 | + m_PrefabInternal: {fileID: 100100000} | ||
59 | + serializedVersion: 5 | ||
60 | + m_Component: | ||
61 | + - component: {fileID: 22479264} | ||
62 | + - component: {fileID: 22288988} | ||
63 | + - component: {fileID: 11459012} | ||
64 | + - component: {fileID: 11408050} | ||
65 | + - component: {fileID: 11456372} | ||
66 | + - component: {fileID: 225819852034701160} | ||
67 | + m_Layer: 5 | ||
68 | + m_Name: DebugLogItem | ||
69 | + m_TagString: Untagged | ||
70 | + m_Icon: {fileID: 0} | ||
71 | + m_NavMeshLayer: 0 | ||
72 | + m_StaticEditorFlags: 0 | ||
73 | + m_IsActive: 1 | ||
74 | +--- !u!114 &11404142 | ||
75 | +MonoBehaviour: | ||
76 | + m_ObjectHideFlags: 1 | ||
77 | + m_PrefabParentObject: {fileID: 0} | ||
78 | + m_PrefabInternal: {fileID: 100100000} | ||
79 | + m_GameObject: {fileID: 152362} | ||
80 | + m_Enabled: 1 | ||
81 | + m_EditorHideFlags: 0 | ||
82 | + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} | ||
83 | + m_Name: | ||
84 | + m_EditorClassIdentifier: | ||
85 | + m_Material: {fileID: 0} | ||
86 | + m_Color: {r: 1, g: 1, b: 1, a: 1} | ||
87 | + m_RaycastTarget: 0 | ||
88 | + m_OnCullStateChanged: | ||
89 | + m_PersistentCalls: | ||
90 | + m_Calls: [] | ||
91 | + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, | ||
92 | + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null | ||
93 | + m_Sprite: {fileID: 21300000, guid: 33b115bf5efdfa04d8e2e0b70a6643cd, type: 3} | ||
94 | + m_Type: 0 | ||
95 | + m_PreserveAspect: 0 | ||
96 | + m_FillCenter: 1 | ||
97 | + m_FillMethod: 4 | ||
98 | + m_FillAmount: 1 | ||
99 | + m_FillClockwise: 1 | ||
100 | + m_FillOrigin: 0 | ||
101 | +--- !u!114 &11408050 | ||
102 | +MonoBehaviour: | ||
103 | + m_ObjectHideFlags: 1 | ||
104 | + m_PrefabParentObject: {fileID: 0} | ||
105 | + m_PrefabInternal: {fileID: 100100000} | ||
106 | + m_GameObject: {fileID: 166880} | ||
107 | + m_Enabled: 1 | ||
108 | + m_EditorHideFlags: 0 | ||
109 | + m_Script: {fileID: 11500000, guid: d2ea291be9de70a4abfec595203c96c1, type: 3} | ||
110 | + m_Name: | ||
111 | + m_EditorClassIdentifier: | ||
112 | + transformComponent: {fileID: 22479264} | ||
113 | + imageComponent: {fileID: 11459012} | ||
114 | + canvasGroupComponent: {fileID: 225819852034701160} | ||
115 | + logText: {fileID: 114694493629914950} | ||
116 | + logTypeImage: {fileID: 11404142} | ||
117 | + logCountParent: {fileID: 104862} | ||
118 | + logCountText: {fileID: 11432936} | ||
119 | + copyLogButton: {fileID: 224006190298411330} | ||
120 | +--- !u!114 &11411806 | ||
121 | +MonoBehaviour: | ||
122 | + m_ObjectHideFlags: 1 | ||
123 | + m_PrefabParentObject: {fileID: 0} | ||
124 | + m_PrefabInternal: {fileID: 100100000} | ||
125 | + m_GameObject: {fileID: 104862} | ||
126 | + m_Enabled: 1 | ||
127 | + m_EditorHideFlags: 0 | ||
128 | + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} | ||
129 | + m_Name: | ||
130 | + m_EditorClassIdentifier: | ||
131 | + m_Material: {fileID: 0} | ||
132 | + m_Color: {r: 0.42647058, g: 0.42647058, b: 0.42647058, a: 1} | ||
133 | + m_RaycastTarget: 0 | ||
134 | + m_OnCullStateChanged: | ||
135 | + m_PersistentCalls: | ||
136 | + m_Calls: [] | ||
137 | + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, | ||
138 | + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null | ||
139 | + m_Sprite: {fileID: 21300000, guid: b3f0d976f6d6802479d6465d11b3aa68, type: 3} | ||
140 | + m_Type: 1 | ||
141 | + m_PreserveAspect: 0 | ||
142 | + m_FillCenter: 1 | ||
143 | + m_FillMethod: 4 | ||
144 | + m_FillAmount: 1 | ||
145 | + m_FillClockwise: 1 | ||
146 | + m_FillOrigin: 0 | ||
147 | +--- !u!114 &11432936 | ||
148 | +MonoBehaviour: | ||
149 | + m_ObjectHideFlags: 1 | ||
150 | + m_PrefabParentObject: {fileID: 0} | ||
151 | + m_PrefabInternal: {fileID: 100100000} | ||
152 | + m_GameObject: {fileID: 151462} | ||
153 | + m_Enabled: 1 | ||
154 | + m_EditorHideFlags: 0 | ||
155 | + m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} | ||
156 | + m_Name: | ||
157 | + m_EditorClassIdentifier: | ||
158 | + m_Material: {fileID: 0} | ||
159 | + m_Color: {r: 0.83823526, g: 0.84439874, b: 0.84439874, a: 1} | ||
160 | + m_RaycastTarget: 0 | ||
161 | + m_OnCullStateChanged: | ||
162 | + m_PersistentCalls: | ||
163 | + m_Calls: [] | ||
164 | + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, | ||
165 | + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null | ||
166 | + m_FontData: | ||
167 | + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} | ||
168 | + m_FontSize: 16 | ||
169 | + m_FontStyle: 0 | ||
170 | + m_BestFit: 1 | ||
171 | + m_MinSize: 1 | ||
172 | + m_MaxSize: 16 | ||
173 | + m_Alignment: 4 | ||
174 | + m_AlignByGeometry: 0 | ||
175 | + m_RichText: 1 | ||
176 | + m_HorizontalOverflow: 0 | ||
177 | + m_VerticalOverflow: 0 | ||
178 | + m_LineSpacing: 1 | ||
179 | + m_Text: 1 | ||
180 | +--- !u!114 &11456372 | ||
181 | +MonoBehaviour: | ||
182 | + m_ObjectHideFlags: 1 | ||
183 | + m_PrefabParentObject: {fileID: 0} | ||
184 | + m_PrefabInternal: {fileID: 100100000} | ||
185 | + m_GameObject: {fileID: 166880} | ||
186 | + m_Enabled: 1 | ||
187 | + m_EditorHideFlags: 0 | ||
188 | + m_Script: {fileID: 1392445389, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} | ||
189 | + m_Name: | ||
190 | + m_EditorClassIdentifier: | ||
191 | + m_Navigation: | ||
192 | + m_Mode: 3 | ||
193 | + m_SelectOnUp: {fileID: 0} | ||
194 | + m_SelectOnDown: {fileID: 0} | ||
195 | + m_SelectOnLeft: {fileID: 0} | ||
196 | + m_SelectOnRight: {fileID: 0} | ||
197 | + m_Transition: 1 | ||
198 | + m_Colors: | ||
199 | + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} | ||
200 | + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} | ||
201 | + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} | ||
202 | + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} | ||
203 | + m_ColorMultiplier: 1 | ||
204 | + m_FadeDuration: 0.1 | ||
205 | + m_SpriteState: | ||
206 | + m_HighlightedSprite: {fileID: 0} | ||
207 | + m_PressedSprite: {fileID: 0} | ||
208 | + m_DisabledSprite: {fileID: 0} | ||
209 | + m_AnimationTriggers: | ||
210 | + m_NormalTrigger: Normal | ||
211 | + m_HighlightedTrigger: Highlighted | ||
212 | + m_PressedTrigger: Pressed | ||
213 | + m_DisabledTrigger: Disabled | ||
214 | + m_Interactable: 1 | ||
215 | + m_TargetGraphic: {fileID: 11459012} | ||
216 | + m_OnClick: | ||
217 | + m_PersistentCalls: | ||
218 | + m_Calls: [] | ||
219 | + m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0, | ||
220 | + Culture=neutral, PublicKeyToken=null | ||
221 | +--- !u!114 &11459012 | ||
222 | +MonoBehaviour: | ||
223 | + m_ObjectHideFlags: 1 | ||
224 | + m_PrefabParentObject: {fileID: 0} | ||
225 | + m_PrefabInternal: {fileID: 100100000} | ||
226 | + m_GameObject: {fileID: 166880} | ||
227 | + m_Enabled: 1 | ||
228 | + m_EditorHideFlags: 0 | ||
229 | + m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} | ||
230 | + m_Name: | ||
231 | + m_EditorClassIdentifier: | ||
232 | + m_Material: {fileID: 0} | ||
233 | + m_Color: {r: 0.23529412, g: 0.23529412, b: 0.23529412, a: 0.697} | ||
234 | + m_RaycastTarget: 1 | ||
235 | + m_OnCullStateChanged: | ||
236 | + m_PersistentCalls: | ||
237 | + m_Calls: [] | ||
238 | + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, | ||
239 | + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null | ||
240 | + m_Sprite: {fileID: 21300000, guid: 98e8e1cf8dc7dbf469617c2e40c8a944, type: 3} | ||
241 | + m_Type: 1 | ||
242 | + m_PreserveAspect: 0 | ||
243 | + m_FillCenter: 1 | ||
244 | + m_FillMethod: 4 | ||
245 | + m_FillAmount: 1 | ||
246 | + m_FillClockwise: 1 | ||
247 | + m_FillOrigin: 0 | ||
248 | +--- !u!222 &22200920 | ||
249 | +CanvasRenderer: | ||
250 | + m_ObjectHideFlags: 1 | ||
251 | + m_PrefabParentObject: {fileID: 0} | ||
252 | + m_PrefabInternal: {fileID: 100100000} | ||
253 | + m_GameObject: {fileID: 151462} | ||
254 | +--- !u!222 &22233942 | ||
255 | +CanvasRenderer: | ||
256 | + m_ObjectHideFlags: 1 | ||
257 | + m_PrefabParentObject: {fileID: 0} | ||
258 | + m_PrefabInternal: {fileID: 100100000} | ||
259 | + m_GameObject: {fileID: 104862} | ||
260 | +--- !u!222 &22262284 | ||
261 | +CanvasRenderer: | ||
262 | + m_ObjectHideFlags: 1 | ||
263 | + m_PrefabParentObject: {fileID: 0} | ||
264 | + m_PrefabInternal: {fileID: 100100000} | ||
265 | + m_GameObject: {fileID: 152362} | ||
266 | +--- !u!222 &22288988 | ||
267 | +CanvasRenderer: | ||
268 | + m_ObjectHideFlags: 1 | ||
269 | + m_PrefabParentObject: {fileID: 0} | ||
270 | + m_PrefabInternal: {fileID: 100100000} | ||
271 | + m_GameObject: {fileID: 166880} | ||
272 | +--- !u!224 &22420350 | ||
273 | +RectTransform: | ||
274 | + m_ObjectHideFlags: 1 | ||
275 | + m_PrefabParentObject: {fileID: 0} | ||
276 | + m_PrefabInternal: {fileID: 100100000} | ||
277 | + m_GameObject: {fileID: 151462} | ||
278 | + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | ||
279 | + m_LocalPosition: {x: 0, y: 0, z: 0} | ||
280 | + m_LocalScale: {x: 1, y: 1, z: 1} | ||
281 | + m_Children: [] | ||
282 | + m_Father: {fileID: 22461494} | ||
283 | + m_RootOrder: 0 | ||
284 | + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} | ||
285 | + m_AnchorMin: {x: 0, y: 0} | ||
286 | + m_AnchorMax: {x: 1, y: 1} | ||
287 | + m_AnchoredPosition: {x: 0, y: 0} | ||
288 | + m_SizeDelta: {x: -2, y: 0} | ||
289 | + m_Pivot: {x: 0.5, y: 0.5} | ||
290 | +--- !u!224 &22427300 | ||
291 | +RectTransform: | ||
292 | + m_ObjectHideFlags: 1 | ||
293 | + m_PrefabParentObject: {fileID: 0} | ||
294 | + m_PrefabInternal: {fileID: 100100000} | ||
295 | + m_GameObject: {fileID: 152362} | ||
296 | + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | ||
297 | + m_LocalPosition: {x: 0, y: 0, z: 0} | ||
298 | + m_LocalScale: {x: 1, y: 1, z: 1} | ||
299 | + m_Children: [] | ||
300 | + m_Father: {fileID: 22479264} | ||
301 | + m_RootOrder: 0 | ||
302 | + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} | ||
303 | + m_AnchorMin: {x: 0, y: 0.5} | ||
304 | + m_AnchorMax: {x: 0, y: 0.5} | ||
305 | + m_AnchoredPosition: {x: 18, y: 0} | ||
306 | + m_SizeDelta: {x: 25, y: 25} | ||
307 | + m_Pivot: {x: 0.5, y: 0.5} | ||
308 | +--- !u!224 &22461494 | ||
309 | +RectTransform: | ||
310 | + m_ObjectHideFlags: 1 | ||
311 | + m_PrefabParentObject: {fileID: 0} | ||
312 | + m_PrefabInternal: {fileID: 100100000} | ||
313 | + m_GameObject: {fileID: 104862} | ||
314 | + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | ||
315 | + m_LocalPosition: {x: 0, y: 0, z: 0} | ||
316 | + m_LocalScale: {x: 1, y: 1, z: 1} | ||
317 | + m_Children: | ||
318 | + - {fileID: 22420350} | ||
319 | + m_Father: {fileID: 22479264} | ||
320 | + m_RootOrder: 2 | ||
321 | + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} | ||
322 | + m_AnchorMin: {x: 1, y: 0.5} | ||
323 | + m_AnchorMax: {x: 1, y: 0.5} | ||
324 | + m_AnchoredPosition: {x: -28, y: 0} | ||
325 | + m_SizeDelta: {x: 38, y: 28} | ||
326 | + m_Pivot: {x: 0.5, y: 0.5} | ||
327 | +--- !u!224 &22479264 | ||
328 | +RectTransform: | ||
329 | + m_ObjectHideFlags: 1 | ||
330 | + m_PrefabParentObject: {fileID: 0} | ||
331 | + m_PrefabInternal: {fileID: 100100000} | ||
332 | + m_GameObject: {fileID: 166880} | ||
333 | + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | ||
334 | + m_LocalPosition: {x: 0, y: 0, z: 0} | ||
335 | + m_LocalScale: {x: 1, y: 1, z: 1} | ||
336 | + m_Children: | ||
337 | + - {fileID: 22427300} | ||
338 | + - {fileID: 224737693311518052} | ||
339 | + - {fileID: 22461494} | ||
340 | + - {fileID: 224006190298411330} | ||
341 | + m_Father: {fileID: 0} | ||
342 | + m_RootOrder: 0 | ||
343 | + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} | ||
344 | + m_AnchorMin: {x: 0, y: 1} | ||
345 | + m_AnchorMax: {x: 1, y: 1} | ||
346 | + m_AnchoredPosition: {x: 0, y: 0} | ||
347 | + m_SizeDelta: {x: 0, y: 35} | ||
348 | + m_Pivot: {x: 0, y: 1} | ||
349 | +--- !u!1001 &100100000 | ||
350 | +Prefab: | ||
351 | + m_ObjectHideFlags: 1 | ||
352 | + serializedVersion: 2 | ||
353 | + m_Modification: | ||
354 | + m_TransformParent: {fileID: 0} | ||
355 | + m_Modifications: [] | ||
356 | + m_RemovedComponents: [] | ||
357 | + m_ParentPrefab: {fileID: 0} | ||
358 | + m_RootGameObject: {fileID: 166880} | ||
359 | + m_IsPrefabParent: 1 | ||
360 | +--- !u!1 &1396836967994216 | ||
361 | +GameObject: | ||
362 | + m_ObjectHideFlags: 0 | ||
363 | + m_PrefabParentObject: {fileID: 0} | ||
364 | + m_PrefabInternal: {fileID: 100100000} | ||
365 | + serializedVersion: 5 | ||
366 | + m_Component: | ||
367 | + - component: {fileID: 224006190298411330} | ||
368 | + - component: {fileID: 222870443111501910} | ||
369 | + - component: {fileID: 114119781176956926} | ||
370 | + - component: {fileID: 114694923173451186} | ||
371 | + m_Layer: 5 | ||
372 | + m_Name: CopyLogButton | ||
373 | + m_TagString: Untagged | ||
374 | + m_Icon: {fileID: 0} | ||
375 | + m_NavMeshLayer: 0 | ||
376 | + m_StaticEditorFlags: 0 | ||
377 | + m_IsActive: 0 | ||
378 | +--- !u!1 &1503640463151286 | ||
379 | +GameObject: | ||
380 | + m_ObjectHideFlags: 1 | ||
381 | + m_PrefabParentObject: {fileID: 0} | ||
382 | + m_PrefabInternal: {fileID: 100100000} | ||
383 | + serializedVersion: 5 | ||
384 | + m_Component: | ||
385 | + - component: {fileID: 224887990600088790} | ||
386 | + - component: {fileID: 222313182602304162} | ||
387 | + - component: {fileID: 114549765989288124} | ||
388 | + m_Layer: 5 | ||
389 | + m_Name: Text | ||
390 | + m_TagString: Untagged | ||
391 | + m_Icon: {fileID: 0} | ||
392 | + m_NavMeshLayer: 0 | ||
393 | + m_StaticEditorFlags: 0 | ||
394 | + m_IsActive: 1 | ||
395 | +--- !u!1 &1785910143472904 | ||
396 | +GameObject: | ||
397 | + m_ObjectHideFlags: 0 | ||
398 | + m_PrefabParentObject: {fileID: 0} | ||
399 | + m_PrefabInternal: {fileID: 100100000} | ||
400 | + serializedVersion: 5 | ||
401 | + m_Component: | ||
402 | + - component: {fileID: 224737693311518052} | ||
403 | + - component: {fileID: 222175805939703770} | ||
404 | + - component: {fileID: 114694493629914950} | ||
405 | + m_Layer: 5 | ||
406 | + m_Name: LogText | ||
407 | + m_TagString: Untagged | ||
408 | + m_Icon: {fileID: 0} | ||
409 | + m_NavMeshLayer: 0 | ||
410 | + m_StaticEditorFlags: 0 | ||
411 | + m_IsActive: 1 | ||
412 | +--- !u!114 &114119781176956926 | ||
413 | +MonoBehaviour: | ||
414 | + m_ObjectHideFlags: 1 | ||
415 | + m_PrefabParentObject: {fileID: 0} | ||
416 | + m_PrefabInternal: {fileID: 100100000} | ||
417 | + m_GameObject: {fileID: 1396836967994216} | ||
418 | + m_Enabled: 1 | ||
419 | + m_EditorHideFlags: 0 | ||
420 | + m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} | ||
421 | + m_Name: | ||
422 | + m_EditorClassIdentifier: | ||
423 | + m_Material: {fileID: 0} | ||
424 | + m_Color: {r: 0.42647058, g: 0.42647058, b: 0.42647058, a: 1} | ||
425 | + m_RaycastTarget: 1 | ||
426 | + m_OnCullStateChanged: | ||
427 | + m_PersistentCalls: | ||
428 | + m_Calls: [] | ||
429 | + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, | ||
430 | + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null | ||
431 | + m_Sprite: {fileID: 21300000, guid: 066d3840badf4d24dba1d42b4c59b888, type: 3} | ||
432 | + m_Type: 1 | ||
433 | + m_PreserveAspect: 0 | ||
434 | + m_FillCenter: 1 | ||
435 | + m_FillMethod: 4 | ||
436 | + m_FillAmount: 1 | ||
437 | + m_FillClockwise: 1 | ||
438 | + m_FillOrigin: 0 | ||
439 | +--- !u!114 &114549765989288124 | ||
440 | +MonoBehaviour: | ||
441 | + m_ObjectHideFlags: 1 | ||
442 | + m_PrefabParentObject: {fileID: 0} | ||
443 | + m_PrefabInternal: {fileID: 100100000} | ||
444 | + m_GameObject: {fileID: 1503640463151286} | ||
445 | + m_Enabled: 1 | ||
446 | + m_EditorHideFlags: 0 | ||
447 | + m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} | ||
448 | + m_Name: | ||
449 | + m_EditorClassIdentifier: | ||
450 | + m_Material: {fileID: 0} | ||
451 | + m_Color: {r: 0.83823526, g: 0.84439874, b: 0.84439874, a: 1} | ||
452 | + m_RaycastTarget: 0 | ||
453 | + m_OnCullStateChanged: | ||
454 | + m_PersistentCalls: | ||
455 | + m_Calls: [] | ||
456 | + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, | ||
457 | + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null | ||
458 | + m_FontData: | ||
459 | + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} | ||
460 | + m_FontSize: 16 | ||
461 | + m_FontStyle: 0 | ||
462 | + m_BestFit: 0 | ||
463 | + m_MinSize: 1 | ||
464 | + m_MaxSize: 40 | ||
465 | + m_Alignment: 4 | ||
466 | + m_AlignByGeometry: 0 | ||
467 | + m_RichText: 1 | ||
468 | + m_HorizontalOverflow: 0 | ||
469 | + m_VerticalOverflow: 0 | ||
470 | + m_LineSpacing: 1 | ||
471 | + m_Text: Copy | ||
472 | +--- !u!114 &114694493629914950 | ||
473 | +MonoBehaviour: | ||
474 | + m_ObjectHideFlags: 1 | ||
475 | + m_PrefabParentObject: {fileID: 0} | ||
476 | + m_PrefabInternal: {fileID: 100100000} | ||
477 | + m_GameObject: {fileID: 1785910143472904} | ||
478 | + m_Enabled: 1 | ||
479 | + m_EditorHideFlags: 0 | ||
480 | + m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} | ||
481 | + m_Name: | ||
482 | + m_EditorClassIdentifier: | ||
483 | + m_Material: {fileID: 0} | ||
484 | + m_Color: {r: 0.83823526, g: 0.84439874, b: 0.84439874, a: 1} | ||
485 | + m_RaycastTarget: 0 | ||
486 | + m_OnCullStateChanged: | ||
487 | + m_PersistentCalls: | ||
488 | + m_Calls: [] | ||
489 | + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, | ||
490 | + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null | ||
491 | + m_FontData: | ||
492 | + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} | ||
493 | + m_FontSize: 15 | ||
494 | + m_FontStyle: 0 | ||
495 | + m_BestFit: 0 | ||
496 | + m_MinSize: 1 | ||
497 | + m_MaxSize: 40 | ||
498 | + m_Alignment: 3 | ||
499 | + m_AlignByGeometry: 0 | ||
500 | + m_RichText: 1 | ||
501 | + m_HorizontalOverflow: 1 | ||
502 | + m_VerticalOverflow: 0 | ||
503 | + m_LineSpacing: 1 | ||
504 | + m_Text: Debug.Log summary | ||
505 | +--- !u!114 &114694923173451186 | ||
506 | +MonoBehaviour: | ||
507 | + m_ObjectHideFlags: 1 | ||
508 | + m_PrefabParentObject: {fileID: 0} | ||
509 | + m_PrefabInternal: {fileID: 100100000} | ||
510 | + m_GameObject: {fileID: 1396836967994216} | ||
511 | + m_Enabled: 1 | ||
512 | + m_EditorHideFlags: 0 | ||
513 | + m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} | ||
514 | + m_Name: | ||
515 | + m_EditorClassIdentifier: | ||
516 | + m_Navigation: | ||
517 | + m_Mode: 3 | ||
518 | + m_SelectOnUp: {fileID: 0} | ||
519 | + m_SelectOnDown: {fileID: 0} | ||
520 | + m_SelectOnLeft: {fileID: 0} | ||
521 | + m_SelectOnRight: {fileID: 0} | ||
522 | + m_Transition: 1 | ||
523 | + m_Colors: | ||
524 | + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} | ||
525 | + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} | ||
526 | + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} | ||
527 | + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} | ||
528 | + m_ColorMultiplier: 1 | ||
529 | + m_FadeDuration: 0.1 | ||
530 | + m_SpriteState: | ||
531 | + m_HighlightedSprite: {fileID: 0} | ||
532 | + m_PressedSprite: {fileID: 0} | ||
533 | + m_DisabledSprite: {fileID: 0} | ||
534 | + m_AnimationTriggers: | ||
535 | + m_NormalTrigger: Normal | ||
536 | + m_HighlightedTrigger: Highlighted | ||
537 | + m_PressedTrigger: Pressed | ||
538 | + m_DisabledTrigger: Disabled | ||
539 | + m_Interactable: 1 | ||
540 | + m_TargetGraphic: {fileID: 114119781176956926} | ||
541 | + m_OnClick: | ||
542 | + m_PersistentCalls: | ||
543 | + m_Calls: | ||
544 | + - m_Target: {fileID: 11408050} | ||
545 | + m_MethodName: CopyLog | ||
546 | + m_Mode: 1 | ||
547 | + m_Arguments: | ||
548 | + m_ObjectArgument: {fileID: 0} | ||
549 | + m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine | ||
550 | + m_IntArgument: 0 | ||
551 | + m_FloatArgument: 0 | ||
552 | + m_StringArgument: | ||
553 | + m_BoolArgument: 0 | ||
554 | + m_CallState: 2 | ||
555 | + m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0, | ||
556 | + Culture=neutral, PublicKeyToken=null | ||
557 | +--- !u!222 &222175805939703770 | ||
558 | +CanvasRenderer: | ||
559 | + m_ObjectHideFlags: 1 | ||
560 | + m_PrefabParentObject: {fileID: 0} | ||
561 | + m_PrefabInternal: {fileID: 100100000} | ||
562 | + m_GameObject: {fileID: 1785910143472904} | ||
563 | +--- !u!222 &222313182602304162 | ||
564 | +CanvasRenderer: | ||
565 | + m_ObjectHideFlags: 1 | ||
566 | + m_PrefabParentObject: {fileID: 0} | ||
567 | + m_PrefabInternal: {fileID: 100100000} | ||
568 | + m_GameObject: {fileID: 1503640463151286} | ||
569 | +--- !u!222 &222870443111501910 | ||
570 | +CanvasRenderer: | ||
571 | + m_ObjectHideFlags: 1 | ||
572 | + m_PrefabParentObject: {fileID: 0} | ||
573 | + m_PrefabInternal: {fileID: 100100000} | ||
574 | + m_GameObject: {fileID: 1396836967994216} | ||
575 | +--- !u!224 &224006190298411330 | ||
576 | +RectTransform: | ||
577 | + m_ObjectHideFlags: 1 | ||
578 | + m_PrefabParentObject: {fileID: 0} | ||
579 | + m_PrefabInternal: {fileID: 100100000} | ||
580 | + m_GameObject: {fileID: 1396836967994216} | ||
581 | + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | ||
582 | + m_LocalPosition: {x: 0, y: 0, z: 0} | ||
583 | + m_LocalScale: {x: 1, y: 1, z: 1} | ||
584 | + m_Children: | ||
585 | + - {fileID: 224887990600088790} | ||
586 | + m_Father: {fileID: 22479264} | ||
587 | + m_RootOrder: 3 | ||
588 | + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} | ||
589 | + m_AnchorMin: {x: 0, y: 0} | ||
590 | + m_AnchorMax: {x: 1, y: 0} | ||
591 | + m_AnchoredPosition: {x: 0, y: 2} | ||
592 | + m_SizeDelta: {x: -70, y: 36} | ||
593 | + m_Pivot: {x: 0.5, y: 0} | ||
594 | +--- !u!224 &224737693311518052 | ||
595 | +RectTransform: | ||
596 | + m_ObjectHideFlags: 1 | ||
597 | + m_PrefabParentObject: {fileID: 0} | ||
598 | + m_PrefabInternal: {fileID: 100100000} | ||
599 | + m_GameObject: {fileID: 1785910143472904} | ||
600 | + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | ||
601 | + m_LocalPosition: {x: 0, y: 0, z: 0} | ||
602 | + m_LocalScale: {x: 1, y: 1, z: 1} | ||
603 | + m_Children: [] | ||
604 | + m_Father: {fileID: 22479264} | ||
605 | + m_RootOrder: 1 | ||
606 | + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} | ||
607 | + m_AnchorMin: {x: 0, y: 0} | ||
608 | + m_AnchorMax: {x: 1, y: 1} | ||
609 | + m_AnchoredPosition: {x: 15, y: 0} | ||
610 | + m_SizeDelta: {x: -40, y: -2} | ||
611 | + m_Pivot: {x: 0.5, y: 0.5} | ||
612 | +--- !u!224 &224887990600088790 | ||
613 | +RectTransform: | ||
614 | + m_ObjectHideFlags: 1 | ||
615 | + m_PrefabParentObject: {fileID: 0} | ||
616 | + m_PrefabInternal: {fileID: 100100000} | ||
617 | + m_GameObject: {fileID: 1503640463151286} | ||
618 | + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | ||
619 | + m_LocalPosition: {x: 0, y: 0, z: 0} | ||
620 | + m_LocalScale: {x: 1, y: 1, z: 1} | ||
621 | + m_Children: [] | ||
622 | + m_Father: {fileID: 224006190298411330} | ||
623 | + m_RootOrder: 0 | ||
624 | + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} | ||
625 | + m_AnchorMin: {x: 0, y: 0} | ||
626 | + m_AnchorMax: {x: 1, y: 1} | ||
627 | + m_AnchoredPosition: {x: 0, y: 0} | ||
628 | + m_SizeDelta: {x: 0, y: 0} | ||
629 | + m_Pivot: {x: 0.5, y: 0.5} | ||
630 | +--- !u!225 &225819852034701160 | ||
631 | +CanvasGroup: | ||
632 | + m_ObjectHideFlags: 1 | ||
633 | + m_PrefabParentObject: {fileID: 0} | ||
634 | + m_PrefabInternal: {fileID: 100100000} | ||
635 | + m_GameObject: {fileID: 166880} | ||
636 | + m_Enabled: 1 | ||
637 | + m_Alpha: 1 | ||
638 | + m_Interactable: 1 | ||
639 | + m_BlocksRaycasts: 1 | ||
640 | + m_IgnoreParentGroups: 0 |
Assets/Plugins/IngameDebugConsole/README.txt
0 → 100644
1 | += In-game Debug Console (v1.6.8) = | ||
2 | + | ||
3 | +Documentation: https://github.com/yasirkula/UnityIngameDebugConsole | ||
4 | +FAQ: https://github.com/yasirkula/UnityIngameDebugConsole#faq | ||
5 | +E-mail: yasirkula@gmail.com | ||
6 | + | ||
7 | +You can simply place the IngameDebugConsole prefab to your scene. Hovering the cursor over its properties in the Inspector will reveal explanatory tooltips. |
1 | +using System; | ||
2 | +using UnityEngine; | ||
3 | + | ||
4 | +namespace IngameDebugConsole | ||
5 | +{ | ||
6 | + public class CircularBuffer<T> | ||
7 | + { | ||
8 | + private readonly T[] array; | ||
9 | + private int startIndex; | ||
10 | + | ||
11 | + public int Count { get; private set; } | ||
12 | + public T this[int index] { get { return array[( startIndex + index ) % array.Length]; } } | ||
13 | + | ||
14 | + public CircularBuffer( int capacity ) | ||
15 | + { | ||
16 | + array = new T[capacity]; | ||
17 | + } | ||
18 | + | ||
19 | + // Old elements are overwritten when capacity is reached | ||
20 | + public void Add( T value ) | ||
21 | + { | ||
22 | + if( Count < array.Length ) | ||
23 | + array[Count++] = value; | ||
24 | + else | ||
25 | + { | ||
26 | + array[startIndex] = value; | ||
27 | + if( ++startIndex >= array.Length ) | ||
28 | + startIndex = 0; | ||
29 | + } | ||
30 | + } | ||
31 | + } | ||
32 | + | ||
33 | + public class DynamicCircularBuffer<T> | ||
34 | + { | ||
35 | + private T[] array; | ||
36 | + private int startIndex; | ||
37 | + | ||
38 | + public int Count { get; private set; } | ||
39 | + public int Capacity { get { return array.Length; } } | ||
40 | + | ||
41 | + public T this[int index] | ||
42 | + { | ||
43 | + get { return array[( startIndex + index ) % array.Length]; } | ||
44 | + set { array[( startIndex + index ) % array.Length] = value; } | ||
45 | + } | ||
46 | + | ||
47 | + public DynamicCircularBuffer( int initialCapacity = 2 ) | ||
48 | + { | ||
49 | + array = new T[initialCapacity]; | ||
50 | + } | ||
51 | + | ||
52 | + private void SetCapacity( int capacity ) | ||
53 | + { | ||
54 | + T[] newArray = new T[capacity]; | ||
55 | + if( Count > 0 ) | ||
56 | + { | ||
57 | + int elementsBeforeWrap = Mathf.Min( Count, array.Length - startIndex ); | ||
58 | + Array.Copy( array, startIndex, newArray, 0, elementsBeforeWrap ); | ||
59 | + if( elementsBeforeWrap < Count ) | ||
60 | + Array.Copy( array, 0, newArray, elementsBeforeWrap, Count - elementsBeforeWrap ); | ||
61 | + } | ||
62 | + | ||
63 | + array = newArray; | ||
64 | + startIndex = 0; | ||
65 | + } | ||
66 | + | ||
67 | + /// <summary>Inserts the value to the beginning of the collection.</summary> | ||
68 | + public void AddFirst( T value ) | ||
69 | + { | ||
70 | + if( array.Length == Count ) | ||
71 | + SetCapacity( Mathf.Max( array.Length * 2, 4 ) ); | ||
72 | + | ||
73 | + startIndex = ( startIndex > 0 ) ? ( startIndex - 1 ) : ( array.Length - 1 ); | ||
74 | + array[startIndex] = value; | ||
75 | + Count++; | ||
76 | + } | ||
77 | + | ||
78 | + /// <summary>Adds the value to the end of the collection.</summary> | ||
79 | + public void Add( T value ) | ||
80 | + { | ||
81 | + if( array.Length == Count ) | ||
82 | + SetCapacity( Mathf.Max( array.Length * 2, 4 ) ); | ||
83 | + | ||
84 | + this[Count++] = value; | ||
85 | + } | ||
86 | + | ||
87 | + public void AddRange( DynamicCircularBuffer<T> other ) | ||
88 | + { | ||
89 | + if( other.Count == 0 ) | ||
90 | + return; | ||
91 | + | ||
92 | + if( array.Length < Count + other.Count ) | ||
93 | + SetCapacity( Mathf.Max( array.Length * 2, Count + other.Count ) ); | ||
94 | + | ||
95 | + int insertStartIndex = ( startIndex + Count ) % array.Length; | ||
96 | + int elementsBeforeWrap = Mathf.Min( other.Count, array.Length - insertStartIndex ); | ||
97 | + int otherElementsBeforeWrap = Mathf.Min( other.Count, other.array.Length - other.startIndex ); | ||
98 | + | ||
99 | + Array.Copy( other.array, other.startIndex, array, insertStartIndex, Mathf.Min( elementsBeforeWrap, otherElementsBeforeWrap ) ); | ||
100 | + if( elementsBeforeWrap < otherElementsBeforeWrap ) // This array wrapped before the other array | ||
101 | + Array.Copy( other.array, other.startIndex + elementsBeforeWrap, array, 0, otherElementsBeforeWrap - elementsBeforeWrap ); | ||
102 | + else if( elementsBeforeWrap > otherElementsBeforeWrap ) // The other array wrapped before this array | ||
103 | + Array.Copy( other.array, 0, array, insertStartIndex + otherElementsBeforeWrap, elementsBeforeWrap - otherElementsBeforeWrap ); | ||
104 | + | ||
105 | + int copiedElements = Mathf.Max( elementsBeforeWrap, otherElementsBeforeWrap ); | ||
106 | + if( copiedElements < other.Count ) // Both arrays wrapped and there's still some elements left to copy | ||
107 | + Array.Copy( other.array, copiedElements - otherElementsBeforeWrap, array, copiedElements - elementsBeforeWrap, other.Count - copiedElements ); | ||
108 | + | ||
109 | + Count += other.Count; | ||
110 | + } | ||
111 | + | ||
112 | + public T RemoveFirst() | ||
113 | + { | ||
114 | + T element = array[startIndex]; | ||
115 | + array[startIndex] = default( T ); | ||
116 | + | ||
117 | + if( ++startIndex == array.Length ) | ||
118 | + startIndex = 0; | ||
119 | + | ||
120 | + Count--; | ||
121 | + return element; | ||
122 | + } | ||
123 | + | ||
124 | + public T RemoveLast() | ||
125 | + { | ||
126 | + int index = ( startIndex + Count - 1 ) % array.Length; | ||
127 | + T element = array[index]; | ||
128 | + array[index] = default( T ); | ||
129 | + | ||
130 | + Count--; | ||
131 | + return element; | ||
132 | + } | ||
133 | + | ||
134 | + public int RemoveAll( Predicate<T> shouldRemoveElement ) | ||
135 | + { | ||
136 | + return RemoveAll<T>( shouldRemoveElement, null, null ); | ||
137 | + } | ||
138 | + | ||
139 | + public int RemoveAll<Y>( Predicate<T> shouldRemoveElement, Action<T, int> onElementIndexChanged, DynamicCircularBuffer<Y> synchronizedBuffer ) | ||
140 | + { | ||
141 | + Y[] synchronizedArray = ( synchronizedBuffer != null ) ? synchronizedBuffer.array : null; | ||
142 | + int elementsBeforeWrap = Mathf.Min( Count, array.Length - startIndex ); | ||
143 | + int removedElements = 0; | ||
144 | + int i = startIndex, newIndex = startIndex, endIndex = startIndex + elementsBeforeWrap; | ||
145 | + for( ; i < endIndex; i++ ) | ||
146 | + { | ||
147 | + if( shouldRemoveElement( array[i] ) ) | ||
148 | + removedElements++; | ||
149 | + else | ||
150 | + { | ||
151 | + if( removedElements > 0 ) | ||
152 | + { | ||
153 | + T element = array[i]; | ||
154 | + array[newIndex] = element; | ||
155 | + | ||
156 | + if( synchronizedArray != null ) | ||
157 | + synchronizedArray[newIndex] = synchronizedArray[i]; | ||
158 | + | ||
159 | + if( onElementIndexChanged != null ) | ||
160 | + onElementIndexChanged( element, newIndex - startIndex ); | ||
161 | + } | ||
162 | + | ||
163 | + newIndex++; | ||
164 | + } | ||
165 | + } | ||
166 | + | ||
167 | + i = 0; | ||
168 | + endIndex = Count - elementsBeforeWrap; | ||
169 | + | ||
170 | + if( newIndex < array.Length ) | ||
171 | + { | ||
172 | + for( ; i < endIndex; i++ ) | ||
173 | + { | ||
174 | + if( shouldRemoveElement( array[i] ) ) | ||
175 | + removedElements++; | ||
176 | + else | ||
177 | + { | ||
178 | + T element = array[i]; | ||
179 | + array[newIndex] = element; | ||
180 | + | ||
181 | + if( synchronizedArray != null ) | ||
182 | + synchronizedArray[newIndex] = synchronizedArray[i]; | ||
183 | + | ||
184 | + if( onElementIndexChanged != null ) | ||
185 | + onElementIndexChanged( element, newIndex - startIndex ); | ||
186 | + | ||
187 | + if( ++newIndex == array.Length ) | ||
188 | + { | ||
189 | + i++; | ||
190 | + break; | ||
191 | + } | ||
192 | + } | ||
193 | + } | ||
194 | + } | ||
195 | + | ||
196 | + if( newIndex == array.Length ) | ||
197 | + { | ||
198 | + newIndex = 0; | ||
199 | + for( ; i < endIndex; i++ ) | ||
200 | + { | ||
201 | + if( shouldRemoveElement( array[i] ) ) | ||
202 | + removedElements++; | ||
203 | + else | ||
204 | + { | ||
205 | + if( removedElements > 0 ) | ||
206 | + { | ||
207 | + T element = array[i]; | ||
208 | + array[newIndex] = element; | ||
209 | + | ||
210 | + if( synchronizedArray != null ) | ||
211 | + synchronizedArray[newIndex] = synchronizedArray[i]; | ||
212 | + | ||
213 | + if( onElementIndexChanged != null ) | ||
214 | + onElementIndexChanged( element, newIndex + elementsBeforeWrap ); | ||
215 | + } | ||
216 | + | ||
217 | + newIndex++; | ||
218 | + } | ||
219 | + } | ||
220 | + } | ||
221 | + | ||
222 | + TrimEnd( removedElements ); | ||
223 | + if( synchronizedBuffer != null ) | ||
224 | + synchronizedBuffer.TrimEnd( removedElements ); | ||
225 | + | ||
226 | + return removedElements; | ||
227 | + } | ||
228 | + | ||
229 | + public void TrimStart( int trimCount, Action<T> perElementCallback = null ) | ||
230 | + { | ||
231 | + TrimInternal( trimCount, startIndex, perElementCallback ); | ||
232 | + startIndex = ( startIndex + trimCount ) % array.Length; | ||
233 | + } | ||
234 | + | ||
235 | + public void TrimEnd( int trimCount, Action<T> perElementCallback = null ) | ||
236 | + { | ||
237 | + TrimInternal( trimCount, ( startIndex + Count - trimCount ) % array.Length, perElementCallback ); | ||
238 | + } | ||
239 | + | ||
240 | + private void TrimInternal( int trimCount, int startIndex, Action<T> perElementCallback ) | ||
241 | + { | ||
242 | + int elementsBeforeWrap = Mathf.Min( trimCount, array.Length - startIndex ); | ||
243 | + if( perElementCallback == null ) | ||
244 | + { | ||
245 | + Array.Clear( array, startIndex, elementsBeforeWrap ); | ||
246 | + if( elementsBeforeWrap < trimCount ) | ||
247 | + Array.Clear( array, 0, trimCount - elementsBeforeWrap ); | ||
248 | + } | ||
249 | + else | ||
250 | + { | ||
251 | + for( int i = startIndex, endIndex = startIndex + elementsBeforeWrap; i < endIndex; i++ ) | ||
252 | + { | ||
253 | + perElementCallback( array[i] ); | ||
254 | + array[i] = default( T ); | ||
255 | + } | ||
256 | + | ||
257 | + for( int i = 0, endIndex = trimCount - elementsBeforeWrap; i < endIndex; i++ ) | ||
258 | + { | ||
259 | + perElementCallback( array[i] ); | ||
260 | + array[i] = default( T ); | ||
261 | + } | ||
262 | + } | ||
263 | + | ||
264 | + Count -= trimCount; | ||
265 | + } | ||
266 | + | ||
267 | + public void Clear() | ||
268 | + { | ||
269 | + int elementsBeforeWrap = Mathf.Min( Count, array.Length - startIndex ); | ||
270 | + Array.Clear( array, startIndex, elementsBeforeWrap ); | ||
271 | + if( elementsBeforeWrap < Count ) | ||
272 | + Array.Clear( array, 0, Count - elementsBeforeWrap ); | ||
273 | + | ||
274 | + startIndex = 0; | ||
275 | + Count = 0; | ||
276 | + } | ||
277 | + | ||
278 | + public int IndexOf( T value ) | ||
279 | + { | ||
280 | + int elementsBeforeWrap = Mathf.Min( Count, array.Length - startIndex ); | ||
281 | + int index = Array.IndexOf( array, value, startIndex, elementsBeforeWrap ); | ||
282 | + if( index >= 0 ) | ||
283 | + return index - startIndex; | ||
284 | + | ||
285 | + if( elementsBeforeWrap < Count ) | ||
286 | + { | ||
287 | + index = Array.IndexOf( array, value, 0, Count - elementsBeforeWrap ); | ||
288 | + if( index >= 0 ) | ||
289 | + return index + elementsBeforeWrap; | ||
290 | + } | ||
291 | + | ||
292 | + return -1; | ||
293 | + } | ||
294 | + | ||
295 | + public void ForEach( Action<T> action ) | ||
296 | + { | ||
297 | + int elementsBeforeWrap = Mathf.Min( Count, array.Length - startIndex ); | ||
298 | + for( int i = startIndex, endIndex = startIndex + elementsBeforeWrap; i < endIndex; i++ ) | ||
299 | + action( array[i] ); | ||
300 | + for( int i = 0, endIndex = Count - elementsBeforeWrap; i < endIndex; i++ ) | ||
301 | + action( array[i] ); | ||
302 | + } | ||
303 | + } | ||
304 | +} |
1 | +#if IDG_ENABLE_HELPER_COMMANDS | ||
2 | +using UnityEngine; | ||
3 | + | ||
4 | +namespace IngameDebugConsole.Commands | ||
5 | +{ | ||
6 | + public class PlayerPrefsCommands | ||
7 | + { | ||
8 | + [ConsoleMethod( "prefs.int", "Returns the value of an Integer PlayerPrefs field" ), UnityEngine.Scripting.Preserve] | ||
9 | + public static string PlayerPrefsGetInt( string key ) | ||
10 | + { | ||
11 | + if( !PlayerPrefs.HasKey( key ) ) return "Key Not Found"; | ||
12 | + return PlayerPrefs.GetInt( key ).ToString(); | ||
13 | + } | ||
14 | + | ||
15 | + [ConsoleMethod( "prefs.int", "Sets the value of an Integer PlayerPrefs field" ), UnityEngine.Scripting.Preserve] | ||
16 | + public static void PlayerPrefsSetInt( string key, int value ) | ||
17 | + { | ||
18 | + PlayerPrefs.SetInt( key, value ); | ||
19 | + } | ||
20 | + | ||
21 | + [ConsoleMethod( "prefs.float", "Returns the value of a Float PlayerPrefs field" ), UnityEngine.Scripting.Preserve] | ||
22 | + public static string PlayerPrefsGetFloat( string key ) | ||
23 | + { | ||
24 | + if( !PlayerPrefs.HasKey( key ) ) return "Key Not Found"; | ||
25 | + return PlayerPrefs.GetFloat( key ).ToString(); | ||
26 | + } | ||
27 | + | ||
28 | + [ConsoleMethod( "prefs.float", "Sets the value of a Float PlayerPrefs field" ), UnityEngine.Scripting.Preserve] | ||
29 | + public static void PlayerPrefsSetFloat( string key, float value ) | ||
30 | + { | ||
31 | + PlayerPrefs.SetFloat( key, value ); | ||
32 | + } | ||
33 | + | ||
34 | + [ConsoleMethod( "prefs.string", "Returns the value of a String PlayerPrefs field" ), UnityEngine.Scripting.Preserve] | ||
35 | + public static string PlayerPrefsGetString( string key ) | ||
36 | + { | ||
37 | + if( !PlayerPrefs.HasKey( key ) ) return "Key Not Found"; | ||
38 | + return PlayerPrefs.GetString( key ); | ||
39 | + } | ||
40 | + | ||
41 | + [ConsoleMethod( "prefs.string", "Sets the value of a String PlayerPrefs field" ), UnityEngine.Scripting.Preserve] | ||
42 | + public static void PlayerPrefsSetString( string key, string value ) | ||
43 | + { | ||
44 | + PlayerPrefs.SetString( key, value ); | ||
45 | + } | ||
46 | + | ||
47 | + [ConsoleMethod( "prefs.delete", "Deletes a PlayerPrefs field" ), UnityEngine.Scripting.Preserve] | ||
48 | + public static void PlayerPrefsDelete( string key ) | ||
49 | + { | ||
50 | + PlayerPrefs.DeleteKey( key ); | ||
51 | + } | ||
52 | + | ||
53 | + [ConsoleMethod( "prefs.clear", "Deletes all PlayerPrefs fields" ), UnityEngine.Scripting.Preserve] | ||
54 | + public static void PlayerPrefsClear() | ||
55 | + { | ||
56 | + PlayerPrefs.DeleteAll(); | ||
57 | + } | ||
58 | + } | ||
59 | +} | ||
60 | +#endif |
1 | +#if IDG_ENABLE_HELPER_COMMANDS | ||
2 | +using UnityEngine; | ||
3 | +using UnityEngine.SceneManagement; | ||
4 | + | ||
5 | +namespace IngameDebugConsole.Commands | ||
6 | +{ | ||
7 | + public class SceneCommands | ||
8 | + { | ||
9 | + [ConsoleMethod( "scene.load", "Loads a scene" ), UnityEngine.Scripting.Preserve] | ||
10 | + public static void LoadScene( string sceneName ) | ||
11 | + { | ||
12 | + LoadSceneInternal( sceneName, false, LoadSceneMode.Single ); | ||
13 | + } | ||
14 | + | ||
15 | + [ConsoleMethod( "scene.load", "Loads a scene" ), UnityEngine.Scripting.Preserve] | ||
16 | + public static void LoadScene( string sceneName, LoadSceneMode mode ) | ||
17 | + { | ||
18 | + LoadSceneInternal( sceneName, false, mode ); | ||
19 | + } | ||
20 | + | ||
21 | + [ConsoleMethod( "scene.loadasync", "Loads a scene asynchronously" ), UnityEngine.Scripting.Preserve] | ||
22 | + public static void LoadSceneAsync( string sceneName ) | ||
23 | + { | ||
24 | + LoadSceneInternal( sceneName, true, LoadSceneMode.Single ); | ||
25 | + } | ||
26 | + | ||
27 | + [ConsoleMethod( "scene.loadasync", "Loads a scene asynchronously" ), UnityEngine.Scripting.Preserve] | ||
28 | + public static void LoadSceneAsync( string sceneName, LoadSceneMode mode ) | ||
29 | + { | ||
30 | + LoadSceneInternal( sceneName, true, mode ); | ||
31 | + } | ||
32 | + | ||
33 | + private static void LoadSceneInternal( string sceneName, bool isAsync, LoadSceneMode mode ) | ||
34 | + { | ||
35 | + if( SceneManager.GetSceneByName( sceneName ).IsValid() ) | ||
36 | + { | ||
37 | + Debug.Log( "Scene " + sceneName + " is already loaded" ); | ||
38 | + return; | ||
39 | + } | ||
40 | + | ||
41 | + if( isAsync ) | ||
42 | + SceneManager.LoadSceneAsync( sceneName, mode ); | ||
43 | + else | ||
44 | + SceneManager.LoadScene( sceneName, mode ); | ||
45 | + } | ||
46 | + | ||
47 | + [ConsoleMethod( "scene.unload", "Unloads a scene" ), UnityEngine.Scripting.Preserve] | ||
48 | + public static void UnloadScene( string sceneName ) | ||
49 | + { | ||
50 | + SceneManager.UnloadSceneAsync( sceneName ); | ||
51 | + } | ||
52 | + | ||
53 | + [ConsoleMethod( "scene.restart", "Restarts the active scene" ), UnityEngine.Scripting.Preserve] | ||
54 | + public static void RestartScene() | ||
55 | + { | ||
56 | + SceneManager.LoadScene( SceneManager.GetActiveScene().name, LoadSceneMode.Single ); | ||
57 | + } | ||
58 | + } | ||
59 | +} | ||
60 | +#endif |
1 | +#if IDG_ENABLE_HELPER_COMMANDS | ||
2 | +using UnityEngine; | ||
3 | + | ||
4 | +namespace IngameDebugConsole.Commands | ||
5 | +{ | ||
6 | + public class TimeCommands | ||
7 | + { | ||
8 | + [ConsoleMethod( "time.scale", "Sets the Time.timeScale value" ), UnityEngine.Scripting.Preserve] | ||
9 | + public static void SetTimeScale( float value ) | ||
10 | + { | ||
11 | + Time.timeScale = Mathf.Max( value, 0f ); | ||
12 | + } | ||
13 | + | ||
14 | + [ConsoleMethod( "time.scale", "Returns the current Time.timeScale value" ), UnityEngine.Scripting.Preserve] | ||
15 | + public static float GetTimeScale() | ||
16 | + { | ||
17 | + return Time.timeScale; | ||
18 | + } | ||
19 | + } | ||
20 | +} | ||
21 | +#endif |
1 | +using System; | ||
2 | + | ||
3 | +namespace IngameDebugConsole | ||
4 | +{ | ||
5 | + [AttributeUsage( AttributeTargets.Method, Inherited = false, AllowMultiple = true )] | ||
6 | + public class ConsoleMethodAttribute : Attribute | ||
7 | + { | ||
8 | + private string m_command; | ||
9 | + private string m_description; | ||
10 | + private string[] m_parameterNames; | ||
11 | + | ||
12 | + public string Command { get { return m_command; } } | ||
13 | + public string Description { get { return m_description; } } | ||
14 | + public string[] ParameterNames { get { return m_parameterNames; } } | ||
15 | + | ||
16 | + public ConsoleMethodAttribute( string command, string description, params string[] parameterNames ) | ||
17 | + { | ||
18 | + m_command = command; | ||
19 | + m_description = description; | ||
20 | + m_parameterNames = parameterNames; | ||
21 | + } | ||
22 | + } | ||
23 | +} |
1 | +#if UNITY_EDITOR || UNITY_STANDALONE | ||
2 | +// Unity's Text component doesn't render <b> tag correctly on mobile devices | ||
3 | +#define USE_BOLD_COMMAND_SIGNATURES | ||
4 | +#endif | ||
5 | + | ||
6 | +using UnityEngine; | ||
7 | +using System; | ||
8 | +using System.Collections; | ||
9 | +using System.Collections.Generic; | ||
10 | +using System.Globalization; | ||
11 | +using System.Reflection; | ||
12 | +using System.Text; | ||
13 | +using Object = UnityEngine.Object; | ||
14 | +#if UNITY_EDITOR && UNITY_2021_1_OR_NEWER | ||
15 | +using SystemInfo = UnityEngine.Device.SystemInfo; // To support Device Simulator on Unity 2021.1+ | ||
16 | +#endif | ||
17 | + | ||
18 | +// Manages the console commands, parses console input and handles execution of commands | ||
19 | +// Supported method parameter types: int, float, bool, string, Vector2, Vector3, Vector4 | ||
20 | + | ||
21 | +// Helper class to store important information about a command | ||
22 | +namespace IngameDebugConsole | ||
23 | +{ | ||
24 | + public class ConsoleMethodInfo | ||
25 | + { | ||
26 | + public readonly MethodInfo method; | ||
27 | + public readonly Type[] parameterTypes; | ||
28 | + public readonly object instance; | ||
29 | + | ||
30 | + public readonly string command; | ||
31 | + public readonly string signature; | ||
32 | + public readonly string[] parameters; | ||
33 | + | ||
34 | + public ConsoleMethodInfo( MethodInfo method, Type[] parameterTypes, object instance, string command, string signature, string[] parameters ) | ||
35 | + { | ||
36 | + this.method = method; | ||
37 | + this.parameterTypes = parameterTypes; | ||
38 | + this.instance = instance; | ||
39 | + this.command = command; | ||
40 | + this.signature = signature; | ||
41 | + this.parameters = parameters; | ||
42 | + } | ||
43 | + | ||
44 | + public bool IsValid() | ||
45 | + { | ||
46 | + if( !method.IsStatic && ( instance == null || instance.Equals( null ) ) ) | ||
47 | + return false; | ||
48 | + | ||
49 | + return true; | ||
50 | + } | ||
51 | + } | ||
52 | + | ||
53 | + public static class DebugLogConsole | ||
54 | + { | ||
55 | + public delegate bool ParseFunction( string input, out object output ); | ||
56 | + | ||
57 | + public delegate void CommandExecutedDelegate( string command, object[] parameters ); | ||
58 | + public static event CommandExecutedDelegate OnCommandExecuted; | ||
59 | + | ||
60 | + // All the commands | ||
61 | + private static readonly List<ConsoleMethodInfo> methods = new List<ConsoleMethodInfo>(); | ||
62 | + private static readonly List<ConsoleMethodInfo> matchingMethods = new List<ConsoleMethodInfo>( 4 ); | ||
63 | + | ||
64 | + // All the parse functions | ||
65 | + private static readonly Dictionary<Type, ParseFunction> parseFunctions = new Dictionary<Type, ParseFunction>() | ||
66 | + { | ||
67 | + { typeof( string ), ParseString }, | ||
68 | + { typeof( bool ), ParseBool }, | ||
69 | + { typeof( int ), ParseInt }, | ||
70 | + { typeof( uint ), ParseUInt }, | ||
71 | + { typeof( long ), ParseLong }, | ||
72 | + { typeof( ulong ), ParseULong }, | ||
73 | + { typeof( byte ), ParseByte }, | ||
74 | + { typeof( sbyte ), ParseSByte }, | ||
75 | + { typeof( short ), ParseShort }, | ||
76 | + { typeof( ushort ), ParseUShort }, | ||
77 | + { typeof( char ), ParseChar }, | ||
78 | + { typeof( float ), ParseFloat }, | ||
79 | + { typeof( double ), ParseDouble }, | ||
80 | + { typeof( decimal ), ParseDecimal }, | ||
81 | + { typeof( Vector2 ), ParseVector2 }, | ||
82 | + { typeof( Vector3 ), ParseVector3 }, | ||
83 | + { typeof( Vector4 ), ParseVector4 }, | ||
84 | + { typeof( Quaternion ), ParseQuaternion }, | ||
85 | + { typeof( Color ), ParseColor }, | ||
86 | + { typeof( Color32 ), ParseColor32 }, | ||
87 | + { typeof( Rect ), ParseRect }, | ||
88 | + { typeof( RectOffset ), ParseRectOffset }, | ||
89 | + { typeof( Bounds ), ParseBounds }, | ||
90 | + { typeof( GameObject ), ParseGameObject }, | ||
91 | +#if UNITY_2017_2_OR_NEWER | ||
92 | + { typeof( Vector2Int ), ParseVector2Int }, | ||
93 | + { typeof( Vector3Int ), ParseVector3Int }, | ||
94 | + { typeof( RectInt ), ParseRectInt }, | ||
95 | + { typeof( BoundsInt ), ParseBoundsInt }, | ||
96 | +#endif | ||
97 | + }; | ||
98 | + | ||
99 | + // All the readable names of accepted types | ||
100 | + private static readonly Dictionary<Type, string> typeReadableNames = new Dictionary<Type, string>() | ||
101 | + { | ||
102 | + { typeof( string ), "String" }, | ||
103 | + { typeof( bool ), "Boolean" }, | ||
104 | + { typeof( int ), "Integer" }, | ||
105 | + { typeof( uint ), "Unsigned Integer" }, | ||
106 | + { typeof( long ), "Long" }, | ||
107 | + { typeof( ulong ), "Unsigned Long" }, | ||
108 | + { typeof( byte ), "Byte" }, | ||
109 | + { typeof( sbyte ), "Short Byte" }, | ||
110 | + { typeof( short ), "Short" }, | ||
111 | + { typeof( ushort ), "Unsigned Short" }, | ||
112 | + { typeof( char ), "Char" }, | ||
113 | + { typeof( float ), "Float" }, | ||
114 | + { typeof( double ), "Double" }, | ||
115 | + { typeof( decimal ), "Decimal" } | ||
116 | + }; | ||
117 | + | ||
118 | + // Split arguments of an entered command | ||
119 | + private static readonly List<string> commandArguments = new List<string>( 8 ); | ||
120 | + | ||
121 | + // Command parameter delimeter groups | ||
122 | + private static readonly string[] inputDelimiters = new string[] { "\"\"", "''", "{}", "()", "[]" }; | ||
123 | + | ||
124 | + // CompareInfo used for case-insensitive command name comparison | ||
125 | + internal static readonly CompareInfo caseInsensitiveComparer = new CultureInfo( "en-US" ).CompareInfo; | ||
126 | + | ||
127 | + static DebugLogConsole() | ||
128 | + { | ||
129 | +#if !IDG_DISABLE_HELP_COMMAND | ||
130 | + AddCommand( "help", "Prints all commands", LogAllCommands ); | ||
131 | + AddCommand<string>( "help", "Prints all matching commands", LogAllCommandsWithName ); | ||
132 | +#endif | ||
133 | +#if IDG_ENABLE_HELPER_COMMANDS || IDG_ENABLE_SYSINFO_COMMAND | ||
134 | + AddCommand( "sysinfo", "Prints system information", LogSystemInfo ); | ||
135 | +#endif | ||
136 | + | ||
137 | +#if UNITY_EDITOR || !NETFX_CORE | ||
138 | + // Find all [ConsoleMethod] functions | ||
139 | + // Don't search built-in assemblies for console methods since they can't have any | ||
140 | + string[] ignoredAssemblies = new string[] | ||
141 | + { | ||
142 | + "Unity", | ||
143 | + "System", | ||
144 | + "Mono.", | ||
145 | + "mscorlib", | ||
146 | + "netstandard", | ||
147 | + "TextMeshPro", | ||
148 | + "Microsoft.GeneratedCode", | ||
149 | + "I18N", | ||
150 | + "Boo.", | ||
151 | + "UnityScript.", | ||
152 | + "ICSharpCode.", | ||
153 | + "ExCSS.Unity", | ||
154 | +#if UNITY_EDITOR | ||
155 | + "Assembly-CSharp-Editor", | ||
156 | + "Assembly-UnityScript-Editor", | ||
157 | + "nunit.", | ||
158 | + "SyntaxTree.", | ||
159 | + "AssetStoreTools", | ||
160 | +#endif | ||
161 | + }; | ||
162 | +#endif | ||
163 | + | ||
164 | +#if UNITY_EDITOR || !NETFX_CORE | ||
165 | + foreach( Assembly assembly in AppDomain.CurrentDomain.GetAssemblies() ) | ||
166 | +#else | ||
167 | + foreach( Assembly assembly in new Assembly[] { typeof( DebugLogConsole ).Assembly } ) // On UWP, at least search this plugin's Assembly for console methods | ||
168 | +#endif | ||
169 | + { | ||
170 | +#if( NET_4_6 || NET_STANDARD_2_0 ) && ( UNITY_EDITOR || !NETFX_CORE ) | ||
171 | + if( assembly.IsDynamic ) | ||
172 | + continue; | ||
173 | +#endif | ||
174 | + | ||
175 | + | ||
176 | +#if UNITY_EDITOR || !NETFX_CORE | ||
177 | + string assemblyName = assembly.GetName().Name; | ||
178 | + bool ignoreAssembly = false; | ||
179 | + for( int i = 0; i < ignoredAssemblies.Length; i++ ) | ||
180 | + { | ||
181 | + if( caseInsensitiveComparer.IsPrefix( assemblyName, ignoredAssemblies[i], CompareOptions.IgnoreCase ) ) | ||
182 | + { | ||
183 | + ignoreAssembly = true; | ||
184 | + break; | ||
185 | + } | ||
186 | + } | ||
187 | + | ||
188 | + if( ignoreAssembly ) | ||
189 | + continue; | ||
190 | +#endif | ||
191 | + | ||
192 | + SearchAssemblyForConsoleMethods( assembly ); | ||
193 | + } | ||
194 | + } | ||
195 | + | ||
196 | + public static void SearchAssemblyForConsoleMethods( Assembly assembly ) | ||
197 | + { | ||
198 | + try | ||
199 | + { | ||
200 | + foreach( Type type in assembly.GetExportedTypes() ) | ||
201 | + { | ||
202 | + foreach( MethodInfo method in type.GetMethods( BindingFlags.Static | BindingFlags.Public | BindingFlags.DeclaredOnly ) ) | ||
203 | + { | ||
204 | + foreach( object attribute in method.GetCustomAttributes( typeof( ConsoleMethodAttribute ), false ) ) | ||
205 | + { | ||
206 | + ConsoleMethodAttribute consoleMethod = attribute as ConsoleMethodAttribute; | ||
207 | + if( consoleMethod != null ) | ||
208 | + AddCommand( consoleMethod.Command, consoleMethod.Description, method, null, consoleMethod.ParameterNames ); | ||
209 | + } | ||
210 | + } | ||
211 | + } | ||
212 | + } | ||
213 | + catch( NotSupportedException ) { } | ||
214 | + catch( System.IO.FileNotFoundException ) { } | ||
215 | + catch( ReflectionTypeLoadException ) { } | ||
216 | + catch( Exception e ) | ||
217 | + { | ||
218 | + Debug.LogError( "Couldn't search assembly for [ConsoleMethod] attributes: " + assembly.GetName().Name + "\n" + e.ToString() ); | ||
219 | + } | ||
220 | + } | ||
221 | + | ||
222 | + public static List<ConsoleMethodInfo> GetAllCommands() | ||
223 | + { | ||
224 | + return methods; | ||
225 | + } | ||
226 | + | ||
227 | + // Logs the list of available commands | ||
228 | + public static void LogAllCommands() | ||
229 | + { | ||
230 | + int length = 25; | ||
231 | + for( int i = 0; i < methods.Count; i++ ) | ||
232 | + { | ||
233 | + if( methods[i].IsValid() ) | ||
234 | + length += methods[i].signature.Length + 7; | ||
235 | + } | ||
236 | + | ||
237 | + StringBuilder stringBuilder = new StringBuilder( length ); | ||
238 | + stringBuilder.Append( "Available commands:" ); | ||
239 | + | ||
240 | + for( int i = 0; i < methods.Count; i++ ) | ||
241 | + { | ||
242 | + if( methods[i].IsValid() ) | ||
243 | + stringBuilder.Append( "\n - " ).Append( methods[i].signature ); | ||
244 | + } | ||
245 | + | ||
246 | + Debug.Log( stringBuilder.ToString() ); | ||
247 | + | ||
248 | + // After typing help, the log that lists all the commands should automatically be expanded for better UX | ||
249 | + if( DebugLogManager.Instance ) | ||
250 | + DebugLogManager.Instance.AdjustLatestPendingLog( true, true ); | ||
251 | + } | ||
252 | + | ||
253 | + // Logs the list of available commands that are either equal to commandName or contain commandName as substring | ||
254 | + public static void LogAllCommandsWithName( string commandName ) | ||
255 | + { | ||
256 | + matchingMethods.Clear(); | ||
257 | + | ||
258 | + // First, try to find commands that exactly match the commandName. If there are no such commands, try to find | ||
259 | + // commands that contain commandName as substring | ||
260 | + FindCommands( commandName, false, matchingMethods ); | ||
261 | + if( matchingMethods.Count == 0 ) | ||
262 | + FindCommands( commandName, true, matchingMethods ); | ||
263 | + | ||
264 | + if( matchingMethods.Count == 0 ) | ||
265 | + Debug.LogWarning( string.Concat( "ERROR: can't find command '", commandName, "'" ) ); | ||
266 | + else | ||
267 | + { | ||
268 | + int commandsLength = 25; | ||
269 | + for( int i = 0; i < matchingMethods.Count; i++ ) | ||
270 | + commandsLength += matchingMethods[i].signature.Length + 7; | ||
271 | + | ||
272 | + StringBuilder stringBuilder = new StringBuilder( commandsLength ); | ||
273 | + stringBuilder.Append( "Matching commands:" ); | ||
274 | + | ||
275 | + for( int i = 0; i < matchingMethods.Count; i++ ) | ||
276 | + stringBuilder.Append( "\n - " ).Append( matchingMethods[i].signature ); | ||
277 | + | ||
278 | + Debug.Log( stringBuilder.ToString() ); | ||
279 | + | ||
280 | + if( DebugLogManager.Instance ) | ||
281 | + DebugLogManager.Instance.AdjustLatestPendingLog( true, true ); | ||
282 | + } | ||
283 | + } | ||
284 | + | ||
285 | + // Logs system information | ||
286 | + public static void LogSystemInfo() | ||
287 | + { | ||
288 | + StringBuilder stringBuilder = new StringBuilder( 1024 ); | ||
289 | + stringBuilder.Append( "Rig: " ).AppendSysInfoIfPresent( SystemInfo.deviceModel ).AppendSysInfoIfPresent( SystemInfo.processorType ) | ||
290 | + .AppendSysInfoIfPresent( SystemInfo.systemMemorySize, "MB RAM" ).Append( SystemInfo.processorCount ).Append( " cores\n" ); | ||
291 | + stringBuilder.Append( "OS: " ).Append( SystemInfo.operatingSystem ).Append( "\n" ); | ||
292 | + stringBuilder.Append( "GPU: " ).Append( SystemInfo.graphicsDeviceName ).Append( " " ).Append( SystemInfo.graphicsMemorySize ) | ||
293 | + .Append( "MB " ).Append( SystemInfo.graphicsDeviceVersion ) | ||
294 | + .Append( SystemInfo.graphicsMultiThreaded ? " multi-threaded\n" : "\n" ); | ||
295 | + stringBuilder.Append( "Data Path: " ).Append( Application.dataPath ).Append( "\n" ); | ||
296 | + stringBuilder.Append( "Persistent Data Path: " ).Append( Application.persistentDataPath ).Append( "\n" ); | ||
297 | + stringBuilder.Append( "StreamingAssets Path: " ).Append( Application.streamingAssetsPath ).Append( "\n" ); | ||
298 | + stringBuilder.Append( "Temporary Cache Path: " ).Append( Application.temporaryCachePath ).Append( "\n" ); | ||
299 | + stringBuilder.Append( "Device ID: " ).Append( SystemInfo.deviceUniqueIdentifier ).Append( "\n" ); | ||
300 | + stringBuilder.Append( "Max Texture Size: " ).Append( SystemInfo.maxTextureSize ).Append( "\n" ); | ||
301 | +#if UNITY_5_6_OR_NEWER | ||
302 | + stringBuilder.Append( "Max Cubemap Size: " ).Append( SystemInfo.maxCubemapSize ).Append( "\n" ); | ||
303 | +#endif | ||
304 | + stringBuilder.Append( "Accelerometer: " ).Append( SystemInfo.supportsAccelerometer ? "supported\n" : "not supported\n" ); | ||
305 | + stringBuilder.Append( "Gyro: " ).Append( SystemInfo.supportsGyroscope ? "supported\n" : "not supported\n" ); | ||
306 | + stringBuilder.Append( "Location Service: " ).Append( SystemInfo.supportsLocationService ? "supported\n" : "not supported\n" ); | ||
307 | +#if !UNITY_2019_1_OR_NEWER | ||
308 | + stringBuilder.Append( "Image Effects: " ).Append( SystemInfo.supportsImageEffects ? "supported\n" : "not supported\n" ); | ||
309 | + stringBuilder.Append( "RenderToCubemap: " ).Append( SystemInfo.supportsRenderToCubemap ? "supported\n" : "not supported\n" ); | ||
310 | +#endif | ||
311 | + stringBuilder.Append( "Compute Shaders: " ).Append( SystemInfo.supportsComputeShaders ? "supported\n" : "not supported\n" ); | ||
312 | + stringBuilder.Append( "Shadows: " ).Append( SystemInfo.supportsShadows ? "supported\n" : "not supported\n" ); | ||
313 | + stringBuilder.Append( "Instancing: " ).Append( SystemInfo.supportsInstancing ? "supported\n" : "not supported\n" ); | ||
314 | + stringBuilder.Append( "Motion Vectors: " ).Append( SystemInfo.supportsMotionVectors ? "supported\n" : "not supported\n" ); | ||
315 | + stringBuilder.Append( "3D Textures: " ).Append( SystemInfo.supports3DTextures ? "supported\n" : "not supported\n" ); | ||
316 | +#if UNITY_5_6_OR_NEWER | ||
317 | + stringBuilder.Append( "3D Render Textures: " ).Append( SystemInfo.supports3DRenderTextures ? "supported\n" : "not supported\n" ); | ||
318 | +#endif | ||
319 | + stringBuilder.Append( "2D Array Textures: " ).Append( SystemInfo.supports2DArrayTextures ? "supported\n" : "not supported\n" ); | ||
320 | + stringBuilder.Append( "Cubemap Array Textures: " ).Append( SystemInfo.supportsCubemapArrayTextures ? "supported" : "not supported" ); | ||
321 | + | ||
322 | + Debug.Log( stringBuilder.ToString() ); | ||
323 | + | ||
324 | + // After typing sysinfo, the log that lists system information should automatically be expanded for better UX | ||
325 | + if( DebugLogManager.Instance ) | ||
326 | + DebugLogManager.Instance.AdjustLatestPendingLog( true, true ); | ||
327 | + } | ||
328 | + | ||
329 | + private static StringBuilder AppendSysInfoIfPresent( this StringBuilder sb, string info, string postfix = null ) | ||
330 | + { | ||
331 | + if( info != SystemInfo.unsupportedIdentifier ) | ||
332 | + { | ||
333 | + sb.Append( info ); | ||
334 | + | ||
335 | + if( postfix != null ) | ||
336 | + sb.Append( postfix ); | ||
337 | + | ||
338 | + sb.Append( " " ); | ||
339 | + } | ||
340 | + | ||
341 | + return sb; | ||
342 | + } | ||
343 | + | ||
344 | + private static StringBuilder AppendSysInfoIfPresent( this StringBuilder sb, int info, string postfix = null ) | ||
345 | + { | ||
346 | + if( info > 0 ) | ||
347 | + { | ||
348 | + sb.Append( info ); | ||
349 | + | ||
350 | + if( postfix != null ) | ||
351 | + sb.Append( postfix ); | ||
352 | + | ||
353 | + sb.Append( " " ); | ||
354 | + } | ||
355 | + | ||
356 | + return sb; | ||
357 | + } | ||
358 | + | ||
359 | + // Add a custom Type to the list of recognized command parameter Types | ||
360 | + public static void AddCustomParameterType( Type type, ParseFunction parseFunction, string typeReadableName = null ) | ||
361 | + { | ||
362 | + if( type == null ) | ||
363 | + { | ||
364 | + Debug.LogError( "Parameter type can't be null!" ); | ||
365 | + return; | ||
366 | + } | ||
367 | + else if( parseFunction == null ) | ||
368 | + { | ||
369 | + Debug.LogError( "Parameter parseFunction can't be null!" ); | ||
370 | + return; | ||
371 | + } | ||
372 | + | ||
373 | + parseFunctions[type] = parseFunction; | ||
374 | + | ||
375 | + if( !string.IsNullOrEmpty( typeReadableName ) ) | ||
376 | + typeReadableNames[type] = typeReadableName; | ||
377 | + } | ||
378 | + | ||
379 | + // Remove a custom Type from the list of recognized command parameter Types | ||
380 | + public static void RemoveCustomParameterType( Type type ) | ||
381 | + { | ||
382 | + parseFunctions.Remove( type ); | ||
383 | + typeReadableNames.Remove( type ); | ||
384 | + } | ||
385 | + | ||
386 | + // Add a command related with an instance method (i.e. non static method) | ||
387 | + public static void AddCommandInstance( string command, string description, string methodName, object instance, params string[] parameterNames ) | ||
388 | + { | ||
389 | + if( instance == null ) | ||
390 | + { | ||
391 | + Debug.LogError( "Instance can't be null!" ); | ||
392 | + return; | ||
393 | + } | ||
394 | + | ||
395 | + AddCommand( command, description, methodName, instance.GetType(), instance, parameterNames ); | ||
396 | + } | ||
397 | + | ||
398 | + // Add a command related with a static method (i.e. no instance is required to call the method) | ||
399 | + public static void AddCommandStatic( string command, string description, string methodName, Type ownerType, params string[] parameterNames ) | ||
400 | + { | ||
401 | + AddCommand( command, description, methodName, ownerType, null, parameterNames ); | ||
402 | + } | ||
403 | + | ||
404 | + // Add a command that can be related to either a static or an instance method | ||
405 | + public static void AddCommand( string command, string description, Action method ) { AddCommand( command, description, method.Method, method.Target, null ); } | ||
406 | + public static void AddCommand<T1>( string command, string description, Action<T1> method ) { AddCommand( command, description, method.Method, method.Target, null ); } | ||
407 | + public static void AddCommand<T1>( string command, string description, Func<T1> method ) { AddCommand( command, description, method.Method, method.Target, null ); } | ||
408 | + public static void AddCommand<T1, T2>( string command, string description, Action<T1, T2> method ) { AddCommand( command, description, method.Method, method.Target, null ); } | ||
409 | + public static void AddCommand<T1, T2>( string command, string description, Func<T1, T2> method ) { AddCommand( command, description, method.Method, method.Target, null ); } | ||
410 | + public static void AddCommand<T1, T2, T3>( string command, string description, Action<T1, T2, T3> method ) { AddCommand( command, description, method.Method, method.Target, null ); } | ||
411 | + public static void AddCommand<T1, T2, T3>( string command, string description, Func<T1, T2, T3> method ) { AddCommand( command, description, method.Method, method.Target, null ); } | ||
412 | + public static void AddCommand<T1, T2, T3, T4>( string command, string description, Action<T1, T2, T3, T4> method ) { AddCommand( command, description, method.Method, method.Target, null ); } | ||
413 | + public static void AddCommand<T1, T2, T3, T4>( string command, string description, Func<T1, T2, T3, T4> method ) { AddCommand( command, description, method.Method, method.Target, null ); } | ||
414 | + public static void AddCommand<T1, T2, T3, T4, T5>( string command, string description, Func<T1, T2, T3, T4, T5> method ) { AddCommand( command, description, method.Method, method.Target, null ); } | ||
415 | + public static void AddCommand( string command, string description, Delegate method ) { AddCommand( command, description, method.Method, method.Target, null ); } | ||
416 | + | ||
417 | + // Add a command with custom parameter names | ||
418 | + public static void AddCommand<T1>( string command, string description, Action<T1> method, string parameterName ) { AddCommand( command, description, method.Method, method.Target, new string[1] { parameterName } ); } | ||
419 | + public static void AddCommand<T1, T2>( string command, string description, Action<T1, T2> method, string parameterName1, string parameterName2 ) { AddCommand( command, description, method.Method, method.Target, new string[2] { parameterName1, parameterName2 } ); } | ||
420 | + public static void AddCommand<T1, T2>( string command, string description, Func<T1, T2> method, string parameterName ) { AddCommand( command, description, method.Method, method.Target, new string[1] { parameterName } ); } | ||
421 | + public static void AddCommand<T1, T2, T3>( string command, string description, Action<T1, T2, T3> method, string parameterName1, string parameterName2, string parameterName3 ) { AddCommand( command, description, method.Method, method.Target, new string[3] { parameterName1, parameterName2, parameterName3 } ); } | ||
422 | + public static void AddCommand<T1, T2, T3>( string command, string description, Func<T1, T2, T3> method, string parameterName1, string parameterName2 ) { AddCommand( command, description, method.Method, method.Target, new string[2] { parameterName1, parameterName2 } ); } | ||
423 | + public static void AddCommand<T1, T2, T3, T4>( string command, string description, Action<T1, T2, T3, T4> method, string parameterName1, string parameterName2, string parameterName3, string parameterName4 ) { AddCommand( command, description, method.Method, method.Target, new string[4] { parameterName1, parameterName2, parameterName3, parameterName4 } ); } | ||
424 | + public static void AddCommand<T1, T2, T3, T4>( string command, string description, Func<T1, T2, T3, T4> method, string parameterName1, string parameterName2, string parameterName3 ) { AddCommand( command, description, method.Method, method.Target, new string[3] { parameterName1, parameterName2, parameterName3 } ); } | ||
425 | + public static void AddCommand<T1, T2, T3, T4, T5>( string command, string description, Func<T1, T2, T3, T4, T5> method, string parameterName1, string parameterName2, string parameterName3, string parameterName4 ) { AddCommand( command, description, method.Method, method.Target, new string[4] { parameterName1, parameterName2, parameterName3, parameterName4 } ); } | ||
426 | + public static void AddCommand( string command, string description, Delegate method, params string[] parameterNames ) { AddCommand( command, description, method.Method, method.Target, parameterNames ); } | ||
427 | + | ||
428 | + // Create a new command and set its properties | ||
429 | + private static void AddCommand( string command, string description, string methodName, Type ownerType, object instance, string[] parameterNames ) | ||
430 | + { | ||
431 | + // Get the method from the class | ||
432 | + MethodInfo method = ownerType.GetMethod( methodName, BindingFlags.Public | BindingFlags.NonPublic | ( instance != null ? BindingFlags.Instance : BindingFlags.Static ) ); | ||
433 | + if( method == null ) | ||
434 | + { | ||
435 | + Debug.LogError( methodName + " does not exist in " + ownerType ); | ||
436 | + return; | ||
437 | + } | ||
438 | + | ||
439 | + AddCommand( command, description, method, instance, parameterNames ); | ||
440 | + } | ||
441 | + | ||
442 | + private static void AddCommand( string command, string description, MethodInfo method, object instance, string[] parameterNames ) | ||
443 | + { | ||
444 | + if( string.IsNullOrEmpty( command ) ) | ||
445 | + { | ||
446 | + Debug.LogError( "Command name can't be empty!" ); | ||
447 | + return; | ||
448 | + } | ||
449 | + | ||
450 | + command = command.Trim(); | ||
451 | + if( command.IndexOf( ' ' ) >= 0 ) | ||
452 | + { | ||
453 | + Debug.LogError( "Command name can't contain whitespace: " + command ); | ||
454 | + return; | ||
455 | + } | ||
456 | + | ||
457 | + // Fetch the parameters of the class | ||
458 | + ParameterInfo[] parameters = method.GetParameters(); | ||
459 | + if( parameters == null ) | ||
460 | + parameters = new ParameterInfo[0]; | ||
461 | + | ||
462 | + // Store the parameter types in an array | ||
463 | + Type[] parameterTypes = new Type[parameters.Length]; | ||
464 | + for( int i = 0; i < parameters.Length; i++ ) | ||
465 | + { | ||
466 | + if( parameters[i].ParameterType.IsByRef ) | ||
467 | + { | ||
468 | + Debug.LogError( "Command can't have 'out' or 'ref' parameters" ); | ||
469 | + return; | ||
470 | + } | ||
471 | + | ||
472 | + Type parameterType = parameters[i].ParameterType; | ||
473 | + if( parseFunctions.ContainsKey( parameterType ) || typeof( Component ).IsAssignableFrom( parameterType ) || parameterType.IsEnum || IsSupportedArrayType( parameterType ) ) | ||
474 | + parameterTypes[i] = parameterType; | ||
475 | + else | ||
476 | + { | ||
477 | + Debug.LogError( string.Concat( "Parameter ", parameters[i].Name, "'s Type ", parameterType, " isn't supported" ) ); | ||
478 | + return; | ||
479 | + } | ||
480 | + } | ||
481 | + | ||
482 | + int commandIndex = FindCommandIndex( command ); | ||
483 | + if( commandIndex < 0 ) | ||
484 | + commandIndex = ~commandIndex; | ||
485 | + else | ||
486 | + { | ||
487 | + int commandFirstIndex = commandIndex; | ||
488 | + int commandLastIndex = commandIndex; | ||
489 | + | ||
490 | + while( commandFirstIndex > 0 && caseInsensitiveComparer.Compare( methods[commandFirstIndex - 1].command, command, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) == 0 ) | ||
491 | + commandFirstIndex--; | ||
492 | + while( commandLastIndex < methods.Count - 1 && caseInsensitiveComparer.Compare( methods[commandLastIndex + 1].command, command, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) == 0 ) | ||
493 | + commandLastIndex++; | ||
494 | + | ||
495 | + commandIndex = commandFirstIndex; | ||
496 | + for( int i = commandFirstIndex; i <= commandLastIndex; i++ ) | ||
497 | + { | ||
498 | + int parameterCountDiff = methods[i].parameterTypes.Length - parameterTypes.Length; | ||
499 | + if( parameterCountDiff <= 0 ) | ||
500 | + { | ||
501 | + // We are sorting the commands in 2 steps: | ||
502 | + // 1: Sorting by their 'command' names which is handled by FindCommandIndex | ||
503 | + // 2: Sorting by their parameter counts which is handled here (parameterCountDiff <= 0) | ||
504 | + commandIndex = i + 1; | ||
505 | + | ||
506 | + // Check if this command has been registered before and if it is, overwrite that command | ||
507 | + if( parameterCountDiff == 0 ) | ||
508 | + { | ||
509 | + int j = 0; | ||
510 | + while( j < parameterTypes.Length && parameterTypes[j] == methods[i].parameterTypes[j] ) | ||
511 | + j++; | ||
512 | + | ||
513 | + if( j >= parameterTypes.Length ) | ||
514 | + { | ||
515 | + commandIndex = i; | ||
516 | + commandLastIndex--; | ||
517 | + methods.RemoveAt( i-- ); | ||
518 | + | ||
519 | + continue; | ||
520 | + } | ||
521 | + } | ||
522 | + } | ||
523 | + } | ||
524 | + } | ||
525 | + | ||
526 | + // Create the command | ||
527 | + StringBuilder methodSignature = new StringBuilder( 256 ); | ||
528 | + string[] parameterSignatures = new string[parameterTypes.Length]; | ||
529 | + | ||
530 | +#if USE_BOLD_COMMAND_SIGNATURES | ||
531 | + methodSignature.Append( "<b>" ); | ||
532 | +#endif | ||
533 | + methodSignature.Append( command ); | ||
534 | + | ||
535 | + if( parameterTypes.Length > 0 ) | ||
536 | + { | ||
537 | + methodSignature.Append( " " ); | ||
538 | + | ||
539 | + for( int i = 0; i < parameterTypes.Length; i++ ) | ||
540 | + { | ||
541 | + int parameterSignatureStartIndex = methodSignature.Length; | ||
542 | + | ||
543 | + methodSignature.Append( "[" ).Append( GetTypeReadableName( parameterTypes[i] ) ).Append( " " ).Append( ( parameterNames != null && i < parameterNames.Length && !string.IsNullOrEmpty( parameterNames[i] ) ) ? parameterNames[i] : parameters[i].Name ).Append( "]" ); | ||
544 | + | ||
545 | + if( i < parameterTypes.Length - 1 ) | ||
546 | + methodSignature.Append( " " ); | ||
547 | + | ||
548 | + parameterSignatures[i] = methodSignature.ToString( parameterSignatureStartIndex, methodSignature.Length - parameterSignatureStartIndex ); | ||
549 | + } | ||
550 | + } | ||
551 | + | ||
552 | +#if USE_BOLD_COMMAND_SIGNATURES | ||
553 | + methodSignature.Append( "</b>" ); | ||
554 | +#endif | ||
555 | + | ||
556 | + if( !string.IsNullOrEmpty( description ) ) | ||
557 | + methodSignature.Append( ": " ).Append( description ); | ||
558 | + | ||
559 | + methods.Insert( commandIndex, new ConsoleMethodInfo( method, parameterTypes, instance, command, methodSignature.ToString(), parameterSignatures ) ); | ||
560 | + } | ||
561 | + | ||
562 | + // Remove all commands with the matching command name from the console | ||
563 | + public static void RemoveCommand( string command ) | ||
564 | + { | ||
565 | + if( !string.IsNullOrEmpty( command ) ) | ||
566 | + { | ||
567 | + for( int i = methods.Count - 1; i >= 0; i-- ) | ||
568 | + { | ||
569 | + if( caseInsensitiveComparer.Compare( methods[i].command, command, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) == 0 ) | ||
570 | + methods.RemoveAt( i ); | ||
571 | + } | ||
572 | + } | ||
573 | + } | ||
574 | + | ||
575 | + // Remove all commands with the matching method from the console | ||
576 | + public static void RemoveCommand( Action method ) { RemoveCommand( method.Method ); } | ||
577 | + public static void RemoveCommand<T1>( Action<T1> method ) { RemoveCommand( method.Method ); } | ||
578 | + public static void RemoveCommand<T1>( Func<T1> method ) { RemoveCommand( method.Method ); } | ||
579 | + public static void RemoveCommand<T1, T2>( Action<T1, T2> method ) { RemoveCommand( method.Method ); } | ||
580 | + public static void RemoveCommand<T1, T2>( Func<T1, T2> method ) { RemoveCommand( method.Method ); } | ||
581 | + public static void RemoveCommand<T1, T2, T3>( Action<T1, T2, T3> method ) { RemoveCommand( method.Method ); } | ||
582 | + public static void RemoveCommand<T1, T2, T3>( Func<T1, T2, T3> method ) { RemoveCommand( method.Method ); } | ||
583 | + public static void RemoveCommand<T1, T2, T3, T4>( Action<T1, T2, T3, T4> method ) { RemoveCommand( method.Method ); } | ||
584 | + public static void RemoveCommand<T1, T2, T3, T4>( Func<T1, T2, T3, T4> method ) { RemoveCommand( method.Method ); } | ||
585 | + public static void RemoveCommand<T1, T2, T3, T4, T5>( Func<T1, T2, T3, T4, T5> method ) { RemoveCommand( method.Method ); } | ||
586 | + public static void RemoveCommand( Delegate method ) { RemoveCommand( method.Method ); } | ||
587 | + | ||
588 | + public static void RemoveCommand( MethodInfo method ) | ||
589 | + { | ||
590 | + if( method != null ) | ||
591 | + { | ||
592 | + for( int i = methods.Count - 1; i >= 0; i-- ) | ||
593 | + { | ||
594 | + if( methods[i].method == method ) | ||
595 | + methods.RemoveAt( i ); | ||
596 | + } | ||
597 | + } | ||
598 | + } | ||
599 | + | ||
600 | + // Returns the first command that starts with the entered argument | ||
601 | + public static string GetAutoCompleteCommand( string commandStart, string previousSuggestion ) | ||
602 | + { | ||
603 | + int commandIndex = FindCommandIndex( !string.IsNullOrEmpty( previousSuggestion ) ? previousSuggestion : commandStart ); | ||
604 | + if( commandIndex < 0 ) | ||
605 | + { | ||
606 | + commandIndex = ~commandIndex; | ||
607 | + return ( commandIndex < methods.Count && caseInsensitiveComparer.IsPrefix( methods[commandIndex].command, commandStart, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) ) ? methods[commandIndex].command : null; | ||
608 | + } | ||
609 | + | ||
610 | + // Find the next command that starts with commandStart and is different from previousSuggestion | ||
611 | + for( int i = commandIndex + 1; i < methods.Count; i++ ) | ||
612 | + { | ||
613 | + if( caseInsensitiveComparer.Compare( methods[i].command, previousSuggestion, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) == 0 ) | ||
614 | + continue; | ||
615 | + else if( caseInsensitiveComparer.IsPrefix( methods[i].command, commandStart, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) ) | ||
616 | + return methods[i].command; | ||
617 | + else | ||
618 | + break; | ||
619 | + } | ||
620 | + | ||
621 | + // Couldn't find a command that follows previousSuggestion and satisfies commandStart, loop back to the beginning of the autocomplete suggestions | ||
622 | + string result = null; | ||
623 | + for( int i = commandIndex - 1; i >= 0 && caseInsensitiveComparer.IsPrefix( methods[i].command, commandStart, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ); i-- ) | ||
624 | + result = methods[i].command; | ||
625 | + | ||
626 | + return result; | ||
627 | + } | ||
628 | + | ||
629 | + // Parse the command and try to execute it | ||
630 | + public static void ExecuteCommand( string command ) | ||
631 | + { | ||
632 | + if( command == null ) | ||
633 | + return; | ||
634 | + | ||
635 | + command = command.Trim(); | ||
636 | + | ||
637 | + if( command.Length == 0 ) | ||
638 | + return; | ||
639 | + | ||
640 | + // Split the command's arguments | ||
641 | + commandArguments.Clear(); | ||
642 | + FetchArgumentsFromCommand( command, commandArguments ); | ||
643 | + | ||
644 | + // Find all matching commands | ||
645 | + matchingMethods.Clear(); | ||
646 | + bool parameterCountMismatch = false; | ||
647 | + int commandIndex = FindCommandIndex( commandArguments[0] ); | ||
648 | + if( commandIndex >= 0 ) | ||
649 | + { | ||
650 | + string _command = commandArguments[0]; | ||
651 | + | ||
652 | + int commandLastIndex = commandIndex; | ||
653 | + while( commandIndex > 0 && caseInsensitiveComparer.Compare( methods[commandIndex - 1].command, _command, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) == 0 ) | ||
654 | + commandIndex--; | ||
655 | + while( commandLastIndex < methods.Count - 1 && caseInsensitiveComparer.Compare( methods[commandLastIndex + 1].command, _command, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) == 0 ) | ||
656 | + commandLastIndex++; | ||
657 | + | ||
658 | + while( commandIndex <= commandLastIndex ) | ||
659 | + { | ||
660 | + if( !methods[commandIndex].IsValid() ) | ||
661 | + { | ||
662 | + methods.RemoveAt( commandIndex ); | ||
663 | + commandLastIndex--; | ||
664 | + } | ||
665 | + else | ||
666 | + { | ||
667 | + // Check if number of parameters match | ||
668 | + if( methods[commandIndex].parameterTypes.Length == commandArguments.Count - 1 ) | ||
669 | + matchingMethods.Add( methods[commandIndex] ); | ||
670 | + else | ||
671 | + parameterCountMismatch = true; | ||
672 | + | ||
673 | + commandIndex++; | ||
674 | + } | ||
675 | + } | ||
676 | + } | ||
677 | + | ||
678 | + if( matchingMethods.Count == 0 ) | ||
679 | + { | ||
680 | + string _command = commandArguments[0]; | ||
681 | + FindCommands( _command, !parameterCountMismatch, matchingMethods ); | ||
682 | + | ||
683 | + if( matchingMethods.Count == 0 ) | ||
684 | + Debug.LogWarning( string.Concat( "ERROR: can't find command '", _command, "'" ) ); | ||
685 | + else | ||
686 | + { | ||
687 | + int commandsLength = _command.Length + 75; | ||
688 | + for( int i = 0; i < matchingMethods.Count; i++ ) | ||
689 | + commandsLength += matchingMethods[i].signature.Length + 7; | ||
690 | + | ||
691 | + StringBuilder stringBuilder = new StringBuilder( commandsLength ); | ||
692 | + if( parameterCountMismatch ) | ||
693 | + stringBuilder.Append( "ERROR: '" ).Append( _command ).Append( "' doesn't take " ).Append( commandArguments.Count - 1 ).Append( " parameter(s). Available command(s):" ); | ||
694 | + else | ||
695 | + stringBuilder.Append( "ERROR: can't find command '" ).Append( _command ).Append( "'. Did you mean:" ); | ||
696 | + | ||
697 | + for( int i = 0; i < matchingMethods.Count; i++ ) | ||
698 | + stringBuilder.Append( "\n - " ).Append( matchingMethods[i].signature ); | ||
699 | + | ||
700 | + Debug.LogWarning( stringBuilder.ToString() ); | ||
701 | + | ||
702 | + // The log that lists method signature(s) for this command should automatically be expanded for better UX | ||
703 | + if( DebugLogManager.Instance ) | ||
704 | + DebugLogManager.Instance.AdjustLatestPendingLog( true, true ); | ||
705 | + } | ||
706 | + | ||
707 | + return; | ||
708 | + } | ||
709 | + | ||
710 | + ConsoleMethodInfo methodToExecute = null; | ||
711 | + object[] parameters = new object[commandArguments.Count - 1]; | ||
712 | + string errorMessage = null; | ||
713 | + for( int i = 0; i < matchingMethods.Count && methodToExecute == null; i++ ) | ||
714 | + { | ||
715 | + ConsoleMethodInfo methodInfo = matchingMethods[i]; | ||
716 | + | ||
717 | + // Parse the parameters into objects | ||
718 | + bool success = true; | ||
719 | + for( int j = 0; j < methodInfo.parameterTypes.Length && success; j++ ) | ||
720 | + { | ||
721 | + try | ||
722 | + { | ||
723 | + string argument = commandArguments[j + 1]; | ||
724 | + Type parameterType = methodInfo.parameterTypes[j]; | ||
725 | + | ||
726 | + object val; | ||
727 | + if( ParseArgument( argument, parameterType, out val ) ) | ||
728 | + parameters[j] = val; | ||
729 | + else | ||
730 | + { | ||
731 | + success = false; | ||
732 | + errorMessage = string.Concat( "ERROR: couldn't parse ", argument, " to ", GetTypeReadableName( parameterType ) ); | ||
733 | + } | ||
734 | + } | ||
735 | + catch( Exception e ) | ||
736 | + { | ||
737 | + success = false; | ||
738 | + errorMessage = "ERROR: " + e.ToString(); | ||
739 | + } | ||
740 | + } | ||
741 | + | ||
742 | + if( success ) | ||
743 | + methodToExecute = methodInfo; | ||
744 | + } | ||
745 | + | ||
746 | + if( methodToExecute == null ) | ||
747 | + Debug.LogWarning( !string.IsNullOrEmpty( errorMessage ) ? errorMessage : "ERROR: something went wrong" ); | ||
748 | + else | ||
749 | + { | ||
750 | + // Execute the method associated with the command | ||
751 | + object result = methodToExecute.method.Invoke( methodToExecute.instance, parameters ); | ||
752 | + if( methodToExecute.method.ReturnType != typeof( void ) ) | ||
753 | + { | ||
754 | + // Print the returned value to the console | ||
755 | + if( result == null || result.Equals( null ) ) | ||
756 | + Debug.Log( "Returned: null" ); | ||
757 | + else | ||
758 | + Debug.Log( "Returned: " + result.ToString() ); | ||
759 | + } | ||
760 | + | ||
761 | + if( OnCommandExecuted != null ) | ||
762 | + OnCommandExecuted( methodToExecute.command, parameters ); | ||
763 | + } | ||
764 | + } | ||
765 | + | ||
766 | + public static void FetchArgumentsFromCommand( string command, List<string> commandArguments ) | ||
767 | + { | ||
768 | + for( int i = 0; i < command.Length; i++ ) | ||
769 | + { | ||
770 | + if( char.IsWhiteSpace( command[i] ) ) | ||
771 | + continue; | ||
772 | + | ||
773 | + int delimiterIndex = IndexOfDelimiterGroup( command[i] ); | ||
774 | + if( delimiterIndex >= 0 ) | ||
775 | + { | ||
776 | + int endIndex = IndexOfDelimiterGroupEnd( command, delimiterIndex, i + 1 ); | ||
777 | + commandArguments.Add( command.Substring( i + 1, endIndex - i - 1 ) ); | ||
778 | + i = ( endIndex < command.Length - 1 && command[endIndex + 1] == ',' ) ? endIndex + 1 : endIndex; | ||
779 | + } | ||
780 | + else | ||
781 | + { | ||
782 | + int endIndex = IndexOfChar( command, ' ', i + 1 ); | ||
783 | + commandArguments.Add( command.Substring( i, command[endIndex - 1] == ',' ? endIndex - 1 - i : endIndex - i ) ); | ||
784 | + i = endIndex; | ||
785 | + } | ||
786 | + } | ||
787 | + } | ||
788 | + | ||
789 | + public static void FindCommands( string commandName, bool allowSubstringMatching, List<ConsoleMethodInfo> matchingCommands ) | ||
790 | + { | ||
791 | + if( allowSubstringMatching ) | ||
792 | + { | ||
793 | + for( int i = 0; i < methods.Count; i++ ) | ||
794 | + { | ||
795 | + if( methods[i].IsValid() && caseInsensitiveComparer.IndexOf( methods[i].command, commandName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) >= 0 ) | ||
796 | + matchingCommands.Add( methods[i] ); | ||
797 | + } | ||
798 | + } | ||
799 | + else | ||
800 | + { | ||
801 | + for( int i = 0; i < methods.Count; i++ ) | ||
802 | + { | ||
803 | + if( methods[i].IsValid() && caseInsensitiveComparer.Compare( methods[i].command, commandName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) == 0 ) | ||
804 | + matchingCommands.Add( methods[i] ); | ||
805 | + } | ||
806 | + } | ||
807 | + } | ||
808 | + | ||
809 | + // Finds all commands that have a matching signature with command | ||
810 | + // - caretIndexIncrements: indices inside "string command" that separate two arguments in the command. This is used to | ||
811 | + // figure out which argument the caret is standing on | ||
812 | + // - commandName: command's name (first argument) | ||
813 | + internal static void GetCommandSuggestions( string command, List<ConsoleMethodInfo> matchingCommands, List<int> caretIndexIncrements, ref string commandName, out int numberOfParameters ) | ||
814 | + { | ||
815 | + bool commandNameCalculated = false; | ||
816 | + bool commandNameFullyTyped = false; | ||
817 | + numberOfParameters = -1; | ||
818 | + for( int i = 0; i < command.Length; i++ ) | ||
819 | + { | ||
820 | + if( char.IsWhiteSpace( command[i] ) ) | ||
821 | + continue; | ||
822 | + | ||
823 | + int delimiterIndex = IndexOfDelimiterGroup( command[i] ); | ||
824 | + if( delimiterIndex >= 0 ) | ||
825 | + { | ||
826 | + int endIndex = IndexOfDelimiterGroupEnd( command, delimiterIndex, i + 1 ); | ||
827 | + if( !commandNameCalculated ) | ||
828 | + { | ||
829 | + commandNameCalculated = true; | ||
830 | + commandNameFullyTyped = command.Length > endIndex; | ||
831 | + | ||
832 | + int commandNameLength = endIndex - i - 1; | ||
833 | + if( commandName == null || commandNameLength == 0 || commandName.Length != commandNameLength || caseInsensitiveComparer.IndexOf( command, commandName, i + 1, commandNameLength, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) != i + 1 ) | ||
834 | + commandName = command.Substring( i + 1, commandNameLength ); | ||
835 | + } | ||
836 | + | ||
837 | + i = ( endIndex < command.Length - 1 && command[endIndex + 1] == ',' ) ? endIndex + 1 : endIndex; | ||
838 | + caretIndexIncrements.Add( i + 1 ); | ||
839 | + } | ||
840 | + else | ||
841 | + { | ||
842 | + int endIndex = IndexOfChar( command, ' ', i + 1 ); | ||
843 | + if( !commandNameCalculated ) | ||
844 | + { | ||
845 | + commandNameCalculated = true; | ||
846 | + commandNameFullyTyped = command.Length > endIndex; | ||
847 | + | ||
848 | + int commandNameLength = command[endIndex - 1] == ',' ? endIndex - 1 - i : endIndex - i; | ||
849 | + if( commandName == null || commandNameLength == 0 || commandName.Length != commandNameLength || caseInsensitiveComparer.IndexOf( command, commandName, i, commandNameLength, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) != i ) | ||
850 | + commandName = command.Substring( i, commandNameLength ); | ||
851 | + } | ||
852 | + | ||
853 | + i = endIndex; | ||
854 | + caretIndexIncrements.Add( i ); | ||
855 | + } | ||
856 | + | ||
857 | + numberOfParameters++; | ||
858 | + } | ||
859 | + | ||
860 | + if( !commandNameCalculated ) | ||
861 | + commandName = string.Empty; | ||
862 | + | ||
863 | + if( !string.IsNullOrEmpty( commandName ) ) | ||
864 | + { | ||
865 | + int commandIndex = FindCommandIndex( commandName ); | ||
866 | + if( commandIndex < 0 ) | ||
867 | + commandIndex = ~commandIndex; | ||
868 | + | ||
869 | + int commandLastIndex = commandIndex; | ||
870 | + if( !commandNameFullyTyped ) | ||
871 | + { | ||
872 | + // Match all commands that start with commandName | ||
873 | + if( commandIndex < methods.Count && caseInsensitiveComparer.IsPrefix( methods[commandIndex].command, commandName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) ) | ||
874 | + { | ||
875 | + while( commandIndex > 0 && caseInsensitiveComparer.IsPrefix( methods[commandIndex - 1].command, commandName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) ) | ||
876 | + commandIndex--; | ||
877 | + while( commandLastIndex < methods.Count - 1 && caseInsensitiveComparer.IsPrefix( methods[commandLastIndex + 1].command, commandName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) ) | ||
878 | + commandLastIndex++; | ||
879 | + } | ||
880 | + else | ||
881 | + commandLastIndex = -1; | ||
882 | + } | ||
883 | + else | ||
884 | + { | ||
885 | + // Match only the commands that are equal to commandName | ||
886 | + if( commandIndex < methods.Count && caseInsensitiveComparer.Compare( methods[commandIndex].command, commandName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) == 0 ) | ||
887 | + { | ||
888 | + while( commandIndex > 0 && caseInsensitiveComparer.Compare( methods[commandIndex - 1].command, commandName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) == 0 ) | ||
889 | + commandIndex--; | ||
890 | + while( commandLastIndex < methods.Count - 1 && caseInsensitiveComparer.Compare( methods[commandLastIndex + 1].command, commandName, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) == 0 ) | ||
891 | + commandLastIndex++; | ||
892 | + } | ||
893 | + else | ||
894 | + commandLastIndex = -1; | ||
895 | + } | ||
896 | + | ||
897 | + for( ; commandIndex <= commandLastIndex; commandIndex++ ) | ||
898 | + { | ||
899 | + if( methods[commandIndex].parameterTypes.Length >= numberOfParameters ) | ||
900 | + matchingCommands.Add( methods[commandIndex] ); | ||
901 | + } | ||
902 | + } | ||
903 | + } | ||
904 | + | ||
905 | + // Find the index of the delimiter group that 'c' belongs to | ||
906 | + private static int IndexOfDelimiterGroup( char c ) | ||
907 | + { | ||
908 | + for( int i = 0; i < inputDelimiters.Length; i++ ) | ||
909 | + { | ||
910 | + if( c == inputDelimiters[i][0] ) | ||
911 | + return i; | ||
912 | + } | ||
913 | + | ||
914 | + return -1; | ||
915 | + } | ||
916 | + | ||
917 | + private static int IndexOfDelimiterGroupEnd( string command, int delimiterIndex, int startIndex ) | ||
918 | + { | ||
919 | + char startChar = inputDelimiters[delimiterIndex][0]; | ||
920 | + char endChar = inputDelimiters[delimiterIndex][1]; | ||
921 | + | ||
922 | + // Check delimiter's depth for array support (e.g. [[1 2] [3 4]] for Vector2 array) | ||
923 | + int depth = 1; | ||
924 | + | ||
925 | + for( int i = startIndex; i < command.Length; i++ ) | ||
926 | + { | ||
927 | + char c = command[i]; | ||
928 | + if( c == endChar && --depth <= 0 ) | ||
929 | + return i; | ||
930 | + else if( c == startChar ) | ||
931 | + depth++; | ||
932 | + } | ||
933 | + | ||
934 | + return command.Length; | ||
935 | + } | ||
936 | + | ||
937 | + // Find the index of char in the string, or return the length of string instead of -1 | ||
938 | + private static int IndexOfChar( string command, char c, int startIndex ) | ||
939 | + { | ||
940 | + int result = command.IndexOf( c, startIndex ); | ||
941 | + if( result < 0 ) | ||
942 | + result = command.Length; | ||
943 | + | ||
944 | + return result; | ||
945 | + } | ||
946 | + | ||
947 | + // Find command's index in the list of registered commands using binary search | ||
948 | + private static int FindCommandIndex( string command ) | ||
949 | + { | ||
950 | + int min = 0; | ||
951 | + int max = methods.Count - 1; | ||
952 | + while( min <= max ) | ||
953 | + { | ||
954 | + int mid = ( min + max ) / 2; | ||
955 | + int comparison = caseInsensitiveComparer.Compare( command, methods[mid].command, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ); | ||
956 | + if( comparison == 0 ) | ||
957 | + return mid; | ||
958 | + else if( comparison < 0 ) | ||
959 | + max = mid - 1; | ||
960 | + else | ||
961 | + min = mid + 1; | ||
962 | + } | ||
963 | + | ||
964 | + return ~min; | ||
965 | + } | ||
966 | + | ||
967 | + public static bool IsSupportedArrayType( Type type ) | ||
968 | + { | ||
969 | + if( type.IsArray ) | ||
970 | + { | ||
971 | + if( type.GetArrayRank() != 1 ) | ||
972 | + return false; | ||
973 | + | ||
974 | + type = type.GetElementType(); | ||
975 | + } | ||
976 | + else if( type.IsGenericType ) | ||
977 | + { | ||
978 | + if( type.GetGenericTypeDefinition() != typeof( List<> ) ) | ||
979 | + return false; | ||
980 | + | ||
981 | + type = type.GetGenericArguments()[0]; | ||
982 | + } | ||
983 | + else | ||
984 | + return false; | ||
985 | + | ||
986 | + return parseFunctions.ContainsKey( type ) || typeof( Component ).IsAssignableFrom( type ) || type.IsEnum; | ||
987 | + } | ||
988 | + | ||
989 | + public static string GetTypeReadableName( Type type ) | ||
990 | + { | ||
991 | + string result; | ||
992 | + if( typeReadableNames.TryGetValue( type, out result ) ) | ||
993 | + return result; | ||
994 | + | ||
995 | + if( IsSupportedArrayType( type ) ) | ||
996 | + { | ||
997 | + Type elementType = type.IsArray ? type.GetElementType() : type.GetGenericArguments()[0]; | ||
998 | + if( typeReadableNames.TryGetValue( elementType, out result ) ) | ||
999 | + return result + "[]"; | ||
1000 | + else | ||
1001 | + return elementType.Name + "[]"; | ||
1002 | + } | ||
1003 | + | ||
1004 | + return type.Name; | ||
1005 | + } | ||
1006 | + | ||
1007 | + public static bool ParseArgument( string input, Type argumentType, out object output ) | ||
1008 | + { | ||
1009 | + ParseFunction parseFunction; | ||
1010 | + if( parseFunctions.TryGetValue( argumentType, out parseFunction ) ) | ||
1011 | + return parseFunction( input, out output ); | ||
1012 | + else if( typeof( Component ).IsAssignableFrom( argumentType ) ) | ||
1013 | + return ParseComponent( input, argumentType, out output ); | ||
1014 | + else if( argumentType.IsEnum ) | ||
1015 | + return ParseEnum( input, argumentType, out output ); | ||
1016 | + else if( IsSupportedArrayType( argumentType ) ) | ||
1017 | + return ParseArray( input, argumentType, out output ); | ||
1018 | + else | ||
1019 | + { | ||
1020 | + output = null; | ||
1021 | + return false; | ||
1022 | + } | ||
1023 | + } | ||
1024 | + | ||
1025 | + public static bool ParseString( string input, out object output ) | ||
1026 | + { | ||
1027 | + output = input; | ||
1028 | + return true; | ||
1029 | + } | ||
1030 | + | ||
1031 | + public static bool ParseBool( string input, out object output ) | ||
1032 | + { | ||
1033 | + if( input == "1" || input.ToLowerInvariant() == "true" ) | ||
1034 | + { | ||
1035 | + output = true; | ||
1036 | + return true; | ||
1037 | + } | ||
1038 | + | ||
1039 | + if( input == "0" || input.ToLowerInvariant() == "false" ) | ||
1040 | + { | ||
1041 | + output = false; | ||
1042 | + return true; | ||
1043 | + } | ||
1044 | + | ||
1045 | + output = false; | ||
1046 | + return false; | ||
1047 | + } | ||
1048 | + | ||
1049 | + public static bool ParseInt( string input, out object output ) | ||
1050 | + { | ||
1051 | + int value; | ||
1052 | + bool result = int.TryParse( input, out value ); | ||
1053 | + | ||
1054 | + output = value; | ||
1055 | + return result; | ||
1056 | + } | ||
1057 | + | ||
1058 | + public static bool ParseUInt( string input, out object output ) | ||
1059 | + { | ||
1060 | + uint value; | ||
1061 | + bool result = uint.TryParse( input, out value ); | ||
1062 | + | ||
1063 | + output = value; | ||
1064 | + return result; | ||
1065 | + } | ||
1066 | + | ||
1067 | + public static bool ParseLong( string input, out object output ) | ||
1068 | + { | ||
1069 | + long value; | ||
1070 | + bool result = long.TryParse( !input.EndsWith( "L", StringComparison.OrdinalIgnoreCase ) ? input : input.Substring( 0, input.Length - 1 ), out value ); | ||
1071 | + | ||
1072 | + output = value; | ||
1073 | + return result; | ||
1074 | + } | ||
1075 | + | ||
1076 | + public static bool ParseULong( string input, out object output ) | ||
1077 | + { | ||
1078 | + ulong value; | ||
1079 | + bool result = ulong.TryParse( !input.EndsWith( "L", StringComparison.OrdinalIgnoreCase ) ? input : input.Substring( 0, input.Length - 1 ), out value ); | ||
1080 | + | ||
1081 | + output = value; | ||
1082 | + return result; | ||
1083 | + } | ||
1084 | + | ||
1085 | + public static bool ParseByte( string input, out object output ) | ||
1086 | + { | ||
1087 | + byte value; | ||
1088 | + bool result = byte.TryParse( input, out value ); | ||
1089 | + | ||
1090 | + output = value; | ||
1091 | + return result; | ||
1092 | + } | ||
1093 | + | ||
1094 | + public static bool ParseSByte( string input, out object output ) | ||
1095 | + { | ||
1096 | + sbyte value; | ||
1097 | + bool result = sbyte.TryParse( input, out value ); | ||
1098 | + | ||
1099 | + output = value; | ||
1100 | + return result; | ||
1101 | + } | ||
1102 | + | ||
1103 | + public static bool ParseShort( string input, out object output ) | ||
1104 | + { | ||
1105 | + short value; | ||
1106 | + bool result = short.TryParse( input, out value ); | ||
1107 | + | ||
1108 | + output = value; | ||
1109 | + return result; | ||
1110 | + } | ||
1111 | + | ||
1112 | + public static bool ParseUShort( string input, out object output ) | ||
1113 | + { | ||
1114 | + ushort value; | ||
1115 | + bool result = ushort.TryParse( input, out value ); | ||
1116 | + | ||
1117 | + output = value; | ||
1118 | + return result; | ||
1119 | + } | ||
1120 | + | ||
1121 | + public static bool ParseChar( string input, out object output ) | ||
1122 | + { | ||
1123 | + char value; | ||
1124 | + bool result = char.TryParse( input, out value ); | ||
1125 | + | ||
1126 | + output = value; | ||
1127 | + return result; | ||
1128 | + } | ||
1129 | + | ||
1130 | + public static bool ParseFloat( string input, out object output ) | ||
1131 | + { | ||
1132 | + float value; | ||
1133 | + bool result = float.TryParse( !input.EndsWith( "f", StringComparison.OrdinalIgnoreCase ) ? input : input.Substring( 0, input.Length - 1 ), NumberStyles.Float, CultureInfo.InvariantCulture, out value ); | ||
1134 | + | ||
1135 | + output = value; | ||
1136 | + return result; | ||
1137 | + } | ||
1138 | + | ||
1139 | + public static bool ParseDouble( string input, out object output ) | ||
1140 | + { | ||
1141 | + double value; | ||
1142 | + bool result = double.TryParse( !input.EndsWith( "f", StringComparison.OrdinalIgnoreCase ) ? input : input.Substring( 0, input.Length - 1 ), NumberStyles.Float, CultureInfo.InvariantCulture, out value ); | ||
1143 | + | ||
1144 | + output = value; | ||
1145 | + return result; | ||
1146 | + } | ||
1147 | + | ||
1148 | + public static bool ParseDecimal( string input, out object output ) | ||
1149 | + { | ||
1150 | + decimal value; | ||
1151 | + bool result = decimal.TryParse( !input.EndsWith( "f", StringComparison.OrdinalIgnoreCase ) ? input : input.Substring( 0, input.Length - 1 ), NumberStyles.Float, CultureInfo.InvariantCulture, out value ); | ||
1152 | + | ||
1153 | + output = value; | ||
1154 | + return result; | ||
1155 | + } | ||
1156 | + | ||
1157 | + public static bool ParseVector2( string input, out object output ) | ||
1158 | + { | ||
1159 | + return ParseVector( input, typeof( Vector2 ), out output ); | ||
1160 | + } | ||
1161 | + | ||
1162 | + public static bool ParseVector3( string input, out object output ) | ||
1163 | + { | ||
1164 | + return ParseVector( input, typeof( Vector3 ), out output ); | ||
1165 | + } | ||
1166 | + | ||
1167 | + public static bool ParseVector4( string input, out object output ) | ||
1168 | + { | ||
1169 | + return ParseVector( input, typeof( Vector4 ), out output ); | ||
1170 | + } | ||
1171 | + | ||
1172 | + public static bool ParseQuaternion( string input, out object output ) | ||
1173 | + { | ||
1174 | + return ParseVector( input, typeof( Quaternion ), out output ); | ||
1175 | + } | ||
1176 | + | ||
1177 | + public static bool ParseColor( string input, out object output ) | ||
1178 | + { | ||
1179 | + return ParseVector( input, typeof( Color ), out output ); | ||
1180 | + } | ||
1181 | + | ||
1182 | + public static bool ParseColor32( string input, out object output ) | ||
1183 | + { | ||
1184 | + return ParseVector( input, typeof( Color32 ), out output ); | ||
1185 | + } | ||
1186 | + | ||
1187 | + public static bool ParseRect( string input, out object output ) | ||
1188 | + { | ||
1189 | + return ParseVector( input, typeof( Rect ), out output ); | ||
1190 | + } | ||
1191 | + | ||
1192 | + public static bool ParseRectOffset( string input, out object output ) | ||
1193 | + { | ||
1194 | + return ParseVector( input, typeof( RectOffset ), out output ); | ||
1195 | + } | ||
1196 | + | ||
1197 | + public static bool ParseBounds( string input, out object output ) | ||
1198 | + { | ||
1199 | + return ParseVector( input, typeof( Bounds ), out output ); | ||
1200 | + } | ||
1201 | + | ||
1202 | +#if UNITY_2017_2_OR_NEWER | ||
1203 | + public static bool ParseVector2Int( string input, out object output ) | ||
1204 | + { | ||
1205 | + return ParseVector( input, typeof( Vector2Int ), out output ); | ||
1206 | + } | ||
1207 | + | ||
1208 | + public static bool ParseVector3Int( string input, out object output ) | ||
1209 | + { | ||
1210 | + return ParseVector( input, typeof( Vector3Int ), out output ); | ||
1211 | + } | ||
1212 | + | ||
1213 | + public static bool ParseRectInt( string input, out object output ) | ||
1214 | + { | ||
1215 | + return ParseVector( input, typeof( RectInt ), out output ); | ||
1216 | + } | ||
1217 | + | ||
1218 | + public static bool ParseBoundsInt( string input, out object output ) | ||
1219 | + { | ||
1220 | + return ParseVector( input, typeof( BoundsInt ), out output ); | ||
1221 | + } | ||
1222 | +#endif | ||
1223 | + | ||
1224 | + public static bool ParseGameObject( string input, out object output ) | ||
1225 | + { | ||
1226 | + output = input == "null" ? null : GameObject.Find( input ); | ||
1227 | + return true; | ||
1228 | + } | ||
1229 | + | ||
1230 | + public static bool ParseComponent( string input, Type componentType, out object output ) | ||
1231 | + { | ||
1232 | + GameObject gameObject = input == "null" ? null : GameObject.Find( input ); | ||
1233 | + output = gameObject ? gameObject.GetComponent( componentType ) : null; | ||
1234 | + return true; | ||
1235 | + } | ||
1236 | + | ||
1237 | + public static bool ParseEnum( string input, Type enumType, out object output ) | ||
1238 | + { | ||
1239 | + const int NONE = 0, OR = 1, AND = 2; | ||
1240 | + | ||
1241 | + int outputInt = 0; | ||
1242 | + int operation = NONE; // 0: nothing, 1: OR with outputInt, 2: AND with outputInt | ||
1243 | + for( int i = 0; i < input.Length; i++ ) | ||
1244 | + { | ||
1245 | + string enumStr; | ||
1246 | + int orIndex = input.IndexOf( '|', i ); | ||
1247 | + int andIndex = input.IndexOf( '&', i ); | ||
1248 | + if( orIndex < 0 ) | ||
1249 | + enumStr = input.Substring( i, ( andIndex < 0 ? input.Length : andIndex ) - i ).Trim(); | ||
1250 | + else | ||
1251 | + enumStr = input.Substring( i, ( andIndex < 0 ? orIndex : Mathf.Min( andIndex, orIndex ) ) - i ).Trim(); | ||
1252 | + | ||
1253 | + int value; | ||
1254 | + if( !int.TryParse( enumStr, out value ) ) | ||
1255 | + { | ||
1256 | + try | ||
1257 | + { | ||
1258 | + // Case-insensitive enum parsing | ||
1259 | + value = Convert.ToInt32( Enum.Parse( enumType, enumStr, true ) ); | ||
1260 | + } | ||
1261 | + catch | ||
1262 | + { | ||
1263 | + output = null; | ||
1264 | + return false; | ||
1265 | + } | ||
1266 | + } | ||
1267 | + | ||
1268 | + if( operation == NONE ) | ||
1269 | + outputInt = value; | ||
1270 | + else if( operation == OR ) | ||
1271 | + outputInt |= value; | ||
1272 | + else | ||
1273 | + outputInt &= value; | ||
1274 | + | ||
1275 | + if( orIndex >= 0 ) | ||
1276 | + { | ||
1277 | + if( andIndex > orIndex ) | ||
1278 | + { | ||
1279 | + operation = AND; | ||
1280 | + i = andIndex; | ||
1281 | + } | ||
1282 | + else | ||
1283 | + { | ||
1284 | + operation = OR; | ||
1285 | + i = orIndex; | ||
1286 | + } | ||
1287 | + } | ||
1288 | + else if( andIndex >= 0 ) | ||
1289 | + { | ||
1290 | + operation = AND; | ||
1291 | + i = andIndex; | ||
1292 | + } | ||
1293 | + else | ||
1294 | + i = input.Length; | ||
1295 | + } | ||
1296 | + | ||
1297 | + output = Enum.ToObject( enumType, outputInt ); | ||
1298 | + return true; | ||
1299 | + } | ||
1300 | + | ||
1301 | + public static bool ParseArray( string input, Type arrayType, out object output ) | ||
1302 | + { | ||
1303 | + List<string> valuesToParse = new List<string>( 2 ); | ||
1304 | + FetchArgumentsFromCommand( input, valuesToParse ); | ||
1305 | + | ||
1306 | + IList result = (IList) Activator.CreateInstance( arrayType, new object[1] { valuesToParse.Count } ); | ||
1307 | + output = result; | ||
1308 | + | ||
1309 | + if( arrayType.IsArray ) | ||
1310 | + { | ||
1311 | + Type elementType = arrayType.GetElementType(); | ||
1312 | + for( int i = 0; i < valuesToParse.Count; i++ ) | ||
1313 | + { | ||
1314 | + object obj; | ||
1315 | + if( !ParseArgument( valuesToParse[i], elementType, out obj ) ) | ||
1316 | + return false; | ||
1317 | + | ||
1318 | + result[i] = obj; | ||
1319 | + } | ||
1320 | + } | ||
1321 | + else | ||
1322 | + { | ||
1323 | + Type elementType = arrayType.GetGenericArguments()[0]; | ||
1324 | + for( int i = 0; i < valuesToParse.Count; i++ ) | ||
1325 | + { | ||
1326 | + object obj; | ||
1327 | + if( !ParseArgument( valuesToParse[i], elementType, out obj ) ) | ||
1328 | + return false; | ||
1329 | + | ||
1330 | + result.Add( obj ); | ||
1331 | + } | ||
1332 | + } | ||
1333 | + | ||
1334 | + return true; | ||
1335 | + } | ||
1336 | + | ||
1337 | + // Create a vector of specified type (fill the blank slots with 0 or ignore unnecessary slots) | ||
1338 | + private static bool ParseVector( string input, Type vectorType, out object output ) | ||
1339 | + { | ||
1340 | + List<string> tokens = new List<string>( input.Replace( ',', ' ' ).Trim().Split( ' ' ) ); | ||
1341 | + for( int i = tokens.Count - 1; i >= 0; i-- ) | ||
1342 | + { | ||
1343 | + tokens[i] = tokens[i].Trim(); | ||
1344 | + if( tokens[i].Length == 0 ) | ||
1345 | + tokens.RemoveAt( i ); | ||
1346 | + } | ||
1347 | + | ||
1348 | + float[] tokenValues = new float[tokens.Count]; | ||
1349 | + for( int i = 0; i < tokens.Count; i++ ) | ||
1350 | + { | ||
1351 | + object val; | ||
1352 | + if( !ParseFloat( tokens[i], out val ) ) | ||
1353 | + { | ||
1354 | + if( vectorType == typeof( Vector3 ) ) | ||
1355 | + output = Vector3.zero; | ||
1356 | + else if( vectorType == typeof( Vector2 ) ) | ||
1357 | + output = Vector2.zero; | ||
1358 | + else | ||
1359 | + output = Vector4.zero; | ||
1360 | + | ||
1361 | + return false; | ||
1362 | + } | ||
1363 | + | ||
1364 | + tokenValues[i] = (float) val; | ||
1365 | + } | ||
1366 | + | ||
1367 | + if( vectorType == typeof( Vector3 ) ) | ||
1368 | + { | ||
1369 | + Vector3 result = Vector3.zero; | ||
1370 | + | ||
1371 | + for( int i = 0; i < tokenValues.Length && i < 3; i++ ) | ||
1372 | + result[i] = tokenValues[i]; | ||
1373 | + | ||
1374 | + output = result; | ||
1375 | + } | ||
1376 | + else if( vectorType == typeof( Vector2 ) ) | ||
1377 | + { | ||
1378 | + Vector2 result = Vector2.zero; | ||
1379 | + | ||
1380 | + for( int i = 0; i < tokenValues.Length && i < 2; i++ ) | ||
1381 | + result[i] = tokenValues[i]; | ||
1382 | + | ||
1383 | + output = result; | ||
1384 | + } | ||
1385 | + else if( vectorType == typeof( Vector4 ) ) | ||
1386 | + { | ||
1387 | + Vector4 result = Vector4.zero; | ||
1388 | + | ||
1389 | + for( int i = 0; i < tokenValues.Length && i < 4; i++ ) | ||
1390 | + result[i] = tokenValues[i]; | ||
1391 | + | ||
1392 | + output = result; | ||
1393 | + } | ||
1394 | + else if( vectorType == typeof( Quaternion ) ) | ||
1395 | + { | ||
1396 | + Quaternion result = Quaternion.identity; | ||
1397 | + | ||
1398 | + for( int i = 0; i < tokenValues.Length && i < 4; i++ ) | ||
1399 | + result[i] = tokenValues[i]; | ||
1400 | + | ||
1401 | + output = result; | ||
1402 | + } | ||
1403 | + else if( vectorType == typeof( Color ) ) | ||
1404 | + { | ||
1405 | + Color result = Color.black; | ||
1406 | + | ||
1407 | + for( int i = 0; i < tokenValues.Length && i < 4; i++ ) | ||
1408 | + result[i] = tokenValues[i]; | ||
1409 | + | ||
1410 | + output = result; | ||
1411 | + } | ||
1412 | + else if( vectorType == typeof( Color32 ) ) | ||
1413 | + { | ||
1414 | + Color32 result = new Color32( 0, 0, 0, 255 ); | ||
1415 | + | ||
1416 | + if( tokenValues.Length > 0 ) | ||
1417 | + result.r = (byte) Mathf.RoundToInt( tokenValues[0] ); | ||
1418 | + if( tokenValues.Length > 1 ) | ||
1419 | + result.g = (byte) Mathf.RoundToInt( tokenValues[1] ); | ||
1420 | + if( tokenValues.Length > 2 ) | ||
1421 | + result.b = (byte) Mathf.RoundToInt( tokenValues[2] ); | ||
1422 | + if( tokenValues.Length > 3 ) | ||
1423 | + result.a = (byte) Mathf.RoundToInt( tokenValues[3] ); | ||
1424 | + | ||
1425 | + output = result; | ||
1426 | + } | ||
1427 | + else if( vectorType == typeof( Rect ) ) | ||
1428 | + { | ||
1429 | + Rect result = Rect.zero; | ||
1430 | + | ||
1431 | + if( tokenValues.Length > 0 ) | ||
1432 | + result.x = tokenValues[0]; | ||
1433 | + if( tokenValues.Length > 1 ) | ||
1434 | + result.y = tokenValues[1]; | ||
1435 | + if( tokenValues.Length > 2 ) | ||
1436 | + result.width = tokenValues[2]; | ||
1437 | + if( tokenValues.Length > 3 ) | ||
1438 | + result.height = tokenValues[3]; | ||
1439 | + | ||
1440 | + output = result; | ||
1441 | + } | ||
1442 | + else if( vectorType == typeof( RectOffset ) ) | ||
1443 | + { | ||
1444 | + RectOffset result = new RectOffset(); | ||
1445 | + | ||
1446 | + if( tokenValues.Length > 0 ) | ||
1447 | + result.left = Mathf.RoundToInt( tokenValues[0] ); | ||
1448 | + if( tokenValues.Length > 1 ) | ||
1449 | + result.right = Mathf.RoundToInt( tokenValues[1] ); | ||
1450 | + if( tokenValues.Length > 2 ) | ||
1451 | + result.top = Mathf.RoundToInt( tokenValues[2] ); | ||
1452 | + if( tokenValues.Length > 3 ) | ||
1453 | + result.bottom = Mathf.RoundToInt( tokenValues[3] ); | ||
1454 | + | ||
1455 | + output = result; | ||
1456 | + } | ||
1457 | + else if( vectorType == typeof( Bounds ) ) | ||
1458 | + { | ||
1459 | + Vector3 center = Vector3.zero; | ||
1460 | + for( int i = 0; i < tokenValues.Length && i < 3; i++ ) | ||
1461 | + center[i] = tokenValues[i]; | ||
1462 | + | ||
1463 | + Vector3 size = Vector3.zero; | ||
1464 | + for( int i = 3; i < tokenValues.Length && i < 6; i++ ) | ||
1465 | + size[i - 3] = tokenValues[i]; | ||
1466 | + | ||
1467 | + output = new Bounds( center, size ); | ||
1468 | + } | ||
1469 | +#if UNITY_2017_2_OR_NEWER | ||
1470 | + else if( vectorType == typeof( Vector3Int ) ) | ||
1471 | + { | ||
1472 | + Vector3Int result = Vector3Int.zero; | ||
1473 | + | ||
1474 | + for( int i = 0; i < tokenValues.Length && i < 3; i++ ) | ||
1475 | + result[i] = Mathf.RoundToInt( tokenValues[i] ); | ||
1476 | + | ||
1477 | + output = result; | ||
1478 | + } | ||
1479 | + else if( vectorType == typeof( Vector2Int ) ) | ||
1480 | + { | ||
1481 | + Vector2Int result = Vector2Int.zero; | ||
1482 | + | ||
1483 | + for( int i = 0; i < tokenValues.Length && i < 2; i++ ) | ||
1484 | + result[i] = Mathf.RoundToInt( tokenValues[i] ); | ||
1485 | + | ||
1486 | + output = result; | ||
1487 | + } | ||
1488 | + else if( vectorType == typeof( RectInt ) ) | ||
1489 | + { | ||
1490 | + RectInt result = new RectInt(); | ||
1491 | + | ||
1492 | + if( tokenValues.Length > 0 ) | ||
1493 | + result.x = Mathf.RoundToInt( tokenValues[0] ); | ||
1494 | + if( tokenValues.Length > 1 ) | ||
1495 | + result.y = Mathf.RoundToInt( tokenValues[1] ); | ||
1496 | + if( tokenValues.Length > 2 ) | ||
1497 | + result.width = Mathf.RoundToInt( tokenValues[2] ); | ||
1498 | + if( tokenValues.Length > 3 ) | ||
1499 | + result.height = Mathf.RoundToInt( tokenValues[3] ); | ||
1500 | + | ||
1501 | + output = result; | ||
1502 | + } | ||
1503 | + else if( vectorType == typeof( BoundsInt ) ) | ||
1504 | + { | ||
1505 | + Vector3Int center = Vector3Int.zero; | ||
1506 | + for( int i = 0; i < tokenValues.Length && i < 3; i++ ) | ||
1507 | + center[i] = Mathf.RoundToInt( tokenValues[i] ); | ||
1508 | + | ||
1509 | + Vector3Int size = Vector3Int.zero; | ||
1510 | + for( int i = 3; i < tokenValues.Length && i < 6; i++ ) | ||
1511 | + size[i - 3] = Mathf.RoundToInt( tokenValues[i] ); | ||
1512 | + | ||
1513 | + output = new BoundsInt( center, size ); | ||
1514 | + } | ||
1515 | +#endif | ||
1516 | + else | ||
1517 | + { | ||
1518 | + output = null; | ||
1519 | + return false; | ||
1520 | + } | ||
1521 | + | ||
1522 | + return true; | ||
1523 | + } | ||
1524 | + } | ||
1525 | +} |
1 | +using System.Collections.Generic; | ||
2 | +using System.Globalization; | ||
3 | +using System.Text; | ||
4 | +using UnityEngine; | ||
5 | + | ||
6 | +// Container for a simple debug entry | ||
7 | +namespace IngameDebugConsole | ||
8 | +{ | ||
9 | + public class DebugLogEntry | ||
10 | + { | ||
11 | + private const int HASH_NOT_CALCULATED = -623218; | ||
12 | + | ||
13 | + public string logString; | ||
14 | + public string stackTrace; | ||
15 | + private string completeLog; | ||
16 | + | ||
17 | + // Sprite to show with this entry | ||
18 | + public Sprite logTypeSpriteRepresentation; | ||
19 | + | ||
20 | + // Collapsed count | ||
21 | + public int count; | ||
22 | + | ||
23 | + // Index of this entry among all collapsed entries | ||
24 | + public int collapsedIndex; | ||
25 | + | ||
26 | + private int hashValue; | ||
27 | + | ||
28 | + public void Initialize( string logString, string stackTrace ) | ||
29 | + { | ||
30 | + this.logString = logString; | ||
31 | + this.stackTrace = stackTrace; | ||
32 | + | ||
33 | + completeLog = null; | ||
34 | + count = 1; | ||
35 | + hashValue = HASH_NOT_CALCULATED; | ||
36 | + } | ||
37 | + | ||
38 | + public void Clear() | ||
39 | + { | ||
40 | + logString = null; | ||
41 | + stackTrace = null; | ||
42 | + completeLog = null; | ||
43 | + } | ||
44 | + | ||
45 | + // Checks if logString or stackTrace contains the search term | ||
46 | + public bool MatchesSearchTerm( string searchTerm ) | ||
47 | + { | ||
48 | + return ( logString != null && DebugLogConsole.caseInsensitiveComparer.IndexOf( logString, searchTerm, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) >= 0 ) || | ||
49 | + ( stackTrace != null && DebugLogConsole.caseInsensitiveComparer.IndexOf( stackTrace, searchTerm, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) >= 0 ); | ||
50 | + } | ||
51 | + | ||
52 | + // Return a string containing complete information about this debug entry | ||
53 | + public override string ToString() | ||
54 | + { | ||
55 | + if( completeLog == null ) | ||
56 | + completeLog = string.Concat( logString, "\n", stackTrace ); | ||
57 | + | ||
58 | + return completeLog; | ||
59 | + } | ||
60 | + | ||
61 | + // Credit: https://stackoverflow.com/a/19250516/2373034 | ||
62 | + public int GetContentHashCode() | ||
63 | + { | ||
64 | + if( hashValue == HASH_NOT_CALCULATED ) | ||
65 | + { | ||
66 | + unchecked | ||
67 | + { | ||
68 | + hashValue = 17; | ||
69 | + hashValue = hashValue * 23 + ( logString == null ? 0 : logString.GetHashCode() ); | ||
70 | + hashValue = hashValue * 23 + ( stackTrace == null ? 0 : stackTrace.GetHashCode() ); | ||
71 | + } | ||
72 | + } | ||
73 | + | ||
74 | + return hashValue; | ||
75 | + } | ||
76 | + } | ||
77 | + | ||
78 | + public struct QueuedDebugLogEntry | ||
79 | + { | ||
80 | + public readonly string logString; | ||
81 | + public readonly string stackTrace; | ||
82 | + public readonly LogType logType; | ||
83 | + | ||
84 | + public QueuedDebugLogEntry( string logString, string stackTrace, LogType logType ) | ||
85 | + { | ||
86 | + this.logString = logString; | ||
87 | + this.stackTrace = stackTrace; | ||
88 | + this.logType = logType; | ||
89 | + } | ||
90 | + | ||
91 | + // Checks if logString or stackTrace contains the search term | ||
92 | + public bool MatchesSearchTerm( string searchTerm ) | ||
93 | + { | ||
94 | + return ( logString != null && DebugLogConsole.caseInsensitiveComparer.IndexOf( logString, searchTerm, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) >= 0 ) || | ||
95 | + ( stackTrace != null && DebugLogConsole.caseInsensitiveComparer.IndexOf( stackTrace, searchTerm, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace ) >= 0 ); | ||
96 | + } | ||
97 | + } | ||
98 | + | ||
99 | + public struct DebugLogEntryTimestamp | ||
100 | + { | ||
101 | + public readonly System.DateTime dateTime; | ||
102 | +#if !IDG_OMIT_ELAPSED_TIME | ||
103 | + public readonly float elapsedSeconds; | ||
104 | +#endif | ||
105 | +#if !IDG_OMIT_FRAMECOUNT | ||
106 | + public readonly int frameCount; | ||
107 | +#endif | ||
108 | + | ||
109 | +#if !IDG_OMIT_ELAPSED_TIME && !IDG_OMIT_FRAMECOUNT | ||
110 | + public DebugLogEntryTimestamp( System.DateTime dateTime, float elapsedSeconds, int frameCount ) | ||
111 | +#elif !IDG_OMIT_ELAPSED_TIME | ||
112 | + public DebugLogEntryTimestamp( System.DateTime dateTime, float elapsedSeconds ) | ||
113 | +#elif !IDG_OMIT_FRAMECOUNT | ||
114 | + public DebugLogEntryTimestamp( System.DateTime dateTime, int frameCount ) | ||
115 | +#else | ||
116 | + public DebugLogEntryTimestamp( System.DateTime dateTime ) | ||
117 | +#endif | ||
118 | + { | ||
119 | + this.dateTime = dateTime; | ||
120 | +#if !IDG_OMIT_ELAPSED_TIME | ||
121 | + this.elapsedSeconds = elapsedSeconds; | ||
122 | +#endif | ||
123 | +#if !IDG_OMIT_FRAMECOUNT | ||
124 | + this.frameCount = frameCount; | ||
125 | +#endif | ||
126 | + } | ||
127 | + | ||
128 | + public void AppendTime( StringBuilder sb ) | ||
129 | + { | ||
130 | + // Add DateTime in format: [HH:mm:ss] | ||
131 | + sb.Append( "[" ); | ||
132 | + | ||
133 | + int hour = dateTime.Hour; | ||
134 | + if( hour >= 10 ) | ||
135 | + sb.Append( hour ); | ||
136 | + else | ||
137 | + sb.Append( "0" ).Append( hour ); | ||
138 | + | ||
139 | + sb.Append( ":" ); | ||
140 | + | ||
141 | + int minute = dateTime.Minute; | ||
142 | + if( minute >= 10 ) | ||
143 | + sb.Append( minute ); | ||
144 | + else | ||
145 | + sb.Append( "0" ).Append( minute ); | ||
146 | + | ||
147 | + sb.Append( ":" ); | ||
148 | + | ||
149 | + int second = dateTime.Second; | ||
150 | + if( second >= 10 ) | ||
151 | + sb.Append( second ); | ||
152 | + else | ||
153 | + sb.Append( "0" ).Append( second ); | ||
154 | + | ||
155 | + sb.Append( "]" ); | ||
156 | + } | ||
157 | + | ||
158 | + public void AppendFullTimestamp( StringBuilder sb ) | ||
159 | + { | ||
160 | + AppendTime( sb ); | ||
161 | + | ||
162 | +#if !IDG_OMIT_ELAPSED_TIME && !IDG_OMIT_FRAMECOUNT | ||
163 | + // Append elapsed seconds and frame count in format: [1.0s at #Frame] | ||
164 | + sb.Append( "[" ).Append( elapsedSeconds.ToString( "F1" ) ).Append( "s at " ).Append( "#" ).Append( frameCount ).Append( "]" ); | ||
165 | +#elif !IDG_OMIT_ELAPSED_TIME | ||
166 | + // Append elapsed seconds in format: [1.0s] | ||
167 | + sb.Append( "[" ).Append( elapsedSeconds.ToString( "F1" ) ).Append( "s]" ); | ||
168 | +#elif !IDG_OMIT_FRAMECOUNT | ||
169 | + // Append frame count in format: [#Frame] | ||
170 | + sb.Append( "[#" ).Append( frameCount ).Append( "]" ); | ||
171 | +#endif | ||
172 | + } | ||
173 | + } | ||
174 | + | ||
175 | + public class DebugLogEntryContentEqualityComparer : EqualityComparer<DebugLogEntry> | ||
176 | + { | ||
177 | + public override bool Equals( DebugLogEntry x, DebugLogEntry y ) | ||
178 | + { | ||
179 | + return x.logString == y.logString && x.stackTrace == y.stackTrace; | ||
180 | + } | ||
181 | + | ||
182 | + public override int GetHashCode( DebugLogEntry obj ) | ||
183 | + { | ||
184 | + return obj.GetContentHashCode(); | ||
185 | + } | ||
186 | + } | ||
187 | +} |
1 | +using UnityEngine; | ||
2 | +using UnityEngine.UI; | ||
3 | +using UnityEngine.EventSystems; | ||
4 | +using System.Text; | ||
5 | +#if UNITY_EDITOR | ||
6 | +using UnityEditor; | ||
7 | +using System.Text.RegularExpressions; | ||
8 | +#endif | ||
9 | + | ||
10 | +// A UI element to show information about a debug entry | ||
11 | +namespace IngameDebugConsole | ||
12 | +{ | ||
13 | + public class DebugLogItem : MonoBehaviour, IPointerClickHandler | ||
14 | + { | ||
15 | + #region Platform Specific Elements | ||
16 | +#if !UNITY_2018_1_OR_NEWER | ||
17 | +#if !UNITY_EDITOR && UNITY_ANDROID | ||
18 | + private static AndroidJavaClass m_ajc = null; | ||
19 | + private static AndroidJavaClass AJC | ||
20 | + { | ||
21 | + get | ||
22 | + { | ||
23 | + if( m_ajc == null ) | ||
24 | + m_ajc = new AndroidJavaClass( "com.yasirkula.unity.DebugConsole" ); | ||
25 | + | ||
26 | + return m_ajc; | ||
27 | + } | ||
28 | + } | ||
29 | + | ||
30 | + private static AndroidJavaObject m_context = null; | ||
31 | + private static AndroidJavaObject Context | ||
32 | + { | ||
33 | + get | ||
34 | + { | ||
35 | + if( m_context == null ) | ||
36 | + { | ||
37 | + using( AndroidJavaObject unityClass = new AndroidJavaClass( "com.unity3d.player.UnityPlayer" ) ) | ||
38 | + { | ||
39 | + m_context = unityClass.GetStatic<AndroidJavaObject>( "currentActivity" ); | ||
40 | + } | ||
41 | + } | ||
42 | + | ||
43 | + return m_context; | ||
44 | + } | ||
45 | + } | ||
46 | +#elif !UNITY_EDITOR && UNITY_IOS | ||
47 | + [System.Runtime.InteropServices.DllImport( "__Internal" )] | ||
48 | + private static extern void _DebugConsole_CopyText( string text ); | ||
49 | +#endif | ||
50 | +#endif | ||
51 | + #endregion | ||
52 | + | ||
53 | +#pragma warning disable 0649 | ||
54 | + // Cached components | ||
55 | + [SerializeField] | ||
56 | + private RectTransform transformComponent; | ||
57 | + public RectTransform Transform { get { return transformComponent; } } | ||
58 | + | ||
59 | + [SerializeField] | ||
60 | + private Image imageComponent; | ||
61 | + public Image Image { get { return imageComponent; } } | ||
62 | + | ||
63 | + [SerializeField] | ||
64 | + private CanvasGroup canvasGroupComponent; | ||
65 | + public CanvasGroup CanvasGroup { get { return canvasGroupComponent; } } | ||
66 | + | ||
67 | + [SerializeField] | ||
68 | + private Text logText; | ||
69 | + [SerializeField] | ||
70 | + private Image logTypeImage; | ||
71 | + | ||
72 | + // Objects related to the collapsed count of the debug entry | ||
73 | + [SerializeField] | ||
74 | + private GameObject logCountParent; | ||
75 | + [SerializeField] | ||
76 | + private Text logCountText; | ||
77 | + | ||
78 | + [SerializeField] | ||
79 | + private RectTransform copyLogButton; | ||
80 | +#pragma warning restore 0649 | ||
81 | + | ||
82 | + // Debug entry to show with this log item | ||
83 | + private DebugLogEntry logEntry; | ||
84 | + public DebugLogEntry Entry { get { return logEntry; } } | ||
85 | + | ||
86 | + private DebugLogEntryTimestamp? logEntryTimestamp; | ||
87 | + public DebugLogEntryTimestamp? Timestamp { get { return logEntryTimestamp; } } | ||
88 | + | ||
89 | + // Index of the entry in the list of entries | ||
90 | + [System.NonSerialized] public int Index; | ||
91 | + | ||
92 | + private bool isExpanded; | ||
93 | + public bool Expanded { get { return isExpanded; } } | ||
94 | + | ||
95 | + private Vector2 logTextOriginalPosition; | ||
96 | + private Vector2 logTextOriginalSize; | ||
97 | + private float copyLogButtonHeight; | ||
98 | + | ||
99 | + private DebugLogRecycledListView listView; | ||
100 | + | ||
101 | + public void Initialize( DebugLogRecycledListView listView ) | ||
102 | + { | ||
103 | + this.listView = listView; | ||
104 | + | ||
105 | + logTextOriginalPosition = logText.rectTransform.anchoredPosition; | ||
106 | + logTextOriginalSize = logText.rectTransform.sizeDelta; | ||
107 | + copyLogButtonHeight = copyLogButton.anchoredPosition.y + copyLogButton.sizeDelta.y + 2f; // 2f: space between text and button | ||
108 | + | ||
109 | +#if !UNITY_EDITOR && UNITY_WEBGL | ||
110 | + copyLogButton.gameObject.AddComponent<DebugLogItemCopyWebGL>().Initialize( this ); | ||
111 | +#endif | ||
112 | + } | ||
113 | + | ||
114 | + public void SetContent( DebugLogEntry logEntry, DebugLogEntryTimestamp? logEntryTimestamp, int entryIndex, bool isExpanded ) | ||
115 | + { | ||
116 | + this.logEntry = logEntry; | ||
117 | + this.logEntryTimestamp = logEntryTimestamp; | ||
118 | + this.Index = entryIndex; | ||
119 | + this.isExpanded = isExpanded; | ||
120 | + | ||
121 | + Vector2 size = transformComponent.sizeDelta; | ||
122 | + if( isExpanded ) | ||
123 | + { | ||
124 | + logText.horizontalOverflow = HorizontalWrapMode.Wrap; | ||
125 | + size.y = listView.SelectedItemHeight; | ||
126 | + | ||
127 | + if( !copyLogButton.gameObject.activeSelf ) | ||
128 | + { | ||
129 | + copyLogButton.gameObject.SetActive( true ); | ||
130 | + | ||
131 | + logText.rectTransform.anchoredPosition = new Vector2( logTextOriginalPosition.x, logTextOriginalPosition.y + copyLogButtonHeight * 0.5f ); | ||
132 | + logText.rectTransform.sizeDelta = logTextOriginalSize - new Vector2( 0f, copyLogButtonHeight ); | ||
133 | + } | ||
134 | + } | ||
135 | + else | ||
136 | + { | ||
137 | + logText.horizontalOverflow = HorizontalWrapMode.Overflow; | ||
138 | + size.y = listView.ItemHeight; | ||
139 | + | ||
140 | + if( copyLogButton.gameObject.activeSelf ) | ||
141 | + { | ||
142 | + copyLogButton.gameObject.SetActive( false ); | ||
143 | + | ||
144 | + logText.rectTransform.anchoredPosition = logTextOriginalPosition; | ||
145 | + logText.rectTransform.sizeDelta = logTextOriginalSize; | ||
146 | + } | ||
147 | + } | ||
148 | + | ||
149 | + transformComponent.sizeDelta = size; | ||
150 | + | ||
151 | + SetText( logEntry, logEntryTimestamp, isExpanded ); | ||
152 | + logTypeImage.sprite = logEntry.logTypeSpriteRepresentation; | ||
153 | + } | ||
154 | + | ||
155 | + // Show the collapsed count of the debug entry | ||
156 | + public void ShowCount() | ||
157 | + { | ||
158 | + logCountText.text = logEntry.count.ToString(); | ||
159 | + | ||
160 | + if( !logCountParent.activeSelf ) | ||
161 | + logCountParent.SetActive( true ); | ||
162 | + } | ||
163 | + | ||
164 | + // Hide the collapsed count of the debug entry | ||
165 | + public void HideCount() | ||
166 | + { | ||
167 | + if( logCountParent.activeSelf ) | ||
168 | + logCountParent.SetActive( false ); | ||
169 | + } | ||
170 | + | ||
171 | + // Update the debug entry's displayed timestamp | ||
172 | + public void UpdateTimestamp( DebugLogEntryTimestamp timestamp ) | ||
173 | + { | ||
174 | + logEntryTimestamp = timestamp; | ||
175 | + | ||
176 | + if( isExpanded || listView.manager.alwaysDisplayTimestamps ) | ||
177 | + SetText( logEntry, timestamp, isExpanded ); | ||
178 | + } | ||
179 | + | ||
180 | + private void SetText( DebugLogEntry logEntry, DebugLogEntryTimestamp? logEntryTimestamp, bool isExpanded ) | ||
181 | + { | ||
182 | + if( !logEntryTimestamp.HasValue || ( !isExpanded && !listView.manager.alwaysDisplayTimestamps ) ) | ||
183 | + logText.text = isExpanded ? logEntry.ToString() : logEntry.logString; | ||
184 | + else | ||
185 | + { | ||
186 | + StringBuilder sb = listView.manager.sharedStringBuilder; | ||
187 | + sb.Length = 0; | ||
188 | + | ||
189 | + if( isExpanded ) | ||
190 | + { | ||
191 | + logEntryTimestamp.Value.AppendFullTimestamp( sb ); | ||
192 | + sb.Append( ": " ).Append( logEntry.ToString() ); | ||
193 | + } | ||
194 | + else | ||
195 | + { | ||
196 | + logEntryTimestamp.Value.AppendTime( sb ); | ||
197 | + sb.Append( " " ).Append( logEntry.logString ); | ||
198 | + } | ||
199 | + | ||
200 | + logText.text = sb.ToString(); | ||
201 | + } | ||
202 | + } | ||
203 | + | ||
204 | + // This log item is clicked, show the debug entry's stack trace | ||
205 | + public void OnPointerClick( PointerEventData eventData ) | ||
206 | + { | ||
207 | +#if UNITY_EDITOR | ||
208 | + if( eventData.button == PointerEventData.InputButton.Right ) | ||
209 | + { | ||
210 | + Match regex = Regex.Match( logEntry.stackTrace, @"\(at .*\.cs:[0-9]+\)$", RegexOptions.Multiline ); | ||
211 | + if( regex.Success ) | ||
212 | + { | ||
213 | + string line = logEntry.stackTrace.Substring( regex.Index + 4, regex.Length - 5 ); | ||
214 | + int lineSeparator = line.IndexOf( ':' ); | ||
215 | + MonoScript script = AssetDatabase.LoadAssetAtPath<MonoScript>( line.Substring( 0, lineSeparator ) ); | ||
216 | + if( script != null ) | ||
217 | + AssetDatabase.OpenAsset( script, int.Parse( line.Substring( lineSeparator + 1 ) ) ); | ||
218 | + } | ||
219 | + } | ||
220 | + else | ||
221 | + listView.OnLogItemClicked( this ); | ||
222 | +#else | ||
223 | + listView.OnLogItemClicked( this ); | ||
224 | +#endif | ||
225 | + } | ||
226 | + | ||
227 | + public void CopyLog() | ||
228 | + { | ||
229 | +#if UNITY_EDITOR || !UNITY_WEBGL | ||
230 | + string log = GetCopyContent(); | ||
231 | + if( string.IsNullOrEmpty( log ) ) | ||
232 | + return; | ||
233 | + | ||
234 | +#if UNITY_EDITOR || UNITY_2018_1_OR_NEWER || ( !UNITY_ANDROID && !UNITY_IOS ) | ||
235 | + GUIUtility.systemCopyBuffer = log; | ||
236 | +#elif UNITY_ANDROID | ||
237 | + AJC.CallStatic( "CopyText", Context, log ); | ||
238 | +#elif UNITY_IOS | ||
239 | + _DebugConsole_CopyText( log ); | ||
240 | +#endif | ||
241 | +#endif | ||
242 | + } | ||
243 | + | ||
244 | + internal string GetCopyContent() | ||
245 | + { | ||
246 | + if( !logEntryTimestamp.HasValue ) | ||
247 | + return logEntry.ToString(); | ||
248 | + else | ||
249 | + { | ||
250 | + StringBuilder sb = listView.manager.sharedStringBuilder; | ||
251 | + sb.Length = 0; | ||
252 | + | ||
253 | + logEntryTimestamp.Value.AppendFullTimestamp( sb ); | ||
254 | + sb.Append( ": " ).Append( logEntry.ToString() ); | ||
255 | + | ||
256 | + return sb.ToString(); | ||
257 | + } | ||
258 | + } | ||
259 | + | ||
260 | + public float CalculateExpandedHeight( DebugLogEntry logEntry, DebugLogEntryTimestamp? logEntryTimestamp ) | ||
261 | + { | ||
262 | + string text = logText.text; | ||
263 | + HorizontalWrapMode wrapMode = logText.horizontalOverflow; | ||
264 | + | ||
265 | + SetText( logEntry, logEntryTimestamp, true ); | ||
266 | + logText.horizontalOverflow = HorizontalWrapMode.Wrap; | ||
267 | + | ||
268 | + float result = logText.preferredHeight + copyLogButtonHeight; | ||
269 | + | ||
270 | + logText.text = text; | ||
271 | + logText.horizontalOverflow = wrapMode; | ||
272 | + | ||
273 | + return Mathf.Max( listView.ItemHeight, result ); | ||
274 | + } | ||
275 | + | ||
276 | + // Return a string containing complete information about the debug entry | ||
277 | + public override string ToString() | ||
278 | + { | ||
279 | + return logEntry.ToString(); | ||
280 | + } | ||
281 | + } | ||
282 | +} |
1 | +#if !UNITY_EDITOR && UNITY_WEBGL | ||
2 | +using System.Runtime.InteropServices; | ||
3 | +using UnityEngine; | ||
4 | +using UnityEngine.EventSystems; | ||
5 | + | ||
6 | +namespace IngameDebugConsole | ||
7 | +{ | ||
8 | + public class DebugLogItemCopyWebGL : MonoBehaviour, IPointerDownHandler, IPointerUpHandler | ||
9 | + { | ||
10 | + [DllImport( "__Internal" )] | ||
11 | + private static extern void IngameDebugConsoleStartCopy( string textToCopy ); | ||
12 | + [DllImport( "__Internal" )] | ||
13 | + private static extern void IngameDebugConsoleCancelCopy(); | ||
14 | + | ||
15 | + private DebugLogItem logItem; | ||
16 | + | ||
17 | + public void Initialize( DebugLogItem logItem ) | ||
18 | + { | ||
19 | + this.logItem = logItem; | ||
20 | + } | ||
21 | + | ||
22 | + public void OnPointerDown( PointerEventData eventData ) | ||
23 | + { | ||
24 | + string log = logItem.GetCopyContent(); | ||
25 | + if( !string.IsNullOrEmpty( log ) ) | ||
26 | + IngameDebugConsoleStartCopy( log ); | ||
27 | + } | ||
28 | + | ||
29 | + public void OnPointerUp( PointerEventData eventData ) | ||
30 | + { | ||
31 | + if( eventData.dragging ) | ||
32 | + IngameDebugConsoleCancelCopy(); | ||
33 | + } | ||
34 | + } | ||
35 | +} | ||
36 | +#endif |
-
Please register or login to post a comment