├── .gitignore ├── .idea └── .idea.CommandConsole │ └── .idea │ ├── .gitignore │ ├── encodings.xml │ ├── indexLayout.xml │ └── vcs.xml ├── .vscode └── settings.json ├── Assets ├── CommandLine.meta ├── CommandLine │ ├── Assets.meta │ ├── Assets │ │ ├── FIXEDSYS-EXCELSIOR-301.TTF │ │ ├── FIXEDSYS-EXCELSIOR-301.TTF.meta │ │ ├── terminal-default-theme @v0.12.prefab │ │ └── terminal-default-theme @v0.12.prefab.meta │ ├── Scripts.meta │ └── Scripts │ │ ├── Console.meta │ │ ├── Console │ │ ├── Attributes.cs │ │ ├── Attributes.cs.meta │ │ ├── CommandSystem.cs │ │ ├── CommandSystem.cs.meta │ │ ├── ConsoleSystem.cs │ │ ├── ConsoleSystem.cs.meta │ │ ├── Interfaces.cs │ │ ├── Interfaces.cs.meta │ │ ├── Utils.cs │ │ └── Utils.cs.meta │ │ ├── Core.meta │ │ ├── Core │ │ ├── Exception.cs │ │ ├── Exception.cs.meta │ │ ├── Lexer.cs │ │ ├── Lexer.cs.meta │ │ ├── SuggestionQuery.cs │ │ ├── SuggestionQuery.cs.meta │ │ ├── SyntaxAnalyzer.cs │ │ ├── SyntaxAnalyzer.cs.meta │ │ ├── Utils.cs │ │ ├── Utils.cs.meta │ │ ├── VirtualMachine.cs │ │ └── VirtualMachine.cs.meta │ │ ├── Helper.meta │ │ ├── Helper │ │ ├── DebugHelper.cs │ │ └── DebugHelper.cs.meta │ │ ├── UnityImpl.meta │ │ └── UnityImpl │ │ ├── GameConsole.cs │ │ ├── GameConsole.cs.meta │ │ ├── GameConsoleAlternativeOptionsPanel.cs │ │ ├── GameConsoleAlternativeOptionsPanel.cs.meta │ │ ├── GameConsoleHeader.cs │ │ ├── GameConsoleHeader.cs.meta │ │ ├── GameConsoleInput.cs │ │ ├── GameConsoleInput.cs.meta │ │ ├── GameConsoleRenderer.cs │ │ └── GameConsoleRenderer.cs.meta ├── Example.meta └── Example │ ├── CommandExample.cs │ ├── CommandExample.cs.meta │ ├── ConsoleExampleScene.unity │ ├── ConsoleExampleScene.unity.meta │ ├── Example.cs │ └── Example.cs.meta ├── LICENSE ├── Packages ├── manifest.json └── packages-lock.json ├── ProjectSettings ├── AudioManager.asset ├── AutoStreamingSettings.asset ├── ClusterInputManager.asset ├── DynamicsManager.asset ├── EditorBuildSettings.asset ├── EditorSettings.asset ├── GraphicsSettings.asset ├── InputManager.asset ├── MemorySettings.asset ├── NavMeshAreas.asset ├── PackageManagerSettings.asset ├── Packages │ └── com.unity.testtools.codecoverage │ │ └── Settings.json ├── Physics2DSettings.asset ├── PresetManager.asset ├── ProjectSettings.asset ├── ProjectVersion.txt ├── QualitySettings.asset ├── SceneTemplateSettings.json ├── ShaderGraphSettings.asset ├── TagManager.asset ├── TimeManager.asset ├── URPProjectSettings.asset ├── UnityConnectSettings.asset ├── VFXManager.asset ├── VersionControlSettings.asset ├── XRSettings.asset └── boot.config ├── README-ch.md ├── README.md ├── Res ├── Untitled (1).png ├── Untitled.png ├── screen-shot.png ├── usage-part-1.png ├── usage.mp4 └── 屏幕截图 2024-01-12 173800.png └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | # This .gitignore file should be placed at the root of your Unity project directory 2 | # 3 | # Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore 4 | # 5 | /[Ll]ibrary/ 6 | /[Tt]emp/ 7 | /[Oo]bj/ 8 | /[Bb]uild/ 9 | /[Bb]uilds/ 10 | /[Ll]ogs/ 11 | /[Uu]ser[Ss]ettings/ 12 | 13 | # MemoryCaptures can get excessive in size. 14 | # They also could contain extremely sensitive data 15 | /[Mm]emoryCaptures/ 16 | 17 | # Recordings can get excessive in size 18 | /[Rr]ecordings/ 19 | 20 | # Uncomment this line if you wish to ignore the asset store tools plugin 21 | # /[Aa]ssets/AssetStoreTools* 22 | 23 | # Autogenerated Jetbrains Rider plugin 24 | /[Aa]ssets/Plugins/Editor/JetBrains* 25 | 26 | # Visual Studio cache directory 27 | .vs/ 28 | 29 | # Gradle cache directory 30 | .gradle/ 31 | 32 | # Autogenerated VS/MD/Consulo solution and project files 33 | ExportedObj/ 34 | .consulo/ 35 | *.csproj 36 | *.unityproj 37 | *.sln 38 | *.suo 39 | *.tmp 40 | *.user 41 | *.userprefs 42 | *.pidb 43 | *.booproj 44 | *.svd 45 | *.pdb 46 | *.mdb 47 | *.opendb 48 | *.VC.db 49 | 50 | # Unity3D generated meta files 51 | *.pidb.meta 52 | *.pdb.meta 53 | *.mdb.meta 54 | 55 | # Unity3D generated file on crash reports 56 | sysinfo.txt 57 | 58 | # Builds 59 | *.apk 60 | *.aab 61 | *.unitypackage 62 | *.app 63 | 64 | # Crashlytics generated file 65 | crashlytics-build.properties 66 | 67 | # Packed Addressables 68 | /[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* 69 | 70 | # Temporary auto-generated Android Assets 71 | /[Aa]ssets/[Ss]treamingAssets/aa.meta 72 | /[Aa]ssets/[Ss]treamingAssets/aa/* 73 | -------------------------------------------------------------------------------- /.idea/.idea.CommandConsole/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /contentModel.xml 6 | /.idea.CommandConsole.iml 7 | /projectSettingsUpdater.xml 8 | /modules.xml 9 | # Editor-based HTTP Client requests 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | -------------------------------------------------------------------------------- /.idea/.idea.CommandConsole/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/.idea.CommandConsole/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/.idea.CommandConsole/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": 3 | { 4 | "**/.DS_Store":true, 5 | "**/.git":true, 6 | "**/.gitmodules":true, 7 | "**/*.booproj":true, 8 | "**/*.pidb":true, 9 | "**/*.suo":true, 10 | "**/*.user":true, 11 | "**/*.userprefs":true, 12 | "**/*.unityproj":true, 13 | "**/*.dll":true, 14 | "**/*.exe":true, 15 | "**/*.pdf":true, 16 | "**/*.mid":true, 17 | "**/*.midi":true, 18 | "**/*.wav":true, 19 | "**/*.gif":true, 20 | "**/*.ico":true, 21 | "**/*.jpg":true, 22 | "**/*.jpeg":true, 23 | "**/*.png":true, 24 | "**/*.psd":true, 25 | "**/*.tga":true, 26 | "**/*.tif":true, 27 | "**/*.tiff":true, 28 | "**/*.3ds":true, 29 | "**/*.3DS":true, 30 | "**/*.fbx":true, 31 | "**/*.FBX":true, 32 | "**/*.lxo":true, 33 | "**/*.LXO":true, 34 | "**/*.ma":true, 35 | "**/*.MA":true, 36 | "**/*.obj":true, 37 | "**/*.OBJ":true, 38 | "**/*.asset":true, 39 | "**/*.cubemap":true, 40 | "**/*.flare":true, 41 | "**/*.mat":true, 42 | "**/*.meta":true, 43 | "**/*.prefab":true, 44 | "**/*.unity":true, 45 | "build/":true, 46 | "Build/":true, 47 | "Library/":true, 48 | "library/":true, 49 | "obj/":true, 50 | "Obj/":true, 51 | "ProjectSettings/":true, 52 | "temp/":true, 53 | "Temp/":true 54 | } 55 | } -------------------------------------------------------------------------------- /Assets/CommandLine.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 135230210c8dde14382abbefd36f2b80 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/CommandLine/Assets.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0015d6c4e186ce6439de8d6f33078122 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/CommandLine/Assets/FIXEDSYS-EXCELSIOR-301.TTF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/529324416/UnityCommandLineInterface/574198026618102dc3906a532bde063713ac85b7/Assets/CommandLine/Assets/FIXEDSYS-EXCELSIOR-301.TTF -------------------------------------------------------------------------------- /Assets/CommandLine/Assets/FIXEDSYS-EXCELSIOR-301.TTF.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7c17a58733c11a643a27f5f4f0a36359 3 | TrueTypeFontImporter: 4 | externalObjects: {} 5 | serializedVersion: 4 6 | fontSize: 16 7 | forceTextureCase: -2 8 | characterSpacing: 0 9 | characterPadding: 1 10 | includeFontData: 1 11 | fontNames: 12 | - Fixedsys Excelsior 3.01 13 | fallbackFontReferences: [] 14 | customCharacters: 15 | fontRenderingMode: 0 16 | ascentCalculationMode: 1 17 | useLegacyBoundsCalculation: 0 18 | shouldRoundAdvanceValue: 1 19 | userData: 20 | assetBundleName: 21 | assetBundleVariant: 22 | -------------------------------------------------------------------------------- /Assets/CommandLine/Assets/terminal-default-theme @v0.12.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 27240d217acc77844a8e410e5841b762 3 | PrefabImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0adedea0fa4d0564398db8bbac09d1f1 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Console.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a6d813add122f92438773e75740b8d59 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Console/Attributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RedSaw.CommandLineInterface{ 4 | 5 | /// 6 | /// command attribute 7 | /// any static method has attach this attribute would be treated 8 | /// as a command method, and it woule be collected by command system automatically 9 | /// 10 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] 11 | public class CommandAttribute : Attribute{ 12 | 13 | /// name of command 14 | public string Name { get; private set; } 15 | 16 | /// description of command 17 | public string Desc { get; set; } 18 | 19 | /// tag of command, for you can query commands by tag, or constraint commands by tag 20 | public string Tag { get; set; } 21 | 22 | public CommandAttribute(string name){ 23 | 24 | this.Name = name; 25 | this.Tag = null; 26 | this.Desc = "command has no description"; 27 | } 28 | public CommandAttribute(){ 29 | 30 | this.Name = null; 31 | this.Tag = null; 32 | this.Desc = "command has no description"; 33 | } 34 | } 35 | 36 | /// 37 | /// a static method which defined this attribute would be treated as 38 | /// parameter parser. 39 | /// 40 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] 41 | public class CommandValueParserAttribute : Attribute{ 42 | 43 | public readonly Type type; 44 | public string Alias { get; set; } 45 | 46 | public CommandValueParserAttribute(Type type){ 47 | 48 | this.type = type; 49 | this.Alias = null; 50 | } 51 | } 52 | 53 | /// 54 | /// any static property or field marked by CommandPropertyAttribute could be visit directly by @ in command line 55 | /// 56 | [AttributeUsage(AttributeTargets.Property|AttributeTargets.Field, AllowMultiple = false, Inherited = false)] 57 | public class CommandPropertyAttribute : Attribute{ 58 | 59 | public readonly string Name; 60 | public readonly string Tag; 61 | public readonly string Desc; 62 | 63 | public CommandPropertyAttribute(string name){ 64 | 65 | this.Name = name; 66 | this.Desc = "property has no description"; 67 | this.Tag = null; 68 | } 69 | public CommandPropertyAttribute(){ 70 | 71 | this.Name = null; 72 | this.Desc = "property has no description"; 73 | this.Tag = null; 74 | } 75 | } 76 | 77 | 78 | } -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Console/Attributes.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 676d2bd05e7b75a40a6d7afef6396e51 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Console/CommandSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3009c9077360c214385554b3a9f3e2f3 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Console/ConsoleSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | namespace RedSaw.CommandLineInterface{ 6 | 7 | 8 | 9 | /// use to record command input history 10 | class InputHistory{ 11 | 12 | readonly List history; 13 | readonly int capacity; 14 | int lastIndex; 15 | 16 | public InputHistory(int capacity){ 17 | 18 | this.lastIndex = 0; 19 | this.capacity = capacity; 20 | this.history = new List(); 21 | } 22 | /// record input string 23 | public void Record(string input){ 24 | 25 | if(history.Count == capacity){ 26 | history.RemoveAt(0); 27 | } 28 | history.Add(input); 29 | lastIndex = history.Count - 1; 30 | } 31 | 32 | /// get last input string 33 | public string Last{ 34 | get{ 35 | if(history.Count == 0)return string.Empty; 36 | if(lastIndex < 0 || lastIndex >= history.Count)lastIndex = history.Count - 1; 37 | return history[lastIndex --]; 38 | } 39 | } 40 | 41 | /// get next input string 42 | public string Next{ 43 | get{ 44 | if(history.Count == 0)return string.Empty; 45 | if(lastIndex >= history.Count || lastIndex < 0)lastIndex = 0; 46 | return history[lastIndex ++]; 47 | } 48 | } 49 | } 50 | 51 | 52 | /// command selector 53 | class LinearSelector{ 54 | 55 | public event Action OnSelectionChanged; 56 | readonly List optionsBuffer; 57 | int currentIndex; 58 | 59 | int TotalCount{ 60 | get{ 61 | if(optionsBuffer == null)return 0; 62 | return optionsBuffer.Count; 63 | } 64 | } 65 | 66 | /// 67 | /// the current selection of the alternative options 68 | /// if value is -1, it means there's no alternative options choosed 69 | /// 70 | public int SelectionIndex => currentIndex; 71 | 72 | public LinearSelector(){ 73 | 74 | this.optionsBuffer = new(); 75 | this.currentIndex = -1; 76 | } 77 | 78 | public void LoadOptions(List options){ 79 | 80 | this.optionsBuffer.Clear(); 81 | this.optionsBuffer.AddRange(options); 82 | this.currentIndex = -1; 83 | } 84 | 85 | public bool GetCurrentSelection(out string selection){ 86 | 87 | selection = string.Empty; 88 | if(currentIndex == -1)return false; 89 | selection = optionsBuffer[currentIndex]; 90 | return true; 91 | } 92 | 93 | /// move to next alternative option 94 | public void MoveNext(){ 95 | 96 | if(TotalCount == 0)return; 97 | if(currentIndex == -1){ 98 | currentIndex = 0; 99 | OnSelectionChanged?.Invoke(currentIndex); 100 | return; 101 | } 102 | currentIndex = currentIndex < TotalCount - 1 ? currentIndex + 1 : 0; 103 | OnSelectionChanged?.Invoke(currentIndex); 104 | } 105 | 106 | /// move to last alternative option 107 | public void MoveLast(){ 108 | 109 | if(TotalCount == 0)return; 110 | if(currentIndex == -1){ 111 | currentIndex = TotalCount - 1; 112 | OnSelectionChanged?.Invoke(currentIndex); 113 | return; 114 | } 115 | currentIndex = currentIndex > 0 ? currentIndex - 1 : TotalCount - 1; 116 | OnSelectionChanged?.Invoke(currentIndex); 117 | } 118 | } 119 | 120 | 121 | public class LogManager where T : Enum{ 122 | /* about output logs */ 123 | 124 | /// console log 125 | public readonly struct Log{ 126 | 127 | public static Log Empty => new(string.Empty, default); 128 | 129 | public readonly string message; 130 | public readonly string color; 131 | public readonly T logType; 132 | 133 | public readonly bool HasColor => 134 | color != null && color.Length > 0; 135 | 136 | public Log(string message, string color, T logType = default){ 137 | 138 | this.message = message; 139 | this.color = color; 140 | this.logType = logType; 141 | } 142 | 143 | public Log(string message, T logType = default){ 144 | 145 | this.message = message; 146 | this.color = null; 147 | this.logType = logType; 148 | } 149 | public override string ToString(){ 150 | return color != null ? $"{message}" : message; 151 | } 152 | } 153 | 154 | /// 155 | /// this event would triggered while receive messages 156 | /// 157 | public event Action OnReceivedMessage; 158 | readonly int logCapacity; 159 | readonly bool hasLogCapacity; 160 | 161 | readonly List logs = new(); 162 | 163 | /// 164 | /// initialize log manager 165 | /// 166 | /// the capacity of logs, -1 as infinite 167 | public LogManager(int logCapacity = -1){ 168 | 169 | this.logCapacity = logCapacity; 170 | this.hasLogCapacity = logCapacity > 0; 171 | } 172 | 173 | /// 174 | /// output a debug log on console, if info is null, then the tag would work on it 175 | /// 176 | public void Output(string info, T type = default){ 177 | 178 | if( info == null || info.Length == 0 ){ 179 | Output(Log.Empty); 180 | return; 181 | } 182 | Output(new(info, type)); 183 | } 184 | /// 185 | /// output a debug log on console, if info is null, then the tag would work on it, 186 | /// the color should in format of '#ffffff'; 187 | /// 188 | public void Output(string info, string color, T type = default){ 189 | 190 | if( color == null || color.Length == 0 ){ 191 | Output(info, type); 192 | return; 193 | } 194 | if( info == null || info.Length == 0 ){ 195 | Output(Log.Empty); 196 | return; 197 | } 198 | Output(new(info, color, type)); 199 | } 200 | 201 | void Output(Log log){ 202 | /* trigger event */ 203 | 204 | logs.Add(log); 205 | if( hasLogCapacity && logs.Count >= logCapacity ){ 206 | logs.RemoveAt(0); 207 | } 208 | OnReceivedMessage?.Invoke(log); 209 | } 210 | 211 | /// 212 | /// get total logs of current console 213 | /// 214 | public IEnumerable AllLogs => logs; 215 | 216 | /// 217 | /// get last logs 218 | /// 219 | public IEnumerable GetLastLogs( int count ){ 220 | 221 | if( count == 0 )yield break; 222 | count = Math.Min(count, logs.Count); 223 | int L = logs.Count - count; 224 | int R = L + count; 225 | for( int i = L; i < R; i ++ ){ 226 | yield return logs[i]; 227 | } 228 | } 229 | 230 | /// 231 | /// get last logs with target tag 232 | /// 233 | public IEnumerable GetLastLogs( int count, T type = default ){ 234 | 235 | if( count == 0 )yield break; 236 | foreach(var log in logs){ 237 | if( log.logType.Equals(type) ){ 238 | yield return log; 239 | } 240 | if( -- count <= 0 )break; 241 | } 242 | } 243 | } 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | /// command console 262 | public class ConsoleController where TLog : Enum{ 263 | 264 | readonly IConsoleRenderer renderer; 265 | readonly IConsoleInput userInput; 266 | readonly CommandSystem commandSystem; 267 | readonly LogManager logManager; 268 | 269 | public event Action OnFocusOut; 270 | public event Action OnFocus; 271 | 272 | readonly int alternativeCommandCount; 273 | readonly bool shouldRecordFailedCommand; 274 | readonly bool outputWithTime; 275 | readonly bool outputStackTraceOfCommandExecution; 276 | 277 | readonly InputHistory inputHistory; 278 | readonly LinearSelector selector; 279 | bool ignoreTextChanged; 280 | 281 | /// initialize console 282 | /// the renderer of console 283 | /// the input of console 284 | 285 | #region About Command System 286 | /// the capacity of command query cache, 20 as default 287 | /// 288 | /// should output stack trace of command excution, it maybe too long.. 289 | /// only use for debug 290 | #endregion 291 | 292 | /// the capacity of input history 293 | /// the count of alternative command options 294 | /// should record failed command input 295 | /// the capacity of output panel 296 | /// should output with time information of [HH:mm:ss] 297 | 298 | /// 299 | public ConsoleController( 300 | IConsoleRenderer renderer, 301 | IConsoleInput userInput, 302 | 303 | int logCapacity = -1, 304 | int inputHistoryCapacity = 20, 305 | int commandQueryCacheCapacity = 20, 306 | int alternativeCommandCount = 8, 307 | bool shouldRecordFailedCommand = true, 308 | bool outputWithTime = true, 309 | bool outputStackTraceOfCommandExecution = true 310 | ){ 311 | // about renderer 312 | this.renderer = renderer; 313 | renderer.BindOnSubmit(OnSubmit); 314 | renderer.BindOnTextChanged(OnTextChanged); 315 | 316 | // about user input 317 | this.userInput = userInput; 318 | 319 | // about command system 320 | commandSystem = new CommandSystem( 321 | commandQueryCacheCapacity:commandQueryCacheCapacity 322 | ); 323 | 324 | logManager = new LogManager(logCapacity); 325 | logManager.OnReceivedMessage += log => { 326 | 327 | if(log.HasColor){ 328 | renderer.Output(log.message, log.color); 329 | return; 330 | } 331 | renderer.Output(log.message); 332 | }; 333 | 334 | // other things - input history 335 | inputHistory = new InputHistory(Math.Max(inputHistoryCapacity, 2)); 336 | 337 | // other things - alternative options 338 | selector = new LinearSelector(); 339 | this.selector.OnSelectionChanged += idx => { 340 | renderer.AlternativeOptionsIndex = idx; 341 | renderer.MoveCursorToEnd(); 342 | }; 343 | 344 | this.alternativeCommandCount = Math.Max(alternativeCommandCount, 1); 345 | this.shouldRecordFailedCommand = shouldRecordFailedCommand; 346 | this.outputWithTime = outputWithTime; 347 | this.outputStackTraceOfCommandExecution = outputStackTraceOfCommandExecution; 348 | } 349 | public void Update(){ 350 | 351 | if(userInput.ShowOrHide){ 352 | renderer.IsVisible = !renderer.IsVisible; 353 | } 354 | if(!renderer.IsVisible)return; 355 | 356 | if(renderer.IsInputFieldFocus){ 357 | 358 | // quit focus 359 | if(userInput.QuitFocus){ 360 | 361 | renderer.QuitFocus(); 362 | OnFocusOut?.Invoke(); 363 | } 364 | else if(userInput.MoveDown){ 365 | 366 | if(renderer.IsAlternativeOptionsActive){ 367 | selector.MoveNext(); 368 | }else{ 369 | ignoreTextChanged = true; 370 | renderer.InputText = inputHistory.Next; 371 | renderer.MoveCursorToEnd(); 372 | } 373 | }else if(userInput.MoveUp){ 374 | 375 | if(renderer.IsAlternativeOptionsActive){ 376 | selector.MoveLast(); 377 | }else{ 378 | ignoreTextChanged = true; 379 | renderer.InputText = inputHistory.Last; 380 | renderer.MoveCursorToEnd(); 381 | } 382 | } 383 | return; 384 | } 385 | if(userInput.Focus){ 386 | renderer.Focus(); 387 | renderer.ActivateInput(); 388 | OnFocus?.Invoke(); 389 | } 390 | } 391 | 392 | string AddTimeInfo(string msg){ 393 | if( outputWithTime ){ 394 | return CLIUtils.TimeInfo + msg; 395 | } 396 | return msg; 397 | } 398 | string[] AddTimeInfo(string[] msgs){ 399 | 400 | if( outputWithTime ){ 401 | var timeInfo = CLIUtils.TimeInfo; 402 | for(int i = 0; i < msgs.Length; i ++){ 403 | msgs[i] = timeInfo + msgs[i]; 404 | } 405 | return msgs; 406 | } 407 | return msgs; 408 | } 409 | 410 | /// Output message on console 411 | public void Output(string msg) => 412 | logManager.Output(AddTimeInfo(msg)); 413 | 414 | /// Output message on console with given color 415 | public void Output(string msg, string color = "#ffffff") => logManager.Output(AddTimeInfo(msg), color); 416 | 417 | /// Output messages on console 418 | public void Output(string[] msgs, string color = "#ffffff"){ 419 | 420 | foreach(var m in AddTimeInfo(msgs)) 421 | logManager.Output(m, color); 422 | } 423 | 424 | /// Output messages on console 425 | public void Output(string[] msgs){ 426 | 427 | foreach(var m in AddTimeInfo(msgs)) 428 | logManager.Output(m); 429 | } 430 | 431 | /// clear current console 432 | public void ClearOutputPanel() => renderer.Clear(); 433 | 434 | public void OnTextChanged(string text){ 435 | /* when input new string, should query commands from commandSystem */ 436 | 437 | if(ignoreTextChanged){ 438 | ignoreTextChanged = false; 439 | return; 440 | } 441 | 442 | string queryText = renderer.InputTextToCursor; 443 | var result = commandSystem.GetCurrentSuggestions(queryText, alternativeCommandCount, CLIUtils.FindSimilarity); 444 | 445 | if(result.Length == 0){ 446 | if(renderer.IsAlternativeOptionsActive){ 447 | renderer.IsAlternativeOptionsActive = false; 448 | } 449 | return; 450 | } 451 | 452 | /* show options panel */ 453 | var optionsBuffer = new List(); 454 | var list = new List(); 455 | foreach(var elem in result){ 456 | optionsBuffer.Add(elem.primary); 457 | list.Add(elem.ToString()); 458 | } 459 | if(!renderer.IsAlternativeOptionsActive){ 460 | renderer.IsAlternativeOptionsActive = true; 461 | } 462 | selector.LoadOptions(optionsBuffer); 463 | renderer.AlternativeOptions = list; 464 | renderer.AlternativeOptionsIndex = selector.SelectionIndex; 465 | } 466 | 467 | /// input string into current console 468 | public void OnSubmit(string text){ 469 | 470 | if(renderer.IsAlternativeOptionsActive && selector.GetCurrentSelection(out string selection)){ 471 | renderer.InputTextToCursor = commandSystem.TakeSuggestion(renderer.InputTextToCursor, selection); 472 | renderer.IsAlternativeOptionsActive = false; 473 | renderer.ActivateInput(); 474 | renderer.SetInputCursorPosition(renderer.InputText.Length); 475 | return; 476 | } 477 | 478 | if(renderer.IsAlternativeOptionsActive) 479 | renderer.IsAlternativeOptionsActive = false; 480 | 481 | Output(text); 482 | if(text.Length > 0){ 483 | var ex = commandSystem.Execute(text, out object executeResult); 484 | if( ex == null ){ 485 | inputHistory.Record(text); 486 | OutputResult(executeResult); 487 | }else{ 488 | if(shouldRecordFailedCommand)inputHistory.Record(text); 489 | Output(ex.Message, "#f27a5f"); 490 | if( outputStackTraceOfCommandExecution ) 491 | Output(ex.StackTrace, "#f27a5f"); 492 | } 493 | renderer.InputText = string.Empty; 494 | } 495 | renderer.MoveScrollBarToEnd(); 496 | renderer.ActivateInput(); 497 | } 498 | 499 | void OutputResult(object instance){ 500 | 501 | if( instance == null )return; 502 | var type = instance.GetType(); 503 | var attr = type.GetCustomAttribute(); 504 | if( attr == null ){ 505 | Output(instance.ToString()); 506 | return; 507 | } 508 | var debugInfos = DebugHelper.GetDebugInfos(instance); 509 | if (debugInfos.Length == 0)return; 510 | Output($"---------- {instance} start ----------"); 511 | foreach(var (message, color) in debugInfos){ 512 | Output(message, color); 513 | } 514 | Output($"---------- {instance} end ----------"); 515 | } 516 | } 517 | } -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Console/ConsoleSystem.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 031211d5272c2a74290c0e943108bbb4 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Console/Interfaces.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | /* 5 | 使用接口描述上层逻辑的功能,目的是为了使得该模块能够保持较低的成本移植到其他引擎或者平台 6 | use interface between the real implemetations to decoupling the whole system's components 7 | */ 8 | 9 | namespace RedSaw.CommandLineInterface{ 10 | 11 | /// 12 | /// define the user input of CommandConsole, cause Unity has two different 13 | /// input solutions. 14 | /// 15 | public interface IConsoleInput{ 16 | 17 | /// push up to get history input or change selection of alternative options 18 | bool MoveUp{ get; } 19 | 20 | /// push down to get history input or change selection of alternative options 21 | bool MoveDown{ get; } 22 | 23 | /// push ctrl+c or other keys to focus on console 24 | bool Focus{ get; } 25 | 26 | /// push esc or other keys to quit focus on console 27 | bool QuitFocus{ get; } 28 | 29 | /// push F1 or other keys to show or hide console 30 | bool ShowOrHide{ get; } 31 | } 32 | 33 | /// define the interface of console renderer 34 | public interface IConsoleRenderer{ 35 | 36 | /// 37 | /// the console renderer would only show the last logs on console 38 | /// for unity text cannot render so much content, if you use another 39 | /// implementation, you can set it as you wish. 40 | /// 41 | int OutputPanelCapacity { get; } 42 | 43 | bool IsVisible{ get; set; } 44 | 45 | #region InputField 46 | 47 | /// check if input field is focused 48 | bool IsInputFieldFocus{ get; } 49 | 50 | /// input text 51 | string InputText{ get; set; } 52 | 53 | /// input text to selection 54 | string InputTextToCursor{ get; set; } 55 | 56 | /// focus on input field 57 | void Focus(); 58 | 59 | /// activate input field 60 | void ActivateInput(); 61 | 62 | /// quit focus on input field 63 | void QuitFocus(); 64 | 65 | /// add listener on input field 66 | void BindOnTextChanged(Action callback); 67 | 68 | /// add listener on input field 69 | void BindOnSubmit(Action callback); 70 | 71 | /// set cursor position 72 | void SetInputCursorPosition(int pos); 73 | 74 | /// make scrollbar position at last position 75 | void MoveScrollBarToEnd(); 76 | 77 | void MoveCursorToEnd(){ 78 | if(InputText != null)SetInputCursorPosition(InputText.Length); 79 | } 80 | 81 | #endregion 82 | 83 | #region OutputPanel 84 | 85 | /// output message 86 | void Output(string msg); 87 | 88 | /// output message 89 | void Output(string[] msg); 90 | 91 | /// output message with given color 92 | void Output(string msg, string color = "#ffffff"); 93 | 94 | /// output message 95 | void Output(string[] msg, string color = "#ffffff"); 96 | 97 | /// clear current console 98 | void Clear(); 99 | 100 | #endregion 101 | 102 | #region AlternativeOptions 103 | 104 | /// is current alternative panel showing now 105 | bool IsAlternativeOptionsActive{ get; set; } 106 | 107 | /// 108 | /// render current alternative options 109 | /// 110 | List AlternativeOptions{ set; } 111 | 112 | /// 113 | /// highlight this alternative options index 114 | /// 115 | int AlternativeOptionsIndex{ set; } 116 | #endregion 117 | } 118 | } -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Console/Interfaces.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 51027716d5725d24f89f30909e369711 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Console/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace RedSaw.CommandLineInterface{ 4 | 5 | public static class CLIUtils{ 6 | 7 | static int GetEditDistance(string X, string Y) 8 | { 9 | int m = X.Length; 10 | int n = Y.Length; 11 | 12 | int[][] T = new int[m + 1][]; 13 | for (int i = 0; i < m + 1; ++i) { 14 | T[i] = new int[n + 1]; 15 | } 16 | 17 | for (int i = 1; i <= m; i++) { 18 | T[i][0] = i; 19 | } 20 | for (int j = 1; j <= n; j++) { 21 | T[0][j] = j; 22 | } 23 | 24 | int cost; 25 | for (int i = 1; i <= m; i++) { 26 | for (int j = 1; j <= n; j++) { 27 | cost = X[i - 1] == Y[j - 1] ? 0: 1; 28 | T[i][j] = Math.Min(Math.Min(T[i - 1][j] + 1, T[i][j - 1] + 1), 29 | T[i - 1][j - 1] + cost); 30 | } 31 | } 32 | 33 | return T[m][n]; 34 | } 35 | 36 | /// find similarity of two strings 37 | /// string x 38 | /// string y 39 | public static float FindSimilarity(string x, string y) { 40 | if (x == null || y == null) return 0; 41 | 42 | float maxLength = Math.Max(x.Length, y.Length); 43 | if (maxLength > 0) { 44 | // optionally ignore case if needed 45 | return (maxLength - GetEditDistance(x, y)) / maxLength; 46 | } 47 | return 1.0f; 48 | } 49 | 50 | /// get time information of [HH:mm:ss] 51 | public static string TimeInfo{ 52 | get{ 53 | var time = DateTime.Now; 54 | return $"[{PadZero(time.Hour)}:{PadZero(time.Minute)}:{PadZero(time.Second)}] "; 55 | } 56 | } 57 | static string PadZero(int value){ 58 | return value < 10 ? $"0{value}" : $"{value}"; 59 | } 60 | } 61 | 62 | 63 | } -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Console/Utils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a21e6653311bfe54083cb7f5fbf57228 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Core.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 294d31a4eef8f9a4297329d8569f2f01 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Core/Exception.cs: -------------------------------------------------------------------------------- 1 | namespace RedSaw.CommandLineInterface{ 2 | 3 | 4 | /// 5 | /// command system exception 6 | /// 7 | public class CommandSystemException : System.Exception{ 8 | public CommandSystemException(string message) : base(message){} 9 | } 10 | 11 | /// 12 | /// command lexer exception 13 | /// 14 | public class CommandLexerException : CommandSystemException{ 15 | public CommandLexerException(string message) : base(message){} 16 | } 17 | 18 | /// 19 | /// command syntax exception 20 | /// 21 | public class CommandSyntaxException : CommandSystemException{ 22 | public CommandSyntaxException(string message) : base(message){} 23 | } 24 | 25 | /// 26 | /// command execute exception 27 | /// 28 | public class CommandExecuteException : CommandSystemException{ 29 | public CommandExecuteException(string message) : base(message){} 30 | } 31 | } -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Core/Exception.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 20c0df9feca1c794a94666ba9ad7fe8b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Core/Lexer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace RedSaw.CommandLineInterface{ 5 | 6 | /// 7 | /// token type of lexer 8 | /// 9 | public enum TokenType{ 10 | 11 | TK_NONE, // nothing 12 | TK_EOF, // end of file 13 | TK_VAR, // @ 14 | TK_INPUT, // input 15 | TK_STRING, // "string" 16 | TK_TRUE, // true 17 | TK_FALSE, // false 18 | TK_NULL, // null 19 | TK_INT, // 123 20 | TK_FLOAT, // 1.3 21 | TK_ASSIGN, // = 22 | TK_DOT, // . 23 | TK_COMMA, // , 24 | TK_COLON, // : 25 | TK_L_BRACKET, // [ 26 | TK_R_BRACKET, // ] 27 | TK_L_PAREN, // ( 28 | TK_R_PAREN, // ) 29 | } 30 | 31 | 32 | /// 33 | /// lexer token 34 | /// 35 | public readonly struct Token{ 36 | 37 | public readonly TokenType type; 38 | public readonly int sourceIdx; 39 | public readonly int startIdx; 40 | public readonly int endIdx; 41 | 42 | public Token(TokenType type, int sourceId, int startIdx, int endIdx){ 43 | 44 | this.type = type; 45 | this.sourceIdx = sourceId; 46 | this.startIdx = startIdx; 47 | this.endIdx = endIdx; 48 | } 49 | } 50 | 51 | /// 52 | /// lexer result, contains a group of LexerResult 53 | /// 54 | public readonly struct LexerResult{ 55 | 56 | public readonly string source; 57 | public readonly Token[] tokens; 58 | public readonly string[] sourceBuffer; 59 | public readonly Token this[int index] => tokens[index]; 60 | public readonly Token LastToken => tokens[^1]; 61 | 62 | public LexerResult(string source, Token[] tokens, string[] sourceBuffer){ 63 | 64 | this.source = source; 65 | this.tokens = tokens; 66 | this.sourceBuffer = sourceBuffer; 67 | } 68 | public readonly void Debug(Action log){ 69 | 70 | foreach(var tk in tokens){ 71 | var src = tk.sourceIdx >= 0 ? sourceBuffer[tk.sourceIdx] : string.Empty; 72 | log($"<{tk.type}> {src}"); 73 | } 74 | } 75 | public readonly string NearInfo(Token token, int radius = 7){ 76 | 77 | int left = Math.Max(0, token.startIdx - radius); 78 | int right = Math.Min(token.endIdx + radius, source.Length - 1); 79 | return source[left..right]; 80 | } 81 | public readonly string NearInfo(int LB, int RB){ 82 | 83 | int left = Math.Max(0, LB); 84 | int right = Math.Min(RB, source.Length - 1); 85 | return source[left..right]; 86 | } 87 | } 88 | 89 | public partial class Lexer{ 90 | 91 | /// 92 | /// check if a string value is an id 93 | /// which starts with '_' or letter and contains only '_' or letter or digit 94 | /// 95 | public static bool IsId(string value){ 96 | 97 | if(value == null || value.Length == 0)return false; 98 | char head = value[0]; 99 | if(head == UNDERLINE || char.IsLetter(head)){ 100 | for(int i = 1; i < value.Length; i ++){ 101 | char c = value[i]; 102 | if(c == UNDERLINE || char.IsLetterOrDigit(c))continue; 103 | } 104 | return true; 105 | } 106 | return false; 107 | } 108 | 109 | // SPECIAL CHARACTERS 110 | public const char EOL = '\n'; 111 | public const char EOF = '\0'; 112 | public const char TAB = '\t'; 113 | public const char CR = '\r'; 114 | public const char WHITE_SPACE = ' '; 115 | 116 | // SYMBOLS 117 | public const char VAR = '@'; 118 | public const char DOT = '.'; 119 | public const char COLON = ':'; 120 | public const char COMMA = ','; 121 | public const char ASSIGN = '='; 122 | public const char L_PAREN = '('; 123 | public const char R_PAREN = ')'; 124 | public const char L_BRACKET = '['; 125 | public const char R_BRACKET = ']'; 126 | public const char SINGLE_QUOTE = '\''; 127 | public const char DOUBLE_QUOTE = '"'; 128 | public const char UNDERLINE = '_'; 129 | public const char NEGATIVE = '-'; 130 | public const char E = 'e'; 131 | 132 | 133 | // KEYWORDS 134 | public const string TRUE = "true"; 135 | public const string FALSE = "false"; 136 | public const string NULL = "null"; 137 | 138 | /// 139 | /// use to preprocess the source input, the source input could only contains 140 | /// 'EOF' and 'WHITE_SPACE' 141 | /// 142 | public static char[] WHITE_SPACE_CHARS = { EOL, EOF, TAB, CR }; 143 | public static char[] SYMBOLS = { VAR, DOT, COLON, COMMA, ASSIGN, L_PAREN, R_PAREN, L_BRACKET, R_BRACKET, SINGLE_QUOTE, DOUBLE_QUOTE }; 144 | public static char[] ID_TERMINATORS = { WHITE_SPACE, EOF, DOT, COLON, COMMA, ASSIGN, L_PAREN, R_PAREN, L_BRACKET, R_BRACKET, SINGLE_QUOTE, DOUBLE_QUOTE }; 145 | 146 | public static bool IsWhiteSpace(char c){ 147 | 148 | foreach(char cc in WHITE_SPACE_CHARS){ 149 | if( c == cc )return true; 150 | } 151 | return char.IsWhiteSpace(c); 152 | } 153 | 154 | bool IsInputTerminator(char c){ 155 | 156 | foreach(char t in ID_TERMINATORS)if(c == t)return true; 157 | return false; 158 | } 159 | /// remove all white space in target source input 160 | string PreProcess(string input){ 161 | 162 | foreach(char c in WHITE_SPACE_CHARS)input = input.Replace(c, WHITE_SPACE); 163 | return input + EOF; 164 | } 165 | } 166 | 167 | /// 168 | /// lexer main logic 169 | /// 170 | public partial class Lexer{ 171 | 172 | int index; 173 | string src; 174 | readonly List tokenValues = new(); 175 | readonly List tokens = new(); 176 | bool HasMore => index < src.Length; 177 | 178 | /// parse input string to tokens as LexerResult 179 | /// input string 180 | /// LexerResult 181 | public LexerResult Parse(string input){ 182 | 183 | // check if target input is null or empty 184 | if(input == null || input.Length == 0) 185 | throw new CommandLexerException("Input string is empty"); 186 | 187 | // preprocess to remove all white space, and recheck if target input is empty 188 | var pp = PreProcess(input); 189 | if(pp.Length == 0) 190 | throw new CommandLexerException("Input string is invalid"); 191 | 192 | // start to parse 193 | return Walk(pp); 194 | } 195 | LexerResult Walk(string input){ 196 | 197 | src = input; 198 | 199 | index = 0; 200 | tokens.Clear(); 201 | tokenValues.Clear(); 202 | 203 | while(HasMore){ 204 | int startIdx = index; 205 | char c = src[ index ++ ]; 206 | switch(c){ 207 | 208 | // skip white space 209 | case WHITE_SPACE:break; 210 | 211 | // stop at the end of file 212 | case EOF: 213 | tokens.Add(new Token(TokenType.TK_EOF, -1, startIdx, index)); 214 | break; 215 | 216 | /* 217 | while start with '@', it would be treated as a property which 218 | could be visit directly by '@' in command line 219 | */ 220 | case VAR: 221 | string varName = NextInput(index); 222 | tokens.Add(new Token(TokenType.TK_VAR, tokenValues.Count, startIdx, index)); 223 | tokenValues.Add(varName); 224 | break; 225 | 226 | // string of double quote 227 | case DOUBLE_QUOTE: 228 | case SINGLE_QUOTE: 229 | string str = NextString(index, c); 230 | tokens.Add(new Token(TokenType.TK_STRING, tokenValues.Count, startIdx, index)); 231 | tokenValues.Add(str); 232 | break; 233 | 234 | // : 235 | case COLON: 236 | tokens.Add(new Token(TokenType.TK_COLON, -1, startIdx, index)); 237 | break; 238 | 239 | // , 240 | case COMMA: 241 | startIdx = index - 1; 242 | tokens.Add(new Token(TokenType.TK_COMMA, -1, startIdx, index)); 243 | break; 244 | 245 | // . 246 | case DOT: 247 | tokens.Add(new Token(TokenType.TK_DOT, -1, startIdx, index)); 248 | break; 249 | 250 | // = 251 | case ASSIGN: 252 | tokens.Add(new Token(TokenType.TK_ASSIGN, -1, startIdx, index)); 253 | break; 254 | 255 | // ( 256 | case L_PAREN: 257 | tokens.Add(new Token(TokenType.TK_L_PAREN, -1, startIdx, index)); 258 | break; 259 | 260 | // ) 261 | case R_PAREN: 262 | tokens.Add(new Token(TokenType.TK_R_PAREN, -1, startIdx, index)); 263 | break; 264 | 265 | // [ 266 | case L_BRACKET: 267 | tokens.Add(new Token(TokenType.TK_L_BRACKET, -1, startIdx, index)); 268 | break; 269 | 270 | // ] 271 | case R_BRACKET: 272 | tokens.Add(new Token(TokenType.TK_R_BRACKET, -1, startIdx, index)); 273 | break; 274 | 275 | // input string 276 | default: 277 | // number 278 | if(c == NEGATIVE || char.IsDigit(c)){ 279 | string number = NextNumber(index, out bool isFloat); 280 | tokens.Add(new Token(isFloat ? TokenType.TK_FLOAT : TokenType.TK_INT, tokenValues.Count, startIdx, index)); 281 | tokenValues.Add(number); 282 | break; 283 | } 284 | 285 | var nextInput = NextInput(index - 1); 286 | 287 | // keywords 288 | switch(nextInput){ 289 | case TRUE: 290 | tokens.Add(new Token(TokenType.TK_TRUE, -2, startIdx, index)); 291 | break; 292 | case FALSE: 293 | tokens.Add(new Token(TokenType.TK_FALSE, -3, startIdx, index)); 294 | break; 295 | case NULL: 296 | tokens.Add(new Token(TokenType.TK_NULL, -4, startIdx, index)); 297 | break; 298 | default: 299 | tokens.Add(new Token(TokenType.TK_INPUT, tokenValues.Count, startIdx, index)); 300 | tokenValues.Add(nextInput); 301 | break; 302 | } 303 | break; 304 | } 305 | } 306 | 307 | return new LexerResult(input, tokens.ToArray(), tokenValues.ToArray()); 308 | } 309 | string NextInput(int startIdx){ 310 | 311 | while(HasMore){ 312 | char c = src[index ++ ]; 313 | if(IsInputTerminator(c)){ 314 | index -- ; 315 | break; 316 | } 317 | } 318 | return src[startIdx..index]; 319 | } 320 | string NextString(int startIdx, char quoteChar){ 321 | 322 | while(HasMore){ 323 | char c = src[index ++ ]; 324 | if(c == quoteChar){ 325 | return src[startIdx..(index - 1)]; 326 | } 327 | } 328 | var nearStr = src[startIdx..index]; 329 | throw new CommandLexerException($"String not terminated near <{nearStr}>"); 330 | } 331 | string NextNumber(int startIdx, out bool isFloat){ 332 | 333 | while(HasMore){ 334 | char c = src[index ++]; 335 | if(c == DOT){ 336 | isFloat = true; 337 | return NextFloat(startIdx); 338 | } 339 | if(char.IsDigit(c))continue; 340 | index -- ; 341 | break; 342 | } 343 | isFloat = false; 344 | return src[(startIdx - 1)..index]; 345 | } 346 | string NextFloat(int startIdx){ 347 | 348 | while(HasMore){ 349 | char c = src[index ++ ]; 350 | if(char.IsDigit(c))continue; 351 | if(c == E)return NextExponent(startIdx); 352 | index -- ; 353 | break; 354 | } 355 | return src[(startIdx - 1)..index]; 356 | } 357 | string NextExponent(int startIdx){ 358 | 359 | while(HasMore){ 360 | char c = src[index ++ ]; 361 | if(char.IsDigit(c))continue; 362 | index -- ; 363 | break; 364 | } 365 | return src[(startIdx - 1)..index]; 366 | } 367 | 368 | } 369 | 370 | 371 | } -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Core/Lexer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dc5f5b26b04358745bc29ddb76c4594e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Core/SuggestionQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | namespace RedSaw.CommandLineInterface 6 | { 7 | 8 | public enum SuggestionType 9 | { 10 | 11 | /// no suggestion 12 | None, 13 | 14 | /// search property or local variable 15 | Variable, 16 | 17 | /// search member of a type 18 | Member, 19 | 20 | /// search command 21 | Command, 22 | } 23 | 24 | /// save current suggestion query information, provide to 25 | /// current command system to get some suggestion back 26 | public readonly struct SuggestionQuery 27 | { 28 | 29 | public static readonly SuggestionQuery None = 30 | new(SuggestionType.None, string.Empty); 31 | 32 | public readonly SuggestionType suggestionType; 33 | public readonly string queryStr; 34 | public readonly Type queryType; 35 | 36 | public SuggestionQuery(SuggestionType suggestionType, string queryStr, Type queryType) 37 | { 38 | 39 | this.suggestionType = suggestionType; 40 | this.queryStr = queryStr; 41 | this.queryType = queryType; 42 | } 43 | public SuggestionQuery(SuggestionType suggestionType, string queryStr) : 44 | this(suggestionType, queryStr, null) 45 | { } 46 | 47 | public override string ToString() 48 | { 49 | return suggestionType switch 50 | { 51 | SuggestionType.Variable => $"Variable -> \"{queryStr}\"", 52 | SuggestionType.Command => $"Command -> \"{queryStr}\"", 53 | SuggestionType.Member => $"Member -> \"{queryType.Name}.{queryStr}\"", 54 | _ => "No Suggestions", 55 | }; 56 | } 57 | } 58 | 59 | public delegate bool QueryVariableType(string name, out Type type); 60 | 61 | /// 62 | /// use to analyze input string and provide suggestions type 63 | /// 64 | class InputBehaviourStateMachine 65 | { 66 | 67 | public readonly Stack stateStack; 68 | private readonly Func getVariableType; 69 | private readonly Func getCallableType; 70 | private InputBehaviourState currentState; 71 | 72 | public string CurrentStatus 73 | { 74 | get 75 | { 76 | if (currentState == null) return ""; 77 | return currentState.ToString(); 78 | } 79 | } 80 | 81 | /// current suggestion 82 | public SuggestionQuery CurrentSuggestionQuery => currentState.GetSuggestion(); 83 | 84 | public InputBehaviourStateMachine(Func getVariableType, Func getCallableType) 85 | { 86 | 87 | this.getVariableType = getVariableType; 88 | this.getCallableType = getCallableType; 89 | stateStack = new Stack(); 90 | currentState = new IBS_Ready(this); 91 | } 92 | public InputBehaviourState TryGetVariableMemberState(string variableName) 93 | { 94 | 95 | var type = getVariableType(variableName); 96 | if (type == null) return new IBS_Unknown(this); 97 | return new IBS_Member(this, type); 98 | } 99 | public InputBehaviourState TryGetCallableMemberState(string callableName) 100 | { 101 | 102 | var type = getCallableType(callableName); 103 | if (type == null) return new IBS_Unknown(this); 104 | return new IBS_Member(this, type); 105 | } 106 | 107 | public void StepForward(char c) 108 | { 109 | 110 | var nextState = currentState.StepForward(c); 111 | if (nextState != currentState) 112 | { 113 | if (nextState.ShouldCollapse) 114 | { 115 | stateStack.Pop(); 116 | } 117 | stateStack.Push(currentState); 118 | currentState = nextState; 119 | } 120 | } 121 | public void StepBackward() 122 | { 123 | 124 | if (currentState.StepBackward()) 125 | { 126 | currentState = stateStack.Pop(); 127 | } 128 | } 129 | public void Reset() 130 | { 131 | 132 | stateStack.Clear(); 133 | currentState = new IBS_Ready(this); 134 | } 135 | public void Reset(string input) 136 | { 137 | 138 | Reset(); 139 | foreach (char c in input) 140 | { 141 | StepForward(c); 142 | } 143 | } 144 | 145 | } 146 | 147 | 148 | 149 | /// character automaton state 150 | abstract class InputBehaviourState 151 | { 152 | 153 | protected readonly InputBehaviourStateMachine stateMachine; 154 | public bool ShouldCollapse { get; protected set; } = false; 155 | 156 | /// 157 | /// mark if current state is after an assign operation 158 | /// 159 | public bool AfterAssign { get; protected set; } = false; 160 | 161 | public InputBehaviourState(InputBehaviourStateMachine stateMachine, bool afterAssign = false) 162 | { 163 | 164 | this.stateMachine = stateMachine; 165 | this.AfterAssign = afterAssign; 166 | } 167 | 168 | /// input new character and check if 169 | /// new input character 170 | /// should change current state to nextState 171 | public abstract InputBehaviourState StepForward(char c); 172 | 173 | /// step backward, return to last state 174 | /// should change current state to last state 175 | public abstract bool StepBackward(); 176 | 177 | /// get suggestion query 178 | /// current suggestion query 179 | public virtual SuggestionQuery GetSuggestion() => SuggestionQuery.None; 180 | 181 | protected IBS_Wait Wait() => new(stateMachine); 182 | protected IBS_Unknown Unknown() => new(stateMachine); 183 | protected IBS_Variable Variable() => new(stateMachine); 184 | protected InputBehaviourState VariableMember(string typeName) => stateMachine.TryGetVariableMemberState(typeName); 185 | protected InputBehaviourState CommandMember(string commandName) => stateMachine.TryGetCallableMemberState(commandName); 186 | protected InputBehaviourState Member(Type type, string name) 187 | { 188 | 189 | MemberInfo member = type.GetDefaultMember(name); 190 | if (member == null) 191 | { 192 | return new IBS_Unknown(stateMachine); 193 | } 194 | return member.MemberType switch 195 | { 196 | MemberTypes.Field => new IBS_Member(stateMachine, ((FieldInfo)member).FieldType), 197 | MemberTypes.Property => new IBS_Member(stateMachine, ((PropertyInfo)member).PropertyType), 198 | _ => new IBS_Unknown(stateMachine), 199 | }; 200 | } 201 | protected IBS_Command Callable(char firstChar, bool shouldCollapse = false, bool afterAssign = false) 202 | { 203 | 204 | return new IBS_Command(stateMachine, firstChar, shouldCollapse, afterAssign); 205 | } 206 | public override string ToString() => ""; 207 | } 208 | 209 | abstract class InputBehaviourStateWithInput : InputBehaviourState 210 | { 211 | 212 | public string CurrentInput { get; protected set; } = string.Empty; 213 | public InputBehaviourStateWithInput( 214 | InputBehaviourStateMachine stateMachine, 215 | string input = "", 216 | bool afterAssign = false 217 | ) : base(stateMachine, afterAssign) 218 | { 219 | this.CurrentInput = input; 220 | } 221 | public override string ToString() => $""; 222 | } 223 | 224 | 225 | 226 | 227 | class IBS_Ready : InputBehaviourStateWithInput 228 | { 229 | public IBS_Ready(InputBehaviourStateMachine stateMachine) : base(stateMachine) { } 230 | public override InputBehaviourState StepForward(char c) 231 | { 232 | switch (c) 233 | { 234 | case Lexer.VAR: 235 | return Variable(); 236 | 237 | case Lexer.DOUBLE_QUOTE: 238 | case Lexer.SINGLE_QUOTE: 239 | return new IBS_String(stateMachine, c); 240 | 241 | case Lexer.UNDERLINE: 242 | return Callable(c, shouldCollapse: true, AfterAssign); 243 | 244 | default: 245 | if (Lexer.IsWhiteSpace(c)) return Wait(); 246 | if (c == Lexer.UNDERLINE || char.IsLetter(c)) return Callable(c); 247 | return Unknown(); 248 | } 249 | 250 | } 251 | public override bool StepBackward() => false; 252 | public override string ToString() => ""; 253 | } 254 | 255 | class IBS_Wait : InputBehaviourStateWithInput 256 | { 257 | 258 | public IBS_Wait(InputBehaviourStateMachine stateMachine, string input = "") : base(stateMachine, input) 259 | { 260 | } 261 | public override InputBehaviourState StepForward(char c) 262 | { 263 | 264 | switch (c) 265 | { 266 | case Lexer.VAR: 267 | return Variable(); 268 | 269 | case Lexer.DOUBLE_QUOTE: 270 | case Lexer.SINGLE_QUOTE: 271 | return new IBS_String(stateMachine, c); 272 | 273 | default: 274 | if (Lexer.IsWhiteSpace(c)) return Wait(); 275 | if (c == Lexer.ASSIGN) return Wait(); 276 | if (char.IsLetter(c) || c == Lexer.UNDERLINE) return Callable(c, shouldCollapse: true, AfterAssign); 277 | return Unknown(); 278 | } 279 | } 280 | 281 | public override bool StepBackward() => true; 282 | public override string ToString() => $""; 283 | } 284 | 285 | 286 | class IBS_Command : InputBehaviourStateWithInput 287 | { 288 | 289 | public IBS_Command( 290 | InputBehaviourStateMachine stateMachine, 291 | char firstChar, 292 | bool shouldCollapse = false, 293 | bool afterAssign = false) : base(stateMachine, firstChar.ToString(), afterAssign) 294 | { 295 | 296 | CurrentInput = firstChar.ToString(); 297 | this.ShouldCollapse = shouldCollapse; 298 | } 299 | public override InputBehaviourState StepForward(char c) 300 | { 301 | if (Lexer.IsWhiteSpace(c)) return Wait(); 302 | if (c == Lexer.DOT) return CommandMember(CurrentInput); 303 | if (c == Lexer.UNDERLINE || char.IsLetterOrDigit(c)) 304 | { 305 | CurrentInput += c; 306 | return this; 307 | } 308 | return Unknown(); 309 | } 310 | public override bool StepBackward() 311 | { 312 | if (CurrentInput.Length == 0) return true; 313 | CurrentInput = CurrentInput[..^1]; 314 | return false; 315 | } 316 | public override SuggestionQuery GetSuggestion() => new(SuggestionType.Command, CurrentInput); 317 | public override string ToString() => $""; 318 | } 319 | 320 | 321 | /// 322 | /// bad state only use to mark the input is bad, 323 | /// and wait for user delete all and reinput 324 | /// 325 | class IBS_Unknown : InputBehaviourState 326 | { 327 | 328 | private int count; 329 | public IBS_Unknown(InputBehaviourStateMachine stateMachine) : base(stateMachine) 330 | { 331 | this.count = 0; 332 | } 333 | public override InputBehaviourState StepForward(char c) 334 | { 335 | count++; 336 | return this; 337 | } 338 | public override bool StepBackward() 339 | { 340 | return --count < 0; 341 | } 342 | public override string ToString() => ""; 343 | } 344 | 345 | class IBS_Variable : InputBehaviourStateWithInput 346 | { 347 | public IBS_Variable(InputBehaviourStateMachine stateMachine, string input = "") : base(stateMachine, input) { } 348 | public override InputBehaviourState StepForward(char c) 349 | { 350 | if (CurrentInput.Length == 0) 351 | { 352 | if (char.IsLetter(c) || c == Lexer.UNDERLINE) 353 | { 354 | CurrentInput += c; 355 | return this; 356 | } 357 | return Unknown(); 358 | } 359 | 360 | if (char.IsLetterOrDigit(c) || c == Lexer.UNDERLINE) 361 | { 362 | 363 | CurrentInput += c; 364 | return this; 365 | } 366 | if (c == Lexer.DOT) 367 | { 368 | 369 | return VariableMember(CurrentInput); 370 | } 371 | return Unknown(); 372 | } 373 | public override bool StepBackward() 374 | { 375 | if (CurrentInput.Length == 0) return true; 376 | CurrentInput = CurrentInput[..^1]; 377 | return false; 378 | } 379 | public override SuggestionQuery GetSuggestion() => new(SuggestionType.Variable, CurrentInput); 380 | public override string ToString() => $""; 381 | } 382 | 383 | class IBS_String : InputBehaviourStateWithInput 384 | { 385 | 386 | private readonly char quote; 387 | public IBS_String(InputBehaviourStateMachine stateMachine, char quote, string input = "") : base(stateMachine, input) 388 | { 389 | this.quote = quote; 390 | } 391 | public override InputBehaviourState StepForward(char c) 392 | { 393 | if (c == quote) return Wait(); 394 | CurrentInput += c; 395 | return this; 396 | } 397 | public override bool StepBackward() 398 | { 399 | if (CurrentInput.Length == 0) return true; 400 | CurrentInput = CurrentInput[..^1]; 401 | return false; 402 | } 403 | public override string ToString() => $""; 404 | } 405 | 406 | class IBS_Member : InputBehaviourStateWithInput 407 | { 408 | private readonly Type queryType; 409 | public IBS_Member(InputBehaviourStateMachine stateMachine, Type type) : base(stateMachine, string.Empty) 410 | { 411 | queryType = type; 412 | } 413 | 414 | public override bool StepBackward() 415 | { 416 | if (CurrentInput.Length == 0) return true; 417 | CurrentInput = CurrentInput[..^1]; 418 | return false; 419 | } 420 | 421 | public override InputBehaviourState StepForward(char c) 422 | { 423 | if (CurrentInput.Length == 0) 424 | { 425 | if (char.IsLetter(c) || c == Lexer.UNDERLINE) 426 | { 427 | CurrentInput += c; 428 | return this; 429 | } 430 | return Unknown(); 431 | } 432 | if (char.IsLetterOrDigit(c) || c == Lexer.UNDERLINE) 433 | { 434 | 435 | CurrentInput += c; 436 | return this; 437 | } 438 | else if (c == Lexer.DOT) 439 | { 440 | 441 | return Member(queryType, CurrentInput); 442 | } 443 | return Callable(c); 444 | } 445 | public override SuggestionQuery GetSuggestion() 446 | { 447 | return new(SuggestionType.Member, CurrentInput, queryType); 448 | } 449 | public override string ToString() => $""; 450 | } 451 | 452 | 453 | 454 | 455 | /// 456 | /// lexer use to parse input realtime 457 | /// 458 | public class CharAutomaton 459 | { 460 | 461 | private readonly InputBehaviourStateMachine stateMachine; 462 | private string lastInput = string.Empty; 463 | public string CurrentStatus => stateMachine.CurrentStatus; 464 | 465 | public CharAutomaton( 466 | Func getVariableType, 467 | Func getCallableType 468 | ) 469 | { 470 | stateMachine = new InputBehaviourStateMachine(getVariableType, getCallableType); 471 | } 472 | 473 | /// 474 | /// input current input text and get suggestion query 475 | /// 476 | public SuggestionQuery Input(string newInput) 477 | { 478 | 479 | /* input string is empty */ 480 | if (newInput == null || newInput.Length == 0) 481 | { 482 | 483 | stateMachine.Reset(); 484 | lastInput = string.Empty; 485 | return SuggestionQuery.None; 486 | } 487 | 488 | /* input string is equal to last input, it maybe nothing change 489 | or an totally new input */ 490 | if (newInput.Length == lastInput.Length) 491 | { 492 | if (newInput == lastInput) 493 | { 494 | return stateMachine.CurrentSuggestionQuery; 495 | } 496 | Rehandle(newInput); 497 | return stateMachine.CurrentSuggestionQuery; 498 | } 499 | 500 | /* input some new characters */ 501 | if (newInput.Length > lastInput.Length) 502 | { 503 | 504 | /* add some new characters */ 505 | if (newInput.StartsWith(lastInput)) 506 | { 507 | foreach (char c in newInput[lastInput.Length..]) 508 | { 509 | stateMachine.StepForward(c); 510 | } 511 | lastInput = newInput; 512 | return stateMachine.CurrentSuggestionQuery; 513 | } 514 | 515 | /* maybe an totally new input */ 516 | Rehandle(newInput); 517 | return stateMachine.CurrentSuggestionQuery; 518 | } 519 | 520 | /* delete some characaters */ 521 | if (newInput.Length < lastInput.Length) 522 | { 523 | if (lastInput.StartsWith(newInput)) 524 | { 525 | foreach (char _ in lastInput[newInput.Length..]) 526 | { 527 | stateMachine.StepBackward(); 528 | } 529 | lastInput = newInput; 530 | return stateMachine.CurrentSuggestionQuery; 531 | } 532 | Rehandle(newInput); 533 | return stateMachine.CurrentSuggestionQuery; 534 | } 535 | 536 | /* code never run below */ 537 | return stateMachine.CurrentSuggestionQuery; 538 | } 539 | void Rehandle(string input) 540 | { 541 | 542 | lastInput = input; 543 | stateMachine.Reset(input); 544 | } 545 | } 546 | 547 | 548 | /// 549 | /// used to save suggestion information 550 | /// 551 | public readonly struct Suggestion 552 | { 553 | 554 | public readonly string primary; 555 | public readonly string description; 556 | 557 | public Suggestion(string primary, string description) 558 | { 559 | 560 | this.primary = primary; 561 | this.description = description; 562 | } 563 | public Suggestion(string primary) 564 | { 565 | 566 | this.primary = primary; 567 | this.description = string.Empty; 568 | } 569 | public override string ToString() 570 | { 571 | if (this.description.Length > 0) 572 | { 573 | return $"{primary} :{description}"; 574 | } 575 | return primary; 576 | } 577 | } 578 | } -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Core/SuggestionQuery.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: de5a397c444b0ad4dbb630d98fda9ba2 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Core/SyntaxAnalyzer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | 5 | namespace RedSaw.CommandLineInterface 6 | { 7 | 8 | /// 9 | /// syntax tree code, tell VirualMachine how to execute given node 10 | /// 11 | public enum SyntaxTreeCode 12 | { 13 | 14 | FACTOR_STRING, 15 | FACTOR_FLOAT, 16 | FACTOR_TRUE, 17 | FACTOR_FALSE, 18 | FACTOR_NULL, 19 | FACTOR_INT, 20 | FACTOR_ID, 21 | FACTOR_INPUT, 22 | FACTOR_PARAMS, 23 | 24 | OP_SET_ELEMENT, 25 | OP_SET_FIELD, 26 | OP_LOADVAR, 27 | OP_ASSIGN, 28 | OP_INDEX, 29 | OP_CALL, 30 | OP_CVT, 31 | OP_DOT, 32 | } 33 | 34 | /// 35 | /// syntax tree, tree type, and represent the whole execution logic 36 | /// 37 | public class SyntaxTree 38 | { 39 | 40 | public readonly string data; 41 | public readonly SyntaxTreeCode opcode; 42 | public readonly SyntaxTree[] children; 43 | 44 | public SyntaxTree L => children[0]; 45 | public SyntaxTree R => children[1]; 46 | public bool IsLeaf => children.Length == 0; 47 | 48 | /// provide debug info about current node 49 | public string DebugInfo 50 | { 51 | get 52 | { 53 | return opcode switch 54 | { 55 | SyntaxTreeCode.OP_DOT => $"{L?.DebugInfo}.{R?.DebugInfo}", 56 | SyntaxTreeCode.OP_CALL => $"{L?.DebugInfo}{R?.DebugInfo}", 57 | SyntaxTreeCode.OP_INDEX => $"{L?.DebugInfo}[{R?.DebugInfo}]", 58 | SyntaxTreeCode.OP_ASSIGN => $"{L?.DebugInfo} = {R?.DebugInfo}", 59 | SyntaxTreeCode.OP_SET_FIELD => $"{L?.DebugInfo}.{R?.DebugInfo} = {children[2]?.DebugInfo}", 60 | SyntaxTreeCode.OP_CVT => $"{L?.DebugInfo}:{R?.DebugInfo}", 61 | SyntaxTreeCode.FACTOR_PARAMS => $"({string.Join(", ", children.Select(x => x.DebugInfo))})", 62 | SyntaxTreeCode.FACTOR_FLOAT => $"{data}f", 63 | SyntaxTreeCode.FACTOR_STRING => data, 64 | SyntaxTreeCode.FACTOR_TRUE => data, 65 | SyntaxTreeCode.FACTOR_FALSE => data, 66 | SyntaxTreeCode.FACTOR_NULL => data, 67 | SyntaxTreeCode.FACTOR_INT => data, 68 | SyntaxTreeCode.FACTOR_ID => "@" + data, 69 | SyntaxTreeCode.FACTOR_INPUT => "?" + data, 70 | _ => ToString(), 71 | }; 72 | } 73 | } 74 | 75 | public SyntaxTree(SyntaxTreeCode opcode, string data) 76 | { 77 | 78 | this.opcode = opcode; 79 | this.data = data; 80 | this.children = Array.Empty(); 81 | } 82 | public SyntaxTree(SyntaxTreeCode opcode, string data, SyntaxTree L) 83 | { 84 | 85 | this.opcode = opcode; 86 | this.data = data; 87 | this.children = new SyntaxTree[] { L }; 88 | } 89 | public SyntaxTree(SyntaxTreeCode opcode, string data, SyntaxTree L, SyntaxTree R) 90 | { 91 | 92 | this.opcode = opcode; 93 | this.data = data; 94 | this.children = new SyntaxTree[] { L, R }; 95 | } 96 | public SyntaxTree(SyntaxTreeCode opcode, string data, SyntaxTree[] children) 97 | { 98 | 99 | this.opcode = opcode; 100 | this.data = data; 101 | this.children = children; 102 | } 103 | public override string ToString() 104 | { 105 | if (data != null || data.Length > 0) return data; 106 | return opcode.ToString(); 107 | } 108 | } 109 | 110 | /// 111 | /// analyze lexer result and generate syntax tree 112 | /// 113 | public class SyntaxAnalyzer 114 | { 115 | 116 | LexerResult lexerResult; 117 | int index; 118 | 119 | /// 120 | /// indicate that while system has occur a 'command' parse process 121 | /// and if true, then the next input would be treated as command parameters 122 | /// 123 | bool __command_latch; 124 | 125 | Token[] Tokens => lexerResult.tokens; 126 | string[] TokenValues => lexerResult.sourceBuffer; 127 | 128 | public bool HasMore => index < Tokens.Length; 129 | public Token CurrentToken => Tokens[index]; 130 | public Token NextToken => Tokens[index++]; 131 | 132 | /// 133 | /// analyze lexer result and generate syntax tree 134 | /// 135 | /// lexer result 136 | /// syntax tree 137 | /// when syntax error occured 138 | public SyntaxTree Analyze(LexerResult lexerResult) 139 | { 140 | 141 | this.lexerResult = lexerResult; 142 | 143 | index = 0; 144 | __command_latch = false; 145 | return NextExpr(true); 146 | } 147 | 148 | /// 149 | /// match current token type, if match, then move to next token 150 | /// 151 | bool Match(TokenType tokenType) 152 | { 153 | 154 | if (HasMore && CurrentToken.type == tokenType) 155 | { 156 | index++; 157 | return true; 158 | } 159 | return false; 160 | } 161 | /// 162 | /// only check if current token type is matched, don't move 163 | /// 164 | bool PeekMatch(TokenType tokenType) 165 | { 166 | return HasMore && CurrentToken.type == tokenType; 167 | } 168 | 169 | /// 170 | /// check if token is Input and is Id legal 171 | /// 172 | bool MatchId(out SyntaxTree factor) 173 | { 174 | 175 | var token = CurrentToken; 176 | if (token.type == TokenType.TK_INPUT && Lexer.IsId(TokenValues[token.sourceIdx])) 177 | { 178 | factor = new SyntaxTree(SyntaxTreeCode.FACTOR_ID, TokenValues[token.sourceIdx]); 179 | index++; 180 | return true; 181 | } 182 | factor = null; 183 | return false; 184 | } 185 | SyntaxTree NextParameterList() 186 | { 187 | 188 | if (Match(TokenType.TK_R_PAREN)) 189 | return new SyntaxTree(SyntaxTreeCode.FACTOR_PARAMS, string.Empty); 190 | 191 | __command_latch = true; 192 | List parameters = new(); 193 | while (HasMore) 194 | { 195 | parameters.Add(NextExpr()); 196 | if (Match(TokenType.TK_R_PAREN)) break; 197 | if (Match(TokenType.TK_COMMA)) continue; 198 | } 199 | __command_latch = false; 200 | return new SyntaxTree(SyntaxTreeCode.FACTOR_PARAMS, string.Empty, parameters.ToArray()); 201 | } 202 | SyntaxTree NextParameterList_Command() 203 | { 204 | 205 | __command_latch = true; 206 | List parameters = new(); 207 | while (HasMore) 208 | { 209 | if (Match(TokenType.TK_EOF)) break; 210 | parameters.Add(NextExpr()); 211 | } 212 | __command_latch = false; 213 | return new SyntaxTree(SyntaxTreeCode.FACTOR_PARAMS, string.Empty, parameters.ToArray()); 214 | } 215 | SyntaxTree NextFactor() 216 | { 217 | 218 | Token tk = NextToken; 219 | switch (tk.type) 220 | { 221 | 222 | case TokenType.TK_INT: 223 | return new SyntaxTree(SyntaxTreeCode.FACTOR_INT, TokenValues[tk.sourceIdx]); 224 | 225 | case TokenType.TK_FLOAT: 226 | return new SyntaxTree(SyntaxTreeCode.FACTOR_FLOAT, TokenValues[tk.sourceIdx]); 227 | 228 | case TokenType.TK_STRING: 229 | return new SyntaxTree(SyntaxTreeCode.FACTOR_STRING, TokenValues[tk.sourceIdx]); 230 | 231 | case TokenType.TK_NULL: 232 | return new SyntaxTree(SyntaxTreeCode.FACTOR_NULL, Lexer.NULL); 233 | 234 | case TokenType.TK_TRUE: 235 | return new SyntaxTree(SyntaxTreeCode.FACTOR_TRUE, Lexer.TRUE); 236 | 237 | case TokenType.TK_FALSE: 238 | return new SyntaxTree(SyntaxTreeCode.FACTOR_FALSE, Lexer.FALSE); 239 | 240 | case TokenType.TK_VAR: 241 | return new SyntaxTree(SyntaxTreeCode.OP_LOADVAR, TokenValues[tk.sourceIdx]); 242 | 243 | case TokenType.TK_INPUT: 244 | /* 245 | all other input would be treated as command, if next token is '(', 246 | then treat as function call, otherwise treat as command 247 | */ 248 | if (Match(TokenType.TK_L_PAREN)) 249 | { 250 | var functionCmd = new SyntaxTree(SyntaxTreeCode.FACTOR_INPUT, TokenValues[tk.sourceIdx]); 251 | return new SyntaxTree(SyntaxTreeCode.OP_CALL, string.Empty, functionCmd, NextParameterList()); 252 | } 253 | if (__command_latch) 254 | { 255 | return new SyntaxTree(SyntaxTreeCode.FACTOR_INPUT, TokenValues[tk.sourceIdx]); 256 | } 257 | var commandCall = new SyntaxTree(SyntaxTreeCode.FACTOR_INPUT, TokenValues[tk.sourceIdx]); 258 | return new SyntaxTree(SyntaxTreeCode.OP_CALL, string.Empty, commandCall, NextParameterList_Command()); 259 | 260 | case TokenType.TK_L_PAREN: 261 | SyntaxTree expr = NextExpr(); 262 | if (!Match(TokenType.TK_R_PAREN)) 263 | { 264 | throw new CommandSyntaxException($"Missing ')' near \"..{lexerResult.NearInfo(tk)}\""); 265 | } 266 | return expr; 267 | } 268 | throw new CommandSyntaxException($"Unexpected token {tk.type} near \"..{lexerResult.NearInfo(tk)}\""); 269 | } 270 | SyntaxTree NextConverter() 271 | { 272 | 273 | var factor = NextFactor(); 274 | if (Match(TokenType.TK_COLON)) 275 | { 276 | var currentTk = NextToken; 277 | if (currentTk.type == TokenType.TK_STRING || currentTk.type == TokenType.TK_INPUT) 278 | { 279 | var opFactor = new SyntaxTree(SyntaxTreeCode.FACTOR_INPUT, TokenValues[currentTk.sourceIdx]); 280 | var opConverter = new SyntaxTree(SyntaxTreeCode.OP_CVT, string.Empty, factor, opFactor); 281 | return opConverter; 282 | } 283 | throw new CommandSyntaxException($"Unexpected token near \"..{lexerResult.NearInfo(currentTk)}\""); 284 | } 285 | return factor; 286 | } 287 | SyntaxTree NextAccess() 288 | { 289 | 290 | SyntaxTree tmp = NextConverter(); 291 | while (HasMore) 292 | { 293 | int startPosition = CurrentToken.startIdx; 294 | 295 | // dot 296 | if (Match(TokenType.TK_DOT)) 297 | { 298 | if (MatchId(out var id)) 299 | { 300 | 301 | // function call 302 | if (Match(TokenType.TK_L_PAREN)) 303 | { 304 | var callable = new SyntaxTree(SyntaxTreeCode.OP_DOT, string.Empty, tmp, id); 305 | tmp = new SyntaxTree(SyntaxTreeCode.OP_CALL, string.Empty, callable, NextParameterList()); 306 | continue; 307 | } 308 | 309 | tmp = new SyntaxTree(SyntaxTreeCode.OP_DOT, string.Empty, tmp, id); 310 | continue; 311 | } 312 | throw new CommandSyntaxException($"Unexpected token near \"..{lexerResult.NearInfo(startPosition, CurrentToken.endIdx)}\""); 313 | } 314 | 315 | // index 316 | if (Match(TokenType.TK_L_BRACKET)) 317 | { 318 | SyntaxTree expr = NextExpr(); 319 | if (!Match(TokenType.TK_R_BRACKET)) 320 | { 321 | throw new CommandSyntaxException($"Missing ']' near \"..{lexerResult.NearInfo(startPosition, CurrentToken.endIdx)}\""); 322 | } 323 | tmp = new SyntaxTree(SyntaxTreeCode.OP_INDEX, string.Empty, tmp, expr); 324 | continue; 325 | } 326 | break; 327 | } 328 | return tmp; 329 | } 330 | public SyntaxTree NextExpr(bool isRoot = false) 331 | { 332 | 333 | SyntaxTree access = NextAccess(); 334 | if (!HasMore || PeekMatch(TokenType.TK_EOF)) 335 | return access; 336 | 337 | if (Match(TokenType.TK_ASSIGN)) 338 | { 339 | 340 | // instance.fieldName = 341 | if (access.opcode == SyntaxTreeCode.OP_DOT) 342 | { 343 | SyntaxTree expr = NextExpr(); 344 | return new SyntaxTree(SyntaxTreeCode.OP_SET_FIELD, string.Empty, new SyntaxTree[]{ 345 | access.children[0], 346 | access.children[1], 347 | expr 348 | }); 349 | } 350 | 351 | // instance["test"] = 10 or instance[10] = 10 352 | if (access.opcode == SyntaxTreeCode.OP_INDEX) 353 | { 354 | SyntaxTree expr = NextExpr(); 355 | return new SyntaxTree(SyntaxTreeCode.OP_SET_ELEMENT, string.Empty, new SyntaxTree[]{ 356 | access.children[0], 357 | access.children[1], 358 | expr 359 | }); 360 | } 361 | 362 | return new SyntaxTree(SyntaxTreeCode.OP_ASSIGN, string.Empty, access, NextExpr()); 363 | } 364 | if (isRoot) 365 | { 366 | throw new CommandSyntaxException($"Unexpected token {CurrentToken.type} near \"..{lexerResult.NearInfo(CurrentToken)}\""); 367 | } 368 | return access; 369 | } 370 | } 371 | } -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Core/SyntaxAnalyzer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a58a1265b6dd8334cb10af5049b1f823 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Core/Utils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0d604af26230e0e47b4d07a7cb0e5296 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Core/VirtualMachine.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c6f04fdcd5288554a92e342eec1540e9 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Helper.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ff1aab6dcc1df3f45809446ee4b8684e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Helper/DebugHelper.cs: -------------------------------------------------------------------------------- 1 | /* 2 | provide an attribute to mark debug info, when we use CommandHelper.Debug(), we can get all debug info 3 | include parent object's debug info 4 | */ 5 | 6 | using System; 7 | using System.Reflection; 8 | using System.Collections.Generic; 9 | 10 | namespace RedSaw.CommandLineInterface{ 11 | 12 | /// 13 | /// mark one field or property is debug info, when we use CommandHelper.Debug(), we can get all debug info 14 | /// include parent object's debug info 15 | /// 16 | [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property, AllowMultiple = false, Inherited = true)] 17 | public class DebugInfoAttribute: Attribute{ 18 | 19 | public string Key{ get; protected set; } 20 | 21 | /// you must provide an color key, and any invalid colors would be changed to white 22 | public string Color{ get; set; } 23 | 24 | public DebugInfoAttribute(string key){ 25 | 26 | this.Key = key; 27 | this.Color = "#ffffff"; 28 | } 29 | public DebugInfoAttribute(){ 30 | 31 | this.Key = string.Empty; 32 | this.Color = "#ffffff"; 33 | } 34 | } 35 | 36 | /// 37 | /// mark one element is container of debug info 38 | /// 39 | [AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct, AllowMultiple = true, Inherited = false)] 40 | public class DebugObjectAttribute: Attribute{ 41 | 42 | public string Title{ get; protected set; } 43 | 44 | public DebugObjectAttribute(string title){ 45 | this.Title = title; 46 | } 47 | public DebugObjectAttribute(){ 48 | this.Title = null; 49 | } 50 | } 51 | 52 | public static class DebugHelper{ 53 | 54 | 55 | /// get all parent types of given type 56 | static Type[] GetParentTypes(Type type){ 57 | 58 | Stack typeList = new(); 59 | Type parent = type.BaseType; 60 | while(parent != null){ 61 | typeList.Push(parent); 62 | parent = parent.BaseType; 63 | } 64 | return typeList.ToArray(); 65 | } 66 | 67 | /// 68 | /// get all debug informations from given instance 69 | /// 70 | /// the instance to get debug info 71 | /// the depth of debug info 72 | /// only get debug info from given namespace 73 | public static (string, string)[] GetDebugInfos(this object instance, int depth = 0, int depthLimit = 5, string constraintNamespace = null){ 74 | 75 | if( depth >= depthLimit )return Array.Empty<(string, string)>(); 76 | 77 | var usedKeys = new HashSet(); 78 | Type currentType = instance.GetType(); 79 | Type[] types = GetParentTypes(currentType); 80 | 81 | List<(string, string)> totalDebugInfos = new(); 82 | foreach(Type type in types){ 83 | if( constraintNamespace != null && !type.Namespace.StartsWith(constraintNamespace) )continue; 84 | totalDebugInfos.AddRange(GetDebugInfos(type, instance, usedKeys, depth)); 85 | } 86 | totalDebugInfos.AddRange(GetDebugInfos(currentType, instance, usedKeys, depth)); 87 | return totalDebugInfos.ToArray(); 88 | } 89 | /// 90 | /// get all debug informations from given instance 91 | /// 92 | static List<(string, string)> GetDebugInfos(Type type, object instance, HashSet usedKeys, int depth){ 93 | 94 | if(type == null)return new List<(string, string)>(); 95 | List<(string, string)> debugInfos = new(); 96 | string typeName = type.Name; 97 | string space = new(' ', depth * 4); 98 | string typeSpace = new('-', depth * 4); 99 | debugInfos.Add(($">{typeSpace}[{typeName}]", "#ffffff")); 100 | 101 | /* read field infos */ 102 | foreach(FieldInfo fieldInfo in type.GetFields(BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance)){ 103 | var attr = fieldInfo.GetCustomAttribute(); 104 | if(attr != null){ 105 | string title = attr.Key == string.Empty ? fieldInfo.Name : attr.Key; 106 | if(usedKeys.Contains(title))continue; 107 | usedKeys.Add(title); 108 | var subTypeAttr = fieldInfo.FieldType.GetCustomAttribute(); 109 | if(subTypeAttr != null){ 110 | debugInfos.AddRange(GetDebugInfos(fieldInfo.GetValue(instance), depth + 1)); 111 | continue; 112 | } 113 | debugInfos.Add(($">{space}{typeName}.{title}: {fieldInfo.GetValue(instance)}", attr.Color)); 114 | } 115 | } 116 | 117 | /* read property infos */ 118 | foreach(PropertyInfo propertyInfo in type.GetProperties(BindingFlags.NonPublic|BindingFlags.Public|BindingFlags.Instance)){ 119 | var attr = propertyInfo.GetCustomAttribute(); 120 | if(attr != null){ 121 | string title = attr.Key == string.Empty ? propertyInfo.Name : attr.Key; 122 | if(usedKeys.Contains(title))continue; 123 | usedKeys.Add(title); 124 | var subTypeAttr = propertyInfo.PropertyType.GetCustomAttribute(); 125 | if(subTypeAttr != null){ 126 | debugInfos.AddRange(GetDebugInfos(propertyInfo.GetValue(instance), depth + 1)); 127 | continue; 128 | } 129 | debugInfos.Add(($">{space}{typeName}.{title}: {propertyInfo.GetValue(instance)}", attr.Color)); 130 | } 131 | } 132 | return debugInfos; 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/Helper/DebugHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 546093bfbcd104f4884ca4aefba2ec61 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/UnityImpl.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9b5993f5a2f280e4c98d2e0bc492e6eb 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/UnityImpl/GameConsole.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEditor.EditorTools; 3 | using UnityEngine; 4 | 5 | namespace RedSaw.CommandLineInterface.UnityImpl 6 | { 7 | 8 | /// the final wrapper of CommandConsoleSystem build in Unity 9 | public class GameConsole : MonoBehaviour 10 | { 11 | 12 | [Header("Initialize Parameters")] 13 | [SerializeField] 14 | private GameConsoleRenderer consoleRenderer; 15 | 16 | [SerializeField] 17 | private GameConsoleHeader headerBar; 18 | 19 | [SerializeField, Tooltip("the capacity of input history, at least 1")] 20 | private int inputHistoryCapacity = 20; 21 | 22 | [SerializeField, Tooltip("the capacity of command query cache, at least 1")] 23 | private int commandQueryCacheCapacity = 20; 24 | 25 | [SerializeField, Tooltip("alternative command options count, at least 1")] 26 | private int alternativeCommandCount = 8; 27 | 28 | [SerializeField, Tooltip("should output with time information of [HH:mm:ss]")] 29 | private bool shouldOutputWithTime = true; 30 | 31 | [SerializeField, Tooltip("should record failed command input")] 32 | private bool shouldRecordFailedCommand = true; 33 | 34 | [SerializeField, Tooltip("should receive unity message")] 35 | private bool shouldReceiveUnityMessage = true; 36 | 37 | [SerializeField, Tooltip("[debug] output virtual machine exception call stack")] 38 | private bool shouldOutputVMExceptionStack = false; 39 | 40 | [SerializeField, Tooltip("initialize on awake")] 41 | private bool initializeOnAwake = true; 42 | 43 | ConsoleController console; 44 | 45 | void Awake() 46 | { 47 | if(initializeOnAwake)Init(); 48 | } 49 | 50 | /// initialize console, call this function to initialize console 51 | public void Init(){ 52 | 53 | if (consoleRenderer == null) 54 | { 55 | Debug.LogError("ConsoleRenderer is missing!!"); 56 | gameObject.SetActive(false); 57 | return; 58 | } 59 | gameObject.SetActive(true); 60 | 61 | /* intialize console */ 62 | console = new ConsoleController( 63 | consoleRenderer, 64 | new UserInput(), 65 | 66 | inputHistoryCapacity: inputHistoryCapacity, 67 | commandQueryCacheCapacity: commandQueryCacheCapacity, 68 | alternativeCommandCount: alternativeCommandCount, 69 | shouldRecordFailedCommand: shouldRecordFailedCommand, 70 | outputWithTime: shouldOutputWithTime, 71 | outputStackTraceOfCommandExecution: shouldOutputVMExceptionStack 72 | ); 73 | if (shouldReceiveUnityMessage) Application.logMessageReceived += UnityConsoleLog; 74 | 75 | 76 | headerBar.Init( (RectTransform)transform ); 77 | } 78 | 79 | void Update() => console.Update(); 80 | void OnDestroy() 81 | { 82 | if (shouldReceiveUnityMessage) 83 | Application.logMessageReceived -= UnityConsoleLog; 84 | } 85 | 86 | void UnityConsoleLog(string msg, string stack, LogType type) 87 | { 88 | 89 | console.Output(msg, GetHexColor(type)); 90 | } 91 | string GetHexColor(LogType type) 92 | { 93 | return type switch 94 | { 95 | LogType.Error or LogType.Exception or LogType.Assert => "#b13c45", 96 | LogType.Warning => "yellow", 97 | _ => "#fffde3", 98 | }; 99 | } 100 | 101 | /// clear output of current console 102 | public void ClearOutput() => console.ClearOutputPanel(); 103 | } 104 | } -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/UnityImpl/GameConsole.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2104a3d8c11d33849ba85e6a79797ea2 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/UnityImpl/GameConsoleAlternativeOptionsPanel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | 6 | namespace RedSaw.CommandLineInterface.UnityImpl 7 | { 8 | 9 | /// default implementation of alternative options panel 10 | public class GameConsoleAlternativeOptionsPanel : MonoBehaviour 11 | { 12 | 13 | [SerializeField] private Text textPanel; 14 | 15 | List options; 16 | 17 | public int SelectionIndex 18 | { 19 | set 20 | { 21 | textPanel.text = string.Empty; 22 | if (options == null || options.Count == 0) return; 23 | string output = string.Empty; 24 | for (int i = 0; i < options.Count; i++) 25 | { 26 | if (i == value) 27 | { 28 | output += $"{options[i]}\n"; 29 | continue; 30 | } 31 | output += options[i] + "\n"; 32 | } 33 | textPanel.text = output; 34 | } 35 | } 36 | 37 | /// render current alternative options 38 | public void SetOptions(List values) 39 | { 40 | this.options = values; 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/UnityImpl/GameConsoleAlternativeOptionsPanel.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9465ef6881de94c4095bf5f16081b782 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/UnityImpl/GameConsoleHeader.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.UI; 3 | using UnityEngine.EventSystems; 4 | using System; 5 | 6 | namespace RedSaw.CommandLineInterface{ 7 | 8 | [RequireComponent(typeof(Image))] 9 | public class GameConsoleHeader : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler, IPointerMoveHandler 10 | { 11 | [SerializeField] private Color hoverColor = Color.white; 12 | private Image headerImage; 13 | private Color normalColor; 14 | private RectTransform movTarget; 15 | private Vector2 movPos; 16 | // private Action movCb; 17 | 18 | bool shouldMov; 19 | Vector2 startPos; 20 | 21 | public void Init(RectTransform target){ 22 | 23 | this.movTarget = target; 24 | headerImage = GetComponent(); 25 | if( headerImage == null ){ 26 | throw new System.Exception("GameConsoleHeader must attach to a GameObject with Image component"); 27 | } 28 | normalColor = headerImage.color; 29 | } 30 | public void OnPointerEnter(PointerEventData eventData) 31 | { 32 | headerImage.color = hoverColor; 33 | } 34 | 35 | public void OnPointerExit(PointerEventData eventData) 36 | { 37 | headerImage.color = normalColor; 38 | } 39 | 40 | public void OnPointerDown(PointerEventData eventData) 41 | { 42 | shouldMov = true; 43 | startPos = eventData.position; 44 | movPos = movTarget.position; 45 | } 46 | public void OnPointerMove(PointerEventData eventData) 47 | { 48 | if(shouldMov){ 49 | Vector2 offset = eventData.position - startPos; 50 | movTarget.position = movPos + offset; 51 | } 52 | } 53 | 54 | public void OnPointerUp(PointerEventData eventData) 55 | { 56 | shouldMov = false; 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/UnityImpl/GameConsoleHeader.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 89c87e651b422b94d9463fa0ced6d3de 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/UnityImpl/GameConsoleInput.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace RedSaw.CommandLineInterface.UnityImpl 4 | { 5 | 6 | /// default implementation of IConsoleInput with legacy Input 7 | public class UserInput : IConsoleInput 8 | { 9 | /// push ctrl+c or other keys to focus on console 10 | public bool Focus => Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.C); 11 | 12 | /// push up to get history input or change selection of alternative options 13 | public bool MoveUp => Input.GetKeyDown(KeyCode.UpArrow); 14 | 15 | /// push down to get history input or change selection of alternative options 16 | public bool MoveDown => Input.GetKeyDown(KeyCode.DownArrow); 17 | 18 | /// push esc or other keys to quit focus on console 19 | public bool QuitFocus => Input.GetKeyDown(KeyCode.Escape); 20 | 21 | /// push F1 or other keys to show or hide console 22 | public bool ShowOrHide => Input.GetKeyDown(KeyCode.F1); 23 | } 24 | } -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/UnityImpl/GameConsoleInput.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 612a875795f387847925f3ff8631965d 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/UnityImpl/GameConsoleRenderer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | using UnityEngine.UI; 6 | using UnityEngine.EventSystems; 7 | 8 | namespace RedSaw.CommandLineInterface.UnityImpl 9 | { 10 | 11 | /// default implementation of IConsoleRenderer with Unity legacy UI 12 | public class GameConsoleRenderer : MonoBehaviour, IConsoleRenderer 13 | { 14 | [SerializeField] private Text outputPanel; 15 | [SerializeField] private InputField inputField; 16 | [SerializeField] private GameConsoleAlternativeOptionsPanel optionsPanel; 17 | 18 | private int outputPanelCapacity = 400; 19 | private int lineCount = 0; 20 | 21 | public bool IsVisible 22 | { 23 | get => gameObject.activeSelf; 24 | set => gameObject.SetActive(value); 25 | } 26 | 27 | public bool IsInputFieldFocus => inputField.isFocused; 28 | public int OutputPanelCapacity => outputPanelCapacity; 29 | 30 | public string InputText 31 | { 32 | 33 | get => inputField.text; 34 | set => inputField.text = value; 35 | } 36 | public string InputTextToCursor 37 | { 38 | 39 | get => inputField.text[..inputField.caretPosition]; 40 | set => inputField.text = value + inputField.text[inputField.caretPosition..]; 41 | } 42 | 43 | public bool IsAlternativeOptionsActive 44 | { 45 | get => optionsPanel.gameObject.activeSelf; 46 | set => optionsPanel.gameObject.SetActive(value); 47 | } 48 | public List AlternativeOptions 49 | { 50 | set => optionsPanel.SetOptions(value); 51 | } 52 | public int AlternativeOptionsIndex 53 | { 54 | set => optionsPanel.SelectionIndex = value; 55 | } 56 | 57 | public void ActivateInput() 58 | { 59 | inputField.Select(); 60 | inputField.ActivateInputField(); 61 | StartCoroutine(DisableHighlight()); 62 | } 63 | IEnumerator DisableHighlight() 64 | { 65 | 66 | //Get original selection color 67 | Color originalTextColor = inputField.selectionColor; 68 | //Remove alpha 69 | originalTextColor.a = 0f; 70 | 71 | //Apply new selection color without alpha 72 | inputField.selectionColor = originalTextColor; 73 | 74 | //Wait one Frame(MUST DO THIS!) 75 | yield return null; 76 | 77 | //Change the caret pos to the end of the text 78 | inputField.caretPosition = inputField.text.Length; 79 | 80 | //Return alpha 81 | originalTextColor.a = 1f; 82 | 83 | //Apply new selection color with alpha 84 | inputField.selectionColor = originalTextColor; 85 | } 86 | 87 | public void SetInputCursorPosition(int value) 88 | { 89 | inputField.caretPosition = Mathf.Clamp(value, 0, inputField.text.Length); 90 | } 91 | 92 | public void MoveScrollBarToEnd() 93 | { 94 | StartCoroutine(MoveToLast()); 95 | } 96 | IEnumerator MoveToLast() 97 | { 98 | yield return null; 99 | GetComponentInChildren(true).verticalNormalizedPosition = 0; 100 | } 101 | 102 | public void BindOnSubmit(Action callback) 103 | { 104 | /* 105 | cause maybe the implementation of the game console in Non-Unity GameEngine 106 | so use Action instead of UnityEvent 107 | */ 108 | 109 | inputField.onSubmit.AddListener((string input) => 110 | { 111 | callback?.Invoke(input); 112 | }); 113 | } 114 | public void BindOnTextChanged(Action callback) 115 | { 116 | /* 117 | cause maybe the implementation of the game console in Non-Unity GameEngine 118 | so use Action instead of UnityEvent 119 | */ 120 | inputField.onValueChanged.AddListener((string input) => 121 | { 122 | callback?.Invoke(input); 123 | }); 124 | } 125 | 126 | public void Focus() 127 | { 128 | inputField.Select(); 129 | EventSystem.current.SetSelectedGameObject(inputField.gameObject); 130 | } 131 | public void QuitFocus() 132 | { 133 | EventSystem.current.SetSelectedGameObject(null); 134 | } 135 | 136 | public void Clear() 137 | { 138 | lineCount = 0; 139 | outputPanel.text = string.Empty; 140 | } 141 | public void Output(string msg) 142 | { 143 | try 144 | { 145 | outputPanel.text += msg + "\n"; 146 | if (++lineCount > outputPanelCapacity) 147 | { 148 | outputPanel.text = outputPanel.text[(outputPanel.text.IndexOf('\n') + 1)..]; 149 | lineCount--; 150 | } 151 | /* update outputPanel's size */ 152 | var generator = new TextGenerator(); 153 | var settings = outputPanel.GetGenerationSettings(outputPanel.rectTransform.sizeDelta); 154 | outputPanel.rectTransform.sizeDelta = 155 | new Vector2(outputPanel.rectTransform.sizeDelta.x, generator.GetPreferredHeight(outputPanel.text, settings)); 156 | 157 | GetComponentInChildren().verticalNormalizedPosition = 0; 158 | } 159 | catch (Exception) 160 | { 161 | Clear(); 162 | } 163 | } 164 | public void Output(string[] msgs) 165 | { 166 | Output(string.Concat(msgs, '\n')); 167 | } 168 | public void Output(string msg, string color = "#ffffff") 169 | { 170 | Output($"{msg}"); 171 | } 172 | public void Output(string[] msgs, string color = "#ffffff") 173 | { 174 | 175 | string msg = string.Empty; 176 | foreach (string line in msgs) 177 | { 178 | msg += $"{line}\n"; 179 | } 180 | Output(msg); 181 | } 182 | } 183 | } -------------------------------------------------------------------------------- /Assets/CommandLine/Scripts/UnityImpl/GameConsoleRenderer.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a8aee9544275c4142a4db54acfd95168 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Example.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 78d8776bea74f1341a08d0b7ab20410d 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Assets/Example/CommandExample.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using RedSaw.CommandLineInterface; 3 | 4 | public class MyClass{ 5 | 6 | [DebugInfo("health", Color = "#ff0000")] 7 | public int health = 100; 8 | 9 | [DebugInfo("name")] 10 | public string name = "hello world"; 11 | 12 | public void Do(){ 13 | Debug.Log("do something"); 14 | } 15 | } 16 | 17 | public static class CommandExample 18 | { 19 | [CommandProperty("myObj")] 20 | public static MyClass myObject = new MyClass(); 21 | 22 | 23 | [Command("print")] 24 | static void Print(object value){ 25 | if( value == null ){ 26 | Debug.Log("null"); 27 | return; 28 | } 29 | Debug.Log(value); 30 | } 31 | 32 | [Command("debug")] 33 | static void DebugTest(int a, bool v1, bool v2) 34 | { 35 | Debug.Log(a); 36 | Debug.Log(v1); 37 | Debug.Log(v2); 38 | } 39 | 40 | [Command("add")] 41 | static int Add(int a, int b = 1){ 42 | return a + b; 43 | } 44 | 45 | 46 | [Command] 47 | static void printType(object value){ 48 | if( value == null){ 49 | Debug.Log(typeof(void)); 50 | return; 51 | } 52 | Debug.Log(value.GetType()); 53 | } 54 | 55 | [CommandValueParser(typeof(Vector2), Alias = "pos2")] 56 | public static bool TryParseVector2(string input, out object data){ 57 | 58 | string[] result= input.Trim(new char[]{'(', ')'}).Split(','); 59 | if(result.Length == 2){ 60 | if(float.TryParse(result[0], out float x) && float.TryParse(result[1], out float y)){ 61 | 62 | data = new Vector2(x, y); 63 | return true; 64 | } 65 | } 66 | data = default; 67 | return false; 68 | } 69 | 70 | [CommandValueParser(typeof(Vector3), Alias = "v3")] 71 | public static bool TryParseVector3(string input, out object data){ 72 | 73 | switch(input){ 74 | case "up": 75 | data = Vector3.up; 76 | return true; 77 | case "down": 78 | data = Vector3.down; 79 | return true; 80 | case "left": 81 | data = Vector3.left; 82 | return true; 83 | case "right": 84 | data = Vector3.right; 85 | return true; 86 | case "forward": 87 | data = Vector3.forward; 88 | return true; 89 | case "back": 90 | data = Vector3.back; 91 | return true; 92 | } 93 | 94 | string[] result= input.Trim(new char[]{'(', ')'}).Split(','); 95 | if(result.Length == 3){ 96 | if( float.TryParse(result[0], out float x) && 97 | float.TryParse(result[1], out float y) && 98 | float.TryParse(result[2], out float z)){ 99 | 100 | data = new Vector3(x, y, z); 101 | return true; 102 | } 103 | } 104 | data = default; 105 | return false; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /Assets/Example/CommandExample.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cc1be2ecba79b5e4486b9c6b75956ea9 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Assets/Example/ConsoleExampleScene.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9fc0d4010bbf28b4594072e72b8655ab 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Assets/Example/Example.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using RedSaw.CommandLineInterface; 4 | using UnityEngine; 5 | 6 | public class Example : MonoBehaviour 7 | { 8 | [CommandProperty("player")] 9 | private static Example Instance { get; set; } 10 | 11 | public Vector3 pos{ 12 | get => transform.position; 13 | set => transform.position = value; 14 | } 15 | 16 | public void Start(){ 17 | Example.Instance = this; 18 | } 19 | 20 | public void Jump(float value = 10){ 21 | GetComponent().AddForce(Vector3.up * value, ForceMode.Impulse); 22 | transform.rotation = Random.rotation; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Assets/Example/Example.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 54642f428f22b2948a74fdc46e392590 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 PrinceBiscuit 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Packages/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.unity.collab-proxy": "1.17.7", 4 | "com.unity.feature.development": "1.0.1", 5 | "com.unity.ide.rider": "3.0.16", 6 | "com.unity.ide.visualstudio": "2.0.16", 7 | "com.unity.ide.vscode": "1.2.5", 8 | "com.unity.test-framework": "1.1.31", 9 | "com.unity.textmeshpro": "3.0.6", 10 | "com.unity.timeline": "1.6.4", 11 | "com.unity.ugui": "1.0.0", 12 | "com.unity.visualscripting": "1.7.8", 13 | "com.unity.modules.ai": "1.0.0", 14 | "com.unity.modules.androidjni": "1.0.0", 15 | "com.unity.modules.animation": "1.0.0", 16 | "com.unity.modules.assetbundle": "1.0.0", 17 | "com.unity.modules.audio": "1.0.0", 18 | "com.unity.modules.cloth": "1.0.0", 19 | "com.unity.modules.director": "1.0.0", 20 | "com.unity.modules.imageconversion": "1.0.0", 21 | "com.unity.modules.imgui": "1.0.0", 22 | "com.unity.modules.jsonserialize": "1.0.0", 23 | "com.unity.modules.particlesystem": "1.0.0", 24 | "com.unity.modules.physics": "1.0.0", 25 | "com.unity.modules.physics2d": "1.0.0", 26 | "com.unity.modules.screencapture": "1.0.0", 27 | "com.unity.modules.terrain": "1.0.0", 28 | "com.unity.modules.terrainphysics": "1.0.0", 29 | "com.unity.modules.tilemap": "1.0.0", 30 | "com.unity.modules.ui": "1.0.0", 31 | "com.unity.modules.uielements": "1.0.0", 32 | "com.unity.modules.umbra": "1.0.0", 33 | "com.unity.modules.unityanalytics": "1.0.0", 34 | "com.unity.modules.unitywebrequest": "1.0.0", 35 | "com.unity.modules.unitywebrequestassetbundle": "1.0.0", 36 | "com.unity.modules.unitywebrequestaudio": "1.0.0", 37 | "com.unity.modules.unitywebrequesttexture": "1.0.0", 38 | "com.unity.modules.unitywebrequestwww": "1.0.0", 39 | "com.unity.modules.vehicles": "1.0.0", 40 | "com.unity.modules.video": "1.0.0", 41 | "com.unity.modules.vr": "1.0.0", 42 | "com.unity.modules.wind": "1.0.0", 43 | "com.unity.modules.xr": "1.0.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Packages/packages-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "com.unity.collab-proxy": { 4 | "version": "1.17.7", 5 | "depth": 0, 6 | "source": "registry", 7 | "dependencies": { 8 | "com.unity.services.core": "1.0.1" 9 | }, 10 | "url": "https://packages.unity.cn" 11 | }, 12 | "com.unity.editorcoroutines": { 13 | "version": "1.0.0", 14 | "depth": 1, 15 | "source": "registry", 16 | "dependencies": {}, 17 | "url": "https://packages.unity.cn" 18 | }, 19 | "com.unity.ext.nunit": { 20 | "version": "1.0.6", 21 | "depth": 1, 22 | "source": "registry", 23 | "dependencies": {}, 24 | "url": "https://packages.unity.cn" 25 | }, 26 | "com.unity.feature.development": { 27 | "version": "1.0.1", 28 | "depth": 0, 29 | "source": "builtin", 30 | "dependencies": { 31 | "com.unity.ide.visualstudio": "2.0.16", 32 | "com.unity.ide.rider": "3.0.16", 33 | "com.unity.ide.vscode": "1.2.5", 34 | "com.unity.editorcoroutines": "1.0.0", 35 | "com.unity.performance.profile-analyzer": "1.1.1", 36 | "com.unity.test-framework": "1.1.31", 37 | "com.unity.testtools.codecoverage": "1.2.2" 38 | } 39 | }, 40 | "com.unity.ide.rider": { 41 | "version": "3.0.16", 42 | "depth": 0, 43 | "source": "registry", 44 | "dependencies": { 45 | "com.unity.ext.nunit": "1.0.6" 46 | }, 47 | "url": "https://packages.unity.cn" 48 | }, 49 | "com.unity.ide.visualstudio": { 50 | "version": "2.0.16", 51 | "depth": 0, 52 | "source": "registry", 53 | "dependencies": { 54 | "com.unity.test-framework": "1.1.9" 55 | }, 56 | "url": "https://packages.unity.cn" 57 | }, 58 | "com.unity.ide.vscode": { 59 | "version": "1.2.5", 60 | "depth": 0, 61 | "source": "registry", 62 | "dependencies": {}, 63 | "url": "https://packages.unity.cn" 64 | }, 65 | "com.unity.nuget.newtonsoft-json": { 66 | "version": "3.0.2", 67 | "depth": 2, 68 | "source": "registry", 69 | "dependencies": {}, 70 | "url": "https://packages.unity.cn" 71 | }, 72 | "com.unity.performance.profile-analyzer": { 73 | "version": "1.1.1", 74 | "depth": 1, 75 | "source": "registry", 76 | "dependencies": {}, 77 | "url": "https://packages.unity.cn" 78 | }, 79 | "com.unity.services.core": { 80 | "version": "1.6.0", 81 | "depth": 1, 82 | "source": "registry", 83 | "dependencies": { 84 | "com.unity.modules.unitywebrequest": "1.0.0", 85 | "com.unity.nuget.newtonsoft-json": "3.0.2", 86 | "com.unity.modules.androidjni": "1.0.0" 87 | }, 88 | "url": "https://packages.unity.cn" 89 | }, 90 | "com.unity.settings-manager": { 91 | "version": "1.0.3", 92 | "depth": 2, 93 | "source": "registry", 94 | "dependencies": {}, 95 | "url": "https://packages.unity.cn" 96 | }, 97 | "com.unity.test-framework": { 98 | "version": "1.1.31", 99 | "depth": 0, 100 | "source": "registry", 101 | "dependencies": { 102 | "com.unity.ext.nunit": "1.0.6", 103 | "com.unity.modules.imgui": "1.0.0", 104 | "com.unity.modules.jsonserialize": "1.0.0" 105 | }, 106 | "url": "https://packages.unity.cn" 107 | }, 108 | "com.unity.testtools.codecoverage": { 109 | "version": "1.2.2", 110 | "depth": 1, 111 | "source": "registry", 112 | "dependencies": { 113 | "com.unity.test-framework": "1.0.16", 114 | "com.unity.settings-manager": "1.0.1" 115 | }, 116 | "url": "https://packages.unity.cn" 117 | }, 118 | "com.unity.textmeshpro": { 119 | "version": "3.0.6", 120 | "depth": 0, 121 | "source": "registry", 122 | "dependencies": { 123 | "com.unity.ugui": "1.0.0" 124 | }, 125 | "url": "https://packages.unity.cn" 126 | }, 127 | "com.unity.timeline": { 128 | "version": "1.6.4", 129 | "depth": 0, 130 | "source": "registry", 131 | "dependencies": { 132 | "com.unity.modules.director": "1.0.0", 133 | "com.unity.modules.animation": "1.0.0", 134 | "com.unity.modules.audio": "1.0.0", 135 | "com.unity.modules.particlesystem": "1.0.0" 136 | }, 137 | "url": "https://packages.unity.cn" 138 | }, 139 | "com.unity.ugui": { 140 | "version": "1.0.0", 141 | "depth": 0, 142 | "source": "builtin", 143 | "dependencies": { 144 | "com.unity.modules.ui": "1.0.0", 145 | "com.unity.modules.imgui": "1.0.0" 146 | } 147 | }, 148 | "com.unity.visualscripting": { 149 | "version": "1.7.8", 150 | "depth": 0, 151 | "source": "registry", 152 | "dependencies": { 153 | "com.unity.ugui": "1.0.0", 154 | "com.unity.modules.jsonserialize": "1.0.0" 155 | }, 156 | "url": "https://packages.unity.cn" 157 | }, 158 | "com.unity.modules.ai": { 159 | "version": "1.0.0", 160 | "depth": 0, 161 | "source": "builtin", 162 | "dependencies": {} 163 | }, 164 | "com.unity.modules.androidjni": { 165 | "version": "1.0.0", 166 | "depth": 0, 167 | "source": "builtin", 168 | "dependencies": {} 169 | }, 170 | "com.unity.modules.animation": { 171 | "version": "1.0.0", 172 | "depth": 0, 173 | "source": "builtin", 174 | "dependencies": {} 175 | }, 176 | "com.unity.modules.assetbundle": { 177 | "version": "1.0.0", 178 | "depth": 0, 179 | "source": "builtin", 180 | "dependencies": {} 181 | }, 182 | "com.unity.modules.audio": { 183 | "version": "1.0.0", 184 | "depth": 0, 185 | "source": "builtin", 186 | "dependencies": {} 187 | }, 188 | "com.unity.modules.cloth": { 189 | "version": "1.0.0", 190 | "depth": 0, 191 | "source": "builtin", 192 | "dependencies": { 193 | "com.unity.modules.physics": "1.0.0" 194 | } 195 | }, 196 | "com.unity.modules.director": { 197 | "version": "1.0.0", 198 | "depth": 0, 199 | "source": "builtin", 200 | "dependencies": { 201 | "com.unity.modules.audio": "1.0.0", 202 | "com.unity.modules.animation": "1.0.0" 203 | } 204 | }, 205 | "com.unity.modules.imageconversion": { 206 | "version": "1.0.0", 207 | "depth": 0, 208 | "source": "builtin", 209 | "dependencies": {} 210 | }, 211 | "com.unity.modules.imgui": { 212 | "version": "1.0.0", 213 | "depth": 0, 214 | "source": "builtin", 215 | "dependencies": {} 216 | }, 217 | "com.unity.modules.jsonserialize": { 218 | "version": "1.0.0", 219 | "depth": 0, 220 | "source": "builtin", 221 | "dependencies": {} 222 | }, 223 | "com.unity.modules.particlesystem": { 224 | "version": "1.0.0", 225 | "depth": 0, 226 | "source": "builtin", 227 | "dependencies": {} 228 | }, 229 | "com.unity.modules.physics": { 230 | "version": "1.0.0", 231 | "depth": 0, 232 | "source": "builtin", 233 | "dependencies": {} 234 | }, 235 | "com.unity.modules.physics2d": { 236 | "version": "1.0.0", 237 | "depth": 0, 238 | "source": "builtin", 239 | "dependencies": {} 240 | }, 241 | "com.unity.modules.screencapture": { 242 | "version": "1.0.0", 243 | "depth": 0, 244 | "source": "builtin", 245 | "dependencies": { 246 | "com.unity.modules.imageconversion": "1.0.0" 247 | } 248 | }, 249 | "com.unity.modules.subsystems": { 250 | "version": "1.0.0", 251 | "depth": 1, 252 | "source": "builtin", 253 | "dependencies": { 254 | "com.unity.modules.jsonserialize": "1.0.0" 255 | } 256 | }, 257 | "com.unity.modules.terrain": { 258 | "version": "1.0.0", 259 | "depth": 0, 260 | "source": "builtin", 261 | "dependencies": {} 262 | }, 263 | "com.unity.modules.terrainphysics": { 264 | "version": "1.0.0", 265 | "depth": 0, 266 | "source": "builtin", 267 | "dependencies": { 268 | "com.unity.modules.physics": "1.0.0", 269 | "com.unity.modules.terrain": "1.0.0" 270 | } 271 | }, 272 | "com.unity.modules.tilemap": { 273 | "version": "1.0.0", 274 | "depth": 0, 275 | "source": "builtin", 276 | "dependencies": { 277 | "com.unity.modules.physics2d": "1.0.0" 278 | } 279 | }, 280 | "com.unity.modules.ui": { 281 | "version": "1.0.0", 282 | "depth": 0, 283 | "source": "builtin", 284 | "dependencies": {} 285 | }, 286 | "com.unity.modules.uielements": { 287 | "version": "1.0.0", 288 | "depth": 0, 289 | "source": "builtin", 290 | "dependencies": { 291 | "com.unity.modules.ui": "1.0.0", 292 | "com.unity.modules.imgui": "1.0.0", 293 | "com.unity.modules.jsonserialize": "1.0.0", 294 | "com.unity.modules.uielementsnative": "1.0.0" 295 | } 296 | }, 297 | "com.unity.modules.uielementsnative": { 298 | "version": "1.0.0", 299 | "depth": 1, 300 | "source": "builtin", 301 | "dependencies": { 302 | "com.unity.modules.ui": "1.0.0", 303 | "com.unity.modules.imgui": "1.0.0", 304 | "com.unity.modules.jsonserialize": "1.0.0" 305 | } 306 | }, 307 | "com.unity.modules.umbra": { 308 | "version": "1.0.0", 309 | "depth": 0, 310 | "source": "builtin", 311 | "dependencies": {} 312 | }, 313 | "com.unity.modules.unityanalytics": { 314 | "version": "1.0.0", 315 | "depth": 0, 316 | "source": "builtin", 317 | "dependencies": { 318 | "com.unity.modules.unitywebrequest": "1.0.0", 319 | "com.unity.modules.jsonserialize": "1.0.0" 320 | } 321 | }, 322 | "com.unity.modules.unitywebrequest": { 323 | "version": "1.0.0", 324 | "depth": 0, 325 | "source": "builtin", 326 | "dependencies": {} 327 | }, 328 | "com.unity.modules.unitywebrequestassetbundle": { 329 | "version": "1.0.0", 330 | "depth": 0, 331 | "source": "builtin", 332 | "dependencies": { 333 | "com.unity.modules.assetbundle": "1.0.0", 334 | "com.unity.modules.unitywebrequest": "1.0.0" 335 | } 336 | }, 337 | "com.unity.modules.unitywebrequestaudio": { 338 | "version": "1.0.0", 339 | "depth": 0, 340 | "source": "builtin", 341 | "dependencies": { 342 | "com.unity.modules.unitywebrequest": "1.0.0", 343 | "com.unity.modules.audio": "1.0.0" 344 | } 345 | }, 346 | "com.unity.modules.unitywebrequesttexture": { 347 | "version": "1.0.0", 348 | "depth": 0, 349 | "source": "builtin", 350 | "dependencies": { 351 | "com.unity.modules.unitywebrequest": "1.0.0", 352 | "com.unity.modules.imageconversion": "1.0.0" 353 | } 354 | }, 355 | "com.unity.modules.unitywebrequestwww": { 356 | "version": "1.0.0", 357 | "depth": 0, 358 | "source": "builtin", 359 | "dependencies": { 360 | "com.unity.modules.unitywebrequest": "1.0.0", 361 | "com.unity.modules.unitywebrequestassetbundle": "1.0.0", 362 | "com.unity.modules.unitywebrequestaudio": "1.0.0", 363 | "com.unity.modules.audio": "1.0.0", 364 | "com.unity.modules.assetbundle": "1.0.0", 365 | "com.unity.modules.imageconversion": "1.0.0" 366 | } 367 | }, 368 | "com.unity.modules.vehicles": { 369 | "version": "1.0.0", 370 | "depth": 0, 371 | "source": "builtin", 372 | "dependencies": { 373 | "com.unity.modules.physics": "1.0.0" 374 | } 375 | }, 376 | "com.unity.modules.video": { 377 | "version": "1.0.0", 378 | "depth": 0, 379 | "source": "builtin", 380 | "dependencies": { 381 | "com.unity.modules.audio": "1.0.0", 382 | "com.unity.modules.ui": "1.0.0", 383 | "com.unity.modules.unitywebrequest": "1.0.0" 384 | } 385 | }, 386 | "com.unity.modules.vr": { 387 | "version": "1.0.0", 388 | "depth": 0, 389 | "source": "builtin", 390 | "dependencies": { 391 | "com.unity.modules.jsonserialize": "1.0.0", 392 | "com.unity.modules.physics": "1.0.0", 393 | "com.unity.modules.xr": "1.0.0" 394 | } 395 | }, 396 | "com.unity.modules.wind": { 397 | "version": "1.0.0", 398 | "depth": 0, 399 | "source": "builtin", 400 | "dependencies": {} 401 | }, 402 | "com.unity.modules.xr": { 403 | "version": "1.0.0", 404 | "depth": 0, 405 | "source": "builtin", 406 | "dependencies": { 407 | "com.unity.modules.physics": "1.0.0", 408 | "com.unity.modules.jsonserialize": "1.0.0", 409 | "com.unity.modules.subsystems": "1.0.0" 410 | } 411 | } 412 | } 413 | } 414 | -------------------------------------------------------------------------------- /ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!11 &1 4 | AudioManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Volume: 1 8 | Rolloff Scale: 1 9 | Doppler Factor: 1 10 | Default Speaker Mode: 2 11 | m_SampleRate: 0 12 | m_DSPBufferSize: 1024 13 | m_VirtualVoiceCount: 512 14 | m_RealVoiceCount: 32 15 | m_SpatializerPlugin: 16 | m_AmbisonicDecoderPlugin: 17 | m_DisableAudio: 0 18 | m_VirtualizeEffects: 1 19 | m_RequestedDSPBufferSize: 1024 20 | -------------------------------------------------------------------------------- /ProjectSettings/AutoStreamingSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1200 &1 4 | AutoStreamingSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | mSearchMode: 15 8 | mCustomSearchFile: 9 | mTextureSearchString: 10 | mMeshSearchString: 11 | mTextures: [] 12 | mAudios: [] 13 | mMeshes: [] 14 | mScenes: [] 15 | mConfigCCD: 16 | useCCD: 0 17 | cosKey: 18 | projectGuid: 19 | bucketUuid: 20 | bucketName: 21 | badgeName: 22 | -------------------------------------------------------------------------------- /ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!236 &1 4 | ClusterInputManager: 5 | m_ObjectHideFlags: 0 6 | m_Inputs: [] 7 | -------------------------------------------------------------------------------- /ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!55 &1 4 | PhysicsManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 11 7 | m_Gravity: {x: 0, y: -9.81, z: 0} 8 | m_DefaultMaterial: {fileID: 0} 9 | m_BounceThreshold: 2 10 | m_SleepThreshold: 0.005 11 | m_DefaultContactOffset: 0.01 12 | m_DefaultSolverIterations: 6 13 | m_DefaultSolverVelocityIterations: 1 14 | m_QueriesHitBackfaces: 0 15 | m_QueriesHitTriggers: 1 16 | m_EnableAdaptiveForce: 0 17 | m_ClothInterCollisionDistance: 0 18 | m_ClothInterCollisionStiffness: 0 19 | m_ContactsGeneration: 1 20 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 21 | m_AutoSimulation: 1 22 | m_AutoSyncTransforms: 0 23 | m_ReuseCollisionCallbacks: 1 24 | m_ClothInterCollisionSettingsToggle: 0 25 | m_ContactPairsMode: 0 26 | m_BroadphaseType: 0 27 | m_WorldBounds: 28 | m_Center: {x: 0, y: 0, z: 0} 29 | m_Extent: {x: 250, y: 250, z: 250} 30 | m_WorldSubdivisions: 8 31 | m_FrictionType: 0 32 | m_EnableEnhancedDeterminism: 0 33 | m_EnableUnifiedHeightmaps: 1 34 | m_DefaultMaxAngluarSpeed: 7 35 | -------------------------------------------------------------------------------- /ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1045 &1 4 | EditorBuildSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Scenes: [] 8 | m_configObjects: {} 9 | -------------------------------------------------------------------------------- /ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!159 &1 4 | EditorSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 11 7 | m_ExternalVersionControlSupport: Visible Meta Files 8 | m_SerializationMode: 2 9 | m_LineEndingsForNewScripts: 0 10 | m_DefaultBehaviorMode: 0 11 | m_PrefabRegularEnvironment: {fileID: 0} 12 | m_PrefabUIEnvironment: {fileID: 0} 13 | m_SpritePackerMode: 0 14 | m_SpritePackerPaddingPower: 1 15 | m_EtcTextureCompressorBehavior: 1 16 | m_EtcTextureFastCompressor: 1 17 | m_EtcTextureNormalCompressor: 2 18 | m_EtcTextureBestCompressor: 4 19 | m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp;asmref 20 | m_ProjectGenerationRootNamespace: 21 | m_CollabEditorSettings: 22 | inProgressEnabled: 1 23 | m_EnableTextureStreamingInEditMode: 1 24 | m_EnableTextureStreamingInPlayMode: 1 25 | m_AsyncShaderCompilation: 1 26 | m_EnterPlayModeOptionsEnabled: 0 27 | m_EnterPlayModeOptions: 3 28 | m_ShowLightmapResolutionOverlay: 1 29 | m_UseLegacyProbeSampleCount: 0 30 | m_SerializeInlineMappingsOnOneLine: 1 31 | -------------------------------------------------------------------------------- /ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!30 &1 4 | GraphicsSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 13 7 | m_Deferred: 8 | m_Mode: 1 9 | m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} 10 | m_DeferredReflections: 11 | m_Mode: 1 12 | m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} 13 | m_ScreenSpaceShadows: 14 | m_Mode: 1 15 | m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} 16 | m_LegacyDeferred: 17 | m_Mode: 1 18 | m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} 19 | m_DepthNormals: 20 | m_Mode: 1 21 | m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} 22 | m_MotionVectors: 23 | m_Mode: 1 24 | m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} 25 | m_LightHalo: 26 | m_Mode: 1 27 | m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} 28 | m_LensFlare: 29 | m_Mode: 1 30 | m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} 31 | m_AlwaysIncludedShaders: 32 | - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} 33 | - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} 34 | - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} 35 | - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} 36 | - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} 37 | - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} 38 | m_PreloadedShaders: [] 39 | m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, 40 | type: 0} 41 | m_CustomRenderPipeline: {fileID: 0} 42 | m_TransparencySortMode: 0 43 | m_TransparencySortAxis: {x: 0, y: 0, z: 1} 44 | m_DefaultRenderingPath: 1 45 | m_DefaultMobileRenderingPath: 1 46 | m_TierSettings: [] 47 | m_LightmapStripping: 0 48 | m_FogStripping: 0 49 | m_InstancingStripping: 0 50 | m_LightmapKeepPlain: 1 51 | m_LightmapKeepDirCombined: 1 52 | m_LightmapKeepDynamicPlain: 1 53 | m_LightmapKeepDynamicDirCombined: 1 54 | m_LightmapKeepShadowMask: 1 55 | m_LightmapKeepSubtractive: 1 56 | m_FogKeepLinear: 1 57 | m_FogKeepExp: 1 58 | m_FogKeepExp2: 1 59 | m_AlbedoSwatchInfos: [] 60 | m_LightsUseLinearIntensity: 0 61 | m_LightsUseColorTemperature: 0 62 | m_LogWhenShaderIsCompiled: 0 63 | m_AllowEnlightenSupportForUpgradedProject: 0 64 | -------------------------------------------------------------------------------- /ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!13 &1 4 | InputManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_Axes: 8 | - serializedVersion: 3 9 | m_Name: Horizontal 10 | descriptiveName: 11 | descriptiveNegativeName: 12 | negativeButton: left 13 | positiveButton: right 14 | altNegativeButton: a 15 | altPositiveButton: d 16 | gravity: 3 17 | dead: 0.001 18 | sensitivity: 3 19 | snap: 1 20 | invert: 0 21 | type: 0 22 | axis: 0 23 | joyNum: 0 24 | - serializedVersion: 3 25 | m_Name: Vertical 26 | descriptiveName: 27 | descriptiveNegativeName: 28 | negativeButton: down 29 | positiveButton: up 30 | altNegativeButton: s 31 | altPositiveButton: w 32 | gravity: 3 33 | dead: 0.001 34 | sensitivity: 3 35 | snap: 1 36 | invert: 0 37 | type: 0 38 | axis: 0 39 | joyNum: 0 40 | - serializedVersion: 3 41 | m_Name: Fire1 42 | descriptiveName: 43 | descriptiveNegativeName: 44 | negativeButton: 45 | positiveButton: left ctrl 46 | altNegativeButton: 47 | altPositiveButton: mouse 0 48 | gravity: 1000 49 | dead: 0.001 50 | sensitivity: 1000 51 | snap: 0 52 | invert: 0 53 | type: 0 54 | axis: 0 55 | joyNum: 0 56 | - serializedVersion: 3 57 | m_Name: Fire2 58 | descriptiveName: 59 | descriptiveNegativeName: 60 | negativeButton: 61 | positiveButton: left alt 62 | altNegativeButton: 63 | altPositiveButton: mouse 1 64 | gravity: 1000 65 | dead: 0.001 66 | sensitivity: 1000 67 | snap: 0 68 | invert: 0 69 | type: 0 70 | axis: 0 71 | joyNum: 0 72 | - serializedVersion: 3 73 | m_Name: Fire3 74 | descriptiveName: 75 | descriptiveNegativeName: 76 | negativeButton: 77 | positiveButton: left shift 78 | altNegativeButton: 79 | altPositiveButton: mouse 2 80 | gravity: 1000 81 | dead: 0.001 82 | sensitivity: 1000 83 | snap: 0 84 | invert: 0 85 | type: 0 86 | axis: 0 87 | joyNum: 0 88 | - serializedVersion: 3 89 | m_Name: Jump 90 | descriptiveName: 91 | descriptiveNegativeName: 92 | negativeButton: 93 | positiveButton: space 94 | altNegativeButton: 95 | altPositiveButton: 96 | gravity: 1000 97 | dead: 0.001 98 | sensitivity: 1000 99 | snap: 0 100 | invert: 0 101 | type: 0 102 | axis: 0 103 | joyNum: 0 104 | - serializedVersion: 3 105 | m_Name: Mouse X 106 | descriptiveName: 107 | descriptiveNegativeName: 108 | negativeButton: 109 | positiveButton: 110 | altNegativeButton: 111 | altPositiveButton: 112 | gravity: 0 113 | dead: 0 114 | sensitivity: 0.1 115 | snap: 0 116 | invert: 0 117 | type: 1 118 | axis: 0 119 | joyNum: 0 120 | - serializedVersion: 3 121 | m_Name: Mouse Y 122 | descriptiveName: 123 | descriptiveNegativeName: 124 | negativeButton: 125 | positiveButton: 126 | altNegativeButton: 127 | altPositiveButton: 128 | gravity: 0 129 | dead: 0 130 | sensitivity: 0.1 131 | snap: 0 132 | invert: 0 133 | type: 1 134 | axis: 1 135 | joyNum: 0 136 | - serializedVersion: 3 137 | m_Name: Mouse ScrollWheel 138 | descriptiveName: 139 | descriptiveNegativeName: 140 | negativeButton: 141 | positiveButton: 142 | altNegativeButton: 143 | altPositiveButton: 144 | gravity: 0 145 | dead: 0 146 | sensitivity: 0.1 147 | snap: 0 148 | invert: 0 149 | type: 1 150 | axis: 2 151 | joyNum: 0 152 | - serializedVersion: 3 153 | m_Name: Horizontal 154 | descriptiveName: 155 | descriptiveNegativeName: 156 | negativeButton: 157 | positiveButton: 158 | altNegativeButton: 159 | altPositiveButton: 160 | gravity: 0 161 | dead: 0.19 162 | sensitivity: 1 163 | snap: 0 164 | invert: 0 165 | type: 2 166 | axis: 0 167 | joyNum: 0 168 | - serializedVersion: 3 169 | m_Name: Vertical 170 | descriptiveName: 171 | descriptiveNegativeName: 172 | negativeButton: 173 | positiveButton: 174 | altNegativeButton: 175 | altPositiveButton: 176 | gravity: 0 177 | dead: 0.19 178 | sensitivity: 1 179 | snap: 0 180 | invert: 1 181 | type: 2 182 | axis: 1 183 | joyNum: 0 184 | - serializedVersion: 3 185 | m_Name: Fire1 186 | descriptiveName: 187 | descriptiveNegativeName: 188 | negativeButton: 189 | positiveButton: joystick button 0 190 | altNegativeButton: 191 | altPositiveButton: 192 | gravity: 1000 193 | dead: 0.001 194 | sensitivity: 1000 195 | snap: 0 196 | invert: 0 197 | type: 0 198 | axis: 0 199 | joyNum: 0 200 | - serializedVersion: 3 201 | m_Name: Fire2 202 | descriptiveName: 203 | descriptiveNegativeName: 204 | negativeButton: 205 | positiveButton: joystick button 1 206 | altNegativeButton: 207 | altPositiveButton: 208 | gravity: 1000 209 | dead: 0.001 210 | sensitivity: 1000 211 | snap: 0 212 | invert: 0 213 | type: 0 214 | axis: 0 215 | joyNum: 0 216 | - serializedVersion: 3 217 | m_Name: Fire3 218 | descriptiveName: 219 | descriptiveNegativeName: 220 | negativeButton: 221 | positiveButton: joystick button 2 222 | altNegativeButton: 223 | altPositiveButton: 224 | gravity: 1000 225 | dead: 0.001 226 | sensitivity: 1000 227 | snap: 0 228 | invert: 0 229 | type: 0 230 | axis: 0 231 | joyNum: 0 232 | - serializedVersion: 3 233 | m_Name: Jump 234 | descriptiveName: 235 | descriptiveNegativeName: 236 | negativeButton: 237 | positiveButton: joystick button 3 238 | altNegativeButton: 239 | altPositiveButton: 240 | gravity: 1000 241 | dead: 0.001 242 | sensitivity: 1000 243 | snap: 0 244 | invert: 0 245 | type: 0 246 | axis: 0 247 | joyNum: 0 248 | - serializedVersion: 3 249 | m_Name: Submit 250 | descriptiveName: 251 | descriptiveNegativeName: 252 | negativeButton: 253 | positiveButton: return 254 | altNegativeButton: 255 | altPositiveButton: joystick button 0 256 | gravity: 1000 257 | dead: 0.001 258 | sensitivity: 1000 259 | snap: 0 260 | invert: 0 261 | type: 0 262 | axis: 0 263 | joyNum: 0 264 | - serializedVersion: 3 265 | m_Name: Submit 266 | descriptiveName: 267 | descriptiveNegativeName: 268 | negativeButton: 269 | positiveButton: enter 270 | altNegativeButton: 271 | altPositiveButton: space 272 | gravity: 1000 273 | dead: 0.001 274 | sensitivity: 1000 275 | snap: 0 276 | invert: 0 277 | type: 0 278 | axis: 0 279 | joyNum: 0 280 | - serializedVersion: 3 281 | m_Name: Cancel 282 | descriptiveName: 283 | descriptiveNegativeName: 284 | negativeButton: 285 | positiveButton: escape 286 | altNegativeButton: 287 | altPositiveButton: joystick button 1 288 | gravity: 1000 289 | dead: 0.001 290 | sensitivity: 1000 291 | snap: 0 292 | invert: 0 293 | type: 0 294 | axis: 0 295 | joyNum: 0 296 | - serializedVersion: 3 297 | m_Name: Enable Debug Button 1 298 | descriptiveName: 299 | descriptiveNegativeName: 300 | negativeButton: 301 | positiveButton: left ctrl 302 | altNegativeButton: 303 | altPositiveButton: joystick button 8 304 | gravity: 0 305 | dead: 0 306 | sensitivity: 0 307 | snap: 0 308 | invert: 0 309 | type: 0 310 | axis: 0 311 | joyNum: 0 312 | - serializedVersion: 3 313 | m_Name: Enable Debug Button 2 314 | descriptiveName: 315 | descriptiveNegativeName: 316 | negativeButton: 317 | positiveButton: backspace 318 | altNegativeButton: 319 | altPositiveButton: joystick button 9 320 | gravity: 0 321 | dead: 0 322 | sensitivity: 0 323 | snap: 0 324 | invert: 0 325 | type: 0 326 | axis: 0 327 | joyNum: 0 328 | - serializedVersion: 3 329 | m_Name: Debug Reset 330 | descriptiveName: 331 | descriptiveNegativeName: 332 | negativeButton: 333 | positiveButton: left alt 334 | altNegativeButton: 335 | altPositiveButton: joystick button 1 336 | gravity: 0 337 | dead: 0 338 | sensitivity: 0 339 | snap: 0 340 | invert: 0 341 | type: 0 342 | axis: 0 343 | joyNum: 0 344 | - serializedVersion: 3 345 | m_Name: Debug Next 346 | descriptiveName: 347 | descriptiveNegativeName: 348 | negativeButton: 349 | positiveButton: page down 350 | altNegativeButton: 351 | altPositiveButton: joystick button 5 352 | gravity: 0 353 | dead: 0 354 | sensitivity: 0 355 | snap: 0 356 | invert: 0 357 | type: 0 358 | axis: 0 359 | joyNum: 0 360 | - serializedVersion: 3 361 | m_Name: Debug Previous 362 | descriptiveName: 363 | descriptiveNegativeName: 364 | negativeButton: 365 | positiveButton: page up 366 | altNegativeButton: 367 | altPositiveButton: joystick button 4 368 | gravity: 0 369 | dead: 0 370 | sensitivity: 0 371 | snap: 0 372 | invert: 0 373 | type: 0 374 | axis: 0 375 | joyNum: 0 376 | - serializedVersion: 3 377 | m_Name: Debug Validate 378 | descriptiveName: 379 | descriptiveNegativeName: 380 | negativeButton: 381 | positiveButton: return 382 | altNegativeButton: 383 | altPositiveButton: joystick button 0 384 | gravity: 0 385 | dead: 0 386 | sensitivity: 0 387 | snap: 0 388 | invert: 0 389 | type: 0 390 | axis: 0 391 | joyNum: 0 392 | - serializedVersion: 3 393 | m_Name: Debug Persistent 394 | descriptiveName: 395 | descriptiveNegativeName: 396 | negativeButton: 397 | positiveButton: right shift 398 | altNegativeButton: 399 | altPositiveButton: joystick button 2 400 | gravity: 0 401 | dead: 0 402 | sensitivity: 0 403 | snap: 0 404 | invert: 0 405 | type: 0 406 | axis: 0 407 | joyNum: 0 408 | - serializedVersion: 3 409 | m_Name: Debug Multiplier 410 | descriptiveName: 411 | descriptiveNegativeName: 412 | negativeButton: 413 | positiveButton: left shift 414 | altNegativeButton: 415 | altPositiveButton: joystick button 3 416 | gravity: 0 417 | dead: 0 418 | sensitivity: 0 419 | snap: 0 420 | invert: 0 421 | type: 0 422 | axis: 0 423 | joyNum: 0 424 | - serializedVersion: 3 425 | m_Name: Debug Horizontal 426 | descriptiveName: 427 | descriptiveNegativeName: 428 | negativeButton: left 429 | positiveButton: right 430 | altNegativeButton: 431 | altPositiveButton: 432 | gravity: 1000 433 | dead: 0.001 434 | sensitivity: 1000 435 | snap: 0 436 | invert: 0 437 | type: 0 438 | axis: 0 439 | joyNum: 0 440 | - serializedVersion: 3 441 | m_Name: Debug Vertical 442 | descriptiveName: 443 | descriptiveNegativeName: 444 | negativeButton: down 445 | positiveButton: up 446 | altNegativeButton: 447 | altPositiveButton: 448 | gravity: 1000 449 | dead: 0.001 450 | sensitivity: 1000 451 | snap: 0 452 | invert: 0 453 | type: 0 454 | axis: 0 455 | joyNum: 0 456 | - serializedVersion: 3 457 | m_Name: Debug Vertical 458 | descriptiveName: 459 | descriptiveNegativeName: 460 | negativeButton: down 461 | positiveButton: up 462 | altNegativeButton: 463 | altPositiveButton: 464 | gravity: 1000 465 | dead: 0.001 466 | sensitivity: 1000 467 | snap: 0 468 | invert: 0 469 | type: 2 470 | axis: 6 471 | joyNum: 0 472 | - serializedVersion: 3 473 | m_Name: Debug Horizontal 474 | descriptiveName: 475 | descriptiveNegativeName: 476 | negativeButton: left 477 | positiveButton: right 478 | altNegativeButton: 479 | altPositiveButton: 480 | gravity: 1000 481 | dead: 0.001 482 | sensitivity: 1000 483 | snap: 0 484 | invert: 0 485 | type: 2 486 | axis: 5 487 | joyNum: 0 488 | m_UsePhysicalKeys: 0 489 | -------------------------------------------------------------------------------- /ProjectSettings/MemorySettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!387306366 &1 4 | MemorySettings: 5 | m_ObjectHideFlags: 0 6 | m_EditorMemorySettings: 7 | m_MainAllocatorBlockSize: -1 8 | m_ThreadAllocatorBlockSize: -1 9 | m_MainGfxBlockSize: -1 10 | m_ThreadGfxBlockSize: -1 11 | m_CacheBlockSize: -1 12 | m_TypetreeBlockSize: -1 13 | m_ProfilerBlockSize: -1 14 | m_ProfilerEditorBlockSize: -1 15 | m_BucketAllocatorGranularity: -1 16 | m_BucketAllocatorBucketsCount: -1 17 | m_BucketAllocatorBlockSize: -1 18 | m_BucketAllocatorBlockCount: -1 19 | m_ProfilerBucketAllocatorGranularity: -1 20 | m_ProfilerBucketAllocatorBucketsCount: -1 21 | m_ProfilerBucketAllocatorBlockSize: -1 22 | m_ProfilerBucketAllocatorBlockCount: -1 23 | m_TempAllocatorSizeMain: -1 24 | m_JobTempAllocatorBlockSize: -1 25 | m_BackgroundJobTempAllocatorBlockSize: -1 26 | m_JobTempAllocatorReducedBlockSize: -1 27 | m_TempAllocatorSizeGIBakingWorker: -1 28 | m_TempAllocatorSizeNavMeshWorker: -1 29 | m_TempAllocatorSizeAudioWorker: -1 30 | m_TempAllocatorSizeCloudWorker: -1 31 | m_TempAllocatorSizeGfx: -1 32 | m_TempAllocatorSizeJobWorker: -1 33 | m_TempAllocatorSizeBackgroundWorker: -1 34 | m_TempAllocatorSizePreloadManager: -1 35 | m_PlatformMemorySettings: {} 36 | -------------------------------------------------------------------------------- /ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!126 &1 4 | NavMeshProjectSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | areas: 8 | - name: Walkable 9 | cost: 1 10 | - name: Not Walkable 11 | cost: 1 12 | - name: Jump 13 | cost: 2 14 | - name: 15 | cost: 1 16 | - name: 17 | cost: 1 18 | - name: 19 | cost: 1 20 | - name: 21 | cost: 1 22 | - name: 23 | cost: 1 24 | - name: 25 | cost: 1 26 | - name: 27 | cost: 1 28 | - name: 29 | cost: 1 30 | - name: 31 | cost: 1 32 | - name: 33 | cost: 1 34 | - name: 35 | cost: 1 36 | - name: 37 | cost: 1 38 | - name: 39 | cost: 1 40 | - name: 41 | cost: 1 42 | - name: 43 | cost: 1 44 | - name: 45 | cost: 1 46 | - name: 47 | cost: 1 48 | - name: 49 | cost: 1 50 | - name: 51 | cost: 1 52 | - name: 53 | cost: 1 54 | - name: 55 | cost: 1 56 | - name: 57 | cost: 1 58 | - name: 59 | cost: 1 60 | - name: 61 | cost: 1 62 | - name: 63 | cost: 1 64 | - name: 65 | cost: 1 66 | - name: 67 | cost: 1 68 | - name: 69 | cost: 1 70 | - name: 71 | cost: 1 72 | m_LastAgentTypeID: -887442657 73 | m_Settings: 74 | - serializedVersion: 2 75 | agentTypeID: 0 76 | agentRadius: 0.5 77 | agentHeight: 2 78 | agentSlope: 45 79 | agentClimb: 0.75 80 | ledgeDropHeight: 0 81 | maxJumpAcrossDistance: 0 82 | minRegionArea: 2 83 | manualCellSize: 0 84 | cellSize: 0.16666667 85 | manualTileSize: 0 86 | tileSize: 256 87 | accuratePlacement: 0 88 | debug: 89 | m_Flags: 0 90 | m_SettingNames: 91 | - Humanoid 92 | -------------------------------------------------------------------------------- /ProjectSettings/PackageManagerSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!114 &1 4 | MonoBehaviour: 5 | m_ObjectHideFlags: 61 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_GameObject: {fileID: 0} 10 | m_Enabled: 1 11 | m_EditorHideFlags: 0 12 | m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} 13 | m_Name: 14 | m_EditorClassIdentifier: 15 | m_EnablePreReleasePackages: 0 16 | m_EnablePackageDependencies: 0 17 | m_AdvancedSettingsExpanded: 1 18 | m_ScopedRegistriesSettingsExpanded: 1 19 | m_SeeAllPackageVersions: 0 20 | oneTimeWarningShown: 0 21 | m_Registries: 22 | - m_Id: main 23 | m_Name: 24 | m_Url: https://packages.unity.cn 25 | m_Scopes: [] 26 | m_IsDefault: 1 27 | m_Capabilities: 7 28 | m_ConfigSource: 0 29 | m_UserSelectedRegistryName: 30 | m_UserAddingNewScopedRegistry: 0 31 | m_RegistryInfoDraft: 32 | m_Modified: 0 33 | m_ErrorMessage: 34 | m_UserModificationsInstanceId: -830 35 | m_OriginalInstanceId: -832 36 | m_LoadAssets: 0 37 | -------------------------------------------------------------------------------- /ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "m_Name": "Settings", 3 | "m_Path": "ProjectSettings/Packages/com.unity.testtools.codecoverage/Settings.json", 4 | "m_Dictionary": { 5 | "m_DictionaryValues": [] 6 | } 7 | } -------------------------------------------------------------------------------- /ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!19 &1 4 | Physics2DSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 4 7 | m_Gravity: {x: 0, y: -9.81} 8 | m_DefaultMaterial: {fileID: 0} 9 | m_VelocityIterations: 8 10 | m_PositionIterations: 3 11 | m_VelocityThreshold: 1 12 | m_MaxLinearCorrection: 0.2 13 | m_MaxAngularCorrection: 8 14 | m_MaxTranslationSpeed: 100 15 | m_MaxRotationSpeed: 360 16 | m_BaumgarteScale: 0.2 17 | m_BaumgarteTimeOfImpactScale: 0.75 18 | m_TimeToSleep: 0.5 19 | m_LinearSleepTolerance: 0.01 20 | m_AngularSleepTolerance: 2 21 | m_DefaultContactOffset: 0.01 22 | m_JobOptions: 23 | serializedVersion: 2 24 | useMultithreading: 0 25 | useConsistencySorting: 0 26 | m_InterpolationPosesPerJob: 100 27 | m_NewContactsPerJob: 30 28 | m_CollideContactsPerJob: 100 29 | m_ClearFlagsPerJob: 200 30 | m_ClearBodyForcesPerJob: 200 31 | m_SyncDiscreteFixturesPerJob: 50 32 | m_SyncContinuousFixturesPerJob: 50 33 | m_FindNearestContactsPerJob: 100 34 | m_UpdateTriggerContactsPerJob: 100 35 | m_IslandSolverCostThreshold: 100 36 | m_IslandSolverBodyCostScale: 1 37 | m_IslandSolverContactCostScale: 10 38 | m_IslandSolverJointCostScale: 10 39 | m_IslandSolverBodiesPerJob: 50 40 | m_IslandSolverContactsPerJob: 50 41 | m_AutoSimulation: 1 42 | m_QueriesHitTriggers: 1 43 | m_QueriesStartInColliders: 1 44 | m_CallbacksOnDisable: 1 45 | m_ReuseCollisionCallbacks: 1 46 | m_AutoSyncTransforms: 0 47 | m_AlwaysShowColliders: 0 48 | m_ShowColliderSleep: 1 49 | m_ShowColliderContacts: 0 50 | m_ShowColliderAABB: 0 51 | m_ContactArrowScale: 0.2 52 | m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} 53 | m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} 54 | m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} 55 | m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} 56 | m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 57 | -------------------------------------------------------------------------------- /ProjectSettings/PresetManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!1386491679 &1 4 | PresetManager: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 2 7 | m_DefaultPresets: {} 8 | -------------------------------------------------------------------------------- /ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!129 &1 4 | PlayerSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 23 7 | productGUID: 17985cfac24b4a7429bf22378ab75bba 8 | AndroidProfiler: 0 9 | AndroidFilterTouchesWhenObscured: 0 10 | AndroidEnableSustainedPerformanceMode: 0 11 | defaultScreenOrientation: 4 12 | targetDevice: 2 13 | useOnDemandResources: 0 14 | accelerometerFrequency: 60 15 | companyName: DefaultCompany 16 | productName: CommandConsole 17 | defaultCursor: {fileID: 0} 18 | cursorHotspot: {x: 0, y: 0} 19 | m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} 20 | m_ShowUnitySplashScreen: 1 21 | m_ShowUnitySplashLogo: 1 22 | m_SplashScreenOverlayOpacity: 1 23 | m_SplashScreenAnimation: 1 24 | m_SplashScreenLogoStyle: 1 25 | m_SplashScreenDrawMode: 0 26 | m_SplashScreenBackgroundAnimationZoom: 1 27 | m_SplashScreenLogoAnimationZoom: 1 28 | m_SplashScreenBackgroundLandscapeAspect: 1 29 | m_SplashScreenBackgroundPortraitAspect: 1 30 | m_SplashScreenBackgroundLandscapeUvs: 31 | serializedVersion: 2 32 | x: 0 33 | y: 0 34 | width: 1 35 | height: 1 36 | m_SplashScreenBackgroundPortraitUvs: 37 | serializedVersion: 2 38 | x: 0 39 | y: 0 40 | width: 1 41 | height: 1 42 | m_SplashScreenLogos: [] 43 | m_VirtualRealitySplashScreen: {fileID: 0} 44 | m_ShowUnitySplashAds: 0 45 | m_AdsAndroidGameId: 46 | m_AdsIosGameId: 47 | m_ShowSplashAdsSlogan: 0 48 | m_SloganImage: {fileID: 0} 49 | m_SloganHeight: 150 50 | m_HolographicTrackingLossScreen: {fileID: 0} 51 | defaultScreenWidth: 1920 52 | defaultScreenHeight: 1080 53 | defaultScreenWidthWeb: 960 54 | defaultScreenHeightWeb: 600 55 | m_StereoRenderingPath: 0 56 | m_ActiveColorSpace: 1 57 | m_MTRendering: 1 58 | mipStripping: 0 59 | numberOfMipsStripped: 0 60 | m_StackTraceTypes: 010000000100000001000000010000000100000001000000 61 | iosShowActivityIndicatorOnLoading: -1 62 | androidShowActivityIndicatorOnLoading: -1 63 | iosUseCustomAppBackgroundBehavior: 0 64 | iosAllowHTTPDownload: 1 65 | allowedAutorotateToPortrait: 1 66 | allowedAutorotateToPortraitUpsideDown: 1 67 | allowedAutorotateToLandscapeRight: 1 68 | allowedAutorotateToLandscapeLeft: 1 69 | useOSAutorotation: 1 70 | use32BitDisplayBuffer: 1 71 | preserveFramebufferAlpha: 0 72 | disableDepthAndStencilBuffers: 0 73 | androidStartInFullscreen: 1 74 | androidRenderOutsideSafeArea: 1 75 | androidUseSwappy: 1 76 | androidBlitType: 0 77 | androidResizableWindow: 0 78 | androidDefaultWindowWidth: 1920 79 | androidDefaultWindowHeight: 1080 80 | androidMinimumWindowWidth: 400 81 | androidMinimumWindowHeight: 300 82 | androidFullscreenMode: 1 83 | defaultIsNativeResolution: 1 84 | macRetinaSupport: 1 85 | runInBackground: 1 86 | captureSingleScreen: 0 87 | muteOtherAudioSources: 0 88 | Prepare IOS For Recording: 0 89 | Force IOS Speakers When Recording: 0 90 | deferSystemGesturesMode: 0 91 | hideHomeButton: 0 92 | submitAnalytics: 1 93 | usePlayerLog: 1 94 | autoStreaming: 0 95 | useAnimationStreaming: 0 96 | useFontStreaming: 0 97 | autoStreamingId: 98 | instantGameAppId: 99 | bakeCollisionMeshes: 0 100 | forceSingleInstance: 0 101 | useFlipModelSwapchain: 1 102 | resizableWindow: 0 103 | useMacAppStoreValidation: 0 104 | macAppStoreCategory: public.app-category.games 105 | gpuSkinning: 1 106 | xboxPIXTextureCapture: 0 107 | xboxEnableAvatar: 0 108 | xboxEnableKinect: 0 109 | xboxEnableKinectAutoTracking: 0 110 | xboxEnableFitness: 0 111 | visibleInBackground: 1 112 | allowFullscreenSwitch: 1 113 | fullscreenMode: 1 114 | xboxSpeechDB: 0 115 | xboxEnableHeadOrientation: 0 116 | xboxEnableGuest: 0 117 | xboxEnablePIXSampling: 0 118 | metalFramebufferOnly: 0 119 | xboxOneResolution: 0 120 | xboxOneSResolution: 0 121 | xboxOneXResolution: 3 122 | xboxOneMonoLoggingLevel: 0 123 | xboxOneLoggingLevel: 1 124 | xboxOneDisableEsram: 0 125 | xboxOneEnableTypeOptimization: 0 126 | xboxOnePresentImmediateThreshold: 0 127 | switchQueueCommandMemory: 0 128 | switchQueueControlMemory: 16384 129 | switchQueueComputeMemory: 262144 130 | switchNVNShaderPoolsGranularity: 33554432 131 | switchNVNDefaultPoolsGranularity: 16777216 132 | switchNVNOtherPoolsGranularity: 16777216 133 | switchNVNMaxPublicTextureIDCount: 0 134 | switchNVNMaxPublicSamplerIDCount: 0 135 | stadiaPresentMode: 0 136 | stadiaTargetFramerate: 0 137 | vulkanNumSwapchainBuffers: 3 138 | vulkanEnableSetSRGBWrite: 0 139 | vulkanEnablePreTransform: 1 140 | vulkanEnableLateAcquireNextImage: 0 141 | vulkanEnableCommandBufferRecycling: 1 142 | m_SupportedAspectRatios: 143 | 4:3: 1 144 | 5:4: 1 145 | 16:10: 1 146 | 16:9: 1 147 | Others: 1 148 | bundleVersion: 0.1 149 | preloadedAssets: [] 150 | metroInputSource: 0 151 | wsaTransparentSwapchain: 0 152 | m_HolographicPauseOnTrackingLoss: 1 153 | xboxOneDisableKinectGpuReservation: 1 154 | xboxOneEnable7thCore: 1 155 | vrSettings: 156 | enable360StereoCapture: 0 157 | isWsaHolographicRemotingEnabled: 0 158 | enableFrameTimingStats: 0 159 | enableOpenGLProfilerGPURecorders: 1 160 | useHDRDisplay: 0 161 | D3DHDRBitDepth: 0 162 | m_ColorGamuts: 00000000 163 | targetPixelDensity: 30 164 | resolutionScalingMode: 0 165 | resetResolutionOnWindowResize: 0 166 | androidSupportedAspectRatio: 1 167 | androidMaxAspectRatio: 2.1 168 | applicationIdentifier: {} 169 | buildNumber: 170 | Standalone: 0 171 | iPhone: 0 172 | tvOS: 0 173 | overrideDefaultApplicationIdentifier: 0 174 | AndroidBundleVersionCode: 1 175 | AndroidMinSdkVersion: 22 176 | AndroidTargetSdkVersion: 0 177 | AndroidPreferredInstallLocation: 1 178 | aotOptions: 179 | stripEngineCode: 1 180 | iPhoneStrippingLevel: 0 181 | iPhoneScriptCallOptimization: 0 182 | ForceInternetPermission: 0 183 | ForceSDCardPermission: 0 184 | CreateWallpaper: 0 185 | APKExpansionFiles: 0 186 | keepLoadedShadersAlive: 0 187 | StripUnusedMeshComponents: 1 188 | VertexChannelCompressionMask: 4054 189 | iPhoneSdkVersion: 988 190 | iOSTargetOSVersionString: 11.0 191 | tvOSSdkVersion: 0 192 | tvOSRequireExtendedGameController: 0 193 | tvOSTargetOSVersionString: 11.0 194 | uIPrerenderedIcon: 0 195 | uIRequiresPersistentWiFi: 0 196 | uIRequiresFullScreen: 1 197 | uIStatusBarHidden: 1 198 | uIExitOnSuspend: 0 199 | uIStatusBarStyle: 0 200 | appleTVSplashScreen: {fileID: 0} 201 | appleTVSplashScreen2x: {fileID: 0} 202 | tvOSSmallIconLayers: [] 203 | tvOSSmallIconLayers2x: [] 204 | tvOSLargeIconLayers: [] 205 | tvOSLargeIconLayers2x: [] 206 | tvOSTopShelfImageLayers: [] 207 | tvOSTopShelfImageLayers2x: [] 208 | tvOSTopShelfImageWideLayers: [] 209 | tvOSTopShelfImageWideLayers2x: [] 210 | iOSLaunchScreenType: 0 211 | iOSLaunchScreenPortrait: {fileID: 0} 212 | iOSLaunchScreenLandscape: {fileID: 0} 213 | iOSLaunchScreenBackgroundColor: 214 | serializedVersion: 2 215 | rgba: 0 216 | iOSLaunchScreenFillPct: 100 217 | iOSLaunchScreenSize: 100 218 | iOSLaunchScreenCustomXibPath: 219 | iOSLaunchScreeniPadType: 0 220 | iOSLaunchScreeniPadImage: {fileID: 0} 221 | iOSLaunchScreeniPadBackgroundColor: 222 | serializedVersion: 2 223 | rgba: 0 224 | iOSLaunchScreeniPadFillPct: 100 225 | iOSLaunchScreeniPadSize: 100 226 | iOSLaunchScreeniPadCustomXibPath: 227 | iOSLaunchScreenCustomStoryboardPath: 228 | iOSLaunchScreeniPadCustomStoryboardPath: 229 | iOSDeviceRequirements: [] 230 | iOSURLSchemes: [] 231 | macOSURLSchemes: [] 232 | iOSBackgroundModes: 0 233 | iOSMetalForceHardShadows: 0 234 | metalEditorSupport: 1 235 | metalAPIValidation: 1 236 | iOSRenderExtraFrameOnPause: 0 237 | iosCopyPluginsCodeInsteadOfSymlink: 0 238 | appleDeveloperTeamID: 239 | iOSManualSigningProvisioningProfileID: 240 | tvOSManualSigningProvisioningProfileID: 241 | iOSManualSigningProvisioningProfileType: 0 242 | tvOSManualSigningProvisioningProfileType: 0 243 | appleEnableAutomaticSigning: 0 244 | iOSRequireARKit: 0 245 | iOSAutomaticallyDetectAndAddCapabilities: 1 246 | appleEnableProMotion: 0 247 | shaderPrecisionModel: 0 248 | clonedFromGUID: c0afd0d1d80e3634a9dac47e8a0426ea 249 | templatePackageId: com.unity.template.3d@8.1.3 250 | templateDefaultScene: Assets/Scenes/SampleScene.unity 251 | useCustomMainManifest: 0 252 | useCustomLauncherManifest: 0 253 | useCustomMainGradleTemplate: 0 254 | useCustomLauncherGradleManifest: 0 255 | useCustomBaseGradleTemplate: 0 256 | useCustomGradlePropertiesTemplate: 0 257 | useCustomProguardFile: 0 258 | AndroidTargetArchitectures: 1 259 | AndroidTargetDevices: 0 260 | AndroidSplashScreenScale: 0 261 | androidSplashScreen: {fileID: 0} 262 | AndroidKeystoreName: 263 | AndroidKeyaliasName: 264 | AndroidBuildApkPerCpuArchitecture: 0 265 | AndroidTVCompatibility: 0 266 | AndroidIsGame: 1 267 | AndroidEnableTango: 0 268 | androidEnableBanner: 1 269 | androidUseLowAccuracyLocation: 0 270 | androidUseCustomKeystore: 0 271 | m_AndroidBanners: 272 | - width: 320 273 | height: 180 274 | banner: {fileID: 0} 275 | androidGamepadSupportLevel: 0 276 | chromeosInputEmulation: 1 277 | AndroidMinifyWithR8: 0 278 | AndroidMinifyRelease: 0 279 | AndroidMinifyDebug: 0 280 | AndroidValidateAppBundleSize: 1 281 | AndroidAppBundleSizeToValidate: 150 282 | m_BuildTargetIcons: [] 283 | m_BuildTargetPlatformIcons: [] 284 | m_BuildTargetBatching: 285 | - m_BuildTarget: Standalone 286 | m_StaticBatching: 1 287 | m_DynamicBatching: 0 288 | - m_BuildTarget: tvOS 289 | m_StaticBatching: 1 290 | m_DynamicBatching: 0 291 | - m_BuildTarget: Android 292 | m_StaticBatching: 1 293 | m_DynamicBatching: 0 294 | - m_BuildTarget: iPhone 295 | m_StaticBatching: 1 296 | m_DynamicBatching: 0 297 | - m_BuildTarget: WebGL 298 | m_StaticBatching: 0 299 | m_DynamicBatching: 0 300 | m_BuildTargetShaderSettings: [] 301 | m_BuildTargetGraphicsJobs: 302 | - m_BuildTarget: MacStandaloneSupport 303 | m_GraphicsJobs: 0 304 | - m_BuildTarget: Switch 305 | m_GraphicsJobs: 1 306 | - m_BuildTarget: MetroSupport 307 | m_GraphicsJobs: 1 308 | - m_BuildTarget: AppleTVSupport 309 | m_GraphicsJobs: 0 310 | - m_BuildTarget: BJMSupport 311 | m_GraphicsJobs: 1 312 | - m_BuildTarget: LinuxStandaloneSupport 313 | m_GraphicsJobs: 1 314 | - m_BuildTarget: PS4Player 315 | m_GraphicsJobs: 1 316 | - m_BuildTarget: iOSSupport 317 | m_GraphicsJobs: 0 318 | - m_BuildTarget: WindowsStandaloneSupport 319 | m_GraphicsJobs: 1 320 | - m_BuildTarget: XboxOnePlayer 321 | m_GraphicsJobs: 1 322 | - m_BuildTarget: LuminSupport 323 | m_GraphicsJobs: 0 324 | - m_BuildTarget: AndroidPlayer 325 | m_GraphicsJobs: 0 326 | - m_BuildTarget: WebGLSupport 327 | m_GraphicsJobs: 0 328 | m_BuildTargetGraphicsJobMode: 329 | - m_BuildTarget: PS4Player 330 | m_GraphicsJobMode: 0 331 | - m_BuildTarget: XboxOnePlayer 332 | m_GraphicsJobMode: 0 333 | m_BuildTargetGraphicsAPIs: 334 | - m_BuildTarget: AndroidPlayer 335 | m_APIs: 150000000b000000 336 | m_Automatic: 1 337 | - m_BuildTarget: iOSSupport 338 | m_APIs: 10000000 339 | m_Automatic: 1 340 | - m_BuildTarget: AppleTVSupport 341 | m_APIs: 10000000 342 | m_Automatic: 1 343 | - m_BuildTarget: WebGLSupport 344 | m_APIs: 0b000000 345 | m_Automatic: 1 346 | m_BuildTargetVRSettings: 347 | - m_BuildTarget: Standalone 348 | m_Enabled: 0 349 | m_Devices: 350 | - Oculus 351 | - OpenVR 352 | m_DefaultShaderChunkSizeInMB: 16 353 | m_DefaultShaderChunkCount: 0 354 | openGLRequireES31: 0 355 | openGLRequireES31AEP: 0 356 | openGLRequireES32: 0 357 | m_TemplateCustomTags: {} 358 | mobileMTRendering: 359 | Android: 1 360 | iPhone: 1 361 | tvOS: 1 362 | m_BuildTargetGroupLightmapEncodingQuality: 363 | - m_BuildTarget: Android 364 | m_EncodingQuality: 1 365 | - m_BuildTarget: iPhone 366 | m_EncodingQuality: 1 367 | - m_BuildTarget: tvOS 368 | m_EncodingQuality: 1 369 | m_BuildTargetGroupLightmapSettings: [] 370 | m_BuildTargetNormalMapEncoding: 371 | - m_BuildTarget: Android 372 | m_Encoding: 1 373 | - m_BuildTarget: iPhone 374 | m_Encoding: 1 375 | - m_BuildTarget: tvOS 376 | m_Encoding: 1 377 | m_BuildTargetDefaultTextureCompressionFormat: 378 | - m_BuildTarget: Android 379 | m_Format: 3 380 | playModeTestRunnerEnabled: 0 381 | runPlayModeTestAsEditModeTest: 0 382 | actionOnDotNetUnhandledException: 1 383 | enableInternalProfiler: 0 384 | logObjCUncaughtExceptions: 1 385 | enableCrashReportAPI: 0 386 | cameraUsageDescription: 387 | locationUsageDescription: 388 | microphoneUsageDescription: 389 | bluetoothUsageDescription: 390 | switchNMETAOverride: 391 | switchNetLibKey: 392 | switchSocketMemoryPoolSize: 6144 393 | switchSocketAllocatorPoolSize: 128 394 | switchSocketConcurrencyLimit: 14 395 | switchScreenResolutionBehavior: 2 396 | switchUseCPUProfiler: 0 397 | switchUseGOLDLinker: 0 398 | switchLTOSetting: 0 399 | switchApplicationID: 0x01004b9000490000 400 | switchNSODependencies: 401 | switchTitleNames_0: 402 | switchTitleNames_1: 403 | switchTitleNames_2: 404 | switchTitleNames_3: 405 | switchTitleNames_4: 406 | switchTitleNames_5: 407 | switchTitleNames_6: 408 | switchTitleNames_7: 409 | switchTitleNames_8: 410 | switchTitleNames_9: 411 | switchTitleNames_10: 412 | switchTitleNames_11: 413 | switchTitleNames_12: 414 | switchTitleNames_13: 415 | switchTitleNames_14: 416 | switchTitleNames_15: 417 | switchPublisherNames_0: 418 | switchPublisherNames_1: 419 | switchPublisherNames_2: 420 | switchPublisherNames_3: 421 | switchPublisherNames_4: 422 | switchPublisherNames_5: 423 | switchPublisherNames_6: 424 | switchPublisherNames_7: 425 | switchPublisherNames_8: 426 | switchPublisherNames_9: 427 | switchPublisherNames_10: 428 | switchPublisherNames_11: 429 | switchPublisherNames_12: 430 | switchPublisherNames_13: 431 | switchPublisherNames_14: 432 | switchPublisherNames_15: 433 | switchIcons_0: {fileID: 0} 434 | switchIcons_1: {fileID: 0} 435 | switchIcons_2: {fileID: 0} 436 | switchIcons_3: {fileID: 0} 437 | switchIcons_4: {fileID: 0} 438 | switchIcons_5: {fileID: 0} 439 | switchIcons_6: {fileID: 0} 440 | switchIcons_7: {fileID: 0} 441 | switchIcons_8: {fileID: 0} 442 | switchIcons_9: {fileID: 0} 443 | switchIcons_10: {fileID: 0} 444 | switchIcons_11: {fileID: 0} 445 | switchIcons_12: {fileID: 0} 446 | switchIcons_13: {fileID: 0} 447 | switchIcons_14: {fileID: 0} 448 | switchIcons_15: {fileID: 0} 449 | switchSmallIcons_0: {fileID: 0} 450 | switchSmallIcons_1: {fileID: 0} 451 | switchSmallIcons_2: {fileID: 0} 452 | switchSmallIcons_3: {fileID: 0} 453 | switchSmallIcons_4: {fileID: 0} 454 | switchSmallIcons_5: {fileID: 0} 455 | switchSmallIcons_6: {fileID: 0} 456 | switchSmallIcons_7: {fileID: 0} 457 | switchSmallIcons_8: {fileID: 0} 458 | switchSmallIcons_9: {fileID: 0} 459 | switchSmallIcons_10: {fileID: 0} 460 | switchSmallIcons_11: {fileID: 0} 461 | switchSmallIcons_12: {fileID: 0} 462 | switchSmallIcons_13: {fileID: 0} 463 | switchSmallIcons_14: {fileID: 0} 464 | switchSmallIcons_15: {fileID: 0} 465 | switchManualHTML: 466 | switchAccessibleURLs: 467 | switchLegalInformation: 468 | switchMainThreadStackSize: 1048576 469 | switchPresenceGroupId: 470 | switchLogoHandling: 0 471 | switchReleaseVersion: 0 472 | switchDisplayVersion: 1.0.0 473 | switchStartupUserAccount: 0 474 | switchTouchScreenUsage: 0 475 | switchSupportedLanguagesMask: 0 476 | switchLogoType: 0 477 | switchApplicationErrorCodeCategory: 478 | switchUserAccountSaveDataSize: 0 479 | switchUserAccountSaveDataJournalSize: 0 480 | switchApplicationAttribute: 0 481 | switchCardSpecSize: -1 482 | switchCardSpecClock: -1 483 | switchRatingsMask: 0 484 | switchRatingsInt_0: 0 485 | switchRatingsInt_1: 0 486 | switchRatingsInt_2: 0 487 | switchRatingsInt_3: 0 488 | switchRatingsInt_4: 0 489 | switchRatingsInt_5: 0 490 | switchRatingsInt_6: 0 491 | switchRatingsInt_7: 0 492 | switchRatingsInt_8: 0 493 | switchRatingsInt_9: 0 494 | switchRatingsInt_10: 0 495 | switchRatingsInt_11: 0 496 | switchRatingsInt_12: 0 497 | switchLocalCommunicationIds_0: 498 | switchLocalCommunicationIds_1: 499 | switchLocalCommunicationIds_2: 500 | switchLocalCommunicationIds_3: 501 | switchLocalCommunicationIds_4: 502 | switchLocalCommunicationIds_5: 503 | switchLocalCommunicationIds_6: 504 | switchLocalCommunicationIds_7: 505 | switchParentalControl: 0 506 | switchAllowsScreenshot: 1 507 | switchAllowsVideoCapturing: 1 508 | switchAllowsRuntimeAddOnContentInstall: 0 509 | switchDataLossConfirmation: 0 510 | switchUserAccountLockEnabled: 0 511 | switchSystemResourceMemory: 16777216 512 | switchSupportedNpadStyles: 22 513 | switchNativeFsCacheSize: 32 514 | switchIsHoldTypeHorizontal: 0 515 | switchSupportedNpadCount: 8 516 | switchSocketConfigEnabled: 0 517 | switchTcpInitialSendBufferSize: 32 518 | switchTcpInitialReceiveBufferSize: 64 519 | switchTcpAutoSendBufferSizeMax: 256 520 | switchTcpAutoReceiveBufferSizeMax: 256 521 | switchUdpSendBufferSize: 9 522 | switchUdpReceiveBufferSize: 42 523 | switchSocketBufferEfficiency: 4 524 | switchSocketInitializeEnabled: 1 525 | switchNetworkInterfaceManagerInitializeEnabled: 1 526 | switchPlayerConnectionEnabled: 1 527 | switchUseNewStyleFilepaths: 0 528 | switchUseLegacyFmodPriorities: 1 529 | switchUseMicroSleepForYield: 1 530 | switchEnableRamDiskSupport: 0 531 | switchMicroSleepForYieldTime: 25 532 | switchRamDiskSpaceSize: 12 533 | ps4NPAgeRating: 12 534 | ps4NPTitleSecret: 535 | ps4NPTrophyPackPath: 536 | ps4ParentalLevel: 11 537 | ps4ContentID: ED1633-NPXX51362_00-0000000000000000 538 | ps4Category: 0 539 | ps4MasterVersion: 01.00 540 | ps4AppVersion: 01.00 541 | ps4AppType: 0 542 | ps4ParamSfxPath: 543 | ps4VideoOutPixelFormat: 0 544 | ps4VideoOutInitialWidth: 1920 545 | ps4VideoOutBaseModeInitialWidth: 1920 546 | ps4VideoOutReprojectionRate: 60 547 | ps4PronunciationXMLPath: 548 | ps4PronunciationSIGPath: 549 | ps4BackgroundImagePath: 550 | ps4StartupImagePath: 551 | ps4StartupImagesFolder: 552 | ps4IconImagesFolder: 553 | ps4SaveDataImagePath: 554 | ps4SdkOverride: 555 | ps4BGMPath: 556 | ps4ShareFilePath: 557 | ps4ShareOverlayImagePath: 558 | ps4PrivacyGuardImagePath: 559 | ps4ExtraSceSysFile: 560 | ps4NPtitleDatPath: 561 | ps4RemotePlayKeyAssignment: -1 562 | ps4RemotePlayKeyMappingDir: 563 | ps4PlayTogetherPlayerCount: 0 564 | ps4EnterButtonAssignment: 1 565 | ps4ApplicationParam1: 0 566 | ps4ApplicationParam2: 0 567 | ps4ApplicationParam3: 0 568 | ps4ApplicationParam4: 0 569 | ps4DownloadDataSize: 0 570 | ps4GarlicHeapSize: 2048 571 | ps4ProGarlicHeapSize: 2560 572 | playerPrefsMaxSize: 32768 573 | ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ 574 | ps4pnSessions: 1 575 | ps4pnPresence: 1 576 | ps4pnFriends: 1 577 | ps4pnGameCustomData: 1 578 | playerPrefsSupport: 0 579 | enableApplicationExit: 0 580 | resetTempFolder: 1 581 | restrictedAudioUsageRights: 0 582 | ps4UseResolutionFallback: 0 583 | ps4ReprojectionSupport: 0 584 | ps4UseAudio3dBackend: 0 585 | ps4UseLowGarlicFragmentationMode: 1 586 | ps4SocialScreenEnabled: 0 587 | ps4ScriptOptimizationLevel: 0 588 | ps4Audio3dVirtualSpeakerCount: 14 589 | ps4attribCpuUsage: 0 590 | ps4PatchPkgPath: 591 | ps4PatchLatestPkgPath: 592 | ps4PatchChangeinfoPath: 593 | ps4PatchDayOne: 0 594 | ps4attribUserManagement: 0 595 | ps4attribMoveSupport: 0 596 | ps4attrib3DSupport: 0 597 | ps4attribShareSupport: 0 598 | ps4attribExclusiveVR: 0 599 | ps4disableAutoHideSplash: 0 600 | ps4videoRecordingFeaturesUsed: 0 601 | ps4contentSearchFeaturesUsed: 0 602 | ps4CompatibilityPS5: 0 603 | ps4AllowPS5Detection: 0 604 | ps4GPU800MHz: 1 605 | ps4attribEyeToEyeDistanceSettingVR: 0 606 | ps4IncludedModules: [] 607 | ps4attribVROutputEnabled: 0 608 | monoEnv: 609 | splashScreenBackgroundSourceLandscape: {fileID: 0} 610 | splashScreenBackgroundSourcePortrait: {fileID: 0} 611 | blurSplashScreenBackground: 1 612 | spritePackerPolicy: 613 | webGLMemorySize: 16 614 | webGLExceptionSupport: 1 615 | webGLNameFilesAsHashes: 0 616 | webGLDataCaching: 1 617 | webGLDebugSymbols: 0 618 | webGLEmscriptenArgs: 619 | webGLModulesDirectory: 620 | webGLTemplate: APPLICATION:Default 621 | webGLAnalyzeBuildSize: 0 622 | webGLUseEmbeddedResources: 0 623 | webGLCompressionFormat: 1 624 | webGLWasmArithmeticExceptions: 0 625 | webGLLinkerTarget: 1 626 | webGLThreadsSupport: 0 627 | webGLDecompressionFallback: 0 628 | webGLPowerPreference: 2 629 | scriptingDefineSymbols: {} 630 | additionalCompilerArguments: {} 631 | platformArchitecture: {} 632 | scriptingBackend: {} 633 | il2cppCompilerConfiguration: {} 634 | managedStrippingLevel: {} 635 | incrementalIl2cppBuild: {} 636 | suppressCommonWarnings: 1 637 | allowUnsafeCode: 0 638 | useDeterministicCompilation: 1 639 | enableRoslynAnalyzers: 1 640 | selectedPlatform: 0 641 | additionalIl2CppArgs: 642 | scriptingRuntimeVersion: 1 643 | gcIncremental: 1 644 | assemblyVersionValidation: 1 645 | gcWBarrierValidation: 0 646 | apiCompatibilityLevelPerPlatform: {} 647 | m_RenderingPath: 1 648 | m_MobileRenderingPath: 1 649 | metroPackageName: Template_3D 650 | metroPackageVersion: 651 | metroCertificatePath: 652 | metroCertificatePassword: 653 | metroCertificateSubject: 654 | metroCertificateIssuer: 655 | metroCertificateNotAfter: 0000000000000000 656 | metroApplicationDescription: Template_3D 657 | wsaImages: {} 658 | metroTileShortName: 659 | metroTileShowName: 0 660 | metroMediumTileShowName: 0 661 | metroLargeTileShowName: 0 662 | metroWideTileShowName: 0 663 | metroSupportStreamingInstall: 0 664 | metroLastRequiredScene: 0 665 | metroDefaultTileSize: 1 666 | metroTileForegroundText: 2 667 | metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} 668 | metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} 669 | metroSplashScreenUseBackgroundColor: 0 670 | platformCapabilities: {} 671 | metroTargetDeviceFamilies: {} 672 | metroFTAName: 673 | metroFTAFileTypes: [] 674 | metroProtocolName: 675 | vcxProjDefaultLanguage: 676 | XboxOneProductId: 677 | XboxOneUpdateKey: 678 | XboxOneSandboxId: 679 | XboxOneContentId: 680 | XboxOneTitleId: 681 | XboxOneSCId: 682 | XboxOneGameOsOverridePath: 683 | XboxOnePackagingOverridePath: 684 | XboxOneAppManifestOverridePath: 685 | XboxOneVersion: 1.0.0.0 686 | XboxOnePackageEncryption: 0 687 | XboxOnePackageUpdateGranularity: 2 688 | XboxOneDescription: 689 | XboxOneLanguage: 690 | - enus 691 | XboxOneCapability: [] 692 | XboxOneGameRating: {} 693 | XboxOneIsContentPackage: 0 694 | XboxOneEnhancedXboxCompatibilityMode: 0 695 | XboxOneEnableGPUVariability: 1 696 | XboxOneSockets: {} 697 | XboxOneSplashScreen: {fileID: 0} 698 | XboxOneAllowedProductIds: [] 699 | XboxOnePersistentLocalStorageSize: 0 700 | XboxOneXTitleMemory: 8 701 | XboxOneOverrideIdentityName: 702 | XboxOneOverrideIdentityPublisher: 703 | vrEditorSettings: {} 704 | cloudServicesEnabled: 705 | UNet: 1 706 | luminIcon: 707 | m_Name: 708 | m_ModelFolderPath: 709 | m_PortalFolderPath: 710 | luminCert: 711 | m_CertPath: 712 | m_SignPackage: 1 713 | luminIsChannelApp: 0 714 | luminVersion: 715 | m_VersionCode: 1 716 | m_VersionName: 717 | apiCompatibilityLevel: 6 718 | activeInputHandler: 0 719 | windowsGamepadBackendHint: 0 720 | cloudProjectId: 721 | framebufferDepthMemorylessMode: 0 722 | qualitySettingsNames: [] 723 | projectName: 724 | organizationId: 725 | cloudEnabled: 0 726 | legacyClampBlendShapeWeights: 0 727 | playerDataPath: 728 | forceSRGBBlit: 1 729 | virtualTexturingSupportEnabled: 0 730 | -------------------------------------------------------------------------------- /ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 2021.3.16f1c1 2 | m_EditorVersionWithRevision: 2021.3.16f1c1 (56dbfdd6697f) 3 | -------------------------------------------------------------------------------- /ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!47 &1 4 | QualitySettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 5 7 | m_CurrentQuality: 5 8 | m_QualitySettings: 9 | - serializedVersion: 2 10 | name: Very Low 11 | pixelLightCount: 0 12 | shadows: 0 13 | shadowResolution: 0 14 | shadowProjection: 1 15 | shadowCascades: 1 16 | shadowDistance: 15 17 | shadowNearPlaneOffset: 3 18 | shadowCascade2Split: 0.33333334 19 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 20 | shadowmaskMode: 0 21 | blendWeights: 1 22 | textureQuality: 1 23 | anisotropicTextures: 0 24 | antiAliasing: 0 25 | softParticles: 0 26 | softVegetation: 0 27 | realtimeReflectionProbes: 0 28 | billboardsFaceCameraPosition: 0 29 | vSyncCount: 0 30 | lodBias: 0.3 31 | maximumLODLevel: 0 32 | streamingMipmapsActive: 0 33 | streamingMipmapsAddAllCameras: 1 34 | streamingMipmapsMemoryBudget: 512 35 | streamingMipmapsRenderersPerFrame: 512 36 | streamingMipmapsMaxLevelReduction: 2 37 | streamingMipmapsMaxFileIORequests: 1024 38 | particleRaycastBudget: 4 39 | asyncUploadTimeSlice: 2 40 | asyncUploadBufferSize: 16 41 | asyncUploadPersistentBuffer: 1 42 | resolutionScalingFixedDPIFactor: 1 43 | excludedTargetPlatforms: [] 44 | - serializedVersion: 2 45 | name: Low 46 | pixelLightCount: 0 47 | shadows: 0 48 | shadowResolution: 0 49 | shadowProjection: 1 50 | shadowCascades: 1 51 | shadowDistance: 20 52 | shadowNearPlaneOffset: 3 53 | shadowCascade2Split: 0.33333334 54 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 55 | shadowmaskMode: 0 56 | blendWeights: 2 57 | textureQuality: 0 58 | anisotropicTextures: 0 59 | antiAliasing: 0 60 | softParticles: 0 61 | softVegetation: 0 62 | realtimeReflectionProbes: 0 63 | billboardsFaceCameraPosition: 0 64 | vSyncCount: 0 65 | lodBias: 0.4 66 | maximumLODLevel: 0 67 | streamingMipmapsActive: 0 68 | streamingMipmapsAddAllCameras: 1 69 | streamingMipmapsMemoryBudget: 512 70 | streamingMipmapsRenderersPerFrame: 512 71 | streamingMipmapsMaxLevelReduction: 2 72 | streamingMipmapsMaxFileIORequests: 1024 73 | particleRaycastBudget: 16 74 | asyncUploadTimeSlice: 2 75 | asyncUploadBufferSize: 16 76 | asyncUploadPersistentBuffer: 1 77 | resolutionScalingFixedDPIFactor: 1 78 | excludedTargetPlatforms: [] 79 | - serializedVersion: 2 80 | name: Medium 81 | pixelLightCount: 1 82 | shadows: 1 83 | shadowResolution: 0 84 | shadowProjection: 1 85 | shadowCascades: 1 86 | shadowDistance: 20 87 | shadowNearPlaneOffset: 3 88 | shadowCascade2Split: 0.33333334 89 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 90 | shadowmaskMode: 0 91 | blendWeights: 2 92 | textureQuality: 0 93 | anisotropicTextures: 1 94 | antiAliasing: 0 95 | softParticles: 0 96 | softVegetation: 0 97 | realtimeReflectionProbes: 0 98 | billboardsFaceCameraPosition: 0 99 | vSyncCount: 1 100 | lodBias: 0.7 101 | maximumLODLevel: 0 102 | streamingMipmapsActive: 0 103 | streamingMipmapsAddAllCameras: 1 104 | streamingMipmapsMemoryBudget: 512 105 | streamingMipmapsRenderersPerFrame: 512 106 | streamingMipmapsMaxLevelReduction: 2 107 | streamingMipmapsMaxFileIORequests: 1024 108 | particleRaycastBudget: 64 109 | asyncUploadTimeSlice: 2 110 | asyncUploadBufferSize: 16 111 | asyncUploadPersistentBuffer: 1 112 | resolutionScalingFixedDPIFactor: 1 113 | excludedTargetPlatforms: [] 114 | - serializedVersion: 2 115 | name: High 116 | pixelLightCount: 2 117 | shadows: 2 118 | shadowResolution: 1 119 | shadowProjection: 1 120 | shadowCascades: 2 121 | shadowDistance: 40 122 | shadowNearPlaneOffset: 3 123 | shadowCascade2Split: 0.33333334 124 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 125 | shadowmaskMode: 1 126 | blendWeights: 2 127 | textureQuality: 0 128 | anisotropicTextures: 1 129 | antiAliasing: 0 130 | softParticles: 0 131 | softVegetation: 1 132 | realtimeReflectionProbes: 1 133 | billboardsFaceCameraPosition: 1 134 | vSyncCount: 1 135 | lodBias: 1 136 | maximumLODLevel: 0 137 | streamingMipmapsActive: 0 138 | streamingMipmapsAddAllCameras: 1 139 | streamingMipmapsMemoryBudget: 512 140 | streamingMipmapsRenderersPerFrame: 512 141 | streamingMipmapsMaxLevelReduction: 2 142 | streamingMipmapsMaxFileIORequests: 1024 143 | particleRaycastBudget: 256 144 | asyncUploadTimeSlice: 2 145 | asyncUploadBufferSize: 16 146 | asyncUploadPersistentBuffer: 1 147 | resolutionScalingFixedDPIFactor: 1 148 | excludedTargetPlatforms: [] 149 | - serializedVersion: 2 150 | name: Very High 151 | pixelLightCount: 3 152 | shadows: 2 153 | shadowResolution: 2 154 | shadowProjection: 1 155 | shadowCascades: 2 156 | shadowDistance: 70 157 | shadowNearPlaneOffset: 3 158 | shadowCascade2Split: 0.33333334 159 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 160 | shadowmaskMode: 1 161 | blendWeights: 4 162 | textureQuality: 0 163 | anisotropicTextures: 2 164 | antiAliasing: 2 165 | softParticles: 1 166 | softVegetation: 1 167 | realtimeReflectionProbes: 1 168 | billboardsFaceCameraPosition: 1 169 | vSyncCount: 1 170 | lodBias: 1.5 171 | maximumLODLevel: 0 172 | streamingMipmapsActive: 0 173 | streamingMipmapsAddAllCameras: 1 174 | streamingMipmapsMemoryBudget: 512 175 | streamingMipmapsRenderersPerFrame: 512 176 | streamingMipmapsMaxLevelReduction: 2 177 | streamingMipmapsMaxFileIORequests: 1024 178 | particleRaycastBudget: 1024 179 | asyncUploadTimeSlice: 2 180 | asyncUploadBufferSize: 16 181 | asyncUploadPersistentBuffer: 1 182 | resolutionScalingFixedDPIFactor: 1 183 | excludedTargetPlatforms: [] 184 | - serializedVersion: 2 185 | name: Ultra 186 | pixelLightCount: 4 187 | shadows: 2 188 | shadowResolution: 2 189 | shadowProjection: 1 190 | shadowCascades: 4 191 | shadowDistance: 150 192 | shadowNearPlaneOffset: 3 193 | shadowCascade2Split: 0.33333334 194 | shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} 195 | shadowmaskMode: 1 196 | blendWeights: 4 197 | textureQuality: 0 198 | anisotropicTextures: 2 199 | antiAliasing: 2 200 | softParticles: 1 201 | softVegetation: 1 202 | realtimeReflectionProbes: 1 203 | billboardsFaceCameraPosition: 1 204 | vSyncCount: 1 205 | lodBias: 2 206 | maximumLODLevel: 0 207 | streamingMipmapsActive: 0 208 | streamingMipmapsAddAllCameras: 1 209 | streamingMipmapsMemoryBudget: 512 210 | streamingMipmapsRenderersPerFrame: 512 211 | streamingMipmapsMaxLevelReduction: 2 212 | streamingMipmapsMaxFileIORequests: 1024 213 | particleRaycastBudget: 4096 214 | asyncUploadTimeSlice: 2 215 | asyncUploadBufferSize: 16 216 | asyncUploadPersistentBuffer: 1 217 | resolutionScalingFixedDPIFactor: 1 218 | excludedTargetPlatforms: [] 219 | m_PerPlatformDefaultQuality: 220 | Android: 2 221 | Lumin: 5 222 | GameCoreScarlett: 5 223 | GameCoreXboxOne: 5 224 | Nintendo 3DS: 5 225 | Nintendo Switch: 5 226 | PS4: 5 227 | PS5: 5 228 | Stadia: 5 229 | Standalone: 5 230 | WebGL: 3 231 | Windows Store Apps: 5 232 | XboxOne: 5 233 | iPhone: 2 234 | tvOS: 2 235 | -------------------------------------------------------------------------------- /ProjectSettings/SceneTemplateSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "templatePinStates": [], 3 | "dependencyTypeInfos": [ 4 | { 5 | "userAdded": false, 6 | "type": "UnityEngine.AnimationClip", 7 | "ignore": false, 8 | "defaultInstantiationMode": 0, 9 | "supportsModification": true 10 | }, 11 | { 12 | "userAdded": false, 13 | "type": "UnityEditor.Animations.AnimatorController", 14 | "ignore": false, 15 | "defaultInstantiationMode": 0, 16 | "supportsModification": true 17 | }, 18 | { 19 | "userAdded": false, 20 | "type": "UnityEngine.AnimatorOverrideController", 21 | "ignore": false, 22 | "defaultInstantiationMode": 0, 23 | "supportsModification": true 24 | }, 25 | { 26 | "userAdded": false, 27 | "type": "UnityEditor.Audio.AudioMixerController", 28 | "ignore": false, 29 | "defaultInstantiationMode": 0, 30 | "supportsModification": true 31 | }, 32 | { 33 | "userAdded": false, 34 | "type": "UnityEngine.ComputeShader", 35 | "ignore": true, 36 | "defaultInstantiationMode": 1, 37 | "supportsModification": true 38 | }, 39 | { 40 | "userAdded": false, 41 | "type": "UnityEngine.Cubemap", 42 | "ignore": false, 43 | "defaultInstantiationMode": 0, 44 | "supportsModification": true 45 | }, 46 | { 47 | "userAdded": false, 48 | "type": "UnityEngine.GameObject", 49 | "ignore": false, 50 | "defaultInstantiationMode": 0, 51 | "supportsModification": true 52 | }, 53 | { 54 | "userAdded": false, 55 | "type": "UnityEditor.LightingDataAsset", 56 | "ignore": false, 57 | "defaultInstantiationMode": 0, 58 | "supportsModification": false 59 | }, 60 | { 61 | "userAdded": false, 62 | "type": "UnityEngine.LightingSettings", 63 | "ignore": false, 64 | "defaultInstantiationMode": 0, 65 | "supportsModification": true 66 | }, 67 | { 68 | "userAdded": false, 69 | "type": "UnityEngine.Material", 70 | "ignore": false, 71 | "defaultInstantiationMode": 0, 72 | "supportsModification": true 73 | }, 74 | { 75 | "userAdded": false, 76 | "type": "UnityEditor.MonoScript", 77 | "ignore": true, 78 | "defaultInstantiationMode": 1, 79 | "supportsModification": true 80 | }, 81 | { 82 | "userAdded": false, 83 | "type": "UnityEngine.PhysicMaterial", 84 | "ignore": false, 85 | "defaultInstantiationMode": 0, 86 | "supportsModification": true 87 | }, 88 | { 89 | "userAdded": false, 90 | "type": "UnityEngine.PhysicsMaterial2D", 91 | "ignore": false, 92 | "defaultInstantiationMode": 0, 93 | "supportsModification": true 94 | }, 95 | { 96 | "userAdded": false, 97 | "type": "UnityEngine.Rendering.PostProcessing.PostProcessProfile", 98 | "ignore": false, 99 | "defaultInstantiationMode": 0, 100 | "supportsModification": true 101 | }, 102 | { 103 | "userAdded": false, 104 | "type": "UnityEngine.Rendering.PostProcessing.PostProcessResources", 105 | "ignore": false, 106 | "defaultInstantiationMode": 0, 107 | "supportsModification": true 108 | }, 109 | { 110 | "userAdded": false, 111 | "type": "UnityEngine.Rendering.VolumeProfile", 112 | "ignore": false, 113 | "defaultInstantiationMode": 0, 114 | "supportsModification": true 115 | }, 116 | { 117 | "userAdded": false, 118 | "type": "UnityEditor.SceneAsset", 119 | "ignore": false, 120 | "defaultInstantiationMode": 0, 121 | "supportsModification": false 122 | }, 123 | { 124 | "userAdded": false, 125 | "type": "UnityEngine.Shader", 126 | "ignore": true, 127 | "defaultInstantiationMode": 1, 128 | "supportsModification": true 129 | }, 130 | { 131 | "userAdded": false, 132 | "type": "UnityEngine.ShaderVariantCollection", 133 | "ignore": true, 134 | "defaultInstantiationMode": 1, 135 | "supportsModification": true 136 | }, 137 | { 138 | "userAdded": false, 139 | "type": "UnityEngine.Texture", 140 | "ignore": false, 141 | "defaultInstantiationMode": 0, 142 | "supportsModification": true 143 | }, 144 | { 145 | "userAdded": false, 146 | "type": "UnityEngine.Texture2D", 147 | "ignore": false, 148 | "defaultInstantiationMode": 0, 149 | "supportsModification": true 150 | }, 151 | { 152 | "userAdded": false, 153 | "type": "UnityEngine.Timeline.TimelineAsset", 154 | "ignore": false, 155 | "defaultInstantiationMode": 0, 156 | "supportsModification": true 157 | } 158 | ], 159 | "defaultDependencyTypeInfo": { 160 | "userAdded": false, 161 | "type": "", 162 | "ignore": false, 163 | "defaultInstantiationMode": 1, 164 | "supportsModification": true 165 | }, 166 | "newSceneOverride": 0 167 | } -------------------------------------------------------------------------------- /ProjectSettings/ShaderGraphSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!114 &1 4 | MonoBehaviour: 5 | m_ObjectHideFlags: 61 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_GameObject: {fileID: 0} 10 | m_Enabled: 1 11 | m_EditorHideFlags: 0 12 | m_Script: {fileID: 11500000, guid: de02f9e1d18f588468e474319d09a723, type: 3} 13 | m_Name: 14 | m_EditorClassIdentifier: 15 | customInterpolatorErrorThreshold: 32 16 | customInterpolatorWarningThreshold: 16 17 | -------------------------------------------------------------------------------- /ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!78 &1 4 | TagManager: 5 | serializedVersion: 2 6 | tags: [] 7 | layers: 8 | - Default 9 | - TransparentFX 10 | - Ignore Raycast 11 | - 12 | - Water 13 | - UI 14 | - 15 | - 16 | - 17 | - 18 | - 19 | - 20 | - 21 | - 22 | - 23 | - 24 | - 25 | - 26 | - 27 | - 28 | - 29 | - 30 | - 31 | - 32 | - 33 | - 34 | - 35 | - 36 | - 37 | - 38 | - 39 | - 40 | m_SortingLayers: 41 | - name: Default 42 | uniqueID: 0 43 | locked: 0 44 | -------------------------------------------------------------------------------- /ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!5 &1 4 | TimeManager: 5 | m_ObjectHideFlags: 0 6 | Fixed Timestep: 0.02 7 | Maximum Allowed Timestep: 0.33333334 8 | m_TimeScale: 1 9 | Maximum Particle Timestep: 0.03 10 | -------------------------------------------------------------------------------- /ProjectSettings/URPProjectSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!114 &1 4 | MonoBehaviour: 5 | m_ObjectHideFlags: 61 6 | m_CorrespondingSourceObject: {fileID: 0} 7 | m_PrefabInstance: {fileID: 0} 8 | m_PrefabAsset: {fileID: 0} 9 | m_GameObject: {fileID: 0} 10 | m_Enabled: 1 11 | m_EditorHideFlags: 0 12 | m_Script: {fileID: 11500000, guid: 247994e1f5a72c2419c26a37e9334c01, type: 3} 13 | m_Name: 14 | m_EditorClassIdentifier: 15 | m_LastMaterialVersion: 5 16 | -------------------------------------------------------------------------------- /ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!310 &1 4 | UnityConnectSettings: 5 | m_ObjectHideFlags: 0 6 | serializedVersion: 1 7 | m_Enabled: 0 8 | m_TestMode: 0 9 | m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events 10 | m_EventUrl: https://cdp.cloud.unity3d.com/v1/events 11 | m_ConfigUrl: https://config.uca.cloud.unity3d.com 12 | m_DashboardUrl: https://dashboard.unity3d.com 13 | m_CNEventUrl: https://cdp.cloud.unity.cn/v1/events 14 | m_CNConfigUrl: https://cdp.cloud.unity.cn/config 15 | m_TestInitMode: 0 16 | CrashReportingSettings: 17 | m_EventUrl: https://perf-events.cloud.unity.cn 18 | m_Enabled: 0 19 | m_LogBufferSize: 10 20 | m_CaptureEditorExceptions: 1 21 | UnityPurchasingSettings: 22 | m_Enabled: 0 23 | m_TestMode: 0 24 | UnityAnalyticsSettings: 25 | m_Enabled: 1 26 | m_TestMode: 0 27 | m_InitializeOnStartup: 1 28 | m_PackageRequiringCoreStatsPresent: 0 29 | UnityAdsSettings: 30 | m_Enabled: 0 31 | m_InitializeOnStartup: 1 32 | m_TestMode: 0 33 | m_IosGameId: 34 | m_AndroidGameId: 35 | m_GameIds: {} 36 | m_GameId: 37 | PerformanceReportingSettings: 38 | m_Enabled: 0 39 | -------------------------------------------------------------------------------- /ProjectSettings/VFXManager.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!937362698 &1 4 | VFXManager: 5 | m_ObjectHideFlags: 0 6 | m_IndirectShader: {fileID: 0} 7 | m_CopyBufferShader: {fileID: 0} 8 | m_SortShader: {fileID: 0} 9 | m_StripUpdateShader: {fileID: 0} 10 | m_RenderPipeSettingsPath: 11 | m_FixedTimeStep: 0.016666668 12 | m_MaxDeltaTime: 0.05 13 | -------------------------------------------------------------------------------- /ProjectSettings/VersionControlSettings.asset: -------------------------------------------------------------------------------- 1 | %YAML 1.1 2 | %TAG !u! tag:unity3d.com,2011: 3 | --- !u!890905787 &1 4 | VersionControlSettings: 5 | m_ObjectHideFlags: 0 6 | m_Mode: Visible Meta Files 7 | m_CollabEditorSettings: 8 | inProgressEnabled: 1 9 | -------------------------------------------------------------------------------- /ProjectSettings/XRSettings.asset: -------------------------------------------------------------------------------- 1 | { 2 | "m_SettingKeys": [ 3 | "VR Device Disabled", 4 | "VR Device User Alert" 5 | ], 6 | "m_SettingValues": [ 7 | "False", 8 | "False" 9 | ] 10 | } -------------------------------------------------------------------------------- /ProjectSettings/boot.config: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/529324416/UnityCommandLineInterface/574198026618102dc3906a532bde063713ac85b7/ProjectSettings/boot.config -------------------------------------------------------------------------------- /README-ch.md: -------------------------------------------------------------------------------- 1 | # UnityCommandLineInterface 2 | 3 | [![openupm](https://img.shields.io/npm/v/com.redsaw.commandline?label=openupm®istry_uri=https://package.openupm.com)](https://openupm.com/packages/com.redsaw.commandline/) 4 | 5 | [English Document](./README.md) 6 | 7 | ## 简介 8 | 9 | 这个项目是一个游戏内置的控制台项目,主要用于执行一些简短的命令或者函数,或者用于设置某些数值等 10 | 11 |
12 | 13 |
14 | 15 | https://github.com/529324416/UnityCommandLineInterface/assets/30776995/a2290fff-ffa5-421f-8a4a-a5274e9c6d87 16 | 17 | ## 特性: 18 | - 使用简单,无需任何额外配置或者复杂学习流程 19 | - 模块间高度解耦合,其内部组件可以单独使用 20 | - 轻量级,无任何依赖 21 | - 支持输入文本提示 22 | - 支持自定义 23 | - 全版本支持 24 | 25 | 26 | ## 如何使用 27 | 28 | ## 1.注册命令 29 | 30 | 命令的注册和旧版的命令系统是一致的,不同的点在于,旧版的系统会审查目标静态函数的参数列表能够正常解析,而新版的系统不会,任何错误都会在执行的时候报出。不过注册行为同样简单,只需要在静态函数之上添加一个`Command`特性即可。 31 | 32 | ```csharp 33 | [Command("my_cmd")] 34 | public static void SomeMethod(){ 35 | /* do something here .. */ 36 | } 37 | ``` 38 | 39 | 不过除了静态函数之外,新版系统还支持注册实例函数,不过注册实例函数只能手动注册,以及在拥有了**控制台变量**的情况下,注册实例函数的意义变得微乎其微。 40 | 41 | ## 2.执行命令 42 | 43 | 命令的执行和之前大有不同,首先,命令的执行有两种模式,第一种,你可以像执行一般的命令一样执行。 44 | 45 | ``` 46 | your_command args1 args2 47 | ``` 48 | 49 | 但是同样,你可以像执行一个函数一样执行命令 50 | 51 | ``` 52 | your_command(args1, args2) 53 | ``` 54 | 55 | 这么做的理由很简单,前者是继续维系执行简单命令的便捷,后者的意义在于,新版的命令系统支持获取命令的返回结果,于是在函数执行模式的时候,你可以对结果内部的信息继续访问。如下所示: 56 | 57 | ``` 58 | get_enemy("slime").Jump() 59 | ``` 60 | 61 | ## 3.控制台变量 62 | 63 | 接着就是新版系统的重点了,新版控制台支持你注册一个控制台变量,并通过`@`对其进行访问,这会使得命令系统整体变得非常自由。假设你有一个主角,如下所示: 64 | 65 | ```csharp 66 | public class Player : MonoBehaviour{ 67 | 68 | public int health = 100; 69 | 70 | public void Jump(){ 71 | // code for jump.. 72 | } 73 | } 74 | ``` 75 | 76 | 你可以设置一个静态变量,并为该变量添加一个`CommandProperty`来注册一个控制台变量。如下所示: 77 | 78 | ```csharp 79 | public class Player: MonoBehaviour{ 80 | 81 | [CommandProperty("player")] 82 | public static Player Instance; 83 | } 84 | ``` 85 | 86 | (当然,游戏启动的时候你得往Instance写入一个实例) 87 | 88 | 之后你就可以在控制台对其所有的内部信息进行访问了,不需要任何额外的参数,完全只借助于C#的反射系统以获取的强大的动态能力,下面这些命令都是支持且有效的。 89 | 90 | ``` 91 | @player.health = 100 92 | @player.Jump() 93 | ``` 94 | 95 | 同理,任何需要一个变量的地方都可以通过`@`来引用你需要的变量,比如作为其他命令的参数 96 | 97 | ``` 98 | @enemy.Atk(@player) 99 | ``` 100 | 101 | 控制台变量可以注册给一个字段或者一个属性,这意味着同一个控制台变量可以代表着不同的对象,比如离玩家最近的Npc。 102 | 103 | ```csharp 104 | [CommandProperty("nearest")] 105 | public static Npc NearestNpc{ 106 | get{ 107 | // code for getting nearest npc.. 108 | } 109 | } 110 | ``` 111 | 112 | 有了这个特性的支持,你可以将一组命令作为某个对象的子函数,不需要编写额外的信息,只需要将这个变量注册为控制台变量即可。 113 | 114 | 总的来说它大大提高了控制台的自由度 115 | 116 | ## 4.变量访问支持 117 | 118 | 新版的控制台支持访问成员和覆写成员,你可以通过一般点号或者中括号来对目标对象的成员或者子元素进行访问,前面已经展示了一些。类似于访问成员,或者覆写子元素都是支持的。 119 | 120 | ``` 121 | @player.DoSomething() 122 | @player.buffs["buff_id"].AddTime(100) 123 | @enemies["slime"] = @new_slime 124 | ``` 125 | 126 | 当然,这种访问是可以连续的,就像正常的语法那样 127 | 128 | ``` 129 | @something.member.sub_member["elements"].member.sub_member.field.function().member 130 | ``` 131 | 132 | 尽管这么做有些憨,这里面关于中括号的判断是根据目标对象来的,一般来说,系统会先判断目标对象是否为一个序列型的对象比如数组或者列表等,如果是的话,那么它会要求中括号内部的表达式为一个int字段,如果中括号内部的表达式无法得到一个int字段,这个访问就会失败并正常报错,同理作为字典也是一样的,只不过字典支持更多不同的键类型,所以字典的判断范围会更宽一些。 133 | 134 | ``` 135 | @some_dict[@non_string_type] = get_something() 136 | ``` 137 | 138 | 比如你的字典是一个非字符串类型,那么你可以把其他控制台变量作为它的键值来访问或者覆写。当然,前提是它支持覆写。任何不支持的行为都会报错,只不过控制台会尽可能正确执行你输入的命令。所以最好的办法就是不要设置一些奇怪的命令。 139 | 140 | ## 5.参数解析器 141 | 142 | 旧版控制台有过一个参数解析器,这个解析器在新版里变为了控制台系统理解一般参数的最后手段。简单来说,普通的控制台变量其实只有以下几种 143 | 144 | ``` 145 | string 146 | float 147 | int 148 | bool 149 | null 150 | ``` 151 | 152 | 这些也可以被称为是元类型,属于可以直接被解析出来的数据类型,一般来说是够用了。不过如果你希望控制台可以解析不同的数据类型,那么你可以通过注册参数解析器来实现。 153 | 154 | 举个简单的案例,对于主角的位置而言,它可能是一个`Vector3`类型,你也许希望可以像这样去设置主角的位置 155 | 156 | ``` 157 | @player.pos = "1. 1. 1." 158 | ``` 159 | 160 | 但是显然,字符串是无法转换为位置的,如果你真的想这么做,或者有必要这么做的话,你可以注册一个参数解析器,如下所示: 161 | 162 | ```csharp 163 | [CommandValueParser(typeof(Vector3))] 164 | static bool ParseFunction(string input, out object data){ 165 | // code used to parse your input str 166 | } 167 | ``` 168 | 169 | 这个函数只要符合签名即可,它的内部逻辑你可以自由编写,比如: 170 | 171 | ```csharp 172 | static bool ParseFunction(string input, out object data){ 173 | if(input == "复活点"){ 174 | data = Vector3.zero; 175 | return true; 176 | } 177 | // other prase here .. 178 | 179 | data = default; 180 | return false; 181 | } 182 | ``` 183 | 184 | 那么你就可以这样使用了 185 | 186 | ``` 187 | @player.pos = "复活点" 188 | ``` 189 | 190 | 当然,这里的双引号其实是可以取消的,但是我不建议这么做,因为系统在理解这个输入的时候会首先把`复活点`当成是一个命令,认为你希望执行一个命令获取它的返回值,如果复活点不是命令的话,系统才会尝试去搜索有没有解析器可以将其解析为目标类型的数据。 191 | 192 | ``` 193 | @player.pos = 复活点 194 | ``` 195 | 196 | 所以尽量不要使用这种模棱两可的控制语句。 197 | 198 | 参数解析器一般用于一些比较特殊的情况或者非常复杂的数据类型,不便于手动设置的数据类型。像这里的`Vector3`类型的正确做法应该是什么呢?其实很简单,你可以注册一个这样的命令。 199 | 200 | ```csharp 201 | [Command("v3")] 202 | public static Vector3 BuildVector3(float x, float y, float z){ 203 | return new Vector3(x, y, z); 204 | } 205 | ``` 206 | 207 | 然后像下面这样调用即可 208 | 209 | ``` 210 | @player.pos = v3(0, 0, 0) 211 | ``` 212 | 213 | ## 6.注册Debug信息 214 | 215 | 如果希望直接获取该对象的一些信息,比如我输入了下面的命令到控制台 216 | 217 | ``` 218 | @player 219 | ``` 220 | 221 | 控制台可以直接返回给我一组关于玩家的数据信息,像下面这样 222 | 223 | ![Untitled](https://prod-files-secure.s3.us-west-2.amazonaws.com/cb951e31-0788-4895-93b0-cc33ecfb6339/c3439d55-ae07-483d-9c26-d6082b79d91e/Untitled.png) 224 | 225 | 这个功能属于附加功能,但是我也将其作为了控制台的一个内部特性来实现了。简单来说对于任何你希望了解的参数信息,你可以注册一个`DebugInfo`到它的字段或者属性用以获取它的信息 226 | 227 | ```csharp 228 | public class Player : MonoBehaviour{ 229 | 230 | [DebugInfo] 231 | public int health = 100; 232 | } 233 | ``` 234 | 235 | 这样一来,输入`@player`到控制台就可以直接获取player的所有DebugInfo了,输出如下所示 236 | 237 | ``` 238 | ---------- Player start ---------- 239 | >Player.health: 100 240 | ---------- Player end ---------- 241 | ``` 242 | 243 | 你可以设置它的标题和色彩,如下所示: 244 | 245 | ```csharp 246 | [DebugInfo("custom_title", Color = "#ff0000")] 247 | ``` 248 | 249 | ### 6.1 父类追溯 250 | 251 | 它可以追溯所有父类的信息并从最初的父类开始往下依次传递,如果你有一个这样的继承链 252 | 253 | ```csharp 254 | public class A{ 255 | [DebugInfo] 256 | public int someInt = 999; 257 | } 258 | 259 | public class B{ 260 | [DebugInfo] 261 | public float someFloat = 12.3; 262 | } 263 | 264 | public class C{ 265 | [DebugInfo] 266 | public string someStr = "Hello World!"; 267 | } 268 | ``` 269 | 270 | 如果你实例化了C并进行输出的话,你将获得 271 | 272 | ``` 273 | ---------- C start ---------- 274 | >A.someInt: 999 275 | >B.someFloat: 12.3 276 | >C.someStr: Hello World! 277 | ---------- C end ---------- 278 | ``` 279 | 280 | ### 6.3 子类DebugInfo 281 | 282 | 如果被注册为DebugInfo的对象是一个内部也拥有数个DebugInfo的对象,那么你可以为其类型注册一个`DebugObject`特性,如下所示 283 | 284 | ```csharp 285 | public class A{ 286 | [DebugInfo("my_object")] 287 | public B b = new B(); 288 | } 289 | 290 | [DebugObject] 291 | public class B : MonoBehaviour{ 292 | [DebugInfo("name")] 293 | public string Name => this.gameObject.name; 294 | 295 | [DebugInfo] 296 | public string age = 25; 297 | } 298 | ``` 299 | 300 | 如果你输出了A,将会获得下列信息 301 | 302 | ``` 303 | ---------- C start ---------- 304 | >----[B] 305 | > B.name: "instanceB" 306 | > B.age: 25 307 | ---------- C end ---------- 308 | ``` 309 | 310 | 不过这样一来,可能会出现环状数据展开,比如B引用了C但C又引用了B,所以你可以设置一个展开深度,超过这个深度时,系统会丢弃其子类信息,默认深度为4。 311 | 312 | ## 7.输入提示 313 | 314 | 旧版的控制台系统的提示很好做,只需要搜索所有的命令然后按照一定的相似度函数来进行提示即可。新版的系统多出来了很多信息,总的来说它支持三个元素的提示,即命令,控制台变量和成员对象。 315 | 316 | 不过我第一次做提示引擎,压根不知道怎么做最好,我采用了链式字符自动机的做法,你每输入一个字符都会根据规则改变现有字符状态机的状态从而让控制台知悉你现在正在输入什么,进而弹出提示。 317 | 318 | 这个部分确实难做,要在提示和语法的兼容度之间寻找一个平衡蛮难的。但还是做出来了,不过这部分没有用户和代码相关的部分。 319 | 320 | ![Untitled](https://prod-files-secure.s3.us-west-2.amazonaws.com/cb951e31-0788-4895-93b0-cc33ecfb6339/e53162e0-2261-4342-9a39-cee4aa35d9b8/Untitled.png) 321 | 322 | 字符串相似度采用了编辑距离算法,也就是比较有名的莱文斯坦因字符串距离算法。对你的输入和目标字符串之间计算编辑距离并按照相似度排序。查询时会产生一定空间的缓存,用以复用最近输入的查询结果,从而降低计算损耗。 323 | 324 | ## 8.控制台日志 325 | 326 | 新版控制台最大的一个特点在于解除了日志输出与渲染的耦合,这意味着你控制台本体只会存储日志信息,不会进行任何渲染行为,你可以向控制台索要某个特定类型的日志信息,比如只显示错误信息或者只显示一般信息。 327 | 328 | Unity有一个自带的日志类型,`LogType`,不过你可以定义自己的日志枚举类型,或者直接使用Unity的日志类型。 329 | 330 | ```csharp 331 | /* intialize console */ 332 | console = new ConsoleController( 333 | consoleRenderer, 334 | new UserInput(), 335 | 336 | inputHistoryCapacity: inputHistoryCapacity, 337 | commandQueryCacheCapacity: commandQueryCacheCapacity, 338 | alternativeCommandCount: alternativeCommandCount, 339 | shouldRecordFailedCommand: shouldRecordFailedCommand, 340 | outputWithTime: shouldOutputWithTime, 341 | outputStackTraceOfCommandExecution: shouldOutputVMExceptionStack 342 | ); 343 | ``` 344 | 345 | 以及你可以自由的决定这些日志该如何存储或者保存到什么位置。 346 | 347 | 348 | 349 | ## 其他 350 | 351 | ### 适用的Unity版本 352 | 353 | Unity 2018.03+ 354 | 355 | 这个项目与Unity是高度解耦合的,所以它几乎可以用于所有版本的Unity,你可以为它定义自己的UI来使用,这个项目提供了默认的Unity实现,你可以学习或者自己改造。 356 | 357 | ### 特性支持:基础运算 358 | 359 | 该系统是为了能够进行方便的debug工作而设计的,所以它的定位并不是一个完整的编程语言,所以它现在还不支持基础的运算,比如数学运算,或者逻辑运算等。如果对该类特性有需求的话,可以在github提出,如果的确有必要的话,我会实现的。 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UnityCommandLineInterface 2 | 3 | [![openupm](https://img.shields.io/npm/v/com.redsaw.commandline?label=openupm®istry_uri=https://package.openupm.com)](https://openupm.com/packages/com.redsaw.commandline/) 4 | 5 | [中文文档](./README-ch.md) 6 | 7 | ## Summary 8 | this project is an inner game command console, usually use to execute some short command or set/get property 9 | 10 |
11 | 12 |
13 | 14 | 15 | https://github.com/529324416/UnityCommandLineInterface/assets/30776995/a2290fff-ffa5-421f-8a4a-a5274e9c6d87 16 | 17 | 18 | ## Features: 19 | - **Easy to use**, no need to do much learning 20 | - **Lightweight**, no dependencies 21 | - **Input text suggestion** 22 | - **Support all versions of Unity** 23 | - **Highly Decoupled**, 24 | - **Easy to port to other platforms** 25 | 26 | ## Usage 27 | 28 | ## 1.Register Commands 29 | 30 | Register a command is easy, you just need to add an `Command` attribute to your static method. 31 | 32 | ```csharp 33 | [Command("my_cmd")] 34 | public static void SomeMethod(){ 35 | /* do something here .. */ 36 | } 37 | ``` 38 | 39 | but system also support to add instance method, but now system have a new feature **CommandProperty**, so it's meaningless to register instance method 40 | 41 | ## 2.Execute Commands 42 | 43 | command can be executed in two ways, the first way like this: 44 | 45 | ``` 46 | your_command args1 args2 47 | ``` 48 | 49 | but you can execute it like a method as well 50 | 51 | ``` 52 | your_command(args1, args2) 53 | ``` 54 | 55 | the first way keep it easy to execute a command, and the second way provide the ability to visit the member of return value looks like below. 56 | 57 | ``` 58 | get_enemy("slime").Jump() 59 | ``` 60 | 61 | ## 3.Console Variable 62 | 63 | the new console supports you to register a console variable and access it through `@`, this would make the overall command system very free, assuming that you have an object like this : 64 | 65 | ```csharp 66 | public class Player : MonoBehaviour{ 67 | 68 | public int health = 100; 69 | 70 | public void Jump(){ 71 | // code for jump.. 72 | } 73 | } 74 | ``` 75 | you can setup a static variable, and add an `CommandProperty` attribute for it to register a console variable 76 | 77 | ```csharp 78 | public class Player: MonoBehaviour{ 79 | 80 | [CommandProperty("player")] 81 | public static Player Instance; 82 | } 83 | ``` 84 | (you must write a Player instance into the static variable while the game start) 85 | 86 | Then you can access the inner member of the `Player`, its all rely on C#'s Reflection System, you can use the commands like below: 87 | 88 | ``` 89 | @player.health = 100 90 | @player.Jump() 91 | ``` 92 | 93 | Similarly, anywhere you need a variable, you can use `@` to reference the variable you need, for example, as a parameter to other commands 94 | 95 | ``` 96 | @enemy.Atk(@player) 97 | ``` 98 | 99 | the `CommandProperty` can be added to a Field or a Property, this means the console variable can return different value, like "the Npc nearest to player" 100 | 101 | 102 | ```csharp 103 | [CommandProperty("nearest")] 104 | public static Npc NearestNpc{ 105 | get{ 106 | // code for getting nearest npc.. 107 | } 108 | } 109 | ``` 110 | 111 | ## 4.Access console variable 112 | 113 | you can access or overwrite member of console variable through `.` or `[]` 114 | 115 | ``` 116 | @player.DoSomething() 117 | @player.buffs["buff_id"].AddTime(100) 118 | @enemies["slime"] = @new_slime 119 | ``` 120 | 121 | of course, the access is continues like normal programming language 122 | 123 | ``` 124 | @something.member.sub_member["elements"].member.sub_member.field.function().member 125 | ``` 126 | 127 | the `[]` has some special, it would check if the target member is a sequence object like an Array or List, if it is, then it requires the expression inner `[]` to be an Interger value. 128 | 129 | The Dictionary supports more key type, so the the judgment range of the dictionary would be wider. 130 | 131 | ``` 132 | @some_dict[@non_string_type] = get_something() 133 | ``` 134 | 135 | for example, your dictionary key type is a non-string type, you can use other console variables as the key to read or write. 136 | 137 | ## 5.Value Parse 138 | 139 | the normal value types supported by the CommandSystem has only 5. 140 | 141 | ``` 142 | string 143 | float 144 | int 145 | bool 146 | null 147 | ``` 148 | 149 | you can call these types as meta-type, it can be parsed by command system directly, generally speaking, it is sufficient to use. However, if you want the console to be able to parse different data types, you can do so by reigstering a value parser. 150 | 151 | for example, the position of the player maybe a `Vector3` type, you may wish to set the position of player like this: 152 | 153 | ``` 154 | @player.pos = "1. 1. 1." 155 | ``` 156 | 157 | Obviously, strings cannot be converted to `Vector3`, if you really want to do this, or if it's necessary to do so, you can register a 158 | ValueParser as follows: 159 | 160 | ```csharp 161 | 162 | [CommandValueParser(typeof(Vector3))] 163 | static bool ParseFunction(string input, out object data){ 164 | // code used to parse your input str 165 | } 166 | ``` 167 | 168 | the parse logic is totally free by you. for example, you can parse 'revival_point' to `Vector3` 169 | 170 | ```csharp 171 | static bool ParseFunction(string input, out object data){ 172 | if(input == "revival_point"){ 173 | data = Vector3.zero; 174 | return true; 175 | } 176 | // other prase here .. 177 | 178 | data = default; 179 | return false; 180 | } 181 | ``` 182 | 183 | then you can use the command as follows: 184 | 185 | ``` 186 | @player.pos = "revival_point" 187 | ``` 188 | 189 | the double quotation here is not necessary, but it's not suggestted to do like this. the system would first consider 'revival_point' to a command the try to execute it to get return value. the system would try parse it to `Vector3` only if it cannot find a command named 'revival_point' 190 | 191 | ``` 192 | @player.pos = revival_point 193 | ``` 194 | 195 | so you'd better not to use such ambiguous commands; 196 | 197 | the ValueParser usually used in some special suitations or complex data type, if you want to parse `Vector3`, the right way is registering a command like this: 198 | 199 | ```csharp 200 | [Command("v3")] 201 | public static Vector3 BuildVector3(float x, float y, float z){ 202 | return new Vector3(x, y, z); 203 | } 204 | ``` 205 | 206 | and use it as follows 207 | 208 | ``` 209 | @player.pos = v3(0, 0, 0) 210 | ``` 211 | 212 | ## 6.Register Debug Infos 213 | 214 | it you want to show the information of target object, for example, you just input '@player' to the console. 215 | 216 | ``` 217 | @player 218 | ``` 219 | 220 | the console could output the player informations likes below: 221 | 222 | ![Untitled](./Res/Untitled.png) 223 | 224 | to fetch the information structure, you can add an `DebugInfo` attribute to target field or property 225 | 226 | ```csharp 227 | public class Player : MonoBehaviour{ 228 | 229 | [DebugInfo] 230 | public int health = 100; 231 | } 232 | ``` 233 | 234 | In this way, by entering `@player` to the console, you can directly obtain all DebugInfo of the player, and the output is as follows: 235 | 236 | ``` 237 | ---------- Player start ---------- 238 | >Player.health: 100 239 | ---------- Player end ---------- 240 | ``` 241 | 242 | you can set title and color for it. 243 | 244 | ```csharp 245 | [DebugInfo("custom_title", Color = "#ff0000")] 246 | ``` 247 | 248 | ### 6.1 Tracing Parent Class 249 | 250 | system would trace all its parent class, and output the DebugInfo of the original parent class to the target class one by one 251 | 252 | ```csharp 253 | public class A{ 254 | [DebugInfo] 255 | public int someInt = 999; 256 | } 257 | 258 | public class B{ 259 | [DebugInfo] 260 | public float someFloat = 12.3; 261 | } 262 | 263 | public class C{ 264 | [DebugInfo] 265 | public string someStr = "Hello World!"; 266 | } 267 | ``` 268 | 269 | if you try to show the DebugInfo of C, you will get: 270 | 271 | ``` 272 | ---------- C start ---------- 273 | >A.someInt: 999 274 | >B.someFloat: 12.3 275 | >C.someStr: Hello World! 276 | ---------- C end ---------- 277 | ``` 278 | 279 | ### 6.3 DebugInfo of complex member 280 | 281 | if the member which has beed reigstered as a DebugInfo and its also have inner DebugInfos, you can add an `DebugObject` attribute to the target class: 282 | 283 | ```csharp 284 | public class A{ 285 | [DebugInfo("my_object")] 286 | public B b = new B(); 287 | } 288 | 289 | [DebugObject] 290 | public class B : MonoBehaviour{ 291 | [DebugInfo("name")] 292 | public string Name => this.gameObject.name; 293 | 294 | [DebugInfo] 295 | public string age = 25; 296 | } 297 | ``` 298 | 299 | if you try to ouput A, you will get this: 300 | 301 | ``` 302 | ---------- C start ---------- 303 | >----[B] 304 | > B.name: "instanceB" 305 | > B.age: 25 306 | ---------- C end ---------- 307 | ``` 308 | 309 | However, in this way, there may be circular data reference, such as B referencing C and C referencing B again, so you can set and depth to limit the circular data reference, if depth exceeds the limitation, system will discard the more informations, the default depth limitation is 4 310 | 311 | ## 7.Input Suggestion 312 | 313 | nothing to describe 314 | 315 | ![Untitled](./Res/Untitled%20(1).png) 316 | 317 | ## 8.Console Logs 318 | 319 | the new system has decoupled the output and render behaviours, this means the console only save the log but don't care how to render it. but you need to provide an logType to mark the logs. you can set it while you initialize the game console. 320 | 321 | ```csharp 322 | /* intialize console */ 323 | console = new ConsoleController( 324 | consoleRenderer, 325 | new UserInput(), 326 | 327 | inputHistoryCapacity: inputHistoryCapacity, 328 | commandQueryCacheCapacity: commandQueryCacheCapacity, 329 | alternativeCommandCount: alternativeCommandCount, 330 | shouldRecordFailedCommand: shouldRecordFailedCommand, 331 | outputWithTime: shouldOutputWithTime, 332 | outputStackTraceOfCommandExecution: shouldOutputVMExceptionStack 333 | ); 334 | ``` 335 | and you can decide where to save the logs. 336 | 337 | ## Other 338 | 339 | ### UnityEngine Versions 340 | 341 | Unity 2018.03+ 342 | 343 | it support all version. 344 | 345 | ### Feature Support: Basic Calculation 346 | 347 | The system is designed for convenient debugging work, so its positioning it not a complete programing language, so it currently does not support basic operations such as mathematical or logical operations, if there is a need for this feature, you can proposed on Github, if it is indeed necessary, I will implement it. 348 | -------------------------------------------------------------------------------- /Res/Untitled (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/529324416/UnityCommandLineInterface/574198026618102dc3906a532bde063713ac85b7/Res/Untitled (1).png -------------------------------------------------------------------------------- /Res/Untitled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/529324416/UnityCommandLineInterface/574198026618102dc3906a532bde063713ac85b7/Res/Untitled.png -------------------------------------------------------------------------------- /Res/screen-shot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/529324416/UnityCommandLineInterface/574198026618102dc3906a532bde063713ac85b7/Res/screen-shot.png -------------------------------------------------------------------------------- /Res/usage-part-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/529324416/UnityCommandLineInterface/574198026618102dc3906a532bde063713ac85b7/Res/usage-part-1.png -------------------------------------------------------------------------------- /Res/usage.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/529324416/UnityCommandLineInterface/574198026618102dc3906a532bde063713ac85b7/Res/usage.mp4 -------------------------------------------------------------------------------- /Res/屏幕截图 2024-01-12 173800.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/529324416/UnityCommandLineInterface/574198026618102dc3906a532bde063713ac85b7/Res/屏幕截图 2024-01-12 173800.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"com.redsaw.commandline", 3 | "displayName": "Inner Game Console", 4 | "version": "0.1.1", 5 | "unity": "2018.3", 6 | "description": "an inner game command line tool", 7 | "keywords": ["commandline", "cli", "debugging", "log"], 8 | "category": "Debugging", 9 | "dependencies": {}, 10 | "samples": [ 11 | { 12 | "displayName": "Inner Game Console", 13 | "description": "an inner game command line tool", 14 | "path": "Assets/Scenes/SampleScene" 15 | } 16 | ], 17 | "author": { 18 | "name": "Prince Biscuit", 19 | "url": "https://github.com/529324416" 20 | } 21 | } --------------------------------------------------------------------------------