From 79eaed43c54bd03b45a0614b7791b1b0cb2c148d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=80=9D=E6=B5=B7?= <1464576565@qq.com> Date: Fri, 5 Sep 2025 19:46:30 +0800 Subject: [PATCH] modify --- Editor.meta | 8 + Editor/AlicizaX.Framework.Editor.asmdef | 22 + Editor/AlicizaX.Framework.Editor.asmdef.meta | 7 + Editor/Audio.meta | 8 + Editor/Audio/AudioComponentInspector.cs | 44 + Editor/Audio/AudioComponentInspector.cs.meta | 3 + Editor/Debugger.meta | 8 + Editor/Debugger/Inspector.meta | 8 + .../Inspector/DebuggerComponentInspector.cs | 64 + .../DebuggerComponentInspector.cs.meta | 11 + Editor/Debugger/Res.meta | 8 + Editor/Debugger/Res/DebuggerSkin.guiskin | 1427 +++++++++++++++++ Editor/Debugger/Res/DebuggerSkin.guiskin.meta | 8 + Editor/FSM.meta | 8 + Editor/FSM/FsmDebuggerWindow.cs | 121 ++ Editor/FSM/FsmDebuggerWindow.cs.meta | 3 + Editor/Resource.meta | 8 + Editor/Resource/Encryption.meta | 8 + .../Resource/Encryption/DefaultEncryption.cs | 50 + .../Encryption/DefaultEncryption.cs.meta | 3 + Editor/Resource/Inspector.meta | 3 + .../Inspector/ResourceComponentInspector.cs | 264 +++ .../ResourceComponentInspector.cs.meta | 3 + .../ResourceExtComponentInspector.cs | 59 + .../ResourceExtComponentInspector.cs.meta | 3 + Editor/UI.meta | 8 + Editor/UI/GenerateTool.meta | 3 + .../UI/GenerateTool/UIGenerateEditorTool.cs | 21 + .../GenerateTool/UIGenerateEditorTool.cs.meta | 3 + .../UI/GenerateTool/UIGenerateEditorWindow.cs | 42 + .../UIGenerateEditorWindow.cs.meta | 3 + .../GenerateTool/UIScriptGeneratorHelper.cs | 469 ++++++ .../UIScriptGeneratorHelper.cs.meta | 3 + .../UI/GenerateTool/UISettingEditorWindow.cs | 104 ++ .../UISettingEditorWindow.cs.meta | 3 + Editor/UI/Inspector.meta | 3 + Editor/UI/Inspector/UIComponentInspector.cs | 62 + .../UI/Inspector/UIComponentInspector.cs.meta | 3 + Editor/UI/Res.meta | 8 + Editor/UI/Res/UIRoot.prefab | 312 ++++ Editor/UI/Res/UIRoot.prefab.meta | 7 + Editor/UI/Res/default.txt | 1 + Editor/UI/Res/default.txt.meta | 7 + Editor/UI/UIConfig.meta | 3 + Editor/UI/UIConfig/UIGenerateConfiguration.cs | 99 ++ .../UIConfig/UIGenerateConfiguration.cs.meta | 11 + Editor/UI/UIGlobalPath.cs | 8 + Editor/UI/UIGlobalPath.cs.meta | 3 + LICENSE.md | 201 +++ LICENSE.md.meta | 7 + Plugins.meta | 8 + Plugins/UI.meta | 8 + Plugins/UI/UISourceGenerator.dll | Bin 0 -> 25088 bytes Plugins/UI/UISourceGenerator.dll.meta | 52 + Runtime.meta | 8 + Runtime/AlicizaX.Framework.Runtime.asmdef | 32 + .../AlicizaX.Framework.Runtime.asmdef.meta | 7 + Runtime/Audio.meta | 3 + Runtime/Audio/AudioAgent.cs | 455 ++++++ Runtime/Audio/AudioAgent.cs.meta | 11 + Runtime/Audio/AudioAgentRuntimeState.cs | 33 + Runtime/Audio/AudioAgentRuntimeState.cs.meta | 3 + Runtime/Audio/AudioCategory.cs | 239 +++ Runtime/Audio/AudioCategory.cs.meta | 11 + Runtime/Audio/AudioComponent.cs | 48 + Runtime/Audio/AudioComponent.cs.meta | 11 + Runtime/Audio/AudioData.cs | 67 + Runtime/Audio/AudioData.cs.meta | 3 + Runtime/Audio/AudioGroupConfig.cs | 48 + Runtime/Audio/AudioGroupConfig.cs.meta | 11 + Runtime/Audio/AudioModule.cs | 593 +++++++ Runtime/Audio/AudioModule.cs.meta | 3 + Runtime/Audio/AudioType.cs | 34 + Runtime/Audio/AudioType.cs.meta | 11 + Runtime/Audio/IAudioModule.cs | 130 ++ Runtime/Audio/IAudioModule.cs.meta | 3 + Runtime/Audio/Resources.meta | 8 + Runtime/Audio/Resources/AudioMixer.mixer | 708 ++++++++ Runtime/Audio/Resources/AudioMixer.mixer.meta | 8 + Runtime/Debugger.meta | 8 + Runtime/Debugger/DebuggerActiveWindowType.cs | 28 + .../Debugger/DebuggerActiveWindowType.cs.meta | 11 + .../DebuggerComponent.ConsoleWindow.cs | 420 +++++ .../DebuggerComponent.ConsoleWindow.cs.meta | 11 + ...rComponent.EnvironmentInformationWindow.cs | 74 + ...onent.EnvironmentInformationWindow.cs.meta | 11 + .../Debugger/DebuggerComponent.FpsCounter.cs | 77 + .../DebuggerComponent.FpsCounter.cs.meta | 11 + ...ggerComponent.GraphicsInformationWindow.cs | 163 ++ ...omponent.GraphicsInformationWindow.cs.meta | 11 + ...nent.InputAccelerationInformationWindow.cs | 39 + ...InputAccelerationInformationWindow.cs.meta | 11 + ...Component.InputCompassInformationWindow.cs | 42 + ...nent.InputCompassInformationWindow.cs.meta | 11 + ...mponent.InputGyroscopeInformationWindow.cs | 43 + ...nt.InputGyroscopeInformationWindow.cs.meta | 11 + ...omponent.InputLocationInformationWindow.cs | 44 + ...ent.InputLocationInformationWindow.cs.meta | 11 + ...Component.InputSummaryInformationWindow.cs | 33 + ...nent.InputSummaryInformationWindow.cs.meta | 11 + ...erComponent.InputTouchInformationWindow.cs | 43 + ...ponent.InputTouchInformationWindow.cs.meta | 11 + Runtime/Debugger/DebuggerComponent.LogNode.cs | 119 ++ .../DebuggerComponent.LogNode.cs.meta | 11 + ...erComponent.ObjectPoolInformationWindow.cs | 87 + ...ponent.ObjectPoolInformationWindow.cs.meta | 11 + .../DebuggerComponent.OperationsWindow.cs | 67 + ...DebuggerComponent.OperationsWindow.cs.meta | 11 + ...DebuggerComponent.PathInformationWindow.cs | 29 + ...gerComponent.PathInformationWindow.cs.meta | 11 + ...ggerComponent.ProfilerInformationWindow.cs | 61 + ...omponent.ProfilerInformationWindow.cs.meta | 11 + ...uggerComponent.QualityInformationWindow.cs | 104 ++ ...Component.QualityInformationWindow.cs.meta | 11 + ...omponent.ReferencePoolInformationWindow.cs | 107 ++ ...ent.ReferencePoolInformationWindow.cs.meta | 11 + ...t.RuntimeMemoryInformationWindow.Sample.cs | 62 + ...timeMemoryInformationWindow.Sample.cs.meta | 11 + ...omponent.RuntimeMemoryInformationWindow.cs | 136 ++ ...ent.RuntimeMemoryInformationWindow.cs.meta | 11 + ...onent.RuntimeMemorySummaryWindow.Record.cs | 56 + ....RuntimeMemorySummaryWindow.Record.cs.meta | 11 + ...gerComponent.RuntimeMemorySummaryWindow.cs | 124 ++ ...mponent.RuntimeMemorySummaryWindow.cs.meta | 11 + ...ebuggerComponent.SceneInformationWindow.cs | 38 + ...erComponent.SceneInformationWindow.cs.meta | 11 + ...buggerComponent.ScreenInformationWindow.cs | 92 ++ ...rComponent.ScreenInformationWindow.cs.meta | 11 + ...rComponent.ScrollableDebuggerWindowBase.cs | 95 ++ ...onent.ScrollableDebuggerWindowBase.cs.meta | 11 + .../DebuggerComponent.SettingsWindow.cs | 219 +++ .../DebuggerComponent.SettingsWindow.cs.meta | 11 + ...buggerComponent.SystemInformationWindow.cs | 56 + ...rComponent.SystemInformationWindow.cs.meta | 11 + ...DebuggerComponent.TimeInformationWindow.cs | 70 + ...gerComponent.TimeInformationWindow.cs.meta | 11 + ...gerComponent.WebPlayerInformationWindow.cs | 34 + ...mponent.WebPlayerInformationWindow.cs.meta | 11 + Runtime/Debugger/DebuggerComponent.cs | 418 +++++ Runtime/Debugger/DebuggerComponent.cs.meta | 11 + .../DebuggerManager.DebuggerWindowGroup.cs | 301 ++++ ...ebuggerManager.DebuggerWindowGroup.cs.meta | 11 + Runtime/Debugger/DebuggerModule.cs | 123 ++ Runtime/Debugger/DebuggerModule.cs.meta | 11 + Runtime/Debugger/IDebuggerModule.cs | 38 + Runtime/Debugger/IDebuggerModule.cs.meta | 11 + Runtime/Debugger/IDebuggerWindow.cs | 29 + Runtime/Debugger/IDebuggerWindow.cs.meta | 11 + Runtime/Debugger/IDebuggerWindowGroup.cs | 31 + Runtime/Debugger/IDebuggerWindowGroup.cs.meta | 11 + Runtime/FSM.meta | 8 + Runtime/FSM/FsmComponent.cs | 28 + Runtime/FSM/FsmComponent.cs.meta | 3 + Runtime/FSM/FsmModule.cs | 84 + Runtime/FSM/FsmModule.cs.meta | 3 + Runtime/FSM/HighPerfFSM.cs | 410 +++++ Runtime/FSM/HighPerfFSM.cs.meta | 3 + Runtime/FSM/IFsmModule.cs | 13 + Runtime/FSM/IFsmModule.cs.meta | 2 + Runtime/FSM/SimpleFSM.cs | 259 +++ Runtime/FSM/SimpleFSM.cs.meta | 3 + Runtime/Localization.meta | 3 + Runtime/Localization/Event.meta | 3 + .../Event/LocalizationChangeEvent.cs | 20 + .../Event/LocalizationChangeEvent.cs.meta | 3 + Runtime/Localization/LocalizationComponent.cs | 25 + .../LocalizationComponent.cs.meta | 3 + Runtime/Localization/Manager.meta | 3 + .../Manager/ILocalizationModule.cs | 426 +++++ .../Manager/ILocalizationModule.cs.meta | 3 + .../Manager/LocalizationModule.cs | 737 +++++++++ .../Manager/LocalizationModule.cs.meta | 3 + Runtime/Resource.meta | 8 + Runtime/Resource/EventArgs.meta | 3 + .../AssetDownloadProgressUpdateEventArgs.cs | 55 + ...setDownloadProgressUpdateEventArgs.cs.meta | 3 + Runtime/Resource/Resource.meta | 3 + Runtime/Resource/Resource/Callback.meta | 3 + .../Resource/Callback/LoadAssetCallbacks.cs | 94 ++ .../Callback/LoadAssetCallbacks.cs.meta | 11 + .../Callback/LoadAssetFailureCallback.cs | 11 + .../Callback/LoadAssetFailureCallback.cs.meta | 3 + .../Callback/LoadAssetSuccessCallback.cs | 11 + .../Callback/LoadAssetSuccessCallback.cs.meta | 3 + .../Callback/LoadAssetUpdateCallback.cs | 10 + .../Callback/LoadAssetUpdateCallback.cs.meta | 3 + .../Resource/Callback/LoadResourceStatus.cs | 38 + .../Callback/LoadResourceStatus.cs.meta | 3 + .../Resource/Callback/LoadSceneCallbacks.cs | 94 ++ .../Callback/LoadSceneCallbacks.cs.meta | 3 + .../Callback/LoadSceneFailureCallback.cs | 11 + .../Callback/LoadSceneFailureCallback.cs.meta | 3 + .../Callback/LoadSceneSuccessCallback.cs | 11 + .../Callback/LoadSceneSuccessCallback.cs.meta | 3 + .../Callback/LoadSceneUpdateCallback.cs | 10 + .../Callback/LoadSceneUpdateCallback.cs.meta | 3 + .../Resource/Callback/UnloadSceneCallbacks.cs | 60 + .../Callback/UnloadSceneCallbacks.cs.meta | 3 + .../Callback/UnloadSceneFailureCallback.cs | 9 + .../UnloadSceneFailureCallback.cs.meta | 3 + .../Callback/UnloadSceneSuccessCallback.cs | 9 + .../UnloadSceneSuccessCallback.cs.meta | 3 + Runtime/Resource/Resource/Extension.meta | 3 + .../Resource/Extension/AssetItemObject.cs | 26 + .../Extension/AssetItemObject.cs.meta | 3 + .../Resource/Extension/ISetAssetObject.cs | 23 + .../Extension/ISetAssetObject.cs.meta | 3 + .../Resource/Extension/Implement.meta | 3 + .../Implement/SetSpriteExtensions.cs | 56 + .../Implement/SetSpriteExtensions.cs.meta | 3 + .../Extension/Implement/SetSpriteObject.cs | 116 ++ .../Implement/SetSpriteObject.cs.meta | 3 + .../Resource/Extension/LoadAssetObject.cs | 28 + .../Extension/LoadAssetObject.cs.meta | 3 + .../ResourceExtComponent.Resource.cs | 63 + .../ResourceExtComponent.Resource.cs.meta | 3 + .../ResourceExtComponent.SubSprite.cs | 130 ++ .../ResourceExtComponent.SubSprite.cs.meta | 3 + .../Extension/ResourceExtComponent.cs | 150 ++ .../Extension/ResourceExtComponent.cs.meta | 3 + Runtime/Resource/Resource/HasAssetResult.cs | 43 + .../Resource/Resource/HasAssetResult.cs.meta | 3 + Runtime/Resource/Resource/IResourceModule.cs | 290 ++++ .../Resource/Resource/IResourceModule.cs.meta | 3 + .../Resource/Resource/ReadWritePathType.cs | 23 + .../Resource/ReadWritePathType.cs.meta | 3 + Runtime/Resource/Resource/Reference.meta | 3 + .../Resource/Reference/AssetsReference.cs | 194 +++ .../Reference/AssetsReference.cs.meta | 3 + .../Resource/Reference/AssetsSetHelper.cs | 151 ++ .../Reference/AssetsSetHelper.cs.meta | 3 + .../Resource/Resource/ResourceComponent.cs | 269 ++++ .../Resource/ResourceComponent.cs.meta | 11 + Runtime/Resource/Resource/ResourceLogger.cs | 27 + .../Resource/Resource/ResourceLogger.cs.meta | 3 + .../Resource/ResourceModule.AssetObject.cs | 69 + .../ResourceModule.AssetObject.cs.meta | 3 + .../Resource/ResourceModule.Initialization.cs | 123 ++ .../ResourceModule.Initialization.cs.meta | 3 + .../Resource/Resource/ResourceModule.Pool.cs | 71 + .../Resource/ResourceModule.Pool.cs.meta | 3 + .../Resource/ResourceModule.Services.cs | 178 ++ .../Resource/ResourceModule.Services.cs.meta | 3 + Runtime/Resource/Resource/ResourceModule.cs | 1096 +++++++++++++ .../Resource/Resource/ResourceModule.cs.meta | 3 + Runtime/Scene.meta | 3 + Runtime/Scene/ISceneModule.cs | 90 ++ Runtime/Scene/ISceneModule.cs.meta | 3 + Runtime/Scene/SceneComponent.cs | 16 + Runtime/Scene/SceneComponent.cs.meta | 3 + Runtime/Scene/SceneModule.cs | 408 +++++ Runtime/Scene/SceneModule.cs.meta | 3 + Runtime/Timer.meta | 3 + Runtime/Timer/ITimerModule.cs | 64 + Runtime/Timer/ITimerModule.cs.meta | 3 + Runtime/Timer/TimerComponent.cs | 17 + Runtime/Timer/TimerComponent.cs.meta | 3 + Runtime/Timer/TimerModule.cs | 235 +++ Runtime/Timer/TimerModule.cs.meta | 3 + Runtime/UI.meta | 3 + Runtime/UI/Constant.meta | 3 + Runtime/UI/Constant/EPanelLayer.cs | 44 + Runtime/UI/Constant/EPanelLayer.cs.meta | 3 + Runtime/UI/Constant/Em.cs | 31 + Runtime/UI/Constant/Em.cs.meta | 3 + Runtime/UI/Constant/UIHolderFactory.cs | 78 + Runtime/UI/Constant/UIHolderFactory.cs.meta | 3 + Runtime/UI/Constant/UIMetaRegistry.cs | 56 + Runtime/UI/Constant/UIMetaRegistry.cs.meta | 3 + Runtime/UI/Constant/UIMetadata.cs | 57 + Runtime/UI/Constant/UIMetadata.cs.meta | 3 + Runtime/UI/Constant/UIMetadataFactory.cs | 33 + Runtime/UI/Constant/UIMetadataFactory.cs.meta | 3 + Runtime/UI/Constant/UIResRegistry.cs | 37 + Runtime/UI/Constant/UIResRegistry.cs.meta | 3 + Runtime/UI/Constant/WindowAttribute.cs | 57 + Runtime/UI/Constant/WindowAttribute.cs.meta | 3 + Runtime/UI/EventListenerProxy.cs | 32 + Runtime/UI/EventListenerProxy.cs.meta | 3 + Runtime/UI/Manager.meta | 8 + Runtime/UI/Manager/IUIModule.cs | 24 + Runtime/UI/Manager/IUIModule.cs.meta | 2 + Runtime/UI/Manager/UIModule.Block.cs | 80 + Runtime/UI/Manager/UIModule.Block.cs.meta | 3 + Runtime/UI/Manager/UIModule.Cache.cs | 58 + Runtime/UI/Manager/UIModule.Cache.cs.meta | 3 + Runtime/UI/Manager/UIModule.Initlize.cs | 81 + Runtime/UI/Manager/UIModule.Initlize.cs.meta | 3 + Runtime/UI/Manager/UIModule.Open.cs | 167 ++ Runtime/UI/Manager/UIModule.Open.cs.meta | 3 + Runtime/UI/Manager/UIModule.cs | 107 ++ Runtime/UI/Manager/UIModule.cs.meta | 3 + Runtime/UI/Other.meta | 3 + Runtime/UI/Other/UIBlock.cs | 62 + Runtime/UI/Other/UIBlock.cs.meta | 11 + Runtime/UI/UIBase.meta | 3 + Runtime/UI/UIBase/UIBase.Widget.cs | 124 ++ Runtime/UI/UIBase/UIBase.Widget.cs.meta | 3 + Runtime/UI/UIBase/UIBase.cs | 260 +++ Runtime/UI/UIBase/UIBase.cs.meta | 3 + Runtime/UI/UIBase/UIHolderObjectBase.cs | 117 ++ Runtime/UI/UIBase/UIHolderObjectBase.cs.meta | 3 + Runtime/UI/UIBase/UIState.cs | 14 + Runtime/UI/UIBase/UIState.cs.meta | 3 + Runtime/UI/UIBase/UITabWindow.cs | 142 ++ Runtime/UI/UIBase/UITabWindow.cs.meta | 3 + Runtime/UI/UIBase/UIWidget.cs | 48 + Runtime/UI/UIBase/UIWidget.cs.meta | 3 + Runtime/UI/UIBase/UIWindow.cs | 46 + Runtime/UI/UIBase/UIWindow.cs.meta | 3 + Runtime/UI/UIComponent.cs | 112 ++ Runtime/UI/UIComponent.cs.meta | 3 + package.json | 22 + package.json.meta | 7 + 314 files changed, 18588 insertions(+) create mode 100644 Editor.meta create mode 100644 Editor/AlicizaX.Framework.Editor.asmdef create mode 100644 Editor/AlicizaX.Framework.Editor.asmdef.meta create mode 100644 Editor/Audio.meta create mode 100644 Editor/Audio/AudioComponentInspector.cs create mode 100644 Editor/Audio/AudioComponentInspector.cs.meta create mode 100644 Editor/Debugger.meta create mode 100644 Editor/Debugger/Inspector.meta create mode 100644 Editor/Debugger/Inspector/DebuggerComponentInspector.cs create mode 100644 Editor/Debugger/Inspector/DebuggerComponentInspector.cs.meta create mode 100644 Editor/Debugger/Res.meta create mode 100644 Editor/Debugger/Res/DebuggerSkin.guiskin create mode 100644 Editor/Debugger/Res/DebuggerSkin.guiskin.meta create mode 100644 Editor/FSM.meta create mode 100644 Editor/FSM/FsmDebuggerWindow.cs create mode 100644 Editor/FSM/FsmDebuggerWindow.cs.meta create mode 100644 Editor/Resource.meta create mode 100644 Editor/Resource/Encryption.meta create mode 100644 Editor/Resource/Encryption/DefaultEncryption.cs create mode 100644 Editor/Resource/Encryption/DefaultEncryption.cs.meta create mode 100644 Editor/Resource/Inspector.meta create mode 100644 Editor/Resource/Inspector/ResourceComponentInspector.cs create mode 100644 Editor/Resource/Inspector/ResourceComponentInspector.cs.meta create mode 100644 Editor/Resource/Inspector/ResourceExtComponentInspector.cs create mode 100644 Editor/Resource/Inspector/ResourceExtComponentInspector.cs.meta create mode 100644 Editor/UI.meta create mode 100644 Editor/UI/GenerateTool.meta create mode 100644 Editor/UI/GenerateTool/UIGenerateEditorTool.cs create mode 100644 Editor/UI/GenerateTool/UIGenerateEditorTool.cs.meta create mode 100644 Editor/UI/GenerateTool/UIGenerateEditorWindow.cs create mode 100644 Editor/UI/GenerateTool/UIGenerateEditorWindow.cs.meta create mode 100644 Editor/UI/GenerateTool/UIScriptGeneratorHelper.cs create mode 100644 Editor/UI/GenerateTool/UIScriptGeneratorHelper.cs.meta create mode 100644 Editor/UI/GenerateTool/UISettingEditorWindow.cs create mode 100644 Editor/UI/GenerateTool/UISettingEditorWindow.cs.meta create mode 100644 Editor/UI/Inspector.meta create mode 100644 Editor/UI/Inspector/UIComponentInspector.cs create mode 100644 Editor/UI/Inspector/UIComponentInspector.cs.meta create mode 100644 Editor/UI/Res.meta create mode 100644 Editor/UI/Res/UIRoot.prefab create mode 100644 Editor/UI/Res/UIRoot.prefab.meta create mode 100644 Editor/UI/Res/default.txt create mode 100644 Editor/UI/Res/default.txt.meta create mode 100644 Editor/UI/UIConfig.meta create mode 100644 Editor/UI/UIConfig/UIGenerateConfiguration.cs create mode 100644 Editor/UI/UIConfig/UIGenerateConfiguration.cs.meta create mode 100644 Editor/UI/UIGlobalPath.cs create mode 100644 Editor/UI/UIGlobalPath.cs.meta create mode 100644 LICENSE.md create mode 100644 LICENSE.md.meta create mode 100644 Plugins.meta create mode 100644 Plugins/UI.meta create mode 100644 Plugins/UI/UISourceGenerator.dll create mode 100644 Plugins/UI/UISourceGenerator.dll.meta create mode 100644 Runtime.meta create mode 100644 Runtime/AlicizaX.Framework.Runtime.asmdef create mode 100644 Runtime/AlicizaX.Framework.Runtime.asmdef.meta create mode 100644 Runtime/Audio.meta create mode 100644 Runtime/Audio/AudioAgent.cs create mode 100644 Runtime/Audio/AudioAgent.cs.meta create mode 100644 Runtime/Audio/AudioAgentRuntimeState.cs create mode 100644 Runtime/Audio/AudioAgentRuntimeState.cs.meta create mode 100644 Runtime/Audio/AudioCategory.cs create mode 100644 Runtime/Audio/AudioCategory.cs.meta create mode 100644 Runtime/Audio/AudioComponent.cs create mode 100644 Runtime/Audio/AudioComponent.cs.meta create mode 100644 Runtime/Audio/AudioData.cs create mode 100644 Runtime/Audio/AudioData.cs.meta create mode 100644 Runtime/Audio/AudioGroupConfig.cs create mode 100644 Runtime/Audio/AudioGroupConfig.cs.meta create mode 100644 Runtime/Audio/AudioModule.cs create mode 100644 Runtime/Audio/AudioModule.cs.meta create mode 100644 Runtime/Audio/AudioType.cs create mode 100644 Runtime/Audio/AudioType.cs.meta create mode 100644 Runtime/Audio/IAudioModule.cs create mode 100644 Runtime/Audio/IAudioModule.cs.meta create mode 100644 Runtime/Audio/Resources.meta create mode 100644 Runtime/Audio/Resources/AudioMixer.mixer create mode 100644 Runtime/Audio/Resources/AudioMixer.mixer.meta create mode 100644 Runtime/Debugger.meta create mode 100644 Runtime/Debugger/DebuggerActiveWindowType.cs create mode 100644 Runtime/Debugger/DebuggerActiveWindowType.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.ConsoleWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.ConsoleWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.EnvironmentInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.EnvironmentInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.FpsCounter.cs create mode 100644 Runtime/Debugger/DebuggerComponent.FpsCounter.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.GraphicsInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.GraphicsInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.InputAccelerationInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.InputAccelerationInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.InputCompassInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.InputCompassInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.InputGyroscopeInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.InputGyroscopeInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.InputLocationInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.InputLocationInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.InputSummaryInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.InputSummaryInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.InputTouchInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.InputTouchInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.LogNode.cs create mode 100644 Runtime/Debugger/DebuggerComponent.LogNode.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.ObjectPoolInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.ObjectPoolInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.OperationsWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.OperationsWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.PathInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.PathInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.ProfilerInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.ProfilerInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.QualityInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.QualityInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.ReferencePoolInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.ReferencePoolInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.Sample.cs create mode 100644 Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.Sample.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.Record.cs create mode 100644 Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.Record.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.SceneInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.SceneInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.ScreenInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.ScreenInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.ScrollableDebuggerWindowBase.cs create mode 100644 Runtime/Debugger/DebuggerComponent.ScrollableDebuggerWindowBase.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.SettingsWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.SettingsWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.SystemInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.SystemInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.TimeInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.TimeInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.WebPlayerInformationWindow.cs create mode 100644 Runtime/Debugger/DebuggerComponent.WebPlayerInformationWindow.cs.meta create mode 100644 Runtime/Debugger/DebuggerComponent.cs create mode 100644 Runtime/Debugger/DebuggerComponent.cs.meta create mode 100644 Runtime/Debugger/DebuggerManager.DebuggerWindowGroup.cs create mode 100644 Runtime/Debugger/DebuggerManager.DebuggerWindowGroup.cs.meta create mode 100644 Runtime/Debugger/DebuggerModule.cs create mode 100644 Runtime/Debugger/DebuggerModule.cs.meta create mode 100644 Runtime/Debugger/IDebuggerModule.cs create mode 100644 Runtime/Debugger/IDebuggerModule.cs.meta create mode 100644 Runtime/Debugger/IDebuggerWindow.cs create mode 100644 Runtime/Debugger/IDebuggerWindow.cs.meta create mode 100644 Runtime/Debugger/IDebuggerWindowGroup.cs create mode 100644 Runtime/Debugger/IDebuggerWindowGroup.cs.meta create mode 100644 Runtime/FSM.meta create mode 100644 Runtime/FSM/FsmComponent.cs create mode 100644 Runtime/FSM/FsmComponent.cs.meta create mode 100644 Runtime/FSM/FsmModule.cs create mode 100644 Runtime/FSM/FsmModule.cs.meta create mode 100644 Runtime/FSM/HighPerfFSM.cs create mode 100644 Runtime/FSM/HighPerfFSM.cs.meta create mode 100644 Runtime/FSM/IFsmModule.cs create mode 100644 Runtime/FSM/IFsmModule.cs.meta create mode 100644 Runtime/FSM/SimpleFSM.cs create mode 100644 Runtime/FSM/SimpleFSM.cs.meta create mode 100644 Runtime/Localization.meta create mode 100644 Runtime/Localization/Event.meta create mode 100644 Runtime/Localization/Event/LocalizationChangeEvent.cs create mode 100644 Runtime/Localization/Event/LocalizationChangeEvent.cs.meta create mode 100644 Runtime/Localization/LocalizationComponent.cs create mode 100644 Runtime/Localization/LocalizationComponent.cs.meta create mode 100644 Runtime/Localization/Manager.meta create mode 100644 Runtime/Localization/Manager/ILocalizationModule.cs create mode 100644 Runtime/Localization/Manager/ILocalizationModule.cs.meta create mode 100644 Runtime/Localization/Manager/LocalizationModule.cs create mode 100644 Runtime/Localization/Manager/LocalizationModule.cs.meta create mode 100644 Runtime/Resource.meta create mode 100644 Runtime/Resource/EventArgs.meta create mode 100644 Runtime/Resource/EventArgs/AssetDownloadProgressUpdateEventArgs.cs create mode 100644 Runtime/Resource/EventArgs/AssetDownloadProgressUpdateEventArgs.cs.meta create mode 100644 Runtime/Resource/Resource.meta create mode 100644 Runtime/Resource/Resource/Callback.meta create mode 100644 Runtime/Resource/Resource/Callback/LoadAssetCallbacks.cs create mode 100644 Runtime/Resource/Resource/Callback/LoadAssetCallbacks.cs.meta create mode 100644 Runtime/Resource/Resource/Callback/LoadAssetFailureCallback.cs create mode 100644 Runtime/Resource/Resource/Callback/LoadAssetFailureCallback.cs.meta create mode 100644 Runtime/Resource/Resource/Callback/LoadAssetSuccessCallback.cs create mode 100644 Runtime/Resource/Resource/Callback/LoadAssetSuccessCallback.cs.meta create mode 100644 Runtime/Resource/Resource/Callback/LoadAssetUpdateCallback.cs create mode 100644 Runtime/Resource/Resource/Callback/LoadAssetUpdateCallback.cs.meta create mode 100644 Runtime/Resource/Resource/Callback/LoadResourceStatus.cs create mode 100644 Runtime/Resource/Resource/Callback/LoadResourceStatus.cs.meta create mode 100644 Runtime/Resource/Resource/Callback/LoadSceneCallbacks.cs create mode 100644 Runtime/Resource/Resource/Callback/LoadSceneCallbacks.cs.meta create mode 100644 Runtime/Resource/Resource/Callback/LoadSceneFailureCallback.cs create mode 100644 Runtime/Resource/Resource/Callback/LoadSceneFailureCallback.cs.meta create mode 100644 Runtime/Resource/Resource/Callback/LoadSceneSuccessCallback.cs create mode 100644 Runtime/Resource/Resource/Callback/LoadSceneSuccessCallback.cs.meta create mode 100644 Runtime/Resource/Resource/Callback/LoadSceneUpdateCallback.cs create mode 100644 Runtime/Resource/Resource/Callback/LoadSceneUpdateCallback.cs.meta create mode 100644 Runtime/Resource/Resource/Callback/UnloadSceneCallbacks.cs create mode 100644 Runtime/Resource/Resource/Callback/UnloadSceneCallbacks.cs.meta create mode 100644 Runtime/Resource/Resource/Callback/UnloadSceneFailureCallback.cs create mode 100644 Runtime/Resource/Resource/Callback/UnloadSceneFailureCallback.cs.meta create mode 100644 Runtime/Resource/Resource/Callback/UnloadSceneSuccessCallback.cs create mode 100644 Runtime/Resource/Resource/Callback/UnloadSceneSuccessCallback.cs.meta create mode 100644 Runtime/Resource/Resource/Extension.meta create mode 100644 Runtime/Resource/Resource/Extension/AssetItemObject.cs create mode 100644 Runtime/Resource/Resource/Extension/AssetItemObject.cs.meta create mode 100644 Runtime/Resource/Resource/Extension/ISetAssetObject.cs create mode 100644 Runtime/Resource/Resource/Extension/ISetAssetObject.cs.meta create mode 100644 Runtime/Resource/Resource/Extension/Implement.meta create mode 100644 Runtime/Resource/Resource/Extension/Implement/SetSpriteExtensions.cs create mode 100644 Runtime/Resource/Resource/Extension/Implement/SetSpriteExtensions.cs.meta create mode 100644 Runtime/Resource/Resource/Extension/Implement/SetSpriteObject.cs create mode 100644 Runtime/Resource/Resource/Extension/Implement/SetSpriteObject.cs.meta create mode 100644 Runtime/Resource/Resource/Extension/LoadAssetObject.cs create mode 100644 Runtime/Resource/Resource/Extension/LoadAssetObject.cs.meta create mode 100644 Runtime/Resource/Resource/Extension/ResourceExtComponent.Resource.cs create mode 100644 Runtime/Resource/Resource/Extension/ResourceExtComponent.Resource.cs.meta create mode 100644 Runtime/Resource/Resource/Extension/ResourceExtComponent.SubSprite.cs create mode 100644 Runtime/Resource/Resource/Extension/ResourceExtComponent.SubSprite.cs.meta create mode 100644 Runtime/Resource/Resource/Extension/ResourceExtComponent.cs create mode 100644 Runtime/Resource/Resource/Extension/ResourceExtComponent.cs.meta create mode 100644 Runtime/Resource/Resource/HasAssetResult.cs create mode 100644 Runtime/Resource/Resource/HasAssetResult.cs.meta create mode 100644 Runtime/Resource/Resource/IResourceModule.cs create mode 100644 Runtime/Resource/Resource/IResourceModule.cs.meta create mode 100644 Runtime/Resource/Resource/ReadWritePathType.cs create mode 100644 Runtime/Resource/Resource/ReadWritePathType.cs.meta create mode 100644 Runtime/Resource/Resource/Reference.meta create mode 100644 Runtime/Resource/Resource/Reference/AssetsReference.cs create mode 100644 Runtime/Resource/Resource/Reference/AssetsReference.cs.meta create mode 100644 Runtime/Resource/Resource/Reference/AssetsSetHelper.cs create mode 100644 Runtime/Resource/Resource/Reference/AssetsSetHelper.cs.meta create mode 100644 Runtime/Resource/Resource/ResourceComponent.cs create mode 100644 Runtime/Resource/Resource/ResourceComponent.cs.meta create mode 100644 Runtime/Resource/Resource/ResourceLogger.cs create mode 100644 Runtime/Resource/Resource/ResourceLogger.cs.meta create mode 100644 Runtime/Resource/Resource/ResourceModule.AssetObject.cs create mode 100644 Runtime/Resource/Resource/ResourceModule.AssetObject.cs.meta create mode 100644 Runtime/Resource/Resource/ResourceModule.Initialization.cs create mode 100644 Runtime/Resource/Resource/ResourceModule.Initialization.cs.meta create mode 100644 Runtime/Resource/Resource/ResourceModule.Pool.cs create mode 100644 Runtime/Resource/Resource/ResourceModule.Pool.cs.meta create mode 100644 Runtime/Resource/Resource/ResourceModule.Services.cs create mode 100644 Runtime/Resource/Resource/ResourceModule.Services.cs.meta create mode 100644 Runtime/Resource/Resource/ResourceModule.cs create mode 100644 Runtime/Resource/Resource/ResourceModule.cs.meta create mode 100644 Runtime/Scene.meta create mode 100644 Runtime/Scene/ISceneModule.cs create mode 100644 Runtime/Scene/ISceneModule.cs.meta create mode 100644 Runtime/Scene/SceneComponent.cs create mode 100644 Runtime/Scene/SceneComponent.cs.meta create mode 100644 Runtime/Scene/SceneModule.cs create mode 100644 Runtime/Scene/SceneModule.cs.meta create mode 100644 Runtime/Timer.meta create mode 100644 Runtime/Timer/ITimerModule.cs create mode 100644 Runtime/Timer/ITimerModule.cs.meta create mode 100644 Runtime/Timer/TimerComponent.cs create mode 100644 Runtime/Timer/TimerComponent.cs.meta create mode 100644 Runtime/Timer/TimerModule.cs create mode 100644 Runtime/Timer/TimerModule.cs.meta create mode 100644 Runtime/UI.meta create mode 100644 Runtime/UI/Constant.meta create mode 100644 Runtime/UI/Constant/EPanelLayer.cs create mode 100644 Runtime/UI/Constant/EPanelLayer.cs.meta create mode 100644 Runtime/UI/Constant/Em.cs create mode 100644 Runtime/UI/Constant/Em.cs.meta create mode 100644 Runtime/UI/Constant/UIHolderFactory.cs create mode 100644 Runtime/UI/Constant/UIHolderFactory.cs.meta create mode 100644 Runtime/UI/Constant/UIMetaRegistry.cs create mode 100644 Runtime/UI/Constant/UIMetaRegistry.cs.meta create mode 100644 Runtime/UI/Constant/UIMetadata.cs create mode 100644 Runtime/UI/Constant/UIMetadata.cs.meta create mode 100644 Runtime/UI/Constant/UIMetadataFactory.cs create mode 100644 Runtime/UI/Constant/UIMetadataFactory.cs.meta create mode 100644 Runtime/UI/Constant/UIResRegistry.cs create mode 100644 Runtime/UI/Constant/UIResRegistry.cs.meta create mode 100644 Runtime/UI/Constant/WindowAttribute.cs create mode 100644 Runtime/UI/Constant/WindowAttribute.cs.meta create mode 100644 Runtime/UI/EventListenerProxy.cs create mode 100644 Runtime/UI/EventListenerProxy.cs.meta create mode 100644 Runtime/UI/Manager.meta create mode 100644 Runtime/UI/Manager/IUIModule.cs create mode 100644 Runtime/UI/Manager/IUIModule.cs.meta create mode 100644 Runtime/UI/Manager/UIModule.Block.cs create mode 100644 Runtime/UI/Manager/UIModule.Block.cs.meta create mode 100644 Runtime/UI/Manager/UIModule.Cache.cs create mode 100644 Runtime/UI/Manager/UIModule.Cache.cs.meta create mode 100644 Runtime/UI/Manager/UIModule.Initlize.cs create mode 100644 Runtime/UI/Manager/UIModule.Initlize.cs.meta create mode 100644 Runtime/UI/Manager/UIModule.Open.cs create mode 100644 Runtime/UI/Manager/UIModule.Open.cs.meta create mode 100644 Runtime/UI/Manager/UIModule.cs create mode 100644 Runtime/UI/Manager/UIModule.cs.meta create mode 100644 Runtime/UI/Other.meta create mode 100644 Runtime/UI/Other/UIBlock.cs create mode 100644 Runtime/UI/Other/UIBlock.cs.meta create mode 100644 Runtime/UI/UIBase.meta create mode 100644 Runtime/UI/UIBase/UIBase.Widget.cs create mode 100644 Runtime/UI/UIBase/UIBase.Widget.cs.meta create mode 100644 Runtime/UI/UIBase/UIBase.cs create mode 100644 Runtime/UI/UIBase/UIBase.cs.meta create mode 100644 Runtime/UI/UIBase/UIHolderObjectBase.cs create mode 100644 Runtime/UI/UIBase/UIHolderObjectBase.cs.meta create mode 100644 Runtime/UI/UIBase/UIState.cs create mode 100644 Runtime/UI/UIBase/UIState.cs.meta create mode 100644 Runtime/UI/UIBase/UITabWindow.cs create mode 100644 Runtime/UI/UIBase/UITabWindow.cs.meta create mode 100644 Runtime/UI/UIBase/UIWidget.cs create mode 100644 Runtime/UI/UIBase/UIWidget.cs.meta create mode 100644 Runtime/UI/UIBase/UIWindow.cs create mode 100644 Runtime/UI/UIBase/UIWindow.cs.meta create mode 100644 Runtime/UI/UIComponent.cs create mode 100644 Runtime/UI/UIComponent.cs.meta create mode 100644 package.json create mode 100644 package.json.meta diff --git a/Editor.meta b/Editor.meta new file mode 100644 index 0000000..812fc7f --- /dev/null +++ b/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 623616b6a77c0c048a198b3db98e9824 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/AlicizaX.Framework.Editor.asmdef b/Editor/AlicizaX.Framework.Editor.asmdef new file mode 100644 index 0000000..1a0c8d0 --- /dev/null +++ b/Editor/AlicizaX.Framework.Editor.asmdef @@ -0,0 +1,22 @@ +{ + "name": "AlicizaX.Framework.Editor", + "rootNamespace": "AlicizaX.Framework.Editor", + "references": [ + "GUID:acfef7cabed3b0a42b25edb1cd4fa259", + "GUID:1619e00706139ce488ff80c0daeea8e7", + "GUID:e34a5702dd353724aa315fb8011f08c3", + "GUID:4d1926c9df5b052469a1c63448b7609a", + "GUID:75b6f2078d190f14dbda4a5b747d709c" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Editor/AlicizaX.Framework.Editor.asmdef.meta b/Editor/AlicizaX.Framework.Editor.asmdef.meta new file mode 100644 index 0000000..2c43c17 --- /dev/null +++ b/Editor/AlicizaX.Framework.Editor.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: fb064c8bf96bac94e90d2f39090daa94 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Audio.meta b/Editor/Audio.meta new file mode 100644 index 0000000..fc3cd25 --- /dev/null +++ b/Editor/Audio.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2e7e6217d22087e449ba363fe2c0fe71 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Audio/AudioComponentInspector.cs b/Editor/Audio/AudioComponentInspector.cs new file mode 100644 index 0000000..89c2952 --- /dev/null +++ b/Editor/Audio/AudioComponentInspector.cs @@ -0,0 +1,44 @@ +using System; +using AlicizaX.Audio.Runtime; +using AlicizaX.Editor; +using UnityEditor; + +namespace AlicizaX.Audio.Editor +{ + [CustomEditor(typeof(AudioComponent))] + internal sealed class AudioComponentInspector : GameFrameworkInspector + { + private SerializedProperty m_InstanceRoot = null; + private SerializedProperty m_AudioMixer = null; + private SerializedProperty m_AudioGroupConfigs = null; + + public override void OnInspectorGUI() + { + base.OnInspectorGUI(); + + serializedObject.Update(); + + AudioComponent t = (AudioComponent)target; + + EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode); + { + EditorGUILayout.PropertyField(m_InstanceRoot); + EditorGUILayout.PropertyField(m_AudioMixer); + + EditorGUILayout.PropertyField(m_AudioGroupConfigs, true); + } + EditorGUI.EndDisabledGroup(); + + serializedObject.ApplyModifiedProperties(); + + Repaint(); + } + + private void OnEnable() + { + m_InstanceRoot = serializedObject.FindProperty("m_InstanceRoot"); + m_AudioMixer = serializedObject.FindProperty("m_AudioMixer"); + m_AudioGroupConfigs = serializedObject.FindProperty("m_AudioGroupConfigs"); + } + } +} diff --git a/Editor/Audio/AudioComponentInspector.cs.meta b/Editor/Audio/AudioComponentInspector.cs.meta new file mode 100644 index 0000000..12f8af5 --- /dev/null +++ b/Editor/Audio/AudioComponentInspector.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 886dba87f6324f2c8b9639a9d4947bff +timeCreated: 1737390298 \ No newline at end of file diff --git a/Editor/Debugger.meta b/Editor/Debugger.meta new file mode 100644 index 0000000..f6fa0c8 --- /dev/null +++ b/Editor/Debugger.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 857c20fbb3a13c74eaee070018a8321c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Debugger/Inspector.meta b/Editor/Debugger/Inspector.meta new file mode 100644 index 0000000..b70a600 --- /dev/null +++ b/Editor/Debugger/Inspector.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e98e73807718166469aac777a004bd7e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Debugger/Inspector/DebuggerComponentInspector.cs b/Editor/Debugger/Inspector/DebuggerComponentInspector.cs new file mode 100644 index 0000000..8540b0d --- /dev/null +++ b/Editor/Debugger/Inspector/DebuggerComponentInspector.cs @@ -0,0 +1,64 @@ +using System; +using AlicizaX.Editor; +using UnityEditor; +using UnityEngine; +using AlicizaX.Debugger.Runtime; + +namespace AlicizaX.Debugger.Editor +{ + [CustomEditor(typeof(DebuggerComponent))] + internal sealed class DebuggerComponentInspector : GameFrameworkInspector + { + private SerializedProperty m_Skin = null; + private SerializedProperty m_ActiveWindow = null; + private SerializedProperty m_ShowFullWindow = null; + private SerializedProperty m_ConsoleWindow = null; + + public override void OnInspectorGUI() + { + base.OnInspectorGUI(); + + serializedObject.Update(); + + DebuggerComponent t = (DebuggerComponent)target; + + EditorGUILayout.PropertyField(m_Skin); + + if (EditorApplication.isPlaying && IsPrefabInHierarchy(t.gameObject)) + { + bool activeWindow = EditorGUILayout.Toggle("Active Window", t.ActiveWindow); + if (activeWindow != t.ActiveWindow) + { + t.ActiveWindow = activeWindow; + } + } + else + { + EditorGUILayout.PropertyField(m_ActiveWindow); + } + + EditorGUILayout.PropertyField(m_ShowFullWindow); + + if (EditorApplication.isPlaying) + { + if (GUILayout.Button("Reset Layout")) + { + t.ResetLayout(); + } + } + + EditorGUILayout.PropertyField(m_ConsoleWindow, true); + + serializedObject.ApplyModifiedProperties(); + } + + + private void OnEnable() + { + m_Skin = serializedObject.FindProperty("m_Skin"); + m_ActiveWindow = serializedObject.FindProperty("m_ActiveWindow"); + m_ShowFullWindow = serializedObject.FindProperty("m_ShowFullWindow"); + m_ConsoleWindow = serializedObject.FindProperty("m_ConsoleWindow"); + } + } +} diff --git a/Editor/Debugger/Inspector/DebuggerComponentInspector.cs.meta b/Editor/Debugger/Inspector/DebuggerComponentInspector.cs.meta new file mode 100644 index 0000000..d584a44 --- /dev/null +++ b/Editor/Debugger/Inspector/DebuggerComponentInspector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 346bd5fc6cf0d76478b39f7e69185a23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Debugger/Res.meta b/Editor/Debugger/Res.meta new file mode 100644 index 0000000..806e0ed --- /dev/null +++ b/Editor/Debugger/Res.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f52e94520a8d8544f84756063cf06d42 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Debugger/Res/DebuggerSkin.guiskin b/Editor/Debugger/Res/DebuggerSkin.guiskin new file mode 100644 index 0000000..16bbd46 --- /dev/null +++ b/Editor/Debugger/Res/DebuggerSkin.guiskin @@ -0,0 +1,1427 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 1 + m_Script: {fileID: 12001, guid: 0000000000000000e000000000000000, type: 0} + m_Name: DebuggerSkin + m_EditorClassIdentifier: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_box: + m_Name: box + m_Normal: + m_Background: {fileID: 11001, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.79999995, g: 0.79999995, b: 0.79999995, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 6 + m_Right: 6 + m_Top: 6 + m_Bottom: 6 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 1 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_button: + m_Name: button + m_Normal: + m_Background: {fileID: 11006, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Hover: + m_Background: {fileID: 11003, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Active: + m_Background: {fileID: 11002, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnNormal: + m_Background: {fileID: 11005, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9019608, g: 0.9019608, b: 0.9019608, a: 1} + m_OnHover: + m_Background: {fileID: 11004, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnActive: + m_Background: {fileID: 11002, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 6 + m_Right: 6 + m_Top: 6 + m_Bottom: 4 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 6 + m_Right: 6 + m_Top: 3 + m_Bottom: 3 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 4 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_toggle: + m_Name: toggle + m_Normal: + m_Background: {fileID: 11018, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.89112896, g: 0.89112896, b: 0.89112896, a: 1} + m_Hover: + m_Background: {fileID: 11014, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Active: + m_Background: {fileID: 11013, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 11016, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.8901961, g: 0.8901961, b: 0.8901961, a: 1} + m_OnHover: + m_Background: {fileID: 11015, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnActive: + m_Background: {fileID: 11017, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 14 + m_Right: 0 + m_Top: 14 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 15 + m_Right: 0 + m_Top: 3 + m_Bottom: 0 + m_Overflow: + m_Left: -1 + m_Right: 0 + m_Top: -4 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_label: + m_Name: label + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 3 + m_Bottom: 3 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 1 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_textField: + m_Name: textfield + m_Normal: + m_Background: {fileID: 11024, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.79999995, g: 0.79999995, b: 0.79999995, a: 1} + m_Hover: + m_Background: {fileID: 11026, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9, g: 0.9, b: 0.9, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 11026, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnNormal: + m_Background: {fileID: 11025, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 3 + m_Right: 3 + m_Top: 3 + m_Bottom: 3 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 0 + m_TextClipping: 1 + m_ImagePosition: 3 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_textArea: + m_Name: textarea + m_Normal: + m_Background: {fileID: 11024, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.9019608, g: 0.9019608, b: 0.9019608, a: 1} + m_Hover: + m_Background: {fileID: 11026, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0.79999995, g: 0.79999995, b: 0.79999995, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 11025, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 3 + m_Right: 3 + m_Top: 3 + m_Bottom: 3 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 1 + m_RichText: 0 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_window: + m_Name: window + m_Normal: + m_Background: {fileID: 11023, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 11022, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 1, g: 1, b: 1, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 8 + m_Right: 8 + m_Top: 18 + m_Bottom: 8 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 10 + m_Right: 10 + m_Top: 20 + m_Bottom: 10 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 1 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: -18} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalSlider: + m_Name: horizontalslider + m_Normal: + m_Background: {fileID: 11009, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 3 + m_Right: 3 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: -1 + m_Right: -1 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: -2 + m_Bottom: -3 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 12 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalSliderThumb: + m_Name: horizontalsliderthumb + m_Normal: + m_Background: {fileID: 11011, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 11012, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 11010, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 4 + m_Right: 4 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 7 + m_Right: 7 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: -1 + m_Right: -1 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 12 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalSlider: + m_Name: verticalslider + m_Normal: + m_Background: {fileID: 11021, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 3 + m_Bottom: 3 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: -1 + m_Bottom: -1 + m_Overflow: + m_Left: -2 + m_Right: -3 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 0 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 12 + m_FixedHeight: 0 + m_StretchWidth: 0 + m_StretchHeight: 1 + m_verticalSliderThumb: + m_Name: verticalsliderthumb + m_Normal: + m_Background: {fileID: 11011, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 11012, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 11010, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 7 + m_Bottom: 7 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: -1 + m_Bottom: -1 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 12 + m_FixedHeight: 0 + m_StretchWidth: 0 + m_StretchHeight: 1 + m_horizontalScrollbar: + m_Name: horizontalscrollbar + m_Normal: + m_Background: {fileID: 11008, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 9 + m_Right: 9 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 4 + m_Right: 4 + m_Top: 1 + m_Bottom: 4 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 15 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalScrollbarThumb: + m_Name: horizontalscrollbarthumb + m_Normal: + m_Background: {fileID: 11007, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 6 + m_Right: 6 + m_Top: 6 + m_Bottom: 6 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 6 + m_Right: 6 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: -1 + m_Bottom: 1 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 13 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalScrollbarLeftButton: + m_Name: horizontalscrollbarleftbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_horizontalScrollbarRightButton: + m_Name: horizontalscrollbarrightbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalScrollbar: + m_Name: verticalscrollbar + m_Normal: + m_Background: {fileID: 11020, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 9 + m_Bottom: 9 + m_Margin: + m_Left: 1 + m_Right: 4 + m_Top: 4 + m_Bottom: 4 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 1 + m_Bottom: 1 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 15 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalScrollbarThumb: + m_Name: verticalscrollbarthumb + m_Normal: + m_Background: {fileID: 11019, guid: 0000000000000000e000000000000000, type: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 6 + m_Right: 6 + m_Top: 6 + m_Bottom: 6 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 6 + m_Bottom: 6 + m_Overflow: + m_Left: -1 + m_Right: -1 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 2 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 15 + m_FixedHeight: 0 + m_StretchWidth: 0 + m_StretchHeight: 1 + m_verticalScrollbarUpButton: + m_Name: verticalscrollbarupbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_verticalScrollbarDownButton: + m_Name: verticalscrollbardownbutton + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_ScrollView: + m_Name: scrollview + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 1 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_CustomStyles: + - m_Name: + m_Normal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Hover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Active: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Focused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnNormal: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnHover: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnActive: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_OnFocused: + m_Background: {fileID: 0} + m_ScaledBackgrounds: [] + m_TextColor: {r: 0, g: 0, b: 0, a: 1} + m_Border: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Margin: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Padding: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Overflow: + m_Left: 0 + m_Right: 0 + m_Top: 0 + m_Bottom: 0 + m_Font: {fileID: 0} + m_FontSize: 0 + m_FontStyle: 0 + m_Alignment: 0 + m_WordWrap: 0 + m_RichText: 1 + m_TextClipping: 0 + m_ImagePosition: 0 + m_ContentOffset: {x: 0, y: 0} + m_FixedWidth: 0 + m_FixedHeight: 0 + m_StretchWidth: 1 + m_StretchHeight: 0 + m_Settings: + m_DoubleClickSelectsWord: 1 + m_TripleClickSelectsLine: 1 + m_CursorColor: {r: 1, g: 1, b: 1, a: 1} + m_CursorFlashSpeed: -1 + m_SelectionColor: {r: 1, g: 0.38403907, b: 0, a: 0.7} diff --git a/Editor/Debugger/Res/DebuggerSkin.guiskin.meta b/Editor/Debugger/Res/DebuggerSkin.guiskin.meta new file mode 100644 index 0000000..53d8507 --- /dev/null +++ b/Editor/Debugger/Res/DebuggerSkin.guiskin.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dce698819fdb70b42b393d9b0b6d420e +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/FSM.meta b/Editor/FSM.meta new file mode 100644 index 0000000..19739d0 --- /dev/null +++ b/Editor/FSM.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: db3480ef83bb9664ca5e816ef897fbbf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/FSM/FsmDebuggerWindow.cs b/Editor/FSM/FsmDebuggerWindow.cs new file mode 100644 index 0000000..c7da860 --- /dev/null +++ b/Editor/FSM/FsmDebuggerWindow.cs @@ -0,0 +1,121 @@ +// ===================== FSMDebugger Window (unchanged API, auto-binding now) ===================== + +using AlicizaX.Fsm; +using UnityEditor; +using UnityEngine; + +internal class FsmDebuggerWindow : EditorWindow +{ + [MenuItem("Window/HighPerfFSM Debugger")] + public static void ShowWindow() + { + var win = GetWindow("FSM Debugger"); + win.Show(); + } + + private Vector2 _scroll; + private bool _autoRefresh = true; + + private void OnEnable() + { + FSMDebugger.SetEnabled(true); + EditorApplication.update += RepaintIfNeeded; + } + + private void OnDisable() + { + EditorApplication.update -= RepaintIfNeeded; + } + + private void RepaintIfNeeded() + { + if (_autoRefresh) Repaint(); + } + + private void OnGUI() + { + using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar)) + { + GUILayout.Label("HighPerfFSM Debugger", EditorStyles.boldLabel); + GUILayout.FlexibleSpace(); + _autoRefresh = GUILayout.Toggle(_autoRefresh, "Auto Refresh", EditorStyles.toolbarButton); + } + + _scroll = EditorGUILayout.BeginScrollView(_scroll); + + foreach (var kv in FSMDebugger.Entries) + { + var entry = kv.Value; + EditorGUILayout.BeginVertical("box"); + + using (new EditorGUILayout.HorizontalScope()) + { + GUILayout.Label($"ID: {entry.Id}", GUILayout.Width(90)); + GUILayout.Label(entry.Name, EditorStyles.boldLabel); + GUILayout.FlexibleSpace(); + } + + var stateName = (entry.StateNameGetter != null) + ? entry.StateNameGetter(entry.StateIndex) ?? entry.StateIndex.ToString() + : entry.StateIndex.ToString(); + + EditorGUILayout.LabelField("State", stateName); + EditorGUILayout.LabelField("TimeInState", entry.TimeInState.ToString("F3")); + + if (entry.BlackboardGetter != null && entry.Fields != null) + { + object bbObj = null; + try + { + bbObj = entry.BlackboardGetter(); + } + catch + { + } + + if (bbObj != null) + { + EditorGUILayout.LabelField("Blackboard"); + EditorGUI.indentLevel++; + for (int i = 0; i < entry.Fields.Length; i++) + { + var f = entry.Fields[i]; + object val = null; + try + { + val = f.GetValue(bbObj); + } + catch + { + } + + EditorGUILayout.LabelField(f.Name, val != null ? val.ToString() : "null"); + } + + EditorGUI.indentLevel--; + } + else + { + EditorGUILayout.HelpBox("Blackboard getter returned null.", MessageType.Info); + } + } + + if (entry.Owner != null && entry.Owner.TryGetTarget(out var owner) && owner != null) + { + EditorGUILayout.ObjectField("Owner", owner, typeof(UnityEngine.Object), true); + } + + EditorGUILayout.EndVertical(); + GUILayout.Space(4); + } + + EditorGUILayout.EndScrollView(); + + if (GUILayout.Button("Clear All (Unregister)")) + { + var ids = new System.Collections.Generic.List(); + foreach (var kv in FSMDebugger.Entries) ids.Add(kv.Key); + foreach (var id in ids) FSMDebugger.Unregister(id); + } + } +} diff --git a/Editor/FSM/FsmDebuggerWindow.cs.meta b/Editor/FSM/FsmDebuggerWindow.cs.meta new file mode 100644 index 0000000..4d6c854 --- /dev/null +++ b/Editor/FSM/FsmDebuggerWindow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b7e760f8a07d441dba01f9a5bf8fff62 +timeCreated: 1745562869 \ No newline at end of file diff --git a/Editor/Resource.meta b/Editor/Resource.meta new file mode 100644 index 0000000..b2b53b4 --- /dev/null +++ b/Editor/Resource.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b34974735d949c545b6b2b60f4d96355 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Resource/Encryption.meta b/Editor/Resource/Encryption.meta new file mode 100644 index 0000000..4a78927 --- /dev/null +++ b/Editor/Resource/Encryption.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1ad0cb4b2fbda55409ca1d4a66e6d4f0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Resource/Encryption/DefaultEncryption.cs b/Editor/Resource/Encryption/DefaultEncryption.cs new file mode 100644 index 0000000..246d0dc --- /dev/null +++ b/Editor/Resource/Encryption/DefaultEncryption.cs @@ -0,0 +1,50 @@ +using AlicizaX.Resource.Runtime; + +namespace AlicizaX.Resource.Editor +{ + using System; + using System.IO; + using YooAsset; + + /// + /// 文件偏移加密方式 + /// + public class FileOffsetEncryption : IEncryptionServices + { + public EncryptResult Encrypt(EncryptFileInfo fileInfo) + { + int offset = 32; + byte[] fileData = File.ReadAllBytes(fileInfo.FileLoadPath); + var encryptedData = new byte[fileData.Length + offset]; + Buffer.BlockCopy(fileData, 0, encryptedData, offset, fileData.Length); + + EncryptResult result = new EncryptResult(); + result.Encrypted = true; + result.EncryptedData = encryptedData; + return result; + } + } + + + /// + /// 文件流加密方式 + /// + public class FileStreamEncryption : IEncryptionServices + { + public const byte KEY = 64; + + public EncryptResult Encrypt(EncryptFileInfo fileInfo) + { + var fileData = File.ReadAllBytes(fileInfo.FileLoadPath); + for (int i = 0; i < fileData.Length; i++) + { + fileData[i] ^= BundleStream.KEY; + } + + EncryptResult result = new EncryptResult(); + result.Encrypted = true; + result.EncryptedData = fileData; + return result; + } + } +} diff --git a/Editor/Resource/Encryption/DefaultEncryption.cs.meta b/Editor/Resource/Encryption/DefaultEncryption.cs.meta new file mode 100644 index 0000000..7233121 --- /dev/null +++ b/Editor/Resource/Encryption/DefaultEncryption.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 39b42829cd724f10999caec280e260f7 +timeCreated: 1737867622 \ No newline at end of file diff --git a/Editor/Resource/Inspector.meta b/Editor/Resource/Inspector.meta new file mode 100644 index 0000000..b6f3339 --- /dev/null +++ b/Editor/Resource/Inspector.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b27fb2c235a240e8a3d034a23c0c5323 +timeCreated: 1737441909 \ No newline at end of file diff --git a/Editor/Resource/Inspector/ResourceComponentInspector.cs b/Editor/Resource/Inspector/ResourceComponentInspector.cs new file mode 100644 index 0000000..fb50c9c --- /dev/null +++ b/Editor/Resource/Inspector/ResourceComponentInspector.cs @@ -0,0 +1,264 @@ +using System; +using System.Collections.Generic; +using AlicizaX.Editor; +using AlicizaX.Resource.Runtime; +using UnityEditor; +using YooAsset; +using YooAsset.Editor; + +namespace AlicizaX.Resource.Editor +{ + [CustomEditor(typeof(ResourceComponent))] + internal sealed class ResourceComponentInspector : GameFrameworkInspector + { + private SerializedProperty _milliseconds = null; + private SerializedProperty _minUnloadUnusedAssetsInterval = null; + private SerializedProperty _maxUnloadUnusedAssetsInterval = null; + private SerializedProperty _useSystemUnloadUnusedAssets = null; + private SerializedProperty _assetAutoReleaseInterval = null; + private SerializedProperty _assetCapacity = null; + private SerializedProperty _assetExpireTime = null; + private SerializedProperty _assetPriority = null; + private SerializedProperty _downloadingMaxNum = null; + private SerializedProperty _failedTryAgain = null; + private SerializedProperty _packageName = null; + private SerializedProperty _decryptionServices = null; + + private int _packageNameIndex = 0; + private string[] _packageNames; + + private List m_DecryptionServicesTypeName = new(); + private int m_DecryptionSelectIndex; + + public override void OnInspectorGUI() + { + base.OnInspectorGUI(); + + serializedObject.Update(); + + ResourceComponent t = (ResourceComponent)target; + + m_DecryptionSelectIndex = EditorGUILayout.Popup("解密服务", m_DecryptionSelectIndex, m_DecryptionServicesTypeName.ToArray()); + string selectService = m_DecryptionServicesTypeName[m_DecryptionSelectIndex]; + if (_decryptionServices.stringValue != selectService) + { + string serviceName = selectService.Equals(NoneOptionName) ? string.Empty : selectService; + _decryptionServices.stringValue = serviceName; + } + + + _packageNames = GetBuildPackageNames().ToArray(); + _packageNameIndex = Array.IndexOf(_packageNames, _packageName.stringValue); + if (_packageNameIndex < 0) + { + _packageNameIndex = 0; + } + + _packageNameIndex = EditorGUILayout.Popup("Package Name", _packageNameIndex, _packageNames); + if (_packageName.stringValue != _packageNames[_packageNameIndex]) + { + _packageName.stringValue = _packageNames[_packageNameIndex]; + } + + int milliseconds = EditorGUILayout.DelayedIntField("Milliseconds", _milliseconds.intValue); + if (milliseconds != _milliseconds.intValue) + { + if (EditorApplication.isPlaying) + { + t.milliseconds = milliseconds; + } + else + { + _milliseconds.longValue = milliseconds; + } + } + + EditorGUILayout.PropertyField(_useSystemUnloadUnusedAssets); + + float minUnloadUnusedAssetsInterval = + EditorGUILayout.Slider("Min Unload Unused Assets Interval", _minUnloadUnusedAssetsInterval.floatValue, 0f, 3600f); + if (Math.Abs(minUnloadUnusedAssetsInterval - _minUnloadUnusedAssetsInterval.floatValue) > 0.01f) + { + if (EditorApplication.isPlaying) + { + t.MinUnloadUnusedAssetsInterval = minUnloadUnusedAssetsInterval; + } + else + { + _minUnloadUnusedAssetsInterval.floatValue = minUnloadUnusedAssetsInterval; + } + } + + float maxUnloadUnusedAssetsInterval = + EditorGUILayout.Slider("Max Unload Unused Assets Interval", _maxUnloadUnusedAssetsInterval.floatValue, 0f, 3600f); + if (Math.Abs(maxUnloadUnusedAssetsInterval - _maxUnloadUnusedAssetsInterval.floatValue) > 0.01f) + { + if (EditorApplication.isPlaying) + { + t.MaxUnloadUnusedAssetsInterval = maxUnloadUnusedAssetsInterval; + } + else + { + _maxUnloadUnusedAssetsInterval.floatValue = maxUnloadUnusedAssetsInterval; + } + } + + float downloadingMaxNum = EditorGUILayout.Slider("Max Downloading Num", _downloadingMaxNum.intValue, 1f, 48f); + if (Math.Abs(downloadingMaxNum - _downloadingMaxNum.intValue) > 0.001f) + { + if (EditorApplication.isPlaying) + { + t.DownloadingMaxNum = (int)downloadingMaxNum; + } + else + { + _downloadingMaxNum.intValue = (int)downloadingMaxNum; + } + } + + float failedTryAgain = EditorGUILayout.Slider("Max FailedTryAgain Count", _failedTryAgain.intValue, 1f, 48f); + if (Math.Abs(failedTryAgain - _failedTryAgain.intValue) > 0.001f) + { + if (EditorApplication.isPlaying) + { + t.FailedTryAgain = (int)failedTryAgain; + } + else + { + _failedTryAgain.intValue = (int)failedTryAgain; + } + } + + EditorGUI.BeginDisabledGroup(EditorApplication.isPlaying); + { + float assetAutoReleaseInterval = EditorGUILayout.DelayedFloatField("Asset Auto Release Interval", _assetAutoReleaseInterval.floatValue); + if (Math.Abs(assetAutoReleaseInterval - _assetAutoReleaseInterval.floatValue) > 0.01f) + { + if (EditorApplication.isPlaying) + { + t.AssetAutoReleaseInterval = assetAutoReleaseInterval; + } + else + { + _assetAutoReleaseInterval.floatValue = assetAutoReleaseInterval; + } + } + + int assetCapacity = EditorGUILayout.DelayedIntField("Asset Capacity", _assetCapacity.intValue); + if (assetCapacity != _assetCapacity.intValue) + { + if (EditorApplication.isPlaying) + { + t.AssetCapacity = assetCapacity; + } + else + { + _assetCapacity.intValue = assetCapacity; + } + } + + float assetExpireTime = EditorGUILayout.DelayedFloatField("Asset Expire Time", _assetExpireTime.floatValue); + if (Math.Abs(assetExpireTime - _assetExpireTime.floatValue) > 0.01f) + { + if (EditorApplication.isPlaying) + { + t.AssetExpireTime = assetExpireTime; + } + else + { + _assetExpireTime.floatValue = assetExpireTime; + } + } + + int assetPriority = EditorGUILayout.DelayedIntField("Asset Priority", _assetPriority.intValue); + if (assetPriority != _assetPriority.intValue) + { + if (EditorApplication.isPlaying) + { + t.AssetPriority = assetPriority; + } + else + { + _assetPriority.intValue = assetPriority; + } + } + } + EditorGUI.EndDisabledGroup(); + + if (EditorApplication.isPlaying && IsPrefabInHierarchy(t.gameObject)) + { + EditorGUILayout.LabelField("Unload Unused Assets", + AlicizaX.Utility.Text.Format("{0:F2} / {1:F2}", t.LastUnloadUnusedAssetsOperationElapseSeconds, t.MaxUnloadUnusedAssetsInterval)); + EditorGUILayout.LabelField("Applicable Game Version", t.ApplicableGameVersion ?? ""); + } + + serializedObject.ApplyModifiedProperties(); + + Repaint(); + } + + protected override void OnCompileComplete() + { + base.OnCompileComplete(); + + RefreshTypeNames(); + } + + private void OnEnable() + { + + _milliseconds = serializedObject.FindProperty("milliseconds"); + _minUnloadUnusedAssetsInterval = serializedObject.FindProperty("minUnloadUnusedAssetsInterval"); + _maxUnloadUnusedAssetsInterval = serializedObject.FindProperty("maxUnloadUnusedAssetsInterval"); + _useSystemUnloadUnusedAssets = serializedObject.FindProperty("useSystemUnloadUnusedAssets"); + _assetAutoReleaseInterval = serializedObject.FindProperty("assetAutoReleaseInterval"); + _assetCapacity = serializedObject.FindProperty("assetCapacity"); + _assetExpireTime = serializedObject.FindProperty("assetExpireTime"); + _assetPriority = serializedObject.FindProperty("assetPriority"); + _downloadingMaxNum = serializedObject.FindProperty("downloadingMaxNum"); + _failedTryAgain = serializedObject.FindProperty("failedTryAgain"); + _packageName = serializedObject.FindProperty("packageName"); + _decryptionServices = serializedObject.FindProperty("_decryptionServices"); + RefreshDecryptionServices(); + RefreshTypeNames(); + } + + + private void RefreshDecryptionServices() + { + m_DecryptionServicesTypeName = new List + { + NoneOptionName + }; + + m_DecryptionServicesTypeName.AddRange(AlicizaX.Utility.Assembly.GetRuntimeTypeNames(typeof(IDecryptionServices))); + + m_DecryptionSelectIndex = m_DecryptionServicesTypeName.IndexOf(_decryptionServices.stringValue); + if (m_DecryptionSelectIndex < 0) + { + m_DecryptionSelectIndex = 0; + } + } + + + private void RefreshTypeNames() + { + serializedObject.ApplyModifiedProperties(); + } + + /// + /// 获取构建包名称列表,用于下拉可选择 + /// + /// + private List GetBuildPackageNames() + { + List result = new List(); + foreach (var package in AssetBundleCollectorSettingData.Setting.Packages) + { + result.Add(package.PackageName); + } + + return result; + } + } +} diff --git a/Editor/Resource/Inspector/ResourceComponentInspector.cs.meta b/Editor/Resource/Inspector/ResourceComponentInspector.cs.meta new file mode 100644 index 0000000..1db3b5a --- /dev/null +++ b/Editor/Resource/Inspector/ResourceComponentInspector.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dfe8d0fdb8d74e33a9b051644dd94676 +timeCreated: 1737458008 \ No newline at end of file diff --git a/Editor/Resource/Inspector/ResourceExtComponentInspector.cs b/Editor/Resource/Inspector/ResourceExtComponentInspector.cs new file mode 100644 index 0000000..d1cb7b1 --- /dev/null +++ b/Editor/Resource/Inspector/ResourceExtComponentInspector.cs @@ -0,0 +1,59 @@ +// using System; +// using AlicizaX.Editor; +// using AlicizaX.Resource.Runtime; +// using UnityEditor; +// using UnityEngine; +// +// namespace AlicizaX.Resource.Editor +// { +// [CustomEditor(typeof(ResourceExtComponent))] +// internal sealed class ResourceExtComponentInspector : GameFrameworkInspector +// { +// private SerializedProperty m_CheckCanReleaseInterval = null; +// private SerializedProperty m_AutoReleaseInterval = null; +// +// public override void OnInspectorGUI() +// { +// base.OnInspectorGUI(); +// serializedObject.Update(); +// +// ResourceExtComponent t = (ResourceExtComponent)target; +// +// EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode); +// { +// EditorGUILayout.BeginVertical("box"); +// { +// float checkCanReleaseInterval = EditorGUILayout.Slider("检查释放间隔", m_CheckCanReleaseInterval.floatValue, 30, 460); +// if (m_CheckCanReleaseInterval.floatValue != checkCanReleaseInterval) +// { +// m_CheckCanReleaseInterval.floatValue = checkCanReleaseInterval; +// } +// +// +// float autoReleaseInterval = EditorGUILayout.Slider("对象池释放间隔", m_AutoReleaseInterval.floatValue, 60, 1800); +// if (m_AutoReleaseInterval.floatValue != autoReleaseInterval) +// { +// m_AutoReleaseInterval.floatValue = autoReleaseInterval; +// } +// } +// +// if (GUILayout.Button("ReleaseUnused")) +// { +// t.ReleaseUnused(); +// } +// +// +// EditorGUILayout.EndVertical(); +// } +// EditorGUI.EndDisabledGroup(); +// +// serializedObject.ApplyModifiedProperties(); +// } +// +// private void OnEnable() +// { +// m_CheckCanReleaseInterval = serializedObject.FindProperty("m_CheckCanReleaseInterval"); +// m_AutoReleaseInterval = serializedObject.FindProperty("m_AutoReleaseInterval"); +// } +// } +// } \ No newline at end of file diff --git a/Editor/Resource/Inspector/ResourceExtComponentInspector.cs.meta b/Editor/Resource/Inspector/ResourceExtComponentInspector.cs.meta new file mode 100644 index 0000000..a5c815d --- /dev/null +++ b/Editor/Resource/Inspector/ResourceExtComponentInspector.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f9278b79811545c38cc9edf00d666bee +timeCreated: 1737441939 \ No newline at end of file diff --git a/Editor/UI.meta b/Editor/UI.meta new file mode 100644 index 0000000..34e2b3a --- /dev/null +++ b/Editor/UI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4a717b10b37856e43972776bbf4cc0ff +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/UI/GenerateTool.meta b/Editor/UI/GenerateTool.meta new file mode 100644 index 0000000..8982afb --- /dev/null +++ b/Editor/UI/GenerateTool.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d9940ca83fab4087ab1116f5a2ea01a4 +timeCreated: 1739274233 \ No newline at end of file diff --git a/Editor/UI/GenerateTool/UIGenerateEditorTool.cs b/Editor/UI/GenerateTool/UIGenerateEditorTool.cs new file mode 100644 index 0000000..47eff80 --- /dev/null +++ b/Editor/UI/GenerateTool/UIGenerateEditorTool.cs @@ -0,0 +1,21 @@ +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace AlicizaX.UI.Editor +{ + public static class UIGenerateEditorTool + { + [MenuItem("GameObject/UI工具/热更工程UI代码", priority = -1)] + public static void GenerateHotfixUIScript() + { + UIGenerateWindow.ShowWindow(Selection.gameObjects.FirstOrDefault(),UIGenerateConfiguration.Instance.UIScriptGenerateConfig.HotFixProjectUIScriptGenerateData); + } + + [MenuItem("GameObject/UI工具/主工程UI代码", priority = -1)] + public static void GenerateMainUIScript() + { + UIGenerateWindow.ShowWindow(Selection.gameObjects.FirstOrDefault(),UIGenerateConfiguration.Instance.UIScriptGenerateConfig.MainProjectUIScriptGenerateData); + } + } +} diff --git a/Editor/UI/GenerateTool/UIGenerateEditorTool.cs.meta b/Editor/UI/GenerateTool/UIGenerateEditorTool.cs.meta new file mode 100644 index 0000000..4d47bb2 --- /dev/null +++ b/Editor/UI/GenerateTool/UIGenerateEditorTool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8df5224864804bc3a206ab20e5024e5c +timeCreated: 1739274266 \ No newline at end of file diff --git a/Editor/UI/GenerateTool/UIGenerateEditorWindow.cs b/Editor/UI/GenerateTool/UIGenerateEditorWindow.cs new file mode 100644 index 0000000..f100aca --- /dev/null +++ b/Editor/UI/GenerateTool/UIGenerateEditorWindow.cs @@ -0,0 +1,42 @@ +using UnityEditor; +using UnityEngine; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using AlicizaX.UI.Editor; +using AlicizaX.UI.Runtime; +using UnityEngine.UIElements; + +public class UIGenerateWindow : EditorWindow +{ + private GameObject targetObject; + + + public static GameObject GetTargetObject() + { + return GetWindow().targetObject; + } + + public static void ShowWindow(GameObject target, UIScriptGenerateData scriptGenerateData) + { + var window = GetWindow(false, "UI Config Editor", false); + window.maxSize = Vector2.zero; + window.minSize = Vector2.zero; + window.rootVisualElement.style.display = DisplayStyle.None; + window.rootVisualElement.parent.style.display = DisplayStyle.None; + window.Initlize(target, scriptGenerateData); + } + + public static void CloseWindow() + { + var window = GetWindow(false, "UI Config Editor"); + window.Close(); + } + + private void Initlize(GameObject target, UIScriptGenerateData scriptGenerateData) + { + targetObject = target; + UIScriptGeneratorHelper.GenerateAndAttachScript(targetObject, scriptGenerateData); + } +} diff --git a/Editor/UI/GenerateTool/UIGenerateEditorWindow.cs.meta b/Editor/UI/GenerateTool/UIGenerateEditorWindow.cs.meta new file mode 100644 index 0000000..d3d2173 --- /dev/null +++ b/Editor/UI/GenerateTool/UIGenerateEditorWindow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7bfa0d164c11486cb3a133829d53dccf +timeCreated: 1739339632 \ No newline at end of file diff --git a/Editor/UI/GenerateTool/UIScriptGeneratorHelper.cs b/Editor/UI/GenerateTool/UIScriptGeneratorHelper.cs new file mode 100644 index 0000000..5760bf9 --- /dev/null +++ b/Editor/UI/GenerateTool/UIScriptGeneratorHelper.cs @@ -0,0 +1,469 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using AlicizaX.UI.Editor; +using AlicizaX.UI.Runtime; +using Sirenix.Utilities.Editor; +using UnityEngine; +using UnityEditor; +using UnityEditor.Callbacks; +using UnityEngine.UI; + +namespace AlicizaX.UI.Editor +{ + enum EBindType + { + None, + Widget, + ListCom, + } + + [Serializable] + class UIBindData + { + public string Name; + public List BindCom; + public EBindType BindType; + + public UIBindData(string name, List bindCom, EBindType bindType = EBindType.None) + { + Name = name; + BindCom = bindCom; + BindType = bindType; + } + + public UIBindData(string name, Component bindCom, EBindType bindType = EBindType.None) + { + Name = name; + BindCom = new List() { bindCom }; + BindType = bindType; + } + } + + + public static class UIScriptGeneratorHelper + { + private static UIGenerateConfiguration _uiGenerateConfiguration; + + static UIGenerateConfiguration UIGenerateConfiguration + { + get + { + if (_uiGenerateConfiguration == null) + { + _uiGenerateConfiguration = UIGenerateConfiguration.Instance; + } + + return _uiGenerateConfiguration; + } + } + + private static string GetVerType(string uiName) + { + foreach (var pair in UIGenerateConfiguration.UIElementRegexConfigs) + { + if (uiName.StartsWith(pair.uiElementRegex)) + { + return pair.componentType; + } + } + + return string.Empty; + } + + private static string[] SplitComName(string name) + { + bool hasCom = name.Contains(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName); + if (!hasCom) return null; + + string comStr = name.Substring(0, name.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName)); + return comStr.Split(UIGenerateConfiguration.UIGenerateCommonData.ComCheckSplitName); + } + + + private static string GetKeyName(string key, string componentName) + { + return $"{key}{componentName.Substring(componentName.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName) + 1)}"; + } + + private static List UIBindDatas = new List(); + private static string GenerateNameSpace = string.Empty; + + private static List ArrayComs = new List(); + + private static void GetBindData(Transform root) + { + for (int i = 0; i < root.childCount; ++i) + { + Transform child = root.GetChild(i); + + bool hasWdiget = child.GetComponent() != null; + + if (UIGenerateConfiguration.UIGenerateCommonData.ExcludeKeywords.Any(k => child.name.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0)) continue; + + bool isArrayComs = child.name.StartsWith(UIGenerateConfiguration.UIGenerateCommonData.ArrayComSplitName); + if (hasWdiget) + { + CollectWidget(child); + } + else if (isArrayComs) + { + string splitCode = UIGenerateConfiguration.UIGenerateCommonData.ArrayComSplitName; + int lastIndex = child.name.LastIndexOf(splitCode); + string text = child.name.Substring(child.name.IndexOf(splitCode) + 1, lastIndex - 1); + if (ArrayComs.Contains(text)) continue; + ArrayComs.Add(text); + List arrayComs = new List(); + for (int j = 0; j < root.childCount; j++) + { + if (root.GetChild(j).name.Contains(text)) + { + arrayComs.Add(root.GetChild(j)); + } + } + + CollectArrayComponent(arrayComs, text); + } + else if (!isArrayComs && !hasWdiget) + { + CollectComponent(child); + GetBindData(child); + } + } + } + + private static void CollectComponent(Transform node) + { + string[] comArray = SplitComName(node.name); + if (comArray != null) + { + foreach (var com in comArray) + { + string typeName = GetVerType(com); + if (string.IsNullOrEmpty(typeName)) continue; + + Component component = node.GetComponent(typeName); + if (component != null) + { + string keyName = GetKeyName(com, node.name); + if (UIBindDatas.Exists(a => a.Name == keyName)) + { + Debug.LogError($"Duplicate key found: {keyName}"); + continue; + } + + UIBindDatas.Add(new UIBindData(keyName, component)); + } + else + { + Debug.LogError($"{node.name} does not have component of type {typeName}"); + } + } + } + } + + private static void CollectWidget(Transform node) + { + if (node.name.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName) != -1 && node.name.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckSplitName) != -1) + { + Debug.LogWarning($"{node.name} 子组件不能包含规则定义符号!"); + return; + } + + UIHolderObjectBase component = node.GetComponent(); + string keyName = node.name; + if (UIBindDatas.Exists(a => a.Name == keyName)) + { + Debug.LogError($"Duplicate key found: {keyName}"); + return; + } + + UIBindDatas.Add(new UIBindData(keyName, component, EBindType.Widget)); + } + + private static void CollectArrayComponent(List arrayNode, string nodeName) + { + string[] comArray = SplitComName(nodeName); + arrayNode = arrayNode.OrderBy(s => int.Parse(s.name.Split('*').Last())).ToList(); + List tempBindDatas = new List(comArray.Length); + + if (comArray != null) + { + int index = 0; + foreach (var com in comArray) + { + foreach (var node in arrayNode) + { + string typeName = GetVerType(com); + if (string.IsNullOrEmpty(typeName)) continue; + + Component component = node.GetComponent(typeName); + if (component != null) + { + string keyName = GetKeyName(com, nodeName) + "List"; + if (tempBindDatas.Count - 1 < index) tempBindDatas.Add(new UIBindData(keyName, new List(), EBindType.ListCom)); + tempBindDatas[index].BindCom.Add(component); + } + else + { + Debug.LogError($"{node.name} does not have component of type {typeName}"); + } + } + + index++; + } + } + + UIBindDatas.AddRange(tempBindDatas.ToArray()); + } + + + private static string GetRefrenceNameSpace() + { + StringBuilder refrenceNameSpaceBuilder = new StringBuilder(); + HashSet nameSpaces = new HashSet(); + nameSpaces.Add("UnityEngine"); + refrenceNameSpaceBuilder.Append($"using UnityEngine;\n"); + foreach (var bindData in UIBindDatas) + { + string nameSpace = bindData.BindCom.FirstOrDefault().GetType().Namespace; + if (bindData.BindType == EBindType.ListCom) + { + if (!nameSpaces.Contains("using System.Collections.Generic;")) + { + refrenceNameSpaceBuilder.Append("using System.Collections.Generic;\n"); + } + } + + if (!nameSpaces.Contains(nameSpace) && !string.IsNullOrEmpty(nameSpace)) + { + nameSpaces.Add(nameSpace); + refrenceNameSpaceBuilder.Append($"using {nameSpace};\n"); + } + } + + return refrenceNameSpaceBuilder.ToString(); + } + + private static string GetVarText(List uiBindDatas) + { + StringBuilder varTextBuilder = new StringBuilder(); + foreach (var bindData in uiBindDatas) + { + var varName = bindData.Name; + varTextBuilder.Append("\t\t[SerializeField]\n"); + varTextBuilder.Append("\t\t[ReadOnly]\n"); + varTextBuilder.Append("\t\t[HideLabel]\n"); + + if (bindData.BindType == EBindType.None) + { + varTextBuilder.Append($"\t\tprivate {bindData.BindCom.FirstOrDefault().GetType().Name} m{varName};\n"); + varTextBuilder.Append($"\t\tpublic {bindData.BindCom.FirstOrDefault().GetType().Name} {varName} => m{varName};\n\n"); + } + else if (bindData.BindType == EBindType.ListCom) + { + varTextBuilder.Append($"\t\tprivate {bindData.BindCom.FirstOrDefault().GetType().Name} [] m{varName} = new {bindData.BindCom.FirstOrDefault().GetType().Name}[{bindData.BindCom.Count}];\n"); + varTextBuilder.Append($"\t\tpublic {bindData.BindCom.FirstOrDefault().GetType().Name} [] {varName} => m{varName};\n\n"); + } + else if (bindData.BindType == EBindType.Widget) + { + varTextBuilder.Append($"\t\tprivate {bindData.BindCom.FirstOrDefault().GetType().Name} m{varName};\n"); + varTextBuilder.Append($"\t\tpublic {bindData.BindCom.FirstOrDefault().GetType().Name} {varName} => m{varName};\n\n"); + } + } + + return varTextBuilder.ToString(); + } + + private static string GenerateScript(string className) + { + StringBuilder scriptBuilder = new StringBuilder(); + scriptBuilder.Append(GetRefrenceNameSpace()); + + scriptBuilder.Append("using Sirenix.OdinInspector;\n"); + scriptBuilder.Append("using AlicizaX.UI.Runtime;\n"); + scriptBuilder.Append($"namespace {GenerateNameSpace}\n"); + scriptBuilder.Append("{\n"); + scriptBuilder.Append($"\t#Attribute#\n"); + scriptBuilder.Append($"\tpublic class {className} : UIHolderObjectBase\n"); + scriptBuilder.Append("\t{\n"); + + scriptBuilder.Append($"\t\tpublic const string ResTag = #Tag#;\n"); + + scriptBuilder.Append("\t\t#region Generated by Script Tool\n\n"); + scriptBuilder.Append(GetVarText(UIBindDatas)); + scriptBuilder.Append("\n\t\t#endregion\n"); + + scriptBuilder.Append("\t}\n"); + scriptBuilder.Append("}\n"); + + return scriptBuilder.ToString(); + } + + public static void GenerateAndAttachScript(GameObject targetObject, UIScriptGenerateData scriptGenerateData) + { + UIBindDatas.Clear(); + GenerateNameSpace = scriptGenerateData.NameSpace; + ArrayComs.Clear(); + + string className = $"{UIGenerateConfiguration.UIGenerateCommonData.GeneratePrefix}_{targetObject.name}"; + + string scriptSavePath = Path.Combine(scriptGenerateData.GenerateHolderCodePath, className + ".cs"); + + + GetBindData(targetObject.transform); + + string scriptContent = GenerateScript(className); + string TagName = $"\"{targetObject.name}\""; + if (scriptGenerateData.LoadType == EUIResLoadType.Resources) + { + string matchWords = string.Empty; + UIGenerateConfiguration.UIGenerateCommonData.CombineWords.Any(t => + { + if (targetObject.name.IndexOf(t.Key) >= 0) + { + matchWords = t.Value; + } + + return targetObject.name.IndexOf(t.Key) >= 0; + }); + string finalPath = Path.Combine(scriptGenerateData.UIPrefabRootPath.Replace("Assets/", "").Replace("Resources/", ""), matchWords); + string didc = Path.Combine(scriptGenerateData.UIPrefabRootPath, matchWords); + if (!Directory.Exists(didc)) + { + Directory.CreateDirectory(didc); + } + + finalPath = Utility.Path.GetRegularPath(finalPath); + TagName = $"\"{finalPath}/{targetObject.name}\""; + } + + scriptContent = scriptContent.Replace("#Tag#", TagName); + + + //#Attribute# + string uiAttribute = $"[UIRes({className}.ResTag, EUIResLoadType.{scriptGenerateData.LoadType.ToString()})]"; + + scriptContent = scriptContent.Replace("#Attribute#", uiAttribute); + + + if (File.Exists(scriptSavePath)) + { + string oldText = File.ReadAllText(scriptSavePath); + if (oldText.Equals(scriptContent)) + { + EditorPrefs.SetString("Generate", className); + CheckHasAttach(); + return; + } + } + + + File.WriteAllText(scriptSavePath, scriptContent); + EditorPrefs.SetString("Generate", className); + AssetDatabase.Refresh(); + } + + + [DidReloadScripts] + private static void CheckHasAttach() + { + bool has = EditorPrefs.HasKey("Generate"); + if (has) + { + UIBindDatas.Clear(); + ArrayComs.Clear(); + var className = EditorPrefs.GetString("Generate"); + var targetObject = UIGenerateWindow.GetTargetObject(); + EditorPrefs.DeleteKey("Generate"); + GetBindData(targetObject.transform); + AttachScriptToGameObject(targetObject, className); + Debug.Log($"Generate {className} Successfully attached to game object"); + UIGenerateWindow.CloseWindow(); + } + } + + + private static void AttachScriptToGameObject(GameObject targetObject, string scriptClassName) + { + Type scriptType = null; + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + if (assembly.FullName.Contains("Editor")) continue; + var types = assembly.GetTypes(); + foreach (var type in types) + { + if (type.IsClass && !type.IsAbstract && type.Name.Contains(scriptClassName)) + { + scriptType = type; + } + } + } + + if (scriptType != null) + { + Component component = targetObject.GetOrAddComponent(scriptType); + FieldInfo[] fields = scriptType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance); + + foreach (FieldInfo field in fields) + { + List componentInObjects = UIBindDatas.Find(data => "m" + data.Name == field.Name)?.BindCom; + if (componentInObjects != null) + { + if (field.FieldType.IsArray) + { + // 获取数组元素类型 + Type elementType = field.FieldType.GetElementType(); + // 创建对应类型的数组 + Array array = Array.CreateInstance(elementType, componentInObjects.Count); + + for (int i = 0; i < componentInObjects.Count; i++) + { + Component comp = componentInObjects[i]; + // 检查元素类型是否匹配 + if (elementType.IsInstanceOfType(comp)) + { + array.SetValue(comp, i); + } + else + { + Debug.LogError($"元素 {i} 类型不匹配,期望 {elementType.Name},实际为 {comp.GetType().Name}"); + // 处理错误,如跳过或终止赋值 + } + } + + field.SetValue(component, array); + } + else + { + // 非数组字段取第一个元素 + if (componentInObjects.Count > 0) + { + // 同样检查类型兼容性 + if (field.FieldType.IsInstanceOfType(componentInObjects[0])) + { + field.SetValue(component, componentInObjects[0]); + } + else + { + Debug.LogError($"字段 {field.Name} 类型不匹配,无法赋值"); + } + } + } + } + else + { + Debug.LogError($"字段 {field.Name} 未找到匹配的组件绑定"); + } + } + } + else + { + Debug.LogError($"Could not find the class: {scriptClassName}"); + } + } + } +} diff --git a/Editor/UI/GenerateTool/UIScriptGeneratorHelper.cs.meta b/Editor/UI/GenerateTool/UIScriptGeneratorHelper.cs.meta new file mode 100644 index 0000000..f34dd50 --- /dev/null +++ b/Editor/UI/GenerateTool/UIScriptGeneratorHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f42fd09682fc40898bf7176dbb24d085 +timeCreated: 1739273282 \ No newline at end of file diff --git a/Editor/UI/GenerateTool/UISettingEditorWindow.cs b/Editor/UI/GenerateTool/UISettingEditorWindow.cs new file mode 100644 index 0000000..5cb96f7 --- /dev/null +++ b/Editor/UI/GenerateTool/UISettingEditorWindow.cs @@ -0,0 +1,104 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using Sirenix.OdinInspector; +using Sirenix.OdinInspector.Editor; +using UnityEditor; +using UnityEngine; +using UnityEngine.Windows; + +namespace AlicizaX.UI.Editor +{ + public class UISettingEditorWindow : OdinEditorWindow + { + [MenuItem("Tools/AlicizaX/UI Setting Window")] + private static void OpenWindow() + { + GetWindow().Show(); + } + + + protected override void Initialize() + { + uiGenerateConfiguration = UIGenerateConfiguration.Instance; + UIGenerateCommonData = uiGenerateConfiguration.UIGenerateCommonData; + UIScriptGenerateConfig = uiGenerateConfiguration.UIScriptGenerateConfig; + UIElementRegexConfigs = uiGenerateConfiguration.UIElementRegexConfigs; + RefreshLabel(); + } + + // [Required] [InlineEditor(InlineEditorObjectFieldModes.CompletelyHidden)] [DisableInPlayMode] [HideLabel] + private UIGenerateConfiguration uiGenerateConfiguration; + + [Required] [DisableInPlayMode] [HideLabel] [TabGroup("UI基础设置")] [SerializeField] + public UIGenerateCommonData UIGenerateCommonData; + + [TabGroup("UI基础设置")] [LabelText("脚本生成预览")] [ShowInInspector] [ReadOnly] [OnValueChanged("RefreshLabel")] + private string previewLabel; + + [TabGroup("UI基础设置")] [LabelText("组件生成预览")] [ShowInInspector] [ReadOnly] [OnValueChanged("RefreshLabel")] [SuffixLabel("(下标0开始)")] + private string previewCompLabel; + + [Required] [DisableInPlayMode] [HideLabel] [TabGroup("UI构建配置")] [SerializeField] + public UIScriptGenerateConfig UIScriptGenerateConfig; + + [Required] [DisableInPlayMode] [HideLabel] [TabGroup("UI元素映射")] [SerializeField] [TableList(ShowIndexLabels = false, DrawScrollView = true, AlwaysExpanded = true)] + public List UIElementRegexConfigs; + + private void RefreshLabel() + { + previewLabel = $"{UIGenerateCommonData.GeneratePrefix}_UITestWindow"; + previewCompLabel = $"{UIGenerateCommonData.ArrayComSplitName}Text{UIGenerateCommonData.ComCheckSplitName}Img" + + $"{UIGenerateCommonData.ComCheckEndName}Test{UIGenerateCommonData.ArrayComSplitName}0"; + } + + [TabGroup("UI元素映射")] + [Sirenix.OdinInspector.Button("加载默认")] + private void LoadDefaultConfig() + { + const string Path = UIGlobalPath.DefaultComPath; + string text = System.IO.File.ReadAllText(Path); + UIElementRegexConfigs = JsonConvert.DeserializeObject>(text); + } + + [TabGroup("UI元素映射")] + [Sirenix.OdinInspector.Button("导出")] + private void ExportConfig() + { + var json = JsonConvert.SerializeObject(UIElementRegexConfigs); + System.IO.File.WriteAllText("Assets/uielementconfig.txt", json); + AssetDatabase.Refresh(); + Debug.Log("Export UIElements Finished"); + } + + [TabGroup("UI元素映射")] + [Sirenix.OdinInspector.Button("导入")] + private void ImportConfig(TextAsset text) + { + UIElementRegexConfigs = JsonConvert.DeserializeObject>(text.text); + Debug.Log("Import UIElements Finished"); + } + + + protected override void OnDisable() + { + base.OnDisable(); + uiGenerateConfiguration.UIGenerateCommonData = UIGenerateCommonData; + uiGenerateConfiguration.UIScriptGenerateConfig = UIScriptGenerateConfig; + uiGenerateConfiguration.UIElementRegexConfigs = UIElementRegexConfigs; + EditorUtility.SetDirty(uiGenerateConfiguration); + AssetDatabase.SaveAssets(); + UIGenerateConfiguration.Save(); + } + + protected override void OnDestroy() + { + base.OnDestroy(); + uiGenerateConfiguration.UIGenerateCommonData = UIGenerateCommonData; + uiGenerateConfiguration.UIScriptGenerateConfig = UIScriptGenerateConfig; + uiGenerateConfiguration.UIElementRegexConfigs = UIElementRegexConfigs; + EditorUtility.SetDirty(uiGenerateConfiguration); + AssetDatabase.SaveAssets(); + UIGenerateConfiguration.Save(); + } + } +} diff --git a/Editor/UI/GenerateTool/UISettingEditorWindow.cs.meta b/Editor/UI/GenerateTool/UISettingEditorWindow.cs.meta new file mode 100644 index 0000000..2174bd1 --- /dev/null +++ b/Editor/UI/GenerateTool/UISettingEditorWindow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d6e004459b3645d4b3022bdde89795b9 +timeCreated: 1741340568 \ No newline at end of file diff --git a/Editor/UI/Inspector.meta b/Editor/UI/Inspector.meta new file mode 100644 index 0000000..d01d6e7 --- /dev/null +++ b/Editor/UI/Inspector.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c775eb6a6aca4533bc75c89ef7a219a1 +timeCreated: 1737704983 \ No newline at end of file diff --git a/Editor/UI/Inspector/UIComponentInspector.cs b/Editor/UI/Inspector/UIComponentInspector.cs new file mode 100644 index 0000000..93ac960 --- /dev/null +++ b/Editor/UI/Inspector/UIComponentInspector.cs @@ -0,0 +1,62 @@ +using System; +using AlicizaX.Editor; +using AlicizaX.UI.Runtime; +using UnityEditor; +using UnityEngine; + +namespace AlicizaX.UI.Editor +{ + [CustomEditor(typeof(UIComponent))] + internal sealed class UIComponentInspector : GameFrameworkInspector + { + private SerializedProperty uiRoot; + private SerializedProperty _isOrthographic; + + + public override void OnInspectorGUI() + { + base.OnInspectorGUI(); + serializedObject.Update(); + + EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode); + { + if (uiRoot.objectReferenceValue == null) + { + EditorGUILayout.HelpBox("uiroot can not be null!", MessageType.Error); + } + + EditorGUILayout.BeginHorizontal(); + + GameObject rootPrefab = (GameObject)EditorGUILayout.ObjectField("UI根预设", uiRoot.objectReferenceValue, typeof(GameObject), false); + + if (rootPrefab != uiRoot.objectReferenceValue) + { + uiRoot.objectReferenceValue = rootPrefab; + } + + if (uiRoot.objectReferenceValue == null) + { + if (GUILayout.Button("设置默认")) + { + GameObject defaultPrefab = AssetDatabase.LoadAssetAtPath(UIGlobalPath.UIPrefabPath); + uiRoot.objectReferenceValue = defaultPrefab; + } + } + + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.PropertyField(_isOrthographic); + } + EditorGUI.EndDisabledGroup(); + serializedObject.ApplyModifiedProperties(); + Repaint(); + } + + + private void OnEnable() + { + uiRoot = serializedObject.FindProperty("uiRoot"); + _isOrthographic = serializedObject.FindProperty("_isOrthographic"); + } + } +} diff --git a/Editor/UI/Inspector/UIComponentInspector.cs.meta b/Editor/UI/Inspector/UIComponentInspector.cs.meta new file mode 100644 index 0000000..21d9572 --- /dev/null +++ b/Editor/UI/Inspector/UIComponentInspector.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6c8dd72e28584576864a33af24255060 +timeCreated: 1737704960 \ No newline at end of file diff --git a/Editor/UI/Res.meta b/Editor/UI/Res.meta new file mode 100644 index 0000000..542dd5d --- /dev/null +++ b/Editor/UI/Res.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1adcae2abd30deb478273eb8d2a4a1ac +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/UI/Res/UIRoot.prefab b/Editor/UI/Res/UIRoot.prefab new file mode 100644 index 0000000..8e48d58 --- /dev/null +++ b/Editor/UI/Res/UIRoot.prefab @@ -0,0 +1,312 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &500891838716286123 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5373281001294974998} + - component: {fileID: 743195302492612381} + - component: {fileID: 7227605137039161963} + m_Layer: 0 + m_Name: UICamera + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5373281001294974998 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 500891838716286123} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 86062277508691697} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!20 &743195302492612381 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 500891838716286123} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 3 + m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 10 + far clip plane: 1000 + field of view: 60 + orthographic: 1 + orthographic size: 5 + m_Depth: 2 + m_CullingMask: + serializedVersion: 2 + m_Bits: 32 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 0 + m_AllowMSAA: 0 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!114 &7227605137039161963 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 500891838716286123} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 0 + m_RequiresDepthTextureOption: 0 + m_RequiresOpaqueTextureOption: 0 + m_CameraType: 0 + m_Cameras: [] + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 0 + m_VolumeTrigger: {fileID: 0} + m_VolumeFrameworkUpdateModeOption: 2 + m_RenderPostProcessing: 0 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_AllowHDROutput: 1 + m_UseScreenCoordOverride: 0 + m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0} + m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0} + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 + m_TaaSettings: + m_Quality: 3 + m_FrameInfluence: 0.1 + m_JitterScale: 1 + m_MipBias: 0 + m_VarianceClampScale: 0.9 + m_ContrastAdaptiveSharpening: 0 +--- !u!1 &1735351593002053547 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3110666966229274110} + - component: {fileID: 4697108639102116370} + - component: {fileID: 3475475550181341661} + - component: {fileID: 6941144161958937340} + m_Layer: 5 + m_Name: UICanvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3110666966229274110 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1735351593002053547} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 86062277508691697} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!223 &4697108639102116370 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1735351593002053547} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 1 + m_Camera: {fileID: 743195302492612381} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_VertexColorAlwaysGammaSpace: 1 + m_AdditionalShaderChannelsFlag: 27 + m_UpdateRectTransformForStandalone: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!114 &3475475550181341661 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1735351593002053547} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 1920, y: 1080} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!114 &6941144161958937340 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1735351593002053547} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 55 +--- !u!1 &4612363183729467837 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 86062277508691697} + - component: {fileID: 2601148299080526511} + - component: {fileID: 4177649269856392400} + m_Layer: 0 + m_Name: UIRoot + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &86062277508691697 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4612363183729467837} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 1000, y: 1000, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 3110666966229274110} + - {fileID: 5373281001294974998} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2601148299080526511 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4612363183729467837} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!114 &4177649269856392400 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4612363183729467837} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01614664b831546d2ae94a42149d80ac, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SendPointerHoverToParent: 1 + m_MoveRepeatDelay: 0.5 + m_MoveRepeatRate: 0.1 + m_XRTrackingOrigin: {fileID: 0} + m_ActionsAsset: {fileID: -944628639613478452, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_PointAction: {fileID: -1654692200621890270, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_MoveAction: {fileID: -8784545083839296357, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_SubmitAction: {fileID: 392368643174621059, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_CancelAction: {fileID: 7727032971491509709, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_LeftClickAction: {fileID: 3001919216989983466, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_MiddleClickAction: {fileID: -2185481485913320682, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_RightClickAction: {fileID: -4090225696740746782, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_ScrollWheelAction: {fileID: 6240969308177333660, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_TrackedDevicePositionAction: {fileID: 6564999863303420839, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_TrackedDeviceOrientationAction: {fileID: 7970375526676320489, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3} + m_DeselectOnBackgroundClick: 1 + m_PointerBehavior: 0 + m_CursorLockBehavior: 0 + m_ScrollDeltaPerTick: 6 diff --git a/Editor/UI/Res/UIRoot.prefab.meta b/Editor/UI/Res/UIRoot.prefab.meta new file mode 100644 index 0000000..3c6e9f4 --- /dev/null +++ b/Editor/UI/Res/UIRoot.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9368ff38b2090b2468f8358242026e4b +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/UI/Res/default.txt b/Editor/UI/Res/default.txt new file mode 100644 index 0000000..a7d6231 --- /dev/null +++ b/Editor/UI/Res/default.txt @@ -0,0 +1 @@ +[{"uiElementRegex":"Rect","componentType":"RectTransform"},{"uiElementRegex":"Obj","componentType":"GameObject"},{"uiElementRegex":"Tf","componentType":"Transform"},{"uiElementRegex":"Btn","componentType":"UXButton"},{"uiElementRegex":"Slider","componentType":"Slider"},{"uiElementRegex":"Img","componentType":"Image"},{"uiElementRegex":"RImg","componentType":"RawImage"},{"uiElementRegex":"Scrollbar","componentType":"Scrollbar"},{"uiElementRegex":"ScrollRect","componentType":"ScrollRect"},{"uiElementRegex":"GLayout","componentType":"GridLayoutGroup"},{"uiElementRegex":"HLayout","componentType":"HorizontalLayoutGroup"},{"uiElementRegex":"VLayout","componentType":"VerticalLayoutGroup"},{"uiElementRegex":"Text","componentType":"TMPro.TextMeshProUGUI"},{"uiElementRegex":"TogGroup","componentType":"UXGroup"},{"uiElementRegex":"Mask2D","componentType":"RectMask2D"},{"uiElementRegex":"Video","componentType":"Video.VideoPlayer"},{"uiElementRegex":"Input","componentType":"TMPro.TMP_InputField"},{"uiElementRegex":"CanvasGroup","componentType":"CanvasGroup"},{"uiElementRegex":"ScrollView","componentType":"RecyclerView"},{"uiElementRegex":"Drag","componentType":"UXDraggable"}] \ No newline at end of file diff --git a/Editor/UI/Res/default.txt.meta b/Editor/UI/Res/default.txt.meta new file mode 100644 index 0000000..763af47 --- /dev/null +++ b/Editor/UI/Res/default.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: cd96f3d9dd2a90d40a81a1d4319ce8f2 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/UI/UIConfig.meta b/Editor/UI/UIConfig.meta new file mode 100644 index 0000000..b54f185 --- /dev/null +++ b/Editor/UI/UIConfig.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2f54df36513f40bd80b00f7149741495 +timeCreated: 1737704936 \ No newline at end of file diff --git a/Editor/UI/UIConfig/UIGenerateConfiguration.cs b/Editor/UI/UIConfig/UIGenerateConfiguration.cs new file mode 100644 index 0000000..4fc599b --- /dev/null +++ b/Editor/UI/UIConfig/UIGenerateConfiguration.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using AlicizaX.Editor.Setting; +using AlicizaX; +using AlicizaX.UI.Runtime; +using Newtonsoft.Json; +using Sirenix.OdinInspector; +using UnityEngine; +using UnityEngine.Serialization; + +namespace AlicizaX.UI.Editor +{ + [AlicizaX.Editor.Setting.FilePath("ProjectSettings/UIGenerateConfiguration.asset")] + internal class UIGenerateConfiguration : ScriptableSingleton + { + public UIGenerateCommonData UIGenerateCommonData = new UIGenerateCommonData(); + public UIScriptGenerateConfig UIScriptGenerateConfig = new UIScriptGenerateConfig(); + public List UIElementRegexConfigs = new List(); + } + + [System.Serializable] + public class UIGenerateCommonData + { + [LabelText("组件检查分隔符")] public string ComCheckSplitName = "#"; + [LabelText("组件结尾分隔符")] public string ComCheckEndName = "@"; + [LabelText("数组组件检查分隔符")] public string ArrayComSplitName = "*"; + [LabelText("生成脚本前缀")] public string GeneratePrefix = "ui"; + [LabelText("排除表")] public string[] ExcludeKeywords = { "ViewHolder" }; + + [ShowInInspector] [LabelText("生成路径拼接")] + public Dictionary CombineWords = new Dictionary + { + { "Window", "Window" }, + { "ViewHolder", "ViewHolder" }, + { "Widget", "Widget" }, + }; + } + + [System.Serializable] + public class UIEelementRegexData + { + public string uiElementRegex; + + + [ShowInInspector] [ValueDropdown("GetFilteredTypeList", ExpandAllMenuItems = false)] + public string componentType; + + + private static List cacheFilterType; + + public IEnumerable GetFilteredTypeList() + { + if (cacheFilterType == null) + { + cacheFilterType = AlicizaX.Utility.Assembly.GetTypes() + .Where(m => !m.FullName.Contains("Editor")) + .Where(x => !x.IsAbstract || x.IsInterface) + .Where(x => !x.IsGenericTypeDefinition) + .Where(x => !x.IsSubclassOf(typeof(UIHolderObjectBase))) + .Where(x => x.IsSubclassOf(typeof(Component))) + .Where(x => !x.FullName.Contains("YooAsset")) + .Where(x => !x.FullName.Contains(("Unity.VisualScripting"))) + .Where(x => !x.FullName.Contains(("Cysharp.Threading"))) + .Where(x => !x.FullName.Contains(("UnityEngine.Rendering.UI.Debug"))) + .Where(x => !x.FullName.Contains(("Unity.PerformanceTesting"))) + .Where(x => !x.FullName.Contains(("UnityEngine.TestTools"))) + .Select(x => x.Name).ToList(); + + cacheFilterType.Add(typeof(GameObject).Name); + } + + return cacheFilterType; + } + } + + + [System.Serializable] + public class UIScriptGenerateConfig + { + [BoxGroup("主工程")] public UIScriptGenerateData MainProjectUIScriptGenerateData = new UIScriptGenerateData(); + [BoxGroup("热更工程")] public UIScriptGenerateData HotFixProjectUIScriptGenerateData = new UIScriptGenerateData(); + } + + [System.Serializable] + public class UIScriptGenerateData + { + public string NameSpace; + + [Sirenix.OdinInspector.FolderPath(RequireExistingPath = true, AbsolutePath = false)] + public string GenerateHolderCodePath; + + [Sirenix.OdinInspector.FolderPath(RequireExistingPath = true, AbsolutePath = false)] + public string UIPrefabRootPath; + + public EUIResLoadType LoadType; + } +} diff --git a/Editor/UI/UIConfig/UIGenerateConfiguration.cs.meta b/Editor/UI/UIConfig/UIGenerateConfiguration.cs.meta new file mode 100644 index 0000000..f5a1da8 --- /dev/null +++ b/Editor/UI/UIConfig/UIGenerateConfiguration.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b212f141eb1bc7b4d97c4dc8b938bc5f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/UI/UIGlobalPath.cs b/Editor/UI/UIGlobalPath.cs new file mode 100644 index 0000000..b253c28 --- /dev/null +++ b/Editor/UI/UIGlobalPath.cs @@ -0,0 +1,8 @@ +namespace AlicizaX.UI.Editor +{ + internal static class UIGlobalPath + { + public const string DefaultComPath = "Packages/com.alicizax.unity.framework/Editor/UI/Res/default.txt"; + public const string UIPrefabPath = "Packages/com.alicizax.unity.framework/Editor/UI/Res/UIRoot.prefab"; + } +} diff --git a/Editor/UI/UIGlobalPath.cs.meta b/Editor/UI/UIGlobalPath.cs.meta new file mode 100644 index 0000000..8d37a63 --- /dev/null +++ b/Editor/UI/UIGlobalPath.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 665aa33b3f2e467bb675bb8da795bd21 +timeCreated: 1757063844 \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..4e6513a --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [2023] [ALianBlank of copyright owner][alianblank@outlook.com][https://alianblank.com/] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE.md.meta b/LICENSE.md.meta new file mode 100644 index 0000000..ac1470d --- /dev/null +++ b/LICENSE.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 773fb24b0f5c014438958e2dc2fe62d7 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins.meta b/Plugins.meta new file mode 100644 index 0000000..74627ac --- /dev/null +++ b/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a3e79e61e313b2b4ebf21a5b9afb39a3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UI.meta b/Plugins/UI.meta new file mode 100644 index 0000000..c2a08cf --- /dev/null +++ b/Plugins/UI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f372db68a9aaada4592cb3015b15773d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Plugins/UI/UISourceGenerator.dll b/Plugins/UI/UISourceGenerator.dll new file mode 100644 index 0000000000000000000000000000000000000000..6aad363b7c36d243e4bc7fb7e701110e8743b3c4 GIT binary patch literal 25088 zcmeHv3v^u7b@slGxp!tHjWnZ|jbCGI*<-!1Zj&@LaS{?pX^IoF8XQ7HNz%{)3%5X<2Tju^ZPEnue|z71 zXGXFi=3o8S`unec?7Q#T`|PvNKKtyw&$(yrXyXkZCXI-6TyMTf^eFE9StIc6!5FGT zv%VgpuLQm~`%$Iyd$YUuCbQAOjNPA!4@493blT2Ed#z|@C>>3vqivhJq62oH)m&B< zoa>6-(oWQ=Xr#ROcPG8xUZzWU{CR+=6tq_k5Oq(kdpkTzWL@b2Tr6uhuADA!7)2=$$VnQCl0QLz5q&RwlN+zDZIHQN275UJO%bm`h; z6#5p|Vk-3dPn9dTg;A($s-1TmCQ(0UTn3NG&Oi@!V_XxP$(=#X%7q$z8+UXP&cbz! zprMUMz+tGPm3%X9Wh^!e{24F}+EJ87t0q@nm9MIvTs4}ns+nAMI$u@GRoU4rdkPnw zUM~fn!_=rYbTy1~#v&fZ98hD>rD2|GjEJry+$L6nN^eZ#7!ynr*6ccP2OCm`Jr9M( z8x4Ct?oHcoi{WP23jlSN9&8NM*3@JhP^vGg^SO03wddD`yoNP3wN69B>CI?5TI0!b zZM+V5%uQHz^;uffhvxJ#&1jrrl$drSRERaWiW{*GFYIrGt1F@Cso zINKs~7V?dPS6n2`Dt^&vXFBE=Rxia)8WMpn=K6IEZ1N=oDMwu||y8N-kf)%@8nyu~lAG z<`6e9@8+Hg4dylMF{d5g0a{&+bpBvnt^dKgDVZbSez0z8;K8~}GAFqlyV8?1uc76g zw=wP2Xb*RrhB+L9v3;<2=3z+AB7%ZJJ%&kbuv<|sX_#r+Yf&gQY`O@H@N&}=kJ8-Wmf!N~k8xWr!b%9YG?Bql?;jJ2VR7>@( z6a-4hL05AXrw=3}Cw)vq;hX`}dg+5}ob)kmp+UcaL%|oqSs2_1mMc!8XhXk}C`t^E z^bwCViY%mOehgHGT$)7+4VHmTf}K@j_cQDckSq1&K6DV#W7ja|bA^R{_O-xkY~;vb z*cS<$t47c$;XrI72og4MIAg_A7*yCWV-RYrcw7+2_eYTaFuW=AIH$l%oG_@X432U| z*bN9XY_7PITbWKkltfBgFE5Rh<^!TeeCedg0pSl!_q?diWY4J$8-)O5hhm?GLro-K zl1T{nw9EnA!~VNjn0Tk_Z<{bI7y%1zaOE$~z4_)#p@@F2CImNfrrn=8j)u;(2kb3i zt1N4n9Vv@#gPg|(UQV+`@hju}0 z8+iqZz0t4{%R%H#gm3Wsh=OUcohZeiQLr&&4l(5(_&OLBAM^4gW`! zbaV{U@(xc&g=VAgfatgj;|?}d8bgRDrNnGhO&C&$M$=D4R~{465I4q{crhr6D6t!* z-K7zVy&EMPgF(L6=T$ZMWahQlZcrLAE#Qcw*!06144lW!Ya7Pj#k0ahFZsc3xn{>I$Y{+dd(ZoH+eW%TUAQzZ!$uM&`YUJJsF;sw{(!_PqY9G!JqS zuVJ-Gt*O_7H6@sF^An}2KR-=o?R6!l@u6hPKk>jg)*e6`q{6TogTG^=*Q8d}YevNH zW>0^tA3e!pzZZoPlk3ePti8MmG5vNDR1mNhC}n|0GhpB35Rm#6-aRz>vB>XpXh^CG z_6I)De0IvAtzsJTGH8Z9;Lxy~V#N=epqX}>X;l*I$m|qmU2AO{R5y-hIKf_s=@Rp9 zj@?GxEJXL;7dusPbUSfVMhljCaibD^iy#5>Gl*_@es0D+*bv2ZWKhCf^C*i$z^D7S zN)%*K(e$@^)z+GNcGDbCxG7e3tgh2w?{`5qmT1W4@MCM%1f6mrft56ro07oP#p+8_ zjN0ph?y~-q3ClV+E^2DjULQk7YVhJ^22ES;jEVb&Y5%+WE$UOUUhVaZCu?$@s|h9& z&ZSa87xp>!vB zt9iDe-5_%-zdej9EaLDZBYO+Vv7p$6V+5IvkdR>i5mfNp-imv$0qH(Aic<7PQ7CX5 zN^V+v4~k9y;N}61u%J+lv>)SkMw3?6fK`Lzv7}+1VLMbOKF7If)TxxXH6>(AyhhC1 z^t2gcK2{v5PU5%$eQ+F02+xoZw%-dL`w)QNzFpXB>+`|K8SY6t{B@LVBxXd ziFk-UnoeV-S&xrJGgOV($lR>eWA#^oz4s>ey1;1=`|G`iGj3v}?<1Dt!%0 z^|0>~;_n5vurKxjFcv~VWHstFWesIvU+r}q>5$vhQakj72f?oJ9LRVmQ{5 zFTd6D_)JaxOmzfvt7EEVuAjwy3eUAH%|TY^(jlN`n zH4Wj|M_ITp;){I@h1zpUW0@MmK;Zu$2caG+-HZFmIBZvcjhej=$S4oieTHb}M^R%8 z89|2`oac&5vWR9QkHRKFxBcpEQ7`RL7M&NhB(vd#`EHeYUd6jC?{b-K6-)YqlHo3@68(eXfJ_8<) zRAlgS*xVc-t}bOTJS&D?}r6(2w}WIu_%H2YJ4wTGc1o+zEH@L=rI zpv4{laFth`!;3JUjnxqZv(CB8s5DhM&cJ$joOwO#?bqC0hc>SU0$?; zR;?PH$Fy1Jq|S@aIQpH=zBs}yCq^ctzcKSd%9eT{`W6wUf8^N;o?OrfZ!A<1vrQtHs>~?7jS<+65^b$Tqc?B&oW0} zBhlxBpl$D>3Jd_|pgC7>?`Xq43LJTi=B!QeQ;K|4O!v-N50(Tlx%6MkV%`WTD5i0t zD9`lB7V=CU!u_kbkTB^JxL_NxwShZdT#uQQ_B;njlYH*Q&scINI)(#3{uuOx@zXMc z?ll=M_cNShe7mfYsvyLmBgS|92F>yxEHS9YWcZxH@LvUfO(-1>#m8+s{bzy(T~WcX z#9;XE!85@iEj7=C4Eh%6LE2h&CKROMN{06V8g#4l{tbm|rv{nw+mhv#L4^33P>52> zUu#&m!c3p7)Tjn+01VQqQl?laN9c!D-va+(jb-jo&V=rvb__N`HK0f6-z&K`QgS8~ zp-Y5wP}*Lmd@%xhmHe6ieZp_hDd=F(!zHXq88ped^oM**-z~N(!5AY{=g0|gk6#cN z2Y-;p1b$B7S)ulmzg_}8D_G8NFe3)t zFPy)U`Fd1)9dr7Zezy7U$~M4fgA6|}_S{*{t$vQ)BXkhu2%T1+Dv8j~Mf1N9%~xV{ z5jr81>D6p=pOhO=HfU`%Yk0rR`<`m%9MYbGJ`2Pemt&RX$H0CpSSc_C(bx<7 zZp4#-gB^6SG8%`Je(bBbu0pVy(*9D$!uUR+g0>(TFji06>FOdWO|%84 z6Q2nkHs;Z1{YL?3mP|1WPG?Jt26vhh2cMyF#KgX!*5IZV}Xwf|4~qa zq5A+U^!tq2v|qj7SVlip{@AF2%|2%s^rgx#06r1=GT>3)*8r~&m=*XtlPTM@KL_M; zLBhB461EG^82uG^N~@m*e4c(}DD<3f3gC&zS-=}rF7F7QMeSBVg&tHGdOTe?qjq>c zsr^bb=srM&4k^<_+t<)n3;opC5``h@&M8OA*mJJSW50u>!8}H$Lhs3AT=$_o1}~yd z3ig^Z;5Wee9T(f-FEv%Ve>UMWPP4U$sZrylj2)%l>2pm#-745Pt8VO?P6<7ddz9`xQlHt_nI@vzrf-AKxnTyi}t$MjiEtvHXU@a+l*23 zQu?urec!y@oJ(`-op!%aN6q- zq2HR<(n7&rQ|{Ds|Fu-MNb1D1Iw>mH(J3D*EAe+y(+`1D~ zj&?V-6l89sj9`b*s>Z*OKJ3(qE}Q5+!5m#S(Qk{|ZKl%2EXC1fGfff9@w3eo70l6P zGtHk+cbQvv0{v~KjYV}^=sLIV6ixSUp(8H#GR^UCr3dqzKH5f)3wBodEaJ0^zVBj} zRmOl_iANfuvw_)>`?W4=cQEC-3g+x|>kdX%0PA+^&Xm^syXjsRn^U$4&sm=qj7Q|d zY0=|u9rw4B^rg~+!u{=}Cj=X(4`CtLNiR5cfuDt!0DHydd70gV*oN)NIOE`L49=XIaLR!pM} zfI1xlROqPCf51zav{HqlQ9yXU4mm#L(?ad{u|5^c_ay%#Bsd@BE$!mUoM&09e(8O3 zS)uxTttYua@oKM^whrAX4}pU3Ja{82ABEOdG3B;Efx;Rp^b1f_s`II^Ng91!oaUv~ z+-e9iHIb>%F5cwI$I(g=x=LS#4xXNypp{0=Vm(LOVv3`QV+TjK1a!k*fj1grPp`ZN zy(@Hwz~?YW=YM#uI-km|9)!{x{sm3m%8nkNc z<*Pxf1}#9ml7#_%*i~?OHee8^u1Cr$XobL5fgJ+33cOxmTwoGVL61kus0%kiKx|8(J*EA*R1 z=2pn*rO(oX<$LKAwUp#2sV>)Vp+>1aMV+PZ!!C$C0+LIi)^ckx`DT|KLA}a!)D4BNev_ ze6Mnhn9jXa&|~VEihGo98RIS)@3ZuN{maT}^{n!!@|5~7!EY!SsrFs8Vw>~{Oo4xf zniKx1%4f8NYEm5tEK|>_-wdo#`zWJsMlIX9PdINC&Rc~tRhbhSP;ZwW?-u$oTI_qD zdQ>^5eHgH_`k4Bd`Ztk>0T)z$1Mo@TJ<3#NN%%SSezf{m^&x5XJjE&=!Kgc{RqY{> zJg)q>>}u`v!t)j3`HIjVQ!djFDreQNmAp^;rSi4F$F{J}EMvh8=wLwCv9wQ}3wI^puWbF9om-j_b4GSN-||c-nw)W>IGQJf;M++x1iGaPI)v5r`*QW9#dRY69 z@-OKxNbj#m@2Ax3Fy6;$cHn#Z<1+iNI9ApEGxBSFw$>H;o&Iz1m-~Jt{EIM_OMMG8 zvvfdjRrXaLR8}Zel`-EUt+S%p_j`IK@;yBz^Ytr{lal#eMWafWZxubN>;(L-@-E*B zZM?e2*Qy;U+vD4+%`4gOi)&B&ZuOm_Md}B9jUwSOWq$clpDr=-n7X0*Pkp~tPM3e* zS1OdFpuFN+q5gB_bLv!OQ~4Xdq_$Sojhi7;p(=%Uz*P$GeXA5+*{T%Y^;Rjo51y(t zhZ;~m5$W<($r@Y*nLhCF{XzE*5F)C;>U^ko)!-T|!9+B_R~l(VqnRXoRK*`=r(qcu07753|oW z1fFLCcL@C+fhVN)gw&ou?XLqTh5luM-xm0)Kux)n7Q^XyT$+-FG@q2`NV!hR>l7Yo zyOa~cpK!}ex1@YX;2i=_C_JK*QvQ~dzb)mnQvOGQq;kKcvIGU^7U*3lnv|nLpDpD% zDoa}CWJC3lyRXP63PQYIWCkFLU{<~b-^cuep=|?68g*PAzBf5S?Fhl z{)%vtCc0^t(EWj^l<{-}J?8?h3$77LtH!$RmU5raACU4B0$&z*R$7sMi1q|X7nwTC ziAs5mz%ha2LcbkwUGM>+92d%IfiDY%d@LawLTKgQz_& z^oN9gTFSb?JW=CPwAw9j%wRdU3;jufI-a<|gJ+$^fi@{Wfby@6Cj`E1ei!_&2%Y>~ zUL&y0&pqyz@|eH}1U@A6C#3wcKnidxJs|v29uxS0z$XO0EO1Sb`|ZPt%dd^w1wJJ3 z6@fZVUoayAZTcepJvJ8aSDsULsVCGk>hr2!tJLbW%e5YDkM^MU7us*NuzsCBs((~} zR$uG;Bj1xgzfo$;HP#xXyuZOtstWs}YB?L+7rMevrs9(+zEJ9c#B)+vuq7* zmVjTyi@GLY8+J4*U5nhH;|Ltq z!{Dr@kASlp&wfh*?*nHwPBxnW@5dQQHGK-4jdUg6Y6Xjv0Os!Jq ztJ~EB>U-3~>QB`FQnzS3wHLHkwAZx_`s4bOy5XDZTj4wI`#WFQxXRGSco$au*@Ckl zZ1M7+YmL7Z6#MTyt=l*wd)eYm-Hpay%N~0o-&3*=Ek&j+!xh3+j;jK)!nh*1Dq*E6 zT-CU0P{W@&IP&HDeD_}O-mi4;t+;p4=P>&JqJFwY8K)Dr(M-b`kS&gT-9foZgYlCUafG>0JEaCcGri?WAUMyCu?Yvascz zG{{;zluY$)v-*?STqd4N+Ua$!Jk+%xv=T!(M=M%&wUui<5J$JUc+MKI(z$hZI+w9i z>r?gt9HJ-oiS~|~i!PEKd~H@XVWs=x=^T$FySlHZXVKE#i>RYLJv3lt;=L&g82U;K zWise>ZM+|NYl3wxV$0bXU|rT=9K*2s9NTnaRLnClFqC6Kt(i=Gm3AwNjOui5aSN^Ljc2Xy;X!Nl(w?3cT0fLd>~4{!Ewm$^8nU{F2BF(x zAuWa_U3MA5GU~T-J=?q2FK2_$s*QHvP|8|O+dB|H`?+;b54Gm-T}3Y}+!oKp!Iz+} z;Ve{ZUT3FLmW(jle6^LfGRXvOvj*)3F}@*S$Am#rrz? zAS%_~mFgqRa%Vi7TelZ(!N~e# z#=}|%2Qg99k-avFn`2=qvVi`KJp^jn%4KutBc5SK7}Cn@i6<=D5HCU?c2j)7>Kf!4 z83sHl4t>-8OwS5^-CirPuOqz|gNHycLjNv%D1*`keU>tg6A!Zk)0T|2KWPtT*{5g| zem)_EbzOVonZZKb(0P#r(=G1l$>t+V)N>kiXDqAOE2)Dc)XuVFU1;L+UPs%8(z)b- z_|gX02JEl^d)E4lJ>XJWvso**7ABNhOgZMr8SeTdNNgpxjn{JR@J1^a$3(`3I$-7Y z+I>>Y+gz}`iwTBIVh!}Bh6U+D*uknYshvp_xewM#3LV+)>0X{v*Z+ztCR04er(8S( z8F|!;ACb4s$_-`GA_arrV5j=5j9_~UML5Y8gf9l;)^RWwFtn2vL&=@VK6E6=fkF}W z8!kxK13$C*WuEW`p+Mz~YpVgN)KN8r$ASN;+#sf=HRi0^kHr{XLF>14Vhx+>^Z+VG= zHVvgx6DjOCJg42s91>if5h)HXWM?E>Z;U7QVulN~uryCofzT~ex1P;?z|QO|R5(gl znGH#dCtaYoAIw?lEOKyxg(E$j7KOkJOf_j~EhSxbf=6$WX z57dsX;elSf)5|t>ixA4fkzcUekjf5OLOlct8adg;oCNJviPP~a=Mgv`d1bEbDOZZMSJf6=AsSD_kOF77drUN&;YHq?UWTy6E-5yC?)YF zdO;3vXPvU^Mm%vm4BqRB>5IaX>o5l1hkEmjX%x=<#CwE}%@}}F=lQF($K7Fx+|8L} zKkxq9tUa=ALU)_&T-VSb@42kL_Jav)P(-6s#|)gu;Ms$;F`3BNS$j_oyS6?{Vkw)< zHalAl+F}n54SGR>RrY3bfFdD9U+rQ-FByaph%!=U9X9(ST-cNn`|r;o{|^Xdp3J)*y*h5UX3kj z?wz-L3p;fbJpCdB7b!AHi}S*kws#-`T^I?v>T4{!-Ma-8Z*iJhxTbOR-Nu=D)_%NR6 zxXg^4Tli!KGV?3G;9Obkxk40&L0s>*_%YtoqnpcbF?OkQ^1c*VtaD()jq*7^zbTfO z=xZg=k1$I=}HdZ2&stRC2qIA_;I%V z;9v$%wNS{d94LC$J+D$W$>&kye13Nx)ybyLURG@3cHk*Pa+7yl*2T~2*hFDZ%1hej zO#8qfFrq74@g8v!-&!T<7K-D2lV-f*(gC;)zg15YzB$Kl%Pg0=6IdGG729-xb`xG6 zddIE%aCU4#suI?hMHj-1fNZNfRVWX-ws6a!>&p6X)L!Bx(|aFTP%E^xR6F2UVI;m z79|d40lu-MYDdZ>8IUcthvr{~FD;^Y6(KHa+xU8d&6yU_aT!P-I9B7}KM0Pj7^@#G zy3joj#=>M?abE4knb2v+lGEs)ogxbZ#Nj#?dbl_l+vmnf8DF_sup-+4XTRbBte?lXplFnwF4*E1 zvg;t)CuzUTHv7=TncoljNwkhm2uJo5_O%p(Zovhmj*v3do4Lt%YPgUdqZ8yf*eJdU zd;65okNHSIXWDpizV9T~4H#8QJRyTWrqoL}!EOoo%3ASqB%=0rB8?Ljw##TgDV>d9 z`@fqiie0_nCjYaOhGVA5S^YxclbPTOPRdZOWJkEL3=YXZurgFSJXs{0I6_%+{(^Q% zW{ylRYzpC3v?7c@#vc1WBWq3EGF-^KoEyF2PRve!xUJW&V5a}Ovk=w2o!jKIhlcmT z!^~N{K`BtY4HMNe&-t4mDq;FU{KH$=D>!&Fk`p+~>_>_7XJL=vtf{7$59URpgCDN@qn|z!TK}2Kd!PG`@5Lx-QBw4%qWFCP;c701qbBtC_LoDug=CdLg^50JUnhc0R6@uQzHW^>*vW4+DuY)=r`c9hKCGC5(ZgG`01S% z!iPtk4B>5?P-r^L=ac}I(2P(J$|l)vp-?%L^hVC(*MhJQ6BvSlN1qf0>^+v#Psj)XynVB~*8{zTl@OT)5@L)BU z$Bwg1+#u7yl#Nf7;2)o=lcK`60cM!&K&@xU5nCTC;Q-;pG(H3A&&gHeGu(!tISpYe z&ZUKh<1^4D#|&P7^J}K!=iItQe58_8!|z8?f)h@x`jJzk(mNWsDjmk4ks7%7ZnoId zBb9G{uj$3D{zZSee8q7P-pD32TPH@OCbTuMbCccPZfs<$r#Xxs6z*QC}#0J(EBPBHf0sm%NNu zCxyc=%L@!7Sq$;+6p(rk3(MH)5Z=^uQs8N*Wk|aa>LLTY76OHH;N?ryMaF)JbWlQg zrFiW1Pz9nFVJdBBA~zhCy!!@*8X5P24?lke-(9?xh`vCc$D+cmt#CP+X>~#J+P<%yWAIK`;V|O6BTA``Uo7%harir}w)#!eCd*%L?=7o?Ds>;7s z;(mX?2cBVW67||d!CHeK)|~$>9+fI&;#CZM?`GjIpaEHgKLUV%M2i(_7Nns!v7~Qd z%d(awExng7Y-w3)C3=@HU2=KfvK5ynS`sbsrHPilJqugn_z{=-M)|CZ|zpg@YSGwU?aZfNaElZgn;{o zo7Kl`o$>>%Kgh;*zX)j@sDAIXS_=HuR7o%oVlO5_lfP-jBUK)$wQSt zUc@r4Ub$;K-b@+B3kLj6(yp%7&DZYg#tQ=3UFSK%u1R0y?y`Gt+U5Gi`P|KeeZADR zp|xe{GID!9LvGI)-KOK;w4NM!)qKwvT9SW#4kPeBQ}{$U>R}V_L9WYxde+&Qwp3~( zUR$JrY{JINf%2k=!|-Muq)awLzU}`T1%7K?{j7-Xi9#11={D0Q;2nVLoxi$Gf1&^4ob%>5dk=q{`&ABtx2q?E?`Qfz-wL{Q=50bCORXEP(14jJK zbCKco*#?d*c=_mXaDuKEBWoe=()0T7fUFFp6~?;=hk6V17x&G7cYrNcd`Mjw!5{9k z8IRz6T!}wBhpdGphd%pZfs|Nf5cc4a^wVDSKoWf2(&uKEo3Ey2}`-%>E%f?rwi z!Dlai-L(`omxFH^enEOUXf1NZLFaNGe3e_oVIAaQnZH)d86TbxpnVD!jKaew&SXIk ap3ncL&!F=+;~Q7}H_7||V*W>2;C}%gTh3wt literal 0 HcmV?d00001 diff --git a/Plugins/UI/UISourceGenerator.dll.meta b/Plugins/UI/UISourceGenerator.dll.meta new file mode 100644 index 0000000..e9a36d2 --- /dev/null +++ b/Plugins/UI/UISourceGenerator.dll.meta @@ -0,0 +1,52 @@ +fileFormatVersion: 2 +guid: 4073667e4abebeb479ff5f9810398e92 +labels: +- RoslynAnalyzer +PluginImporter: + externalObjects: {} + serializedVersion: 3 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + Any: + enabled: 1 + settings: + Exclude Editor: 1 + Exclude Linux64: 0 + Exclude OSXUniversal: 0 + Exclude Win: 0 + Exclude Win64: 0 + Editor: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + Linux64: + enabled: 1 + settings: + CPU: AnyCPU + OSXUniversal: + enabled: 1 + settings: + CPU: AnyCPU + Win: + enabled: 1 + settings: + CPU: AnyCPU + Win64: + enabled: 1 + settings: + CPU: AnyCPU + WindowsStoreApps: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime.meta b/Runtime.meta new file mode 100644 index 0000000..c6e3745 --- /dev/null +++ b/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 98f26ef6a50ff52458b160222752dfec +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/AlicizaX.Framework.Runtime.asmdef b/Runtime/AlicizaX.Framework.Runtime.asmdef new file mode 100644 index 0000000..013ed76 --- /dev/null +++ b/Runtime/AlicizaX.Framework.Runtime.asmdef @@ -0,0 +1,32 @@ +{ + "name": "AlicizaX.Framework.Runtime", + "rootNamespace": "AlicizaX.Framework.Runtime", + "references": [ + "GUID:75b6f2078d190f14dbda4a5b747d709c", + "GUID:b4c00b967a932af48b2e067403eecbe2", + "GUID:f51ebe6a0ceec4240a699833d6309b23", + "GUID:e34a5702dd353724aa315fb8011f08c3", + "GUID:760f1778adc613f49a4394fb41ff0bbc", + "GUID:189d55e03d78888459720d730f4d2424" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.alicizax.unity.ui.extension", + "expression": "", + "define": "ALICIZAX_UI_EXTENSION_SUPPORT" + }, + { + "name": "com.alicizax.unity.animationflow", + "expression": "", + "define": "ALICIZAX_UI_ANIMATION_SUPPORT" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Runtime/AlicizaX.Framework.Runtime.asmdef.meta b/Runtime/AlicizaX.Framework.Runtime.asmdef.meta new file mode 100644 index 0000000..72cb334 --- /dev/null +++ b/Runtime/AlicizaX.Framework.Runtime.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1619e00706139ce488ff80c0daeea8e7 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Audio.meta b/Runtime/Audio.meta new file mode 100644 index 0000000..bcc9947 --- /dev/null +++ b/Runtime/Audio.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 65ab69a3b3c64f22b899938b5234583f +timeCreated: 1737390310 \ No newline at end of file diff --git a/Runtime/Audio/AudioAgent.cs b/Runtime/Audio/AudioAgent.cs new file mode 100644 index 0000000..f53c018 --- /dev/null +++ b/Runtime/Audio/AudioAgent.cs @@ -0,0 +1,455 @@ +using AlicizaX.Resource.Runtime; +using AlicizaX; +using UnityEngine; +using UnityEngine.Audio; +using YooAsset; + +namespace AlicizaX.Audio.Runtime +{ + /// + /// 音频代理辅助器。 + /// + public class AudioAgent + { + private int _instanceId; + private AudioSource _source; + private AudioData _audioData; + private IAudioModule _audioModule; + private IResourceModule _resourceModule; + private Transform _transform; + private float _volume = 1.0f; + private float _duration; + private float _fadeoutTimer; + private const float FADEOUT_DURATION = 0.2f; + private bool _inPool; + + /// + /// 音频代理辅助器运行时状态。 + /// + AudioAgentRuntimeState _audioAgentRuntimeState = AudioAgentRuntimeState.None; + + /// + /// 音频代理加载请求。 + /// + class LoadRequest + { + /// + /// 音频代理辅助器加载路径。 + /// + public string Path; + + /// + /// 是否异步。 + /// + public bool BAsync; + + /// + /// 是否池化。 + /// + public bool BInPool; + } + + /// + /// 音频代理加载请求。 + /// + LoadRequest _pendingLoad = null; + + /// + /// AudioSource实例化Id + /// + public int InstanceId => _instanceId; + + /// + /// 资源操作句柄。 + /// + public AudioData AudioData => _audioData; + + /// + /// 音频代理辅助器音频大小。 + /// + public float Volume + { + set + { + if (_source != null) + { + _volume = value; + _source.volume = _volume; + } + } + get => _volume; + } + + /// + /// 音频代理辅助器当前是否空闲。 + /// + public bool IsFree + { + get + { + if (_source != null) + { + return _audioAgentRuntimeState == AudioAgentRuntimeState.End; + } + else + { + return true; + } + } + } + + /// + /// 音频代理辅助器播放秒数。 + /// + public float Duration => _duration; + + /// + /// 音频代理辅助器当前音频长度。 + /// + public float Length + { + get + { + if (_source != null && _source.clip != null) + { + return _source.clip.length; + } + + return 0; + } + } + + /// + /// 音频代理辅助器实例位置。 + /// + public Vector3 Position + { + get => _transform.position; + set => _transform.position = value; + } + + /// + /// 音频代理辅助器是否循环。 + /// + public bool IsLoop + { + get + { + if (_source != null) + { + return _source.loop; + } + else + { + return false; + } + } + set + { + if (_source != null) + { + _source.loop = value; + } + } + } + + /// + /// 音频代理辅助器是否正在播放。 + /// + internal bool IsPlaying + { + get + { + if (_source != null && _source.isPlaying) + { + return true; + } + else + { + return false; + } + } + } + + /// + /// 音频代理辅助器获取当前声源。 + /// + /// + public AudioSource AudioResource() + { + return _source; + } + + /// + /// 创建音频代理辅助器。 + /// + /// 生效路径。 + /// 是否异步。 + /// 音频轨道(类别)。 + /// 是否池化。 + /// 音频代理辅助器。 + public static AudioAgent Create(string path, bool bAsync, AudioCategory audioCategory, bool bInPool = false) + { + AudioAgent audioAgent = new AudioAgent(); + audioAgent.Init(audioCategory); + audioAgent.Load(path, bAsync, bInPool); + return audioAgent; + } + + public static AudioAgent Create(AudioClip clip, AudioCategory audioCategory) + { + AudioAgent audioAgent = new AudioAgent(); + audioAgent.Init(audioCategory); + audioAgent.SetAudioClip(clip); + return audioAgent; + } + + public void SetAudioClip(AudioClip clip) + { + if (_source == null) + return; + + Stop(false); + + if (_audioData != null) + { + AudioData.DeAlloc(_audioData); + _audioData = null; + } + + _source.clip = clip; + if (clip != null) + { + _source.Play(); + _audioAgentRuntimeState = AudioAgentRuntimeState.Playing; + _duration = 0; + } + else + { + _audioAgentRuntimeState = AudioAgentRuntimeState.End; + } + } + + /// + /// 初始化音频代理辅助器。 + /// + /// 音频轨道(类别)。 + /// 音频代理辅助器编号。 + public void Init(AudioCategory audioCategory, int index = 0) + { + _audioModule = ModuleSystem.GetModule(); + _resourceModule = ModuleSystem.GetModule(); + GameObject host = new GameObject(Utility.Text.Format("Audio Agent Helper - {0} - {1}", audioCategory.AudioMixerGroup.name, index)); + host.transform.SetParent(audioCategory.InstanceRoot); + host.transform.localPosition = Vector3.zero; + _transform = host.transform; + _source = host.AddComponent(); + _source.playOnAwake = false; + AudioMixerGroup[] audioMixerGroups = + audioCategory.AudioMixer.FindMatchingGroups(Utility.Text.Format("Master/{0}/{1}", audioCategory.AudioMixerGroup.name, + $"{audioCategory.AudioMixerGroup.name} - {index}")); + _source.outputAudioMixerGroup = audioMixerGroups.Length > 0 ? audioMixerGroups[0] : audioCategory.AudioMixerGroup; + _source.rolloffMode = audioCategory.AudioGroupConfig.audioRolloffMode; + _source.minDistance = audioCategory.AudioGroupConfig.minDistance; + _source.maxDistance = audioCategory.AudioGroupConfig.maxDistance; + _instanceId = _source.GetInstanceID(); + } + + /// + /// 加载音频代理辅助器。 + /// + /// 资源路径。 + /// 是否异步。 + /// 是否池化。 + public void Load(string path, bool bAsync, bool bInPool = false) + { + _inPool = bInPool; + if (_audioAgentRuntimeState == AudioAgentRuntimeState.None || _audioAgentRuntimeState == AudioAgentRuntimeState.End) + { + _duration = 0; + if (!string.IsNullOrEmpty(path)) + { + if (bInPool && _audioModule.AudioClipPool.TryGetValue(path, out var operationHandle)) + { + OnAssetLoadComplete(operationHandle); + return; + } + + if (bAsync) + { + _audioAgentRuntimeState = AudioAgentRuntimeState.Loading; + AssetHandle handle = _resourceModule.LoadAssetAsyncHandle(path); + handle.Completed += OnAssetLoadComplete; + } + else + { + AssetHandle handle = _resourceModule.LoadAssetSyncHandle(path); + OnAssetLoadComplete(handle); + } + } + } + else + { + _pendingLoad = new LoadRequest { Path = path, BAsync = bAsync, BInPool = bInPool }; + + if (_audioAgentRuntimeState == AudioAgentRuntimeState.Playing) + { + Stop(true); + } + } + } + + /// + /// 停止播放音频代理辅助器。 + /// + /// 是否渐出。 + public void Stop(bool fadeout = false) + { + if (_source != null) + { + if (fadeout) + { + _fadeoutTimer = FADEOUT_DURATION; + _audioAgentRuntimeState = AudioAgentRuntimeState.FadingOut; + } + else + { + _source.Stop(); + _audioAgentRuntimeState = AudioAgentRuntimeState.End; + } + } + } + + /// + /// 暂停音频代理辅助器。 + /// + public void Pause() + { + if (_source != null) + { + _source.Pause(); + } + } + + /// + /// 取消暂停音频代理辅助器。 + /// + public void UnPause() + { + if (_source != null) + { + _source.UnPause(); + } + } + + /// + /// 资源加载完成。 + /// + /// 资源操作句柄。 + void OnAssetLoadComplete(AssetHandle handle) + { + if (handle != null) + { + if (_inPool) + { + _audioModule.AudioClipPool.TryAdd(handle.GetAssetInfo().Address, handle); + } + } + + if (_pendingLoad != null) + { + if (!_inPool && handle != null) + { + handle.Dispose(); + } + + _audioAgentRuntimeState = AudioAgentRuntimeState.End; + string path = _pendingLoad.Path; + bool bAsync = _pendingLoad.BAsync; + bool bInPool = _pendingLoad.BInPool; + _pendingLoad = null; + Load(path, bAsync, bInPool); + } + else if (handle != null) + { + if (_audioData != null) + { + AudioData.DeAlloc(_audioData); + _audioData = null; + } + + _audioData = AudioData.Alloc(handle, _inPool); + + _source.clip = handle.AssetObject as AudioClip; + if (_source.clip != null) + { + _source.Play(); + _audioAgentRuntimeState = AudioAgentRuntimeState.Playing; + } + else + { + _audioAgentRuntimeState = AudioAgentRuntimeState.End; + } + } + else + { + _audioAgentRuntimeState = AudioAgentRuntimeState.End; + } + } + + /// + /// 轮询音频代理辅助器。 + /// + /// 逻辑流逝时间,以秒为单位。 + public void Update(float elapseSeconds) + { + if (_audioAgentRuntimeState == AudioAgentRuntimeState.Playing) + { + if (!_source.isPlaying) + { + _audioAgentRuntimeState = AudioAgentRuntimeState.End; + } + } + else if (_audioAgentRuntimeState == AudioAgentRuntimeState.FadingOut) + { + if (_fadeoutTimer > 0f) + { + _fadeoutTimer -= elapseSeconds; + _source.volume = _volume * _fadeoutTimer / FADEOUT_DURATION; + } + else + { + Stop(); + if (_pendingLoad != null) + { + string path = _pendingLoad.Path; + bool bAsync = _pendingLoad.BAsync; + bool bInPool = _pendingLoad.BInPool; + _pendingLoad = null; + Load(path, bAsync, bInPool); + } + + _source.volume = _volume; + } + } + + _duration += elapseSeconds; + } + + /// + /// 销毁音频代理辅助器。 + /// + public void Destroy() + { + if (_transform != null) + { + Object.Destroy(_transform.gameObject); + } + + if (_audioData != null) + { + AudioData.DeAlloc(_audioData); + } + } + } +} diff --git a/Runtime/Audio/AudioAgent.cs.meta b/Runtime/Audio/AudioAgent.cs.meta new file mode 100644 index 0000000..1b0c8d4 --- /dev/null +++ b/Runtime/Audio/AudioAgent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6d4824a14181a0d4d921eb98fec100fe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Audio/AudioAgentRuntimeState.cs b/Runtime/Audio/AudioAgentRuntimeState.cs new file mode 100644 index 0000000..fe9d325 --- /dev/null +++ b/Runtime/Audio/AudioAgentRuntimeState.cs @@ -0,0 +1,33 @@ +namespace AlicizaX.Audio.Runtime +{ + /// + /// 音频代理辅助器运行时状态枚举。 + /// + public enum AudioAgentRuntimeState + { + /// + /// 无状态。 + /// + None, + + /// + /// 加载中状态。 + /// + Loading, + + /// + /// 播放中状态。 + /// + Playing, + + /// + /// 渐渐消失状态。 + /// + FadingOut, + + /// + /// 结束状态。 + /// + End, + }; +} \ No newline at end of file diff --git a/Runtime/Audio/AudioAgentRuntimeState.cs.meta b/Runtime/Audio/AudioAgentRuntimeState.cs.meta new file mode 100644 index 0000000..59ab5a2 --- /dev/null +++ b/Runtime/Audio/AudioAgentRuntimeState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f4ad4b8dc4cf4ecd813e58ec37406ba0 +timeCreated: 1694849619 \ No newline at end of file diff --git a/Runtime/Audio/AudioCategory.cs b/Runtime/Audio/AudioCategory.cs new file mode 100644 index 0000000..1afede2 --- /dev/null +++ b/Runtime/Audio/AudioCategory.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Audio; + +namespace AlicizaX.Audio.Runtime +{ + /// + /// 音频轨道(类别)。 + /// + [Serializable] + public class AudioCategory + { + [SerializeField] private AudioMixer audioMixer = null; + + public List AudioAgents; + private readonly AudioMixerGroup _audioMixerGroup; + private AudioGroupConfig _audioGroupConfig; + private int _maxChannel; + private bool _bEnable = true; + + /// + /// 音频混响器。 + /// + public AudioMixer AudioMixer => audioMixer; + + /// + /// 音频混响器组。 + /// + public AudioMixerGroup AudioMixerGroup => _audioMixerGroup; + + /// + /// 音频组配置。 + /// + public AudioGroupConfig AudioGroupConfig => _audioGroupConfig; + + /// + /// 实例化根节点。 + /// + public Transform InstanceRoot { private set; get; } + + /// + /// 音频轨道是否启用。 + /// + public bool Enable + { + get => _bEnable; + set + { + if (_bEnable != value) + { + _bEnable = value; + if (!_bEnable) + { + foreach (var audioAgent in AudioAgents) + { + if (audioAgent != null) + { + audioAgent.Stop(); + } + } + } + } + } + } + + /// + /// 音频轨道构造函数。 + /// + /// 最大Channel。 + /// 音频混响器。 + /// 音频轨道组配置。 + public AudioCategory(int maxChannel, AudioMixer audioMixer, AudioGroupConfig audioGroupConfig) + { + var audioModule = ModuleSystem.GetModule(); + + this.audioMixer = audioMixer; + _maxChannel = maxChannel; + _audioGroupConfig = audioGroupConfig; + AudioMixerGroup[] audioMixerGroups = audioMixer.FindMatchingGroups(Utility.Text.Format("Master/{0}", audioGroupConfig.AudioType.ToString())); + if (audioMixerGroups.Length > 0) + { + _audioMixerGroup = audioMixerGroups[0]; + } + else + { + _audioMixerGroup = audioMixer.FindMatchingGroups("Master")[0]; + } + + AudioAgents = new List(32); + InstanceRoot = new GameObject(Utility.Text.Format("Audio Category - {0}", _audioMixerGroup.name)).transform; + InstanceRoot.SetParent(audioModule.InstanceRoot); + for (int index = 0; index < _maxChannel; index++) + { + AudioAgent audioAgent = new AudioAgent(); + audioAgent.Init(this, index); + AudioAgents.Add(audioAgent); + } + } + + /// + /// 增加音频。 + /// + /// + public void AddAudio(int num) + { + _maxChannel += num; + for (int i = 0; i < num; i++) + { + AudioAgents.Add(null); + } + } + + public AudioAgent Play(AudioClip clip) + { + if (!_bEnable) + { + return null; + } + + int freeChannel = -1; + float duration = -1; + + for (int i = 0; i < AudioAgents.Count; i++) + { + if (AudioAgents[i].IsFree) + { + freeChannel = i; + break; + } + else if (AudioAgents[i].Duration > duration) + { + duration = AudioAgents[i].Duration; + freeChannel = i; + } + } + + if (freeChannel >= 0) + { + if (AudioAgents[freeChannel] == null) + { + AudioAgents[freeChannel] = AudioAgent.Create(clip, this); + } + else + { + AudioAgents[freeChannel].SetAudioClip(clip); + } + + return AudioAgents[freeChannel]; + } + else + { + Log.Error($"Here is no channel to play audio clip"); + return null; + } + } + + /// + /// 播放音频。 + /// + /// + /// + /// + /// + public AudioAgent Play(string path, bool bAsync, bool bInPool = false) + { + if (!_bEnable) + { + return null; + } + + int freeChannel = -1; + float duration = -1; + + for (int i = 0; i < AudioAgents.Count; i++) + { + if (AudioAgents[i].AudioData?.AssetHandle == null || AudioAgents[i].IsFree) + { + freeChannel = i; + break; + } + else if (AudioAgents[i].Duration > duration) + { + duration = AudioAgents[i].Duration; + freeChannel = i; + } + } + + if (freeChannel >= 0) + { + if (AudioAgents[freeChannel] == null) + { + AudioAgents[freeChannel] = AudioAgent.Create(path, bAsync, this, bInPool); + } + else + { + AudioAgents[freeChannel].Load(path, bAsync, bInPool); + } + + return AudioAgents[freeChannel]; + } + else + { + Log.Error($"Here is no channel to play audio {path}"); + return null; + } + } + + /// + /// 暂停音频。 + /// + /// 是否渐出 + public void Stop(bool fadeout) + { + for (int i = 0; i < AudioAgents.Count; ++i) + { + if (AudioAgents[i] != null) + { + AudioAgents[i].Stop(fadeout); + } + } + } + + /// + /// 音频轨道轮询。 + /// + /// 逻辑流逝时间,以秒为单位。 + public void Update(float elapseSeconds) + { + for (int i = 0; i < AudioAgents.Count; ++i) + { + if (AudioAgents[i] != null) + { + AudioAgents[i].Update(elapseSeconds); + } + } + } + } +} diff --git a/Runtime/Audio/AudioCategory.cs.meta b/Runtime/Audio/AudioCategory.cs.meta new file mode 100644 index 0000000..04711aa --- /dev/null +++ b/Runtime/Audio/AudioCategory.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6aefbd5a06fe1784590d373a93d1cf8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Audio/AudioComponent.cs b/Runtime/Audio/AudioComponent.cs new file mode 100644 index 0000000..3a42305 --- /dev/null +++ b/Runtime/Audio/AudioComponent.cs @@ -0,0 +1,48 @@ +using AlicizaX; +using UnityEngine; +using UnityEngine.Audio; + +namespace AlicizaX.Audio.Runtime +{ + /// + /// 音效管理,为游戏提供统一的音效播放接口。 + /// + /// 场景3D音效挂到场景物件、技能3D音效挂到技能特效上,并在AudioSource的Output上设置对应分类的AudioMixerGroup + [DisallowMultipleComponent] + [AddComponentMenu("Game Framework/Audio")] + public sealed class AudioComponent : MonoBehaviour + { + [SerializeField] private AudioMixer m_AudioMixer; + + [SerializeField] private Transform m_InstanceRoot = null; + + [SerializeField] private AudioGroupConfig[] m_AudioGroupConfigs = null; + + private IAudioModule _audioModule; + + private void Awake() + { + _audioModule = ModuleSystem.RegisterModule(); + } + + /// + /// 初始化音频模块。 + /// + void Start() + { + if (m_InstanceRoot == null) + { + m_InstanceRoot = new GameObject("[AudioModule Instances]").transform; + m_InstanceRoot.SetParent(gameObject.transform); + m_InstanceRoot.localScale = Vector3.one; + } + + if (m_AudioMixer == null) + { + m_AudioMixer = Resources.Load("AudioMixer"); + } + + _audioModule.Initialize(m_AudioGroupConfigs, m_InstanceRoot, m_AudioMixer); + } + } +} diff --git a/Runtime/Audio/AudioComponent.cs.meta b/Runtime/Audio/AudioComponent.cs.meta new file mode 100644 index 0000000..0ad9335 --- /dev/null +++ b/Runtime/Audio/AudioComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7d0b3cff83fd3874394b1b456bb54dab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Audio/AudioData.cs b/Runtime/Audio/AudioData.cs new file mode 100644 index 0000000..ce5732d --- /dev/null +++ b/Runtime/Audio/AudioData.cs @@ -0,0 +1,67 @@ +using AlicizaX; +using YooAsset; + +namespace AlicizaX.Audio.Runtime +{ + /// + /// 音频数据。 + /// + public class AudioData : MemoryObject + { + /// + /// 资源句柄。 + /// + public AssetHandle AssetHandle { private set; get; } + + /// + /// 是否使用对象池。 + /// + public bool InPool { private set; get; } = false; + + public override void InitFromPool() + { + } + + /// + /// 回收到对象池。 + /// + public override void RecycleToPool() + { + if (!InPool) + { + AssetHandle.Dispose(); + } + + InPool = false; + AssetHandle = null; + } + + /// + /// 生成音频数据。 + /// + /// 资源操作句柄。 + /// 是否使用对象池。 + /// 音频数据。 + internal static AudioData Alloc(AssetHandle assetHandle, bool inPool) + { + AudioData ret = MemoryPool.Acquire(); + ret.AssetHandle = assetHandle; + ret.InPool = inPool; + ret.InitFromPool(); + return ret; + } + + /// + /// 回收音频数据。 + /// + /// + internal static void DeAlloc(AudioData audioData) + { + if (audioData != null) + { + MemoryPool.Release(audioData); + audioData.RecycleToPool(); + } + } + } +} diff --git a/Runtime/Audio/AudioData.cs.meta b/Runtime/Audio/AudioData.cs.meta new file mode 100644 index 0000000..429452f --- /dev/null +++ b/Runtime/Audio/AudioData.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 036a1af4acb84666b73909ba28455cfa +timeCreated: 1742472335 \ No newline at end of file diff --git a/Runtime/Audio/AudioGroupConfig.cs b/Runtime/Audio/AudioGroupConfig.cs new file mode 100644 index 0000000..469ae2e --- /dev/null +++ b/Runtime/Audio/AudioGroupConfig.cs @@ -0,0 +1,48 @@ +using System; +using UnityEngine; + +namespace AlicizaX.Audio.Runtime +{ + /// + /// 音频轨道组配置。 + /// + [Serializable] + public sealed class AudioGroupConfig + { + [SerializeField] private string m_Name = null; + + [SerializeField] private bool m_Mute = false; + + [SerializeField, Range(0f, 1f)] private float m_Volume = 1f; + + [SerializeField] private int m_AgentHelperCount = 1; + + public AudioType AudioType; + + public AudioRolloffMode audioRolloffMode = AudioRolloffMode.Logarithmic; + + public float minDistance = 1f; + + public float maxDistance = 500f; + + public string Name + { + get { return m_Name; } + } + + public bool Mute + { + get { return m_Mute; } + } + + public float Volume + { + get { return m_Volume; } + } + + public int AgentHelperCount + { + get { return m_AgentHelperCount; } + } + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioGroupConfig.cs.meta b/Runtime/Audio/AudioGroupConfig.cs.meta new file mode 100644 index 0000000..2a2bb7c --- /dev/null +++ b/Runtime/Audio/AudioGroupConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 067935df275fd1340a935f81e7f3a768 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Audio/AudioModule.cs b/Runtime/Audio/AudioModule.cs new file mode 100644 index 0000000..d5fdbad --- /dev/null +++ b/Runtime/Audio/AudioModule.cs @@ -0,0 +1,593 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using AlicizaX.Resource.Runtime; +using AlicizaX; +using UnityEngine; +using UnityEngine.Audio; +using YooAsset; +using AudioType = AlicizaX.Audio.Runtime.AudioType; + + +namespace AlicizaX.Audio.Runtime +{ + internal class AudioModule : IAudioModule + { + public const string MUSIC_VOLUME_NAME = "MusicVolume"; + public const string UI_SOUND_VOLUME_NAME = "UISoundVolume"; + public const string VOICE_VOLUME_NAME = "VoiceVolume"; + + private AudioMixer _audioMixer; + private Transform _instanceRoot = null; + private AudioGroupConfig[] _audioGroupConfigs = null; + + private float _volume = 1f; + private bool _enable = true; + private readonly AudioCategory[] _audioCategories = new AudioCategory[(int)AudioType.Max]; + private readonly float[] _categoriesVolume = new float[(int)AudioType.Max]; + private bool _bUnityAudioDisabled = false; + + #region Public Propreties + + public Dictionary AudioClipPool { get; set; } = new Dictionary(); + + /// + /// 音频混响器。 + /// + public AudioMixer AudioMixer => _audioMixer; + + /// + /// 实例化根节点。 + /// + public Transform InstanceRoot => _instanceRoot; + + /// + /// 总音量控制。 + /// + public float Volume + { + get + { + if (_bUnityAudioDisabled) + { + return 0.0f; + } + + return _volume; + } + set + { + if (_bUnityAudioDisabled) + { + return; + } + + _volume = value; + AudioListener.volume = _volume; + } + } + + /// + /// 总开关。 + /// + public bool Enable + { + get + { + if (_bUnityAudioDisabled) + { + return false; + } + + return _enable; + } + set + { + if (_bUnityAudioDisabled) + { + return; + } + + _enable = value; + AudioListener.volume = _enable ? _volume : 0f; + } + } + + /// + /// 音乐音量。 + /// + public float MusicVolume + { + get + { + if (_bUnityAudioDisabled) + { + return 0.0f; + } + + return _categoriesVolume[(int)AudioType.Music]; + } + set + { + if (_bUnityAudioDisabled) + { + return; + } + + float volume = Mathf.Clamp(value, 0.0001f, 1.0f); + _categoriesVolume[(int)AudioType.Music] = volume; + _audioMixer.SetFloat(MUSIC_VOLUME_NAME, Mathf.Log10(volume) * 20f); + } + } + + /// + /// 音效音量。 + /// + public float SoundVolume + { + get + { + if (_bUnityAudioDisabled) + { + return 0.0f; + } + + return _categoriesVolume[(int)AudioType.Sound]; + } + set + { + if (_bUnityAudioDisabled) + { + return; + } + + float volume = Mathf.Clamp(value, 0.0001f, 1.0f); + _categoriesVolume[(int)AudioType.Sound] = volume; + _audioMixer.SetFloat("SoundVolume", Mathf.Log10(volume) * 20f); + } + } + + /// + /// UI音效音量。 + /// + public float UISoundVolume + { + get + { + if (_bUnityAudioDisabled) + { + return 0.0f; + } + + return _categoriesVolume[(int)AudioType.UISound]; + } + set + { + if (_bUnityAudioDisabled) + { + return; + } + + float volume = Mathf.Clamp(value, 0.0001f, 1.0f); + _categoriesVolume[(int)AudioType.UISound] = volume; + _audioMixer.SetFloat(UI_SOUND_VOLUME_NAME, Mathf.Log10(volume) * 20f); + } + } + + /// + /// 语音音量。 + /// + public float VoiceVolume + { + get + { + if (_bUnityAudioDisabled) + { + return 0.0f; + } + + return _categoriesVolume[(int)AudioType.Voice]; + } + set + { + if (_bUnityAudioDisabled) + { + return; + } + + float volume = Mathf.Clamp(value, 0.0001f, 1.0f); + _categoriesVolume[(int)AudioType.Voice] = volume; + _audioMixer.SetFloat(VOICE_VOLUME_NAME, Mathf.Log10(volume) * 20f); + } + } + + /// + /// 音乐开关 + /// + public bool MusicEnable + { + get + { + if (_bUnityAudioDisabled) + { + return false; + } + + if (_audioMixer.GetFloat(MUSIC_VOLUME_NAME, out var db)) + { + return db > -80f; + } + else + { + return false; + } + } + set + { + if (_bUnityAudioDisabled) + { + return; + } + + _audioCategories[(int)AudioType.Music].Enable = value; + + // 音乐采用0音量方式,避免恢复播放时的复杂逻辑 + if (value) + { + _audioMixer.SetFloat(MUSIC_VOLUME_NAME, Mathf.Log10(_categoriesVolume[(int)AudioType.Music]) * 20f); + } + else + { + _audioMixer.SetFloat(MUSIC_VOLUME_NAME, -80f); + } + } + } + + /// + /// 音效开关。 + /// + public bool SoundEnable + { + get + { + if (_bUnityAudioDisabled) + { + return false; + } + + return _audioCategories[(int)AudioType.Sound].Enable; + } + set + { + if (_bUnityAudioDisabled) + { + return; + } + + _audioCategories[(int)AudioType.Sound].Enable = value; + } + } + + /// + /// UI音效开关。 + /// + public bool UISoundEnable + { + get + { + if (_bUnityAudioDisabled) + { + return false; + } + + return _audioCategories[(int)AudioType.UISound].Enable; + } + set + { + if (_bUnityAudioDisabled) + { + return; + } + + _audioCategories[(int)AudioType.UISound].Enable = value; + } + } + + /// + /// 语音开关。 + /// + public bool VoiceEnable + { + get + { + if (_bUnityAudioDisabled) + { + return false; + } + + return _audioCategories[(int)AudioType.Voice].Enable; + } + set + { + if (_bUnityAudioDisabled) + { + return; + } + + _audioCategories[(int)AudioType.Voice].Enable = value; + } + } + + #endregion + + private IResourceModule _resourceModule; + + + void IModule.Dispose() + { + StopAll(fadeout: false); + CleanSoundPool(); + } + + /// + /// 初始化音频模块。 + /// + /// 音频轨道组配置。 + /// 实例化根节点。 + /// 音频混响器。 + /// + public void Initialize(AudioGroupConfig[] audioGroupConfigs, Transform instanceRoot = null, AudioMixer audioMixer = null) + { + _resourceModule = ModuleSystem.GetModule(); + if (_instanceRoot == null) + { + _instanceRoot = instanceRoot; + } + + if (audioGroupConfigs == null) + { + throw new GameFrameworkException("AudioGroupConfig[] is invalid."); + } + + _audioGroupConfigs = audioGroupConfigs; + + if (_instanceRoot == null) + { + _instanceRoot = new GameObject("[AudioModule Instances]").transform; + _instanceRoot.localScale = Vector3.one; + UnityEngine.Object.DontDestroyOnLoad(_instanceRoot); + } + +#if UNITY_EDITOR + try + { + TypeInfo typeInfo = typeof(AudioSettings).GetTypeInfo(); + PropertyInfo propertyInfo = typeInfo.GetDeclaredProperty("unityAudioDisabled"); + _bUnityAudioDisabled = (bool)propertyInfo.GetValue(null); + if (_bUnityAudioDisabled) + { + return; + } + } + catch (Exception e) + { + Log.Error(e.ToString()); + } +#endif + + if (audioMixer != null) + { + _audioMixer = audioMixer; + } + + if (_audioMixer == null) + { + _audioMixer = Resources.Load("AudioMixer"); + } + + for (int index = 0; index < (int)AudioType.Max; ++index) + { + AudioType audioType = (AudioType)index; + AudioGroupConfig audioGroupConfig = _audioGroupConfigs.First(t => t.AudioType == audioType); + _audioCategories[index] = new AudioCategory(audioGroupConfig.AgentHelperCount, _audioMixer, audioGroupConfig); + _categoriesVolume[index] = audioGroupConfig.Volume; + } + } + + /// + /// 重启音频模块。 + /// + public void Restart() + { + if (_bUnityAudioDisabled) + { + return; + } + + CleanSoundPool(); + + for (int i = 0; i < (int)AudioType.Max; ++i) + { + var audioCategory = _audioCategories[i]; + if (audioCategory != null) + { + for (int j = 0; j < audioCategory.AudioAgents.Count; ++j) + { + var audioAgent = audioCategory.AudioAgents[j]; + if (audioAgent != null) + { + audioAgent.Destroy(); + audioAgent = null; + } + } + } + + audioCategory = null; + } + + Initialize(_audioGroupConfigs); + } + + /// + /// 播放,如果超过最大发声数采用fadeout的方式复用最久播放的AudioSource。 + /// + /// 声音类型 + /// 声音文件路径 + /// 是否循环播放> + /// 音量(0-1.0) + /// 是否异步加载 + /// 是否支持资源池 + public AudioAgent Play(AudioType type, string path, bool bLoop = false, float volume = 1.0f, bool bAsync = false, bool bInPool = false) + { + if (_bUnityAudioDisabled) + { + return null; + } + + AudioAgent audioAgent = _audioCategories[(int)type].Play(path, bAsync, bInPool); + { + if (audioAgent != null) + { + audioAgent.IsLoop = bLoop; + audioAgent.Volume = volume; + } + + return audioAgent; + } + } + + public AudioAgent Play(AudioType type, AudioClip clip, bool bLoop = false, float volume = 1.0f) + { + if (_bUnityAudioDisabled) + { + return null; + } + + AudioAgent audioAgent = _audioCategories[(int)type].Play(clip); + { + if (audioAgent != null) + { + audioAgent.IsLoop = bLoop; + audioAgent.Volume = volume; + } + + return audioAgent; + } + } + + /// + /// 停止某类声音播放。 + /// + /// 声音类型。 + /// 是否渐消。 + public void Stop(AudioType type, bool fadeout) + { + if (_bUnityAudioDisabled) + { + return; + } + + _audioCategories[(int)type].Stop(fadeout); + } + + /// + /// 停止所有声音。 + /// + /// 是否渐消。 + public void StopAll(bool fadeout) + { + if (_bUnityAudioDisabled) + { + return; + } + + for (int i = 0; i < (int)AudioType.Max; ++i) + { + if (_audioCategories[i] != null) + { + _audioCategories[i].Stop(fadeout); + } + } + } + + /// + /// 预先加载AudioClip,并放入对象池。 + /// + /// AudioClip的AssetPath集合。 + public void PutInAudioPool(List list) + { + if (_bUnityAudioDisabled) + { + return; + } + + foreach (string path in list) + { + if (AudioClipPool != null && !AudioClipPool.ContainsKey(path)) + { + AssetHandle assetData = _resourceModule.LoadAssetAsyncHandle(path); + assetData.Completed += handle => { AudioClipPool?.Add(path, handle); }; + } + } + } + + /// + /// 将部分AudioClip从对象池移出。 + /// + /// AudioClip的AssetPath集合。 + public void RemoveClipFromPool(List list) + { + if (_bUnityAudioDisabled) + { + return; + } + + foreach (string path in list) + { + if (AudioClipPool.ContainsKey(path)) + { + AudioClipPool[path].Dispose(); + AudioClipPool.Remove(path); + } + } + } + + /// + /// 清空AudioClip的对象池。 + /// + public void CleanSoundPool() + { + if (_bUnityAudioDisabled) + { + return; + } + + foreach (var dic in AudioClipPool) + { + dic.Value.Dispose(); + } + + AudioClipPool.Clear(); + } + + /// + /// 音频模块轮询。 + /// + /// 逻辑流逝时间,以秒为单位。 + /// 真实流逝时间,以秒为单位。 + void IModuleUpdate.Update(float elapseSeconds, float realElapseSeconds) + { + foreach (var audioCategory in _audioCategories) + { + if (audioCategory != null) + { + audioCategory.Update(elapseSeconds); + } + } + } + + + public int Priority { get; } + } +} diff --git a/Runtime/Audio/AudioModule.cs.meta b/Runtime/Audio/AudioModule.cs.meta new file mode 100644 index 0000000..c67de97 --- /dev/null +++ b/Runtime/Audio/AudioModule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c35bbf6a82844c72b71b9bbe44f44a1e +timeCreated: 1694847017 \ No newline at end of file diff --git a/Runtime/Audio/AudioType.cs b/Runtime/Audio/AudioType.cs new file mode 100644 index 0000000..82f0014 --- /dev/null +++ b/Runtime/Audio/AudioType.cs @@ -0,0 +1,34 @@ +namespace AlicizaX.Audio.Runtime +{ + /// + /// 音效分类,可分别关闭/开启对应分类音效。 + /// + /// 命名与AudioMixer中分类名保持一致。 + public enum AudioType + { + /// + /// 声音音效。 + /// + Sound, + + /// + /// UI声效。 + /// + UISound, + + /// + /// 背景音乐音效。 + /// + Music, + + /// + /// 人声音效。 + /// + Voice, + + /// + /// 最大。 + /// + Max + } +} \ No newline at end of file diff --git a/Runtime/Audio/AudioType.cs.meta b/Runtime/Audio/AudioType.cs.meta new file mode 100644 index 0000000..2353c7c --- /dev/null +++ b/Runtime/Audio/AudioType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b4b86f93180273b4996d4f8c428def09 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Audio/IAudioModule.cs b/Runtime/Audio/IAudioModule.cs new file mode 100644 index 0000000..b46f74e --- /dev/null +++ b/Runtime/Audio/IAudioModule.cs @@ -0,0 +1,130 @@ +using System.Collections.Generic; +using AlicizaX; +using UnityEngine; +using UnityEngine.Audio; +using YooAsset; + +namespace AlicizaX.Audio.Runtime +{ + public interface IAudioModule:IModule,IModuleUpdate + { + /// + /// 总音量控制。 + /// + public float Volume { get; set; } + + /// + /// 总开关。 + /// + public bool Enable { get; set; } + + /// + /// 音乐音量 + /// + public float MusicVolume { get; set; } + + /// + /// 音效音量。 + /// + public float SoundVolume { get; set; } + + /// + /// UI音效音量。 + /// + public float UISoundVolume { get; set; } + + /// + /// 语音音量。 + /// + public float VoiceVolume { get; set; } + + /// + /// 音乐开关。 + /// + public bool MusicEnable { get; set; } + + /// + /// 音效开关。 + /// + public bool SoundEnable { get; set; } + + /// + /// UI音效开关。 + /// + public bool UISoundEnable { get; set; } + + /// + /// 语音开关。 + /// + public bool VoiceEnable { get; set; } + + /// + /// 音频混响器。 + /// + public AudioMixer AudioMixer { get;} + + /// + /// 实例化根节点。 + /// + public Transform InstanceRoot { get;} + + public Dictionary AudioClipPool { get; set; } + + /// + /// 初始化音频模块。 + /// + /// 音频轨道组配置。 + /// 实例化根节点。 + /// 音频混响器。 + /// + public void Initialize(AudioGroupConfig[] audioGroupConfigs, Transform instanceRoot = null, AudioMixer audioMixer = null); + + /// + /// 重启音频模块。 + /// + public void Restart(); + + /// + /// 播放音频接口。 + /// + /// 如果超过最大发声数采用fadeout的方式复用最久播放的AudioSource。 + /// 声音类型。 + /// 声音文件路径。 + /// 是否循环播放。> + /// 音量(0-1.0)。 + /// 是否异步加载。 + /// 是否支持资源池。 + public AudioAgent Play(AudioType type, string path, bool bLoop = false, float volume = 1.0f, bool bAsync = false, bool bInPool = false); + + public AudioAgent Play(AudioType type,AudioClip clip,bool loop=false,float volume = 1.0f); + /// + /// 停止某类声音播放。 + /// + /// 声音类型。 + /// 是否渐消。 + public void Stop(AudioType type, bool fadeout); + + /// + /// 停止所有声音。 + /// + /// 是否渐消。 + public void StopAll(bool fadeout); + + /// + /// 预先加载AudioClip,并放入对象池。 + /// + /// AudioClip的AssetPath集合。 + public void PutInAudioPool(List list); + + /// + /// 将部分AudioClip从对象池移出。 + /// + /// AudioClip的AssetPath集合。 + public void RemoveClipFromPool(List list); + + /// + /// 清空AudioClip的对象池。 + /// + public void CleanSoundPool(); + } +} diff --git a/Runtime/Audio/IAudioModule.cs.meta b/Runtime/Audio/IAudioModule.cs.meta new file mode 100644 index 0000000..cd3170b --- /dev/null +++ b/Runtime/Audio/IAudioModule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f2a332020228453d8546d5d74aa60f9c +timeCreated: 1694847151 \ No newline at end of file diff --git a/Runtime/Audio/Resources.meta b/Runtime/Audio/Resources.meta new file mode 100644 index 0000000..270c429 --- /dev/null +++ b/Runtime/Audio/Resources.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 64a654fc0d761c24e9676a185ea4df8e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Audio/Resources/AudioMixer.mixer b/Runtime/Audio/Resources/AudioMixer.mixer new file mode 100644 index 0000000..284455c --- /dev/null +++ b/Runtime/Audio/Resources/AudioMixer.mixer @@ -0,0 +1,708 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!244 &-8255999005317483048 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: 5256fbc85eb3f884eb780f730c8da825 + m_EffectName: Attenuation + m_MixLevel: 989fcb1cc28d4de4ea6e56c14101e674 + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!244 &-8165904320333843496 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: 6b8ac75d99b7e57489701e084ea19b1f + m_EffectName: Attenuation + m_MixLevel: b19e64315aff6dc4a81ae36b22fc0492 + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!244 &-7861682458482441818 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: dcc48474d251d554e9f6f95668853a39 + m_EffectName: Receive + m_MixLevel: e6248274fab455749bc046d72eace6de + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!243 &-7758028812591520460 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Sound - 2 + m_AudioMixer: {fileID: 24100000} + m_GroupID: 039cd795affa7134a8d5f5d43d3b659d + m_Children: [] + m_Volume: 2a8ce0f3383c3f0468a04fa3fc5e317d + m_Pitch: b47f0c73299cd9b4fba9896e70683903 + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: -3825599753161013374} + m_UserColorIndex: 2 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!243 &-6280614258348125054 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Sound - 1 + m_AudioMixer: {fileID: 24100000} + m_GroupID: c0d40106c2ffb1a44bd48f50b210ee20 + m_Children: [] + m_Volume: f62a8b3fe89df00409532af739ee4e02 + m_Pitch: 77212647508232a458ac7d48fb55d037 + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: -1890011256548497850} + m_UserColorIndex: 2 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!244 &-4958177229083455073 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: 104113b95764fe344a5d25469377c800 + m_EffectName: Attenuation + m_MixLevel: 9ef29befaad178d4386e9d5ac022f964 + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!243 &-4372808504093502661 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Sound - 3 + m_AudioMixer: {fileID: 24100000} + m_GroupID: 5f20d1b8f9ac1914dac8beae718e7d40 + m_Children: [] + m_Volume: e54edf7c1bf7ee44297e65adce5b10b7 + m_Pitch: 8542b6bfd7b7bfc4d9b961ba97edf0d2 + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: 6637688299338053042} + m_UserColorIndex: 2 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!243 &-4209890294574411305 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Music + m_AudioMixer: {fileID: 24100000} + m_GroupID: efe8591c00084024187b9df78858c0af + m_Children: + - {fileID: 1543978434442340687} + m_Volume: 6d4c2b8bc0ef38d44b2fbff2b3298ab4 + m_Pitch: 862389c428a73854ab442dd043008729 + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: 246003612463095956} + m_UserColorIndex: 1 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!244 &-3825599753161013374 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: d1bbcc7cbe0a53c459c9064925118e41 + m_EffectName: Attenuation + m_MixLevel: e43bb5de098a2ec49807913fa5fdd2f7 + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!243 &-3720557753501792270 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: UISound - 1 + m_AudioMixer: {fileID: 24100000} + m_GroupID: e012b6d2e0501df43a88eb6beff8ae07 + m_Children: [] + m_Volume: 265eaf7c8910ab842a845c7bb5e570c4 + m_Pitch: bf3ca9b57c9a67b40944a59839b12f62 + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: 5734415080786067514} + m_UserColorIndex: 3 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!243 &-3395020342500439107 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Voice + m_AudioMixer: {fileID: 24100000} + m_GroupID: 5117f9b5a365ec049a9d5891c563b893 + m_Children: + - {fileID: -1649243360580130678} + m_Volume: fe15a1b40c14ea646a13dacb15b6a73b + m_Pitch: 3398197a464677a4186e0cecd66bb13c + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: -8165904320333843496} + m_UserColorIndex: 6 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!243 &-2659745067392564156 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Sound - 0 + m_AudioMixer: {fileID: 24100000} + m_GroupID: 71c50c6b966d1f548a63193919ebfbad + m_Children: [] + m_Volume: 7835f2c4248cb3e43a1a773bab1f8b9d + m_Pitch: 30975daa872456b41bc18e0277e301e6 + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: -284252157345190109} + m_UserColorIndex: 2 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!244 &-1890011256548497850 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: 520975c71ea21c249b3cdf1f22032e57 + m_EffectName: Attenuation + m_MixLevel: 932e3e893621c5b46bff3c368017e689 + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!243 &-1649243360580130678 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Voice - 0 + m_AudioMixer: {fileID: 24100000} + m_GroupID: f46651e8ad3c6034b8764fd635dda3fd + m_Children: [] + m_Volume: 0bc64c1c6cebbeb40ba2f724fdcaa257 + m_Pitch: fff252bdd985513469f8607e016fc594 + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: -890847686165078200} + m_UserColorIndex: 6 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!244 &-998299258853400712 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: d7d19927abbe5584080506164cb4e644 + m_EffectName: Attenuation + m_MixLevel: 0b4264524e3eafc49b2daba7fba2ce97 + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!244 &-890847686165078200 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: 4b41d8aa7a7944041a8b9428add83eff + m_EffectName: Attenuation + m_MixLevel: 73b7a9825978be245a1962a1001b0212 + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!244 &-284252157345190109 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: 15668b74147caee41a72f695c3a2de56 + m_EffectName: Attenuation + m_MixLevel: f71e84fb9a62ff24cad690a0a86cc47e + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!244 &-21257493329335984 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: c9a6f7a9214534644bf2e6d83ff86569 + m_EffectName: Distortion + m_MixLevel: 3f356cddae5dba949a6e8f4d20564d3e + m_Parameters: + - m_ParameterName: Level + m_GUID: 080be1914d960974481df4bebe2a2d77 + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!241 &24100000 +AudioMixerController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: AudioMixer + m_OutputGroup: {fileID: 0} + m_MasterGroup: {fileID: 24300002} + m_Snapshots: + - {fileID: 24500006} + m_StartSnapshot: {fileID: 24500006} + m_SuspendThreshold: -80 + m_EnableSuspend: 1 + m_UpdateMode: 0 + m_ExposedParameters: + - guid: 7835f2c4248cb3e43a1a773bab1f8b9d + name: SoundVolume0 + - guid: 41591fd4a32f4034f880ecbc14ee69f1 + name: MusicVolume0 + - guid: 6e0d1a5935a802d41b27d9e2fad3ba2f + name: UISoundVolume0 + - guid: 0bc64c1c6cebbeb40ba2f724fdcaa257 + name: VoiceVolume0 + - guid: f62a8b3fe89df00409532af739ee4e02 + name: SoundVolume1 + - guid: 265eaf7c8910ab842a845c7bb5e570c4 + name: UISoundVolume1 + - guid: 2a8ce0f3383c3f0468a04fa3fc5e317d + name: SoundVolume2 + - guid: e83be6d6c4ae85142a51f584159c4ff6 + name: UISoundVolume2 + - guid: e54edf7c1bf7ee44297e65adce5b10b7 + name: SoundVolume3 + - guid: 2dd26f9dadf160f4bbd77f307c3f4f2e + name: UISoundVolume3 + - guid: ba83e724007d7e9459f157db3a54a741 + name: MasterVolume + - guid: 6d4c2b8bc0ef38d44b2fbff2b3298ab4 + name: MusicVolume + - guid: 3bbd22597ed32714eb271cf06b098c63 + name: SoundVolume + - guid: 7d1c7ed015f5dba4f934c33ef330c5eb + name: UISoundVolume + - guid: fe15a1b40c14ea646a13dacb15b6a73b + name: VoiceVolume + m_AudioMixerGroupViews: + - guids: + - 72c1e77b100e3274fbfb88ca2ca12c4d + - efe8591c00084024187b9df78858c0af + - 648e49a020cf83346a9220d606e4ff39 + - 5117f9b5a365ec049a9d5891c563b893 + - f46651e8ad3c6034b8764fd635dda3fd + - 1cf576bd46399874d9494863d6502d94 + - 71c50c6b966d1f548a63193919ebfbad + - df986418fa3e4ae448a1909ffbb633fb + - 29257697b1e6be546aa0558e342a15a6 + - c0d40106c2ffb1a44bd48f50b210ee20 + - 039cd795affa7134a8d5f5d43d3b659d + - 5f20d1b8f9ac1914dac8beae718e7d40 + - e012b6d2e0501df43a88eb6beff8ae07 + - e84c25a476798ea43a2f6de217af7dba + - 98657376d4096a947953ee04d82830c1 + name: View + m_CurrentViewIndex: 0 + m_TargetSnapshot: {fileID: 24500006} +--- !u!243 &24300002 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Master + m_AudioMixer: {fileID: 24100000} + m_GroupID: 72c1e77b100e3274fbfb88ca2ca12c4d + m_Children: + - {fileID: -4209890294574411305} + - {fileID: 7235523536312936115} + - {fileID: 7185772616558441635} + - {fileID: -3395020342500439107} + m_Volume: ba83e724007d7e9459f157db3a54a741 + m_Pitch: a2d2b77391464bb4887f0bcd3835015b + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: 24400004} + m_UserColorIndex: 8 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!244 &24400004 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: ce944d90cb57ee4418426132d391d900 + m_EffectName: Attenuation + m_MixLevel: 891c3ec10e0ae1b42a95c83727d411f1 + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!245 &24500006 +AudioMixerSnapshotController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Snapshot + m_AudioMixer: {fileID: 24100000} + m_SnapshotID: 91dee90f8902c804c9da7728ea355157 + m_FloatValues: + b47f0c73299cd9b4fba9896e70683903: 1 + ba83e724007d7e9459f157db3a54a741: 0 + fe15a1b40c14ea646a13dacb15b6a73b: 0 + 77212647508232a458ac7d48fb55d037: 1 + 3bbd22597ed32714eb271cf06b098c63: 0 + 30975daa872456b41bc18e0277e301e6: 1 + 6d4c2b8bc0ef38d44b2fbff2b3298ab4: -0.03 + 8542b6bfd7b7bfc4d9b961ba97edf0d2: 1 + m_TransitionOverrides: {} +--- !u!244 &246003612463095956 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: 860c45ba06e3cbd4794061eaefe970a3 + m_EffectName: Attenuation + m_MixLevel: bb4c221c9e3208941b1a2107831692ab + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!244 &281287199725387719 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: 812fbfe4555eb1641aeaeee69a13b4a6 + m_EffectName: Lowpass Simple + m_MixLevel: 391139084347578409e42387008bd110 + m_Parameters: + - m_ParameterName: Cutoff freq + m_GUID: b19756871f24b194d87c7d1fce3159e9 + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!244 &1413273517213151576 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: ce0c93d89826c7349a19b232116484db + m_EffectName: Attenuation + m_MixLevel: 7625365898787684c9bce9298a96f044 + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!243 &1543978434442340687 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Music - 0 + m_AudioMixer: {fileID: 24100000} + m_GroupID: 1cf576bd46399874d9494863d6502d94 + m_Children: [] + m_Volume: 41591fd4a32f4034f880ecbc14ee69f1 + m_Pitch: 9ad7e859a0cd1f142b59ffc659be28a7 + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: -8255999005317483048} + m_UserColorIndex: 1 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!243 &1601410790413250045 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: UISound - 3 + m_AudioMixer: {fileID: 24100000} + m_GroupID: 98657376d4096a947953ee04d82830c1 + m_Children: [] + m_Volume: 2dd26f9dadf160f4bbd77f307c3f4f2e + m_Pitch: 5627fa8b0176a344bbb4e59ac5e648d3 + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: 1413273517213151576} + m_UserColorIndex: 3 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!244 &2567082640316932351 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: a7394d86e1e2a1e4389182f0aec98773 + m_EffectName: Attenuation + m_MixLevel: cb8b6dfd682072d4fb81143ba077bc3f + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!243 &3865010338301366421 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: UISound - 2 + m_AudioMixer: {fileID: 24100000} + m_GroupID: e84c25a476798ea43a2f6de217af7dba + m_Children: [] + m_Volume: e83be6d6c4ae85142a51f584159c4ff6 + m_Pitch: c45439925e1cfd547894fd886160a11c + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: 7834155774142160187} + m_UserColorIndex: 3 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!244 &5734415080786067514 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: 20eac414948135e49b2b54a89235f15e + m_EffectName: Attenuation + m_MixLevel: d087be8c429707c4db724a61186f67f6 + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!244 &5954042604037024145 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: ba434c984ec3c4442bfe3a399c11572b + m_EffectName: Echo + m_MixLevel: 0323002ce01f29a4abebc242337ea816 + m_Parameters: + - m_ParameterName: Delay + m_GUID: f4cc548867353f843bc36a61722c6cbf + - m_ParameterName: Decay + m_GUID: e67c7b4426842f948a83f3dada794a99 + - m_ParameterName: Max channels + m_GUID: 75c7951a8373f4644a44979a8c5776ed + - m_ParameterName: Drymix + m_GUID: 4d26b3b7a84b31d499176a7879734ba1 + - m_ParameterName: Wetmix + m_GUID: 6efaeec45de20b045a4d560994684cba + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!244 &6255340296135181231 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: 3a251f2e65751b349bb255f73f0caeaf + m_EffectName: Duck Volume + m_MixLevel: 58cfdd03ab24c874cbe074238d636208 + m_Parameters: + - m_ParameterName: Threshold + m_GUID: b1b5c57689fc533408e6195c0a9e26a9 + - m_ParameterName: Ratio + m_GUID: dc9c2c635bce58746bd68c7dffb99ca0 + - m_ParameterName: Attack Time + m_GUID: 078ff252301e4594c880cd5754b8a563 + - m_ParameterName: Release Time + m_GUID: b3918efd0a966ea4882714c0f9edd40b + - m_ParameterName: Make-up Gain + m_GUID: 3cf881ca64b7cdb46b35d3593ff2cbe9 + - m_ParameterName: Knee + m_GUID: 31ea47a78d927ab4abf12f854bd4c626 + - m_ParameterName: Sidechain Mix + m_GUID: 468d86503d3572541a33c33bb9693dfd + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!244 &6554641470784401750 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: 4499c94d696ee5741a71370ed7431e12 + m_EffectName: Send + m_MixLevel: f01b57f2509f26f4b8f2772993c2b8c6 + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!244 &6637688299338053042 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: e84926aaeedf4074698e7d7f7f36b78b + m_EffectName: Attenuation + m_MixLevel: 8141a348079ee934686d3569f4758582 + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 +--- !u!243 &7040861873718444651 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: UISound - 0 + m_AudioMixer: {fileID: 24100000} + m_GroupID: 29257697b1e6be546aa0558e342a15a6 + m_Children: [] + m_Volume: 6e0d1a5935a802d41b27d9e2fad3ba2f + m_Pitch: 7d01f3677fe8c5b41a877b64cc509766 + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: -4958177229083455073} + m_UserColorIndex: 3 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!243 &7185772616558441635 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: UISound + m_AudioMixer: {fileID: 24100000} + m_GroupID: df986418fa3e4ae448a1909ffbb633fb + m_Children: + - {fileID: 7040861873718444651} + - {fileID: -3720557753501792270} + - {fileID: 3865010338301366421} + - {fileID: 1601410790413250045} + m_Volume: 7d1c7ed015f5dba4f934c33ef330c5eb + m_Pitch: 611d9d89c8a65b548b591e852596c35d + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: -998299258853400712} + m_UserColorIndex: 3 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!243 &7235523536312936115 +AudioMixerGroupController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Sound + m_AudioMixer: {fileID: 24100000} + m_GroupID: 648e49a020cf83346a9220d606e4ff39 + m_Children: + - {fileID: -2659745067392564156} + - {fileID: -6280614258348125054} + - {fileID: -7758028812591520460} + - {fileID: -4372808504093502661} + m_Volume: 3bbd22597ed32714eb271cf06b098c63 + m_Pitch: 7f8a6510dd472ff4db8b07c5079a2013 + m_Send: 00000000000000000000000000000000 + m_Effects: + - {fileID: 2567082640316932351} + m_UserColorIndex: 2 + m_Mute: 0 + m_Solo: 0 + m_BypassEffects: 0 +--- !u!244 &7834155774142160187 +AudioMixerEffectController: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: + m_EffectID: 09e809d6183106b4a804ff08ca8ff9ed + m_EffectName: Attenuation + m_MixLevel: 92339d91519b09543844a84cea03aed3 + m_Parameters: [] + m_SendTarget: {fileID: 0} + m_EnableWetMix: 0 + m_Bypass: 0 diff --git a/Runtime/Audio/Resources/AudioMixer.mixer.meta b/Runtime/Audio/Resources/AudioMixer.mixer.meta new file mode 100644 index 0000000..68fada2 --- /dev/null +++ b/Runtime/Audio/Resources/AudioMixer.mixer.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1af7a1b121ae17541a1967d430cef006 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger.meta b/Runtime/Debugger.meta new file mode 100644 index 0000000..cdb582f --- /dev/null +++ b/Runtime/Debugger.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c630b51c86a234242a84ff37c9342a82 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerActiveWindowType.cs b/Runtime/Debugger/DebuggerActiveWindowType.cs new file mode 100644 index 0000000..a4ccb3e --- /dev/null +++ b/Runtime/Debugger/DebuggerActiveWindowType.cs @@ -0,0 +1,28 @@ +namespace AlicizaX.Debugger.Runtime +{ + /// + /// 调试器激活窗口类型。 + /// + public enum DebuggerActiveWindowType : byte + { + /// + /// 总是打开。 + /// + AlwaysOpen = 0, + + /// + /// 仅在开发模式时打开。 + /// + OnlyOpenWhenDevelopment, + + /// + /// 仅在编辑器中打开。 + /// + OnlyOpenInEditor, + + /// + /// 总是关闭。 + /// + AlwaysClose, + } +} diff --git a/Runtime/Debugger/DebuggerActiveWindowType.cs.meta b/Runtime/Debugger/DebuggerActiveWindowType.cs.meta new file mode 100644 index 0000000..b73d304 --- /dev/null +++ b/Runtime/Debugger/DebuggerActiveWindowType.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d1baccfe5abb590428eb0322088bf4ce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.ConsoleWindow.cs b/Runtime/Debugger/DebuggerComponent.ConsoleWindow.cs new file mode 100644 index 0000000..972e435 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.ConsoleWindow.cs @@ -0,0 +1,420 @@ + +using System; +using System.Collections.Generic; +using UnityEngine; + + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + [Serializable] + private sealed class ConsoleWindow : IDebuggerWindow + { + private readonly Queue m_LogNodes = new Queue(); + + private Vector2 m_LogScrollPosition = Vector2.zero; + private Vector2 m_StackScrollPosition = Vector2.zero; + private int m_InfoCount = 0; + private int m_WarningCount = 0; + private int m_ErrorCount = 0; + private int m_FatalCount = 0; + private LogNode m_SelectedNode = null; + private bool m_LastLockScroll = true; + private bool m_LastInfoFilter = true; + private bool m_LastWarningFilter = true; + private bool m_LastErrorFilter = true; + private bool m_LastFatalFilter = true; + + [SerializeField] private bool m_LockScroll = true; + + [SerializeField] private int m_MaxLine = 100; + + [SerializeField] private bool m_InfoFilter = true; + + [SerializeField] private bool m_WarningFilter = true; + + [SerializeField] private bool m_ErrorFilter = true; + + [SerializeField] private bool m_FatalFilter = true; + + [SerializeField] private Color32 m_InfoColor = Color.white; + + [SerializeField] private Color32 m_WarningColor = Color.yellow; + + [SerializeField] private Color32 m_ErrorColor = Color.red; + + [SerializeField] private Color32 m_FatalColor = new Color(0.7f, 0.2f, 0.2f); + + public bool LockScroll + { + get { return m_LockScroll; } + set { m_LockScroll = value; } + } + + public int MaxLine + { + get { return m_MaxLine; } + set { m_MaxLine = value; } + } + + public bool InfoFilter + { + get { return m_InfoFilter; } + set { m_InfoFilter = value; } + } + + public bool WarningFilter + { + get { return m_WarningFilter; } + set { m_WarningFilter = value; } + } + + public bool ErrorFilter + { + get { return m_ErrorFilter; } + set { m_ErrorFilter = value; } + } + + public bool FatalFilter + { + get { return m_FatalFilter; } + set { m_FatalFilter = value; } + } + + public int InfoCount + { + get { return m_InfoCount; } + } + + public int WarningCount + { + get { return m_WarningCount; } + } + + public int ErrorCount + { + get { return m_ErrorCount; } + } + + public int FatalCount + { + get { return m_FatalCount; } + } + + public Color32 InfoColor + { + get { return m_InfoColor; } + set { m_InfoColor = value; } + } + + public Color32 WarningColor + { + get { return m_WarningColor; } + set { m_WarningColor = value; } + } + + public Color32 ErrorColor + { + get { return m_ErrorColor; } + set { m_ErrorColor = value; } + } + + public Color32 FatalColor + { + get { return m_FatalColor; } + set { m_FatalColor = value; } + } + + public void Initialize(params object[] args) + { + + Application.logMessageReceived += OnLogMessageReceived; + m_LockScroll = m_LastLockScroll = Utility.PlayerPrefsX.GetBool("Debugger.Console.LockScroll", true); + m_InfoFilter = m_LastInfoFilter = Utility.PlayerPrefsX.GetBool("Debugger.Console.InfoFilter", true); + m_WarningFilter = m_LastWarningFilter = Utility.PlayerPrefsX.GetBool("Debugger.Console.WarningFilter", true); + m_ErrorFilter = m_LastErrorFilter = Utility.PlayerPrefsX.GetBool("Debugger.Console.ErrorFilter", true); + m_FatalFilter = m_LastFatalFilter = Utility.PlayerPrefsX.GetBool("Debugger.Console.FatalFilter", true); + } + + public void Shutdown() + { + Application.logMessageReceived -= OnLogMessageReceived; + Clear(); + } + + public void OnEnter() + { + } + + public void OnLeave() + { + } + + public void OnUpdate(float elapseSeconds, float realElapseSeconds) + { + if (m_LastLockScroll != m_LockScroll) + { + m_LastLockScroll = m_LockScroll; + Utility.PlayerPrefsX.SetBool("Debugger.Console.LockScroll", m_LockScroll); + } + + if (m_LastInfoFilter != m_InfoFilter) + { + m_LastInfoFilter = m_InfoFilter; + Utility.PlayerPrefsX.SetBool("Debugger.Console.InfoFilter", m_InfoFilter); + } + + if (m_LastWarningFilter != m_WarningFilter) + { + m_LastWarningFilter = m_WarningFilter; + Utility.PlayerPrefsX.SetBool("Debugger.Console.WarningFilter", m_WarningFilter); + } + + if (m_LastErrorFilter != m_ErrorFilter) + { + m_LastErrorFilter = m_ErrorFilter; + Utility.PlayerPrefsX.SetBool("Debugger.Console.ErrorFilter", m_ErrorFilter); + } + + if (m_LastFatalFilter != m_FatalFilter) + { + m_LastFatalFilter = m_FatalFilter; + Utility.PlayerPrefsX.SetBool("Debugger.Console.FatalFilter", m_FatalFilter); + } + } + + public void OnDraw() + { + RefreshCount(); + + GUILayout.BeginHorizontal(); + { + if (GUILayout.Button("Clear All", GUILayout.Width(100f))) + { + Clear(); + } + + m_LockScroll = GUILayout.Toggle(m_LockScroll, "Lock Scroll", GUILayout.Width(90f)); + GUILayout.FlexibleSpace(); + m_InfoFilter = GUILayout.Toggle(m_InfoFilter, Utility.Text.Format("Info ({0})", m_InfoCount), GUILayout.Width(90f)); + m_WarningFilter = GUILayout.Toggle(m_WarningFilter, Utility.Text.Format("Warning ({0})", m_WarningCount), GUILayout.Width(90f)); + m_ErrorFilter = GUILayout.Toggle(m_ErrorFilter, Utility.Text.Format("Error ({0})", m_ErrorCount), GUILayout.Width(90f)); + m_FatalFilter = GUILayout.Toggle(m_FatalFilter, Utility.Text.Format("Fatal ({0})", m_FatalCount), GUILayout.Width(90f)); + } + GUILayout.EndHorizontal(); + + GUILayout.BeginVertical("box"); + { + if (m_LockScroll) + { + m_LogScrollPosition.y = float.MaxValue; + } + + m_LogScrollPosition = GUILayout.BeginScrollView(m_LogScrollPosition); + { + bool selected = false; + foreach (LogNode logNode in m_LogNodes) + { + switch (logNode.LogType) + { + case LogType.Log: + if (!m_InfoFilter) + { + continue; + } + + break; + + case LogType.Warning: + if (!m_WarningFilter) + { + continue; + } + + break; + + case LogType.Error: + if (!m_ErrorFilter) + { + continue; + } + + break; + + case LogType.Exception: + if (!m_FatalFilter) + { + continue; + } + + break; + } + + if (GUILayout.Toggle(m_SelectedNode == logNode, GetLogString(logNode))) + { + selected = true; + if (m_SelectedNode != logNode) + { + m_SelectedNode = logNode; + m_StackScrollPosition = Vector2.zero; + } + } + } + + if (!selected) + { + m_SelectedNode = null; + } + } + GUILayout.EndScrollView(); + } + GUILayout.EndVertical(); + + GUILayout.BeginVertical("box"); + { + m_StackScrollPosition = GUILayout.BeginScrollView(m_StackScrollPosition, GUILayout.Height(100f)); + { + if (m_SelectedNode != null) + { + Color32 color = GetLogStringColor(m_SelectedNode.LogType); + if (GUILayout.Button(Utility.Text.Format("{4}{6}{6}{5}", color.r, color.g, color.b, color.a, m_SelectedNode.LogMessage, m_SelectedNode.StackTrack, Environment.NewLine), "label")) + { + CopyToClipboard(Utility.Text.Format("{0}{2}{2}{1}", m_SelectedNode.LogMessage, m_SelectedNode.StackTrack, Environment.NewLine)); + } + } + } + GUILayout.EndScrollView(); + } + GUILayout.EndVertical(); + } + + private void Clear() + { + m_LogNodes.Clear(); + } + + public void RefreshCount() + { + m_InfoCount = 0; + m_WarningCount = 0; + m_ErrorCount = 0; + m_FatalCount = 0; + foreach (LogNode logNode in m_LogNodes) + { + switch (logNode.LogType) + { + case LogType.Log: + m_InfoCount++; + break; + + case LogType.Warning: + m_WarningCount++; + break; + + case LogType.Error: + m_ErrorCount++; + break; + + case LogType.Exception: + m_FatalCount++; + break; + } + } + } + + public void GetRecentLogs(List results) + { + if (results == null) + { + Log.Error("Results is invalid."); + return; + } + + results.Clear(); + foreach (LogNode logNode in m_LogNodes) + { + results.Add(logNode); + } + } + + public void GetRecentLogs(List results, int count) + { + if (results == null) + { + Log.Error("Results is invalid."); + return; + } + + if (count <= 0) + { + Log.Error("Count is invalid."); + return; + } + + int position = m_LogNodes.Count - count; + if (position < 0) + { + position = 0; + } + + int index = 0; + results.Clear(); + foreach (LogNode logNode in m_LogNodes) + { + if (index++ < position) + { + continue; + } + + results.Add(logNode); + } + } + + private void OnLogMessageReceived(string logMessage, string stackTrace, LogType logType) + { + if (logType == LogType.Assert) + { + logType = LogType.Error; + } + + m_LogNodes.Enqueue(LogNode.Create(logType, logMessage, stackTrace)); + while (m_LogNodes.Count > m_MaxLine) + { + MemoryPool.Release(m_LogNodes.Dequeue()); + } + } + + private string GetLogString(LogNode logNode) + { + Color32 color = GetLogStringColor(logNode.LogType); + return Utility.Text.Format("[{4:HH:mm:ss.fff}][{5}] {6}", color.r, color.g, color.b, color.a, logNode.LogTime.ToLocalTime(), logNode.LogFrameCount, logNode.LogMessage); + } + + internal Color32 GetLogStringColor(LogType logType) + { + Color32 color = Color.white; + switch (logType) + { + case LogType.Log: + color = m_InfoColor; + break; + + case LogType.Warning: + color = m_WarningColor; + break; + + case LogType.Error: + color = m_ErrorColor; + break; + + case LogType.Exception: + color = m_FatalColor; + break; + } + + return color; + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.ConsoleWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.ConsoleWindow.cs.meta new file mode 100644 index 0000000..8bfcbe3 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.ConsoleWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4a47e2730f999784eb1bbeefab72ef11 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.EnvironmentInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.EnvironmentInformationWindow.cs new file mode 100644 index 0000000..c3c955b --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.EnvironmentInformationWindow.cs @@ -0,0 +1,74 @@ +using AlicizaX; +using UnityEngine; +#if UNITY_5_5_OR_NEWER +using UnityEngine.Rendering; +#endif + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class EnvironmentInformationWindow : ScrollableDebuggerWindowBase + { + public override void Initialize(params object[] args) + { + + } + + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Environment Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Product Name", Application.productName); + DrawItem("Company Name", Application.companyName); +#if UNITY_5_6_OR_NEWER + DrawItem("Game Identifier", Application.identifier); +#else + DrawItem("Game Identifier", Application.bundleIdentifier); +#endif + DrawItem("Game Framework Version", AppVersion.GameFrameworkVersion); + DrawItem("Game Version", Utility.Text.Format("{0} ({1})", AppVersion.GameVersion, AppVersion.GameFrameworkVersion)); + // DrawItem("Resource Version", m_BaseComponent.EditorResourceMode ? "Unavailable in editor resource mode" : (string.IsNullOrEmpty(m_ResourceComponent.ApplicableGameVersion) ? "Unknown" : Utility.Text.Format("{0} ({1})", m_ResourceComponent.ApplicableGameVersion, m_ResourceComponent.InternalResourceVersion))); + DrawItem("Application Version", Application.version); + DrawItem("Unity Version", Application.unityVersion); + DrawItem("Platform", Application.platform.ToString()); + DrawItem("System Language", Application.systemLanguage.ToString()); + DrawItem("Cloud Project Id", Application.cloudProjectId); +#if UNITY_5_6_OR_NEWER + DrawItem("Build Guid", Application.buildGUID); +#endif + DrawItem("Target Frame Rate", Application.targetFrameRate.ToString()); + DrawItem("Internet Reachability", Application.internetReachability.ToString()); + DrawItem("Background Loading Priority", Application.backgroundLoadingPriority.ToString()); + DrawItem("Is Playing", Application.isPlaying.ToString()); +#if UNITY_5_5_OR_NEWER + DrawItem("Splash Screen Is Finished", SplashScreen.isFinished.ToString()); +#else + DrawItem("Is Showing Splash Screen", Application.isShowingSplashScreen.ToString()); +#endif + DrawItem("Run In Background", Application.runInBackground.ToString()); +#if UNITY_5_5_OR_NEWER + DrawItem("Install Name", Application.installerName); +#endif + DrawItem("Install Mode", Application.installMode.ToString()); + DrawItem("Sandbox Type", Application.sandboxType.ToString()); + DrawItem("Is Mobile Platform", Application.isMobilePlatform.ToString()); + DrawItem("Is Console Platform", Application.isConsolePlatform.ToString()); + DrawItem("Is Editor", Application.isEditor.ToString()); + DrawItem("Is Debug Build", Debug.isDebugBuild.ToString()); +#if UNITY_5_6_OR_NEWER + DrawItem("Is Focused", Application.isFocused.ToString()); +#endif +#if UNITY_2018_2_OR_NEWER + DrawItem("Is Batch Mode", Application.isBatchMode.ToString()); +#endif +#if UNITY_5_3 + DrawItem("Stack Trace Log Type", Application.stackTraceLogType.ToString()); +#endif + } + GUILayout.EndVertical(); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.EnvironmentInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.EnvironmentInformationWindow.cs.meta new file mode 100644 index 0000000..6fea0ee --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.EnvironmentInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c52c8dc8849c5fb429c50b45975a599b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.FpsCounter.cs b/Runtime/Debugger/DebuggerComponent.FpsCounter.cs new file mode 100644 index 0000000..de2cd27 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.FpsCounter.cs @@ -0,0 +1,77 @@ + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class FpsCounter + { + private float m_UpdateInterval; + private float m_CurrentFps; + private int m_Frames; + private float m_Accumulator; + private float m_TimeLeft; + + public FpsCounter(float updateInterval) + { + if (updateInterval <= 0f) + { + Log.Error("Update interval is invalid."); + return; + } + + m_UpdateInterval = updateInterval; + Reset(); + } + + public float UpdateInterval + { + get + { + return m_UpdateInterval; + } + set + { + if (value <= 0f) + { + Log.Error("Update interval is invalid."); + return; + } + + m_UpdateInterval = value; + Reset(); + } + } + + public float CurrentFps + { + get + { + return m_CurrentFps; + } + } + + public void Update(float elapseSeconds, float realElapseSeconds) + { + m_Frames++; + m_Accumulator += realElapseSeconds; + m_TimeLeft -= realElapseSeconds; + + if (m_TimeLeft <= 0f) + { + m_CurrentFps = m_Accumulator > 0f ? m_Frames / m_Accumulator : 0f; + m_Frames = 0; + m_Accumulator = 0f; + m_TimeLeft += m_UpdateInterval; + } + } + + private void Reset() + { + m_CurrentFps = 0f; + m_Frames = 0; + m_Accumulator = 0f; + m_TimeLeft = 0f; + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.FpsCounter.cs.meta b/Runtime/Debugger/DebuggerComponent.FpsCounter.cs.meta new file mode 100644 index 0000000..6c6467c --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.FpsCounter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4f24c805ead515d4286d3c03cd50f750 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.GraphicsInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.GraphicsInformationWindow.cs new file mode 100644 index 0000000..536a9b9 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.GraphicsInformationWindow.cs @@ -0,0 +1,163 @@ +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class GraphicsInformationWindow : ScrollableDebuggerWindowBase + { + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Graphics Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Device ID", SystemInfo.graphicsDeviceID.ToString()); + DrawItem("Device Name", SystemInfo.graphicsDeviceName); + DrawItem("Device Vendor ID", SystemInfo.graphicsDeviceVendorID.ToString()); + DrawItem("Device Vendor", SystemInfo.graphicsDeviceVendor); + DrawItem("Device Type", SystemInfo.graphicsDeviceType.ToString()); + DrawItem("Device Version", SystemInfo.graphicsDeviceVersion); + DrawItem("Memory Size", Utility.Text.Format("{0} MB", SystemInfo.graphicsMemorySize)); + DrawItem("Multi Threaded", SystemInfo.graphicsMultiThreaded.ToString()); +#if UNITY_2019_3_OR_NEWER + DrawItem("Rendering Threading Mode", SystemInfo.renderingThreadingMode.ToString()); +#endif +#if UNITY_2020_1_OR_NEWER + DrawItem("HRD Display Support Flags", SystemInfo.hdrDisplaySupportFlags.ToString()); +#endif + DrawItem("Shader Level", GetShaderLevelString(SystemInfo.graphicsShaderLevel)); + DrawItem("Global Maximum LOD", Shader.globalMaximumLOD.ToString()); +#if UNITY_5_6_OR_NEWER + DrawItem("Global Render Pipeline", Shader.globalRenderPipeline); +#endif +#if UNITY_2020_2_OR_NEWER + DrawItem("Min OpenGLES Version", Graphics.minOpenGLESVersion.ToString()); +#endif +#if UNITY_5_5_OR_NEWER + DrawItem("Active Tier", Graphics.activeTier.ToString()); +#endif +#if UNITY_2017_2_OR_NEWER + DrawItem("Active Color Gamut", Graphics.activeColorGamut.ToString()); +#endif +#if UNITY_2019_2_OR_NEWER + DrawItem("Preserve Frame Buffer Alpha", Graphics.preserveFramebufferAlpha.ToString()); +#endif + DrawItem("NPOT Support", SystemInfo.npotSupport.ToString()); + DrawItem("Max Texture Size", SystemInfo.maxTextureSize.ToString()); + DrawItem("Supported Render Target Count", SystemInfo.supportedRenderTargetCount.ToString()); +#if UNITY_2019_3_OR_NEWER + DrawItem("Supported Random Write Target Count", SystemInfo.supportedRandomWriteTargetCount.ToString()); +#endif +#if UNITY_5_4_OR_NEWER + DrawItem("Copy Texture Support", SystemInfo.copyTextureSupport.ToString()); +#endif +#if UNITY_5_5_OR_NEWER + DrawItem("Uses Reversed ZBuffer", SystemInfo.usesReversedZBuffer.ToString()); +#endif +#if UNITY_5_6_OR_NEWER + DrawItem("Max Cubemap Size", SystemInfo.maxCubemapSize.ToString()); + DrawItem("Graphics UV Starts At Top", SystemInfo.graphicsUVStartsAtTop.ToString()); +#endif +#if UNITY_2020_2_OR_NEWER + DrawItem("Constant Buffer Offset Alignment", SystemInfo.constantBufferOffsetAlignment.ToString()); +#elif UNITY_2019_1_OR_NEWER + DrawItem("Min Constant Buffer Offset Alignment", SystemInfo.minConstantBufferOffsetAlignment.ToString()); +#endif +#if UNITY_2018_3_OR_NEWER + DrawItem("Has Hidden Surface Removal On GPU", SystemInfo.hasHiddenSurfaceRemovalOnGPU.ToString()); + DrawItem("Has Dynamic Uniform Array Indexing In Fragment Shaders", SystemInfo.hasDynamicUniformArrayIndexingInFragmentShaders.ToString()); +#endif +#if UNITY_2019_2_OR_NEWER + DrawItem("Has Mip Max Level", SystemInfo.hasMipMaxLevel.ToString()); +#endif +#if UNITY_2019_3_OR_NEWER + DrawItem("Uses Load Store Actions", SystemInfo.usesLoadStoreActions.ToString()); + DrawItem("Max Compute Buffer Inputs Compute", SystemInfo.maxComputeBufferInputsCompute.ToString()); + DrawItem("Max Compute Buffer Inputs Domain", SystemInfo.maxComputeBufferInputsDomain.ToString()); + DrawItem("Max Compute Buffer Inputs Fragment", SystemInfo.maxComputeBufferInputsFragment.ToString()); + DrawItem("Max Compute Buffer Inputs Geometry", SystemInfo.maxComputeBufferInputsGeometry.ToString()); + DrawItem("Max Compute Buffer Inputs Hull", SystemInfo.maxComputeBufferInputsHull.ToString()); + DrawItem("Max Compute Buffer Inputs Vertex", SystemInfo.maxComputeBufferInputsVertex.ToString()); + DrawItem("Max Compute Work Group Size", SystemInfo.maxComputeWorkGroupSize.ToString()); + DrawItem("Max Compute Work Group Size X", SystemInfo.maxComputeWorkGroupSizeX.ToString()); + DrawItem("Max Compute Work Group Size Y", SystemInfo.maxComputeWorkGroupSizeY.ToString()); + DrawItem("Max Compute Work Group Size Z", SystemInfo.maxComputeWorkGroupSizeZ.ToString()); +#endif +#if UNITY_5_3 || UNITY_5_4 + DrawItem("Supports Stencil", SystemInfo.supportsStencil.ToString()); + DrawItem("Supports Render Textures", SystemInfo.supportsRenderTextures.ToString()); +#endif + DrawItem("Supports Sparse Textures", SystemInfo.supportsSparseTextures.ToString()); + DrawItem("Supports 3D Textures", SystemInfo.supports3DTextures.ToString()); + DrawItem("Supports Shadows", SystemInfo.supportsShadows.ToString()); + DrawItem("Supports Raw Shadow Depth Sampling", SystemInfo.supportsRawShadowDepthSampling.ToString()); +#if !UNITY_2019_1_OR_NEWER + DrawItem("Supports Render To Cubemap", SystemInfo.supportsRenderToCubemap.ToString()); + DrawItem("Supports Image Effects", SystemInfo.supportsImageEffects.ToString()); +#endif + DrawItem("Supports Compute Shader", SystemInfo.supportsComputeShaders.ToString()); + DrawItem("Supports Instancing", SystemInfo.supportsInstancing.ToString()); +#if UNITY_5_4_OR_NEWER + DrawItem("Supports 2D Array Textures", SystemInfo.supports2DArrayTextures.ToString()); + DrawItem("Supports Motion Vectors", SystemInfo.supportsMotionVectors.ToString()); +#endif +#if UNITY_5_5_OR_NEWER + DrawItem("Supports Cubemap Array Textures", SystemInfo.supportsCubemapArrayTextures.ToString()); +#endif +#if UNITY_5_6_OR_NEWER + DrawItem("Supports 3D Render Textures", SystemInfo.supports3DRenderTextures.ToString()); +#endif +#if UNITY_2017_2_OR_NEWER && !UNITY_2017_2_0 || UNITY_2017_1_4 + DrawItem("Supports Texture Wrap Mirror Once", SystemInfo.supportsTextureWrapMirrorOnce.ToString()); +#endif +#if UNITY_2019_1_OR_NEWER + DrawItem("Supports Graphics Fence", SystemInfo.supportsGraphicsFence.ToString()); +#elif UNITY_2017_3_OR_NEWER + DrawItem("Supports GPU Fence", SystemInfo.supportsGPUFence.ToString()); +#endif +#if UNITY_2017_3_OR_NEWER + DrawItem("Supports Async Compute", SystemInfo.supportsAsyncCompute.ToString()); + DrawItem("Supports Multi-sampled Textures", SystemInfo.supportsMultisampledTextures.ToString()); +#endif +#if UNITY_2018_1_OR_NEWER + DrawItem("Supports Async GPU Readback", SystemInfo.supportsAsyncGPUReadback.ToString()); + DrawItem("Supports 32bits Index Buffer", SystemInfo.supports32bitsIndexBuffer.ToString()); + DrawItem("Supports Hardware Quad Topology", SystemInfo.supportsHardwareQuadTopology.ToString()); +#endif +#if UNITY_2018_2_OR_NEWER + DrawItem("Supports Mip Streaming", SystemInfo.supportsMipStreaming.ToString()); + DrawItem("Supports Multi-sample Auto Resolve", SystemInfo.supportsMultisampleAutoResolve.ToString()); +#endif +#if UNITY_2018_3_OR_NEWER + DrawItem("Supports Separated Render Targets Blend", SystemInfo.supportsSeparatedRenderTargetsBlend.ToString()); +#endif +#if UNITY_2019_1_OR_NEWER + DrawItem("Supports Set Constant Buffer", SystemInfo.supportsSetConstantBuffer.ToString()); +#endif +#if UNITY_2019_3_OR_NEWER + DrawItem("Supports Geometry Shaders", SystemInfo.supportsGeometryShaders.ToString()); + DrawItem("Supports Ray Tracing", SystemInfo.supportsRayTracing.ToString()); + DrawItem("Supports Tessellation Shaders", SystemInfo.supportsTessellationShaders.ToString()); +#endif +#if UNITY_2020_1_OR_NEWER + DrawItem("Supports Compressed 3D Textures", SystemInfo.supportsCompressed3DTextures.ToString()); + DrawItem("Supports Conservative Raster", SystemInfo.supportsConservativeRaster.ToString()); + DrawItem("Supports GPU Recorder", SystemInfo.supportsGpuRecorder.ToString()); +#endif +#if UNITY_2020_2_OR_NEWER + DrawItem("Supports Multi-sampled 2D Array Textures", SystemInfo.supportsMultisampled2DArrayTextures.ToString()); + DrawItem("Supports Multiview", SystemInfo.supportsMultiview.ToString()); + DrawItem("Supports Render Target Array Index From Vertex Shader", SystemInfo.supportsRenderTargetArrayIndexFromVertexShader.ToString()); +#endif + } + GUILayout.EndVertical(); + } + + private string GetShaderLevelString(int shaderLevel) + { + return Utility.Text.Format("Shader Model {0}.{1}", shaderLevel / 10, shaderLevel % 10); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.GraphicsInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.GraphicsInformationWindow.cs.meta new file mode 100644 index 0000000..ffd1618 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.GraphicsInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a1342573add002c41b3bb1a8abb8ef3a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.InputAccelerationInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.InputAccelerationInformationWindow.cs new file mode 100644 index 0000000..543e29c --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.InputAccelerationInformationWindow.cs @@ -0,0 +1,39 @@ +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class InputAccelerationInformationWindow : ScrollableDebuggerWindowBase + { + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Input Acceleration Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Acceleration", Input.acceleration.ToString()); + DrawItem("Acceleration Event Count", Input.accelerationEventCount.ToString()); + DrawItem("Acceleration Events", GetAccelerationEventsString(Input.accelerationEvents)); + } + GUILayout.EndVertical(); + } + + private string GetAccelerationEventString(AccelerationEvent accelerationEvent) + { + return Utility.Text.Format("{0}, {1}", accelerationEvent.acceleration, accelerationEvent.deltaTime); + } + + private string GetAccelerationEventsString(AccelerationEvent[] accelerationEvents) + { + string[] accelerationEventStrings = new string[accelerationEvents.Length]; + for (int i = 0; i < accelerationEvents.Length; i++) + { + accelerationEventStrings[i] = GetAccelerationEventString(accelerationEvents[i]); + } + + return string.Join("; ", accelerationEventStrings); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.InputAccelerationInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.InputAccelerationInformationWindow.cs.meta new file mode 100644 index 0000000..e74c151 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.InputAccelerationInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 99bd178c0d097a24d9e04fe1522bca60 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.InputCompassInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.InputCompassInformationWindow.cs new file mode 100644 index 0000000..28e8cb1 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.InputCompassInformationWindow.cs @@ -0,0 +1,42 @@ +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class InputCompassInformationWindow : ScrollableDebuggerWindowBase + { + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Input Compass Information"); + GUILayout.BeginVertical("box"); + { + GUILayout.BeginHorizontal(); + { + if (GUILayout.Button("Enable", GUILayout.Height(30f))) + { + Input.compass.enabled = true; + } + if (GUILayout.Button("Disable", GUILayout.Height(30f))) + { + Input.compass.enabled = false; + } + } + GUILayout.EndHorizontal(); + + DrawItem("Enabled", Input.compass.enabled.ToString()); + if (Input.compass.enabled) + { + DrawItem("Heading Accuracy", Input.compass.headingAccuracy.ToString()); + DrawItem("Magnetic Heading", Input.compass.magneticHeading.ToString()); + DrawItem("Raw Vector", Input.compass.rawVector.ToString()); + DrawItem("Timestamp", Input.compass.timestamp.ToString()); + DrawItem("True Heading", Input.compass.trueHeading.ToString()); + } + } + GUILayout.EndVertical(); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.InputCompassInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.InputCompassInformationWindow.cs.meta new file mode 100644 index 0000000..81d4090 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.InputCompassInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a8e33bb9948f7f54d988d80a6ae6a24a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.InputGyroscopeInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.InputGyroscopeInformationWindow.cs new file mode 100644 index 0000000..a52e050 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.InputGyroscopeInformationWindow.cs @@ -0,0 +1,43 @@ +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class InputGyroscopeInformationWindow : ScrollableDebuggerWindowBase + { + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Input Gyroscope Information"); + GUILayout.BeginVertical("box"); + { + GUILayout.BeginHorizontal(); + { + if (GUILayout.Button("Enable", GUILayout.Height(30f))) + { + Input.gyro.enabled = true; + } + if (GUILayout.Button("Disable", GUILayout.Height(30f))) + { + Input.gyro.enabled = false; + } + } + GUILayout.EndHorizontal(); + + DrawItem("Enabled", Input.gyro.enabled.ToString()); + if (Input.gyro.enabled) + { + DrawItem("Update Interval", Input.gyro.updateInterval.ToString()); + DrawItem("Attitude", Input.gyro.attitude.eulerAngles.ToString()); + DrawItem("Gravity", Input.gyro.gravity.ToString()); + DrawItem("Rotation Rate", Input.gyro.rotationRate.ToString()); + DrawItem("Rotation Rate Unbiased", Input.gyro.rotationRateUnbiased.ToString()); + DrawItem("User Acceleration", Input.gyro.userAcceleration.ToString()); + } + } + GUILayout.EndVertical(); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.InputGyroscopeInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.InputGyroscopeInformationWindow.cs.meta new file mode 100644 index 0000000..613ca98 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.InputGyroscopeInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 709450f452a296b44b4aa939cf37f37c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.InputLocationInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.InputLocationInformationWindow.cs new file mode 100644 index 0000000..dd692bd --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.InputLocationInformationWindow.cs @@ -0,0 +1,44 @@ +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class InputLocationInformationWindow : ScrollableDebuggerWindowBase + { + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Input Location Information"); + GUILayout.BeginVertical("box"); + { + GUILayout.BeginHorizontal(); + { + if (GUILayout.Button("Enable", GUILayout.Height(30f))) + { + Input.location.Start(); + } + if (GUILayout.Button("Disable", GUILayout.Height(30f))) + { + Input.location.Stop(); + } + } + GUILayout.EndHorizontal(); + + DrawItem("Is Enabled By User", Input.location.isEnabledByUser.ToString()); + DrawItem("Status", Input.location.status.ToString()); + if (Input.location.status == LocationServiceStatus.Running) + { + DrawItem("Horizontal Accuracy", Input.location.lastData.horizontalAccuracy.ToString()); + DrawItem("Vertical Accuracy", Input.location.lastData.verticalAccuracy.ToString()); + DrawItem("Longitude", Input.location.lastData.longitude.ToString()); + DrawItem("Latitude", Input.location.lastData.latitude.ToString()); + DrawItem("Altitude", Input.location.lastData.altitude.ToString()); + DrawItem("Timestamp", Input.location.lastData.timestamp.ToString()); + } + } + GUILayout.EndVertical(); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.InputLocationInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.InputLocationInformationWindow.cs.meta new file mode 100644 index 0000000..85e0c9f --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.InputLocationInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 74c5698b7ee786442b28b64003f564fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.InputSummaryInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.InputSummaryInformationWindow.cs new file mode 100644 index 0000000..bf563ef --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.InputSummaryInformationWindow.cs @@ -0,0 +1,33 @@ +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class InputSummaryInformationWindow : ScrollableDebuggerWindowBase + { + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Input Summary Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Back Button Leaves App", Input.backButtonLeavesApp.ToString()); + DrawItem("Device Orientation", Input.deviceOrientation.ToString()); + DrawItem("Mouse Present", Input.mousePresent.ToString()); + DrawItem("Mouse Position", Input.mousePosition.ToString()); + DrawItem("Mouse Scroll Delta", Input.mouseScrollDelta.ToString()); + DrawItem("Any Key", Input.anyKey.ToString()); + DrawItem("Any Key Down", Input.anyKeyDown.ToString()); + DrawItem("Input String", Input.inputString); + DrawItem("IME Is Selected", Input.imeIsSelected.ToString()); + DrawItem("IME Composition Mode", Input.imeCompositionMode.ToString()); + DrawItem("Compensate Sensors", Input.compensateSensors.ToString()); + DrawItem("Composition Cursor Position", Input.compositionCursorPos.ToString()); + DrawItem("Composition String", Input.compositionString); + } + GUILayout.EndVertical(); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.InputSummaryInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.InputSummaryInformationWindow.cs.meta new file mode 100644 index 0000000..10a2fc3 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.InputSummaryInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a2756ce9647dc6a49a66c75437edef8b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.InputTouchInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.InputTouchInformationWindow.cs new file mode 100644 index 0000000..0fe8ffa --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.InputTouchInformationWindow.cs @@ -0,0 +1,43 @@ +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class InputTouchInformationWindow : ScrollableDebuggerWindowBase + { + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Input Touch Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Touch Supported", Input.touchSupported.ToString()); + DrawItem("Touch Pressure Supported", Input.touchPressureSupported.ToString()); + DrawItem("Stylus Touch Supported", Input.stylusTouchSupported.ToString()); + DrawItem("Simulate Mouse With Touches", Input.simulateMouseWithTouches.ToString()); + DrawItem("Multi Touch Enabled", Input.multiTouchEnabled.ToString()); + DrawItem("Touch Count", Input.touchCount.ToString()); + DrawItem("Touches", GetTouchesString(Input.touches)); + } + GUILayout.EndVertical(); + } + + private string GetTouchString(Touch touch) + { + return Utility.Text.Format("{0}, {1}, {2}, {3}, {4}", touch.position, touch.deltaPosition, touch.rawPosition, touch.pressure, touch.phase); + } + + private string GetTouchesString(Touch[] touches) + { + string[] touchStrings = new string[touches.Length]; + for (int i = 0; i < touches.Length; i++) + { + touchStrings[i] = GetTouchString(touches[i]); + } + + return string.Join("; ", touchStrings); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.InputTouchInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.InputTouchInformationWindow.cs.meta new file mode 100644 index 0000000..413d7ac --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.InputTouchInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6a76aa41d893e994f9630028d5e3001d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.LogNode.cs b/Runtime/Debugger/DebuggerComponent.LogNode.cs new file mode 100644 index 0000000..4fe357a --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.LogNode.cs @@ -0,0 +1,119 @@ + +using System; +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + /// + /// 日志记录结点。 + /// + public sealed class LogNode : IMemory + { + private DateTime m_LogTime; + private int m_LogFrameCount; + private LogType m_LogType; + private string m_LogMessage; + private string m_StackTrack; + + /// + /// 初始化日志记录结点的新实例。 + /// + public LogNode() + { + m_LogTime = default(DateTime); + m_LogFrameCount = 0; + m_LogType = LogType.Error; + m_LogMessage = null; + m_StackTrack = null; + } + + /// + /// 获取日志时间。 + /// + public DateTime LogTime + { + get + { + return m_LogTime; + } + } + + /// + /// 获取日志帧计数。 + /// + public int LogFrameCount + { + get + { + return m_LogFrameCount; + } + } + + /// + /// 获取日志类型。 + /// + public LogType LogType + { + get + { + return m_LogType; + } + } + + /// + /// 获取日志内容。 + /// + public string LogMessage + { + get + { + return m_LogMessage; + } + } + + /// + /// 获取日志堆栈信息。 + /// + public string StackTrack + { + get + { + return m_StackTrack; + } + } + + /// + /// 创建日志记录结点。 + /// + /// 日志类型。 + /// 日志内容。 + /// 日志堆栈信息。 + /// 创建的日志记录结点。 + public static LogNode Create(LogType logType, string logMessage, string stackTrack) + { + LogNode logNode = MemoryPool.Acquire(); + logNode.m_LogTime = DateTime.UtcNow; + logNode.m_LogFrameCount = Time.frameCount; + logNode.m_LogType = logType; + logNode.m_LogMessage = logMessage; + logNode.m_StackTrack = stackTrack; + return logNode; + } + + /// + /// 清理日志记录结点。 + /// + public void Clear() + { + m_LogTime = default(DateTime); + m_LogFrameCount = 0; + m_LogType = LogType.Error; + m_LogMessage = null; + m_StackTrack = null; + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.LogNode.cs.meta b/Runtime/Debugger/DebuggerComponent.LogNode.cs.meta new file mode 100644 index 0000000..a9cd9bb --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.LogNode.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 168b4dfdd72224e4bbc5fb606eec23dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.ObjectPoolInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.ObjectPoolInformationWindow.cs new file mode 100644 index 0000000..4345b9f --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.ObjectPoolInformationWindow.cs @@ -0,0 +1,87 @@ +using AlicizaX.ObjectPool; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class ObjectPoolInformationWindow : ScrollableDebuggerWindowBase + { + private IObjectPoolModule m_ObjectPoolComponent = null; + + public override void Initialize(params object[] args) + { + m_ObjectPoolComponent = ModuleSystem.GetModule(); + if (m_ObjectPoolComponent == null) + { + Log.Error("Object pool component is invalid."); + return; + } + } + + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Object Pool Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Object Pool Count", m_ObjectPoolComponent.Count.ToString()); + } + GUILayout.EndVertical(); + ObjectPoolBase[] objectPools = m_ObjectPoolComponent.GetAllObjectPools(true); + for (int i = 0; i < objectPools.Length; i++) + { + DrawObjectPool(objectPools[i]); + } + } + + private void DrawObjectPool(ObjectPoolBase objectPool) + { + GUILayout.Label(Utility.Text.Format("Object Pool: {0}", objectPool.FullName)); + GUILayout.BeginVertical("box"); + { + DrawItem("Name", objectPool.Name); + DrawItem("Type", objectPool.ObjectType.FullName); + DrawItem("Auto Release Interval", objectPool.AutoReleaseInterval.ToString()); + DrawItem("Capacity", objectPool.Capacity.ToString()); + DrawItem("Used Count", objectPool.Count.ToString()); + DrawItem("Can Release Count", objectPool.CanReleaseCount.ToString()); + DrawItem("Expire Time", objectPool.ExpireTime.ToString()); + DrawItem("Priority", objectPool.Priority.ToString()); + ObjectInfo[] objectInfos = objectPool.GetAllObjectInfos(); + GUILayout.BeginHorizontal(); + { + GUILayout.Label("Name"); + GUILayout.Label("Locked", GUILayout.Width(60f)); + GUILayout.Label(objectPool.AllowMultiSpawn ? "Count" : "In Use", GUILayout.Width(60f)); + GUILayout.Label("Flag", GUILayout.Width(60f)); + GUILayout.Label("Priority", GUILayout.Width(60f)); + GUILayout.Label("Last Use Time", GUILayout.Width(120f)); + } + GUILayout.EndHorizontal(); + + if (objectInfos.Length > 0) + { + for (int i = 0; i < objectInfos.Length; i++) + { + GUILayout.BeginHorizontal(); + { + GUILayout.Label(string.IsNullOrEmpty(objectInfos[i].Name) ? "" : objectInfos[i].Name); + GUILayout.Label(objectInfos[i].Locked.ToString(), GUILayout.Width(60f)); + GUILayout.Label(objectPool.AllowMultiSpawn ? objectInfos[i].SpawnCount.ToString() : objectInfos[i].IsInUse.ToString(), GUILayout.Width(60f)); + GUILayout.Label(objectInfos[i].CustomCanReleaseFlag.ToString(), GUILayout.Width(60f)); + GUILayout.Label(objectInfos[i].Priority.ToString(), GUILayout.Width(60f)); + GUILayout.Label(objectInfos[i].LastUseTime.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"), GUILayout.Width(120f)); + } + GUILayout.EndHorizontal(); + } + } + else + { + GUILayout.Label("Object Pool is Empty ..."); + } + } + GUILayout.EndVertical(); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.ObjectPoolInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.ObjectPoolInformationWindow.cs.meta new file mode 100644 index 0000000..03ba5f0 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.ObjectPoolInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5898a6ad35652924590c863ef4bc6341 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.OperationsWindow.cs b/Runtime/Debugger/DebuggerComponent.OperationsWindow.cs new file mode 100644 index 0000000..8cfdf15 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.OperationsWindow.cs @@ -0,0 +1,67 @@ +// //------------------------------------------------------------ +// // Game Framework +// // Copyright © 2013-2021 Jiang Yin. All rights reserved. +// // Homepage: https://gameframework.cn/ +// // Feedback: mailto:ellan@gameframework.cn +// //------------------------------------------------------------ +// +// using GameFrameX.Runtime; +// using UnityEngine; +// +// namespace GameFrameX.Debugger.Runtime +// { +// public sealed partial class DebuggerComponent : GameFrameworkComponent +// { +// private sealed class OperationsWindow : ScrollableDebuggerWindowBase +// { +// protected override void OnDrawScrollableWindow() +// { +// GUILayout.Label("Operations"); +// GUILayout.BeginVertical("box"); +// { +// ObjectPoolComponent objectPoolComponent = GameEntry.GetComponent(); +// if (objectPoolComponent != null) +// { +// if (GUILayout.Button("Object Pool Release", GUILayout.Height(30f))) +// { +// objectPoolComponent.Release(); +// } +// +// if (GUILayout.Button("Object Pool Release All Unused", GUILayout.Height(30f))) +// { +// objectPoolComponent.ReleaseAllUnused(); +// } +// } +// +// ResourceComponent resourceCompoent = GameEntry.GetComponent(); +// if (resourceCompoent != null) +// { +// if (GUILayout.Button("Unload Unused Assets", GUILayout.Height(30f))) +// { +// resourceCompoent.ForceUnloadUnusedAssets(false); +// } +// +// if (GUILayout.Button("Unload Unused Assets and Garbage Collect", GUILayout.Height(30f))) +// { +// resourceCompoent.ForceUnloadUnusedAssets(true); +// } +// } +// +// if (GUILayout.Button("Shutdown Game Framework (None)", GUILayout.Height(30f))) +// { +// GameEntry.Shutdown(ShutdownType.None); +// } +// if (GUILayout.Button("Shutdown Game Framework (Restart)", GUILayout.Height(30f))) +// { +// GameEntry.Shutdown(ShutdownType.Restart); +// } +// if (GUILayout.Button("Shutdown Game Framework (Quit)", GUILayout.Height(30f))) +// { +// GameEntry.Shutdown(ShutdownType.Quit); +// } +// } +// GUILayout.EndVertical(); +// } +// } +// } +// } diff --git a/Runtime/Debugger/DebuggerComponent.OperationsWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.OperationsWindow.cs.meta new file mode 100644 index 0000000..ac4e910 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.OperationsWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a695dd87b92d7374fbe3790f5a25e9d5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.PathInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.PathInformationWindow.cs new file mode 100644 index 0000000..fd8643b --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.PathInformationWindow.cs @@ -0,0 +1,29 @@ +using System; +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class PathInformationWindow : ScrollableDebuggerWindowBase + { + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Path Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Current Directory", Utility.Path.GetRegularPath(Environment.CurrentDirectory)); + DrawItem("Data Path", Utility.Path.GetRegularPath(Application.dataPath)); + DrawItem("Persistent Data Path", Utility.Path.GetRegularPath(Application.persistentDataPath)); + DrawItem("Streaming Assets Path", Utility.Path.GetRegularPath(Application.streamingAssetsPath)); + DrawItem("Temporary Cache Path", Utility.Path.GetRegularPath(Application.temporaryCachePath)); +#if UNITY_2018_3_OR_NEWER + DrawItem("Console Log Path", Utility.Path.GetRegularPath(Application.consoleLogPath)); +#endif + } + GUILayout.EndVertical(); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.PathInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.PathInformationWindow.cs.meta new file mode 100644 index 0000000..1597ff4 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.PathInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e86b5be349fea7b46bf4c9cff62cf940 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.ProfilerInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.ProfilerInformationWindow.cs new file mode 100644 index 0000000..174f8cd --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.ProfilerInformationWindow.cs @@ -0,0 +1,61 @@ + +using AlicizaX; +using UnityEngine; +#if UNITY_5_5_OR_NEWER +using UnityEngine.Profiling; +#endif + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class ProfilerInformationWindow : ScrollableDebuggerWindowBase + { + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Profiler Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Supported", Profiler.supported.ToString()); + DrawItem("Enabled", Profiler.enabled.ToString()); + DrawItem("Enable Binary Log", Profiler.enableBinaryLog ? Utility.Text.Format("True, {0}", Profiler.logFile) : "False"); +#if UNITY_2019_3_OR_NEWER + DrawItem("Enable Allocation Callstacks", Profiler.enableAllocationCallstacks.ToString()); +#endif +#if UNITY_2018_3_OR_NEWER + DrawItem("Area Count", Profiler.areaCount.ToString()); +#endif +#if UNITY_5_3 || UNITY_5_4 + DrawItem("Max Samples Number Per Frame", Profiler.maxNumberOfSamplesPerFrame.ToString()); +#endif +#if UNITY_2018_3_OR_NEWER + DrawItem("Max Used Memory", GetByteLengthString(Profiler.maxUsedMemory)); +#endif +#if UNITY_5_6_OR_NEWER + DrawItem("Mono Used Size", GetByteLengthString(Profiler.GetMonoUsedSizeLong())); + DrawItem("Mono Heap Size", GetByteLengthString(Profiler.GetMonoHeapSizeLong())); + DrawItem("Used Heap Size", GetByteLengthString(Profiler.usedHeapSizeLong)); + DrawItem("Total Allocated Memory", GetByteLengthString(Profiler.GetTotalAllocatedMemoryLong())); + DrawItem("Total Reserved Memory", GetByteLengthString(Profiler.GetTotalReservedMemoryLong())); + DrawItem("Total Unused Reserved Memory", GetByteLengthString(Profiler.GetTotalUnusedReservedMemoryLong())); +#else + DrawItem("Mono Used Size", GetByteLengthString(Profiler.GetMonoUsedSize())); + DrawItem("Mono Heap Size", GetByteLengthString(Profiler.GetMonoHeapSize())); + DrawItem("Used Heap Size", GetByteLengthString(Profiler.usedHeapSize)); + DrawItem("Total Allocated Memory", GetByteLengthString(Profiler.GetTotalAllocatedMemory())); + DrawItem("Total Reserved Memory", GetByteLengthString(Profiler.GetTotalReservedMemory())); + DrawItem("Total Unused Reserved Memory", GetByteLengthString(Profiler.GetTotalUnusedReservedMemory())); +#endif +#if UNITY_2018_1_OR_NEWER + DrawItem("Allocated Memory For Graphics Driver", GetByteLengthString(Profiler.GetAllocatedMemoryForGraphicsDriver())); +#endif +#if UNITY_5_5_OR_NEWER + DrawItem("Temp Allocator Size", GetByteLengthString(Profiler.GetTempAllocatorSize())); +#endif + DrawItem("Marshal Cached HGlobal Size", GetByteLengthString(Utility.Marshal.CachedHGlobalSize)); + } + GUILayout.EndVertical(); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.ProfilerInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.ProfilerInformationWindow.cs.meta new file mode 100644 index 0000000..0e72fe2 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.ProfilerInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 73fd64cd790a9564d9e49d13ed0742c7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.QualityInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.QualityInformationWindow.cs new file mode 100644 index 0000000..a0fbafe --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.QualityInformationWindow.cs @@ -0,0 +1,104 @@ + +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class QualityInformationWindow : ScrollableDebuggerWindowBase + { + private bool m_ApplyExpensiveChanges = false; + + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Quality Level"); + GUILayout.BeginVertical("box"); + { + int currentQualityLevel = QualitySettings.GetQualityLevel(); + + DrawItem("Current Quality Level", QualitySettings.names[currentQualityLevel]); + m_ApplyExpensiveChanges = GUILayout.Toggle(m_ApplyExpensiveChanges, "Apply expensive changes on quality level change."); + + int newQualityLevel = GUILayout.SelectionGrid(currentQualityLevel, QualitySettings.names, 3, "toggle"); + if (newQualityLevel != currentQualityLevel) + { + QualitySettings.SetQualityLevel(newQualityLevel, m_ApplyExpensiveChanges); + } + } + GUILayout.EndVertical(); + + GUILayout.Label("Rendering Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Active Color Space", QualitySettings.activeColorSpace.ToString()); + DrawItem("Desired Color Space", QualitySettings.desiredColorSpace.ToString()); + DrawItem("Max Queued Frames", QualitySettings.maxQueuedFrames.ToString()); + DrawItem("Pixel Light Count", QualitySettings.pixelLightCount.ToString()); + DrawItem("Master Texture Limit", QualitySettings.globalTextureMipmapLimit.ToString()); + DrawItem("Anisotropic Filtering", QualitySettings.anisotropicFiltering.ToString()); + DrawItem("Anti Aliasing", QualitySettings.antiAliasing.ToString()); +#if UNITY_5_5_OR_NEWER + DrawItem("Soft Particles", QualitySettings.softParticles.ToString()); +#endif + DrawItem("Soft Vegetation", QualitySettings.softVegetation.ToString()); + DrawItem("Realtime Reflection Probes", QualitySettings.realtimeReflectionProbes.ToString()); + DrawItem("Billboards Face Camera Position", QualitySettings.billboardsFaceCameraPosition.ToString()); +#if UNITY_2017_1_OR_NEWER + DrawItem("Resolution Scaling Fixed DPI Factor", QualitySettings.resolutionScalingFixedDPIFactor.ToString()); +#endif +#if UNITY_2018_2_OR_NEWER + DrawItem("Texture Streaming Enabled", QualitySettings.streamingMipmapsActive.ToString()); + DrawItem("Texture Streaming Add All Cameras", QualitySettings.streamingMipmapsAddAllCameras.ToString()); + DrawItem("Texture Streaming Memory Budget", QualitySettings.streamingMipmapsMemoryBudget.ToString()); + DrawItem("Texture Streaming Renderers Per Frame", QualitySettings.streamingMipmapsRenderersPerFrame.ToString()); + DrawItem("Texture Streaming Max Level Reduction", QualitySettings.streamingMipmapsMaxLevelReduction.ToString()); + DrawItem("Texture Streaming Max File IO Requests", QualitySettings.streamingMipmapsMaxFileIORequests.ToString()); +#endif + } + GUILayout.EndVertical(); + + GUILayout.Label("Shadows Information"); + GUILayout.BeginVertical("box"); + { +#if UNITY_2017_1_OR_NEWER + DrawItem("Shadowmask Mode", QualitySettings.shadowmaskMode.ToString()); +#endif +#if UNITY_5_5_OR_NEWER + DrawItem("Shadow Quality", QualitySettings.shadows.ToString()); +#endif +#if UNITY_5_4_OR_NEWER + DrawItem("Shadow Resolution", QualitySettings.shadowResolution.ToString()); +#endif + DrawItem("Shadow Projection", QualitySettings.shadowProjection.ToString()); + DrawItem("Shadow Distance", QualitySettings.shadowDistance.ToString()); + DrawItem("Shadow Near Plane Offset", QualitySettings.shadowNearPlaneOffset.ToString()); + DrawItem("Shadow Cascades", QualitySettings.shadowCascades.ToString()); + DrawItem("Shadow Cascade 2 Split", QualitySettings.shadowCascade2Split.ToString()); + DrawItem("Shadow Cascade 4 Split", QualitySettings.shadowCascade4Split.ToString()); + } + GUILayout.EndVertical(); + + GUILayout.Label("Other Information"); + GUILayout.BeginVertical("box"); + { +#if UNITY_2019_1_OR_NEWER + DrawItem("Skin Weights", QualitySettings.skinWeights.ToString()); +#else + DrawItem("Blend Weights", QualitySettings.blendWeights.ToString()); +#endif + DrawItem("VSync Count", QualitySettings.vSyncCount.ToString()); + DrawItem("LOD Bias", QualitySettings.lodBias.ToString()); + DrawItem("Maximum LOD Level", QualitySettings.maximumLODLevel.ToString()); + DrawItem("Particle Raycast Budget", QualitySettings.particleRaycastBudget.ToString()); + DrawItem("Async Upload Time Slice", Utility.Text.Format("{0} ms", QualitySettings.asyncUploadTimeSlice)); + DrawItem("Async Upload Buffer Size", Utility.Text.Format("{0} MB", QualitySettings.asyncUploadBufferSize)); +#if UNITY_2018_3_OR_NEWER + DrawItem("Async Upload Persistent Buffer", QualitySettings.asyncUploadPersistentBuffer.ToString()); +#endif + } + GUILayout.EndVertical(); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.QualityInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.QualityInformationWindow.cs.meta new file mode 100644 index 0000000..64b22d8 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.QualityInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc21c02fdf159d840a5254513a44c065 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.ReferencePoolInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.ReferencePoolInformationWindow.cs new file mode 100644 index 0000000..ca7bafa --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.ReferencePoolInformationWindow.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class ReferencePoolInformationWindow : ScrollableDebuggerWindowBase + { + private readonly Dictionary> m_ReferencePoolInfos = new Dictionary>(StringComparer.Ordinal); + private readonly Comparison m_NormalClassNameComparer = NormalClassNameComparer; + private readonly Comparison m_FullClassNameComparer = FullClassNameComparer; + private bool m_ShowFullClassName = false; + + public override void Initialize(params object[] args) + { + } + + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Reference Pool Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Enable Strict Check", MemoryPool.EnableStrictCheck.ToString()); + DrawItem("Reference Pool Count", MemoryPool.Count.ToString()); + } + GUILayout.EndVertical(); + + m_ShowFullClassName = GUILayout.Toggle(m_ShowFullClassName, "Show Full Class Name"); + m_ReferencePoolInfos.Clear(); + MemoryPoolInfo[] referencePoolInfos = MemoryPool.GetAllMemoryPoolInfos(); + foreach (MemoryPoolInfo referencePoolInfo in referencePoolInfos) + { + string assemblyName = referencePoolInfo.Type.Assembly.GetName().Name; + List results = null; + if (!m_ReferencePoolInfos.TryGetValue(assemblyName, out results)) + { + results = new List(); + m_ReferencePoolInfos.Add(assemblyName, results); + } + + results.Add(referencePoolInfo); + } + + foreach (KeyValuePair> assemblyReferencePoolInfo in m_ReferencePoolInfos) + { + GUILayout.Label(Utility.Text.Format("Assembly: {0}", assemblyReferencePoolInfo.Key)); + GUILayout.BeginVertical("box"); + { + GUILayout.BeginHorizontal(); + { + GUILayout.Label(m_ShowFullClassName ? "Full Class Name" : "Class Name"); + GUILayout.Label("Unused", GUILayout.Width(60f)); + GUILayout.Label("Using", GUILayout.Width(60f)); + GUILayout.Label("Acquire", GUILayout.Width(60f)); + GUILayout.Label("Release", GUILayout.Width(60f)); + GUILayout.Label("Add", GUILayout.Width(60f)); + GUILayout.Label("Remove", GUILayout.Width(60f)); + } + GUILayout.EndHorizontal(); + + if (assemblyReferencePoolInfo.Value.Count > 0) + { + assemblyReferencePoolInfo.Value.Sort(m_ShowFullClassName ? m_FullClassNameComparer : m_NormalClassNameComparer); + foreach (MemoryPoolInfo referencePoolInfo in assemblyReferencePoolInfo.Value) + { + DrawReferencePoolInfo(referencePoolInfo); + } + } + else + { + GUILayout.Label("Reference Pool is Empty ..."); + } + } + GUILayout.EndVertical(); + } + } + + private void DrawReferencePoolInfo(MemoryPoolInfo referencePoolInfo) + { + GUILayout.BeginHorizontal(); + { + GUILayout.Label(m_ShowFullClassName ? referencePoolInfo.Type.FullName : referencePoolInfo.Type.Name); + GUILayout.Label(referencePoolInfo.UnusedMemoryCount.ToString(), GUILayout.Width(60f)); + GUILayout.Label(referencePoolInfo.UsingMemoryCount.ToString(), GUILayout.Width(60f)); + GUILayout.Label(referencePoolInfo.AcquireMemoryCount.ToString(), GUILayout.Width(60f)); + GUILayout.Label(referencePoolInfo.ReleaseMemoryCount.ToString(), GUILayout.Width(60f)); + GUILayout.Label(referencePoolInfo.AddMemoryCount.ToString(), GUILayout.Width(60f)); + GUILayout.Label(referencePoolInfo.RemoveMemoryCount.ToString(), GUILayout.Width(60f)); + } + GUILayout.EndHorizontal(); + } + + private static int NormalClassNameComparer(MemoryPoolInfo a, MemoryPoolInfo b) + { + return a.Type.Name.CompareTo(b.Type.Name); + } + + private static int FullClassNameComparer(MemoryPoolInfo a, MemoryPoolInfo b) + { + return a.Type.FullName.CompareTo(b.Type.FullName); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.ReferencePoolInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.ReferencePoolInformationWindow.cs.meta new file mode 100644 index 0000000..9f4f2a1 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.ReferencePoolInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 33068d0a27ec5684e994545a7995a17c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.Sample.cs b/Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.Sample.cs new file mode 100644 index 0000000..554cbce --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.Sample.cs @@ -0,0 +1,62 @@ +using AlicizaX; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed partial class RuntimeMemoryInformationWindow : ScrollableDebuggerWindowBase where T : UnityEngine.Object + { + private sealed class Sample + { + private readonly string m_Name; + private readonly string m_Type; + private readonly long m_Size; + private bool m_Highlight; + + public Sample(string name, string type, long size) + { + m_Name = name; + m_Type = type; + m_Size = size; + m_Highlight = false; + } + + public string Name + { + get + { + return m_Name; + } + } + + public string Type + { + get + { + return m_Type; + } + } + + public long Size + { + get + { + return m_Size; + } + } + + public bool Highlight + { + get + { + return m_Highlight; + } + set + { + m_Highlight = value; + } + } + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.Sample.cs.meta b/Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.Sample.cs.meta new file mode 100644 index 0000000..3bc4594 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.Sample.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0da72213ab82f43458578c3ce477a57c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.cs new file mode 100644 index 0000000..6afb210 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.cs @@ -0,0 +1,136 @@ + +using System; +using System.Collections.Generic; +using AlicizaX; +using UnityEngine; +#if UNITY_5_5_OR_NEWER +using UnityEngine.Profiling; +#endif + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed partial class RuntimeMemoryInformationWindow : ScrollableDebuggerWindowBase where T : UnityEngine.Object + { + private const int ShowSampleCount = 300; + + private readonly List m_Samples = new List(); + private readonly Comparison m_SampleComparer = SampleComparer; + private DateTime m_SampleTime = DateTime.MinValue; + private long m_SampleSize = 0L; + private long m_DuplicateSampleSize = 0L; + private int m_DuplicateSimpleCount = 0; + + protected override void OnDrawScrollableWindow() + { + string typeName = typeof(T).Name; + GUILayout.Label(Utility.Text.Format("{0} Runtime Memory Information", typeName)); + GUILayout.BeginVertical("box"); + { + if (GUILayout.Button(Utility.Text.Format("Take Sample for {0}", typeName), GUILayout.Height(30f))) + { + TakeSample(); + } + + if (m_SampleTime <= DateTime.MinValue) + { + GUILayout.Label(Utility.Text.Format("Please take sample for {0} first.", typeName)); + } + else + { + if (m_DuplicateSimpleCount > 0) + { + GUILayout.Label(Utility.Text.Format("{0} {1}s ({2}) obtained at {3:yyyy-MM-dd HH:mm:ss}, while {4} {1}s ({5}) might be duplicated.", m_Samples.Count, typeName, GetByteLengthString(m_SampleSize), m_SampleTime.ToLocalTime(), m_DuplicateSimpleCount, GetByteLengthString(m_DuplicateSampleSize))); + } + else + { + GUILayout.Label(Utility.Text.Format("{0} {1}s ({2}) obtained at {3:yyyy-MM-dd HH:mm:ss}.", m_Samples.Count, typeName, GetByteLengthString(m_SampleSize), m_SampleTime.ToLocalTime())); + } + + if (m_Samples.Count > 0) + { + GUILayout.BeginHorizontal(); + { + GUILayout.Label(Utility.Text.Format("{0} Name", typeName)); + GUILayout.Label("Type", GUILayout.Width(240f)); + GUILayout.Label("Size", GUILayout.Width(80f)); + } + GUILayout.EndHorizontal(); + } + + int count = 0; + for (int i = 0; i < m_Samples.Count; i++) + { + GUILayout.BeginHorizontal(); + { + GUILayout.Label(m_Samples[i].Highlight ? Utility.Text.Format("{0}", m_Samples[i].Name) : m_Samples[i].Name); + GUILayout.Label(m_Samples[i].Highlight ? Utility.Text.Format("{0}", m_Samples[i].Type) : m_Samples[i].Type, GUILayout.Width(240f)); + GUILayout.Label(m_Samples[i].Highlight ? Utility.Text.Format("{0}", GetByteLengthString(m_Samples[i].Size)) : GetByteLengthString(m_Samples[i].Size), GUILayout.Width(80f)); + } + GUILayout.EndHorizontal(); + + count++; + if (count >= ShowSampleCount) + { + break; + } + } + } + } + GUILayout.EndVertical(); + } + + private void TakeSample() + { + m_SampleTime = DateTime.UtcNow; + m_SampleSize = 0L; + m_DuplicateSampleSize = 0L; + m_DuplicateSimpleCount = 0; + m_Samples.Clear(); + + T[] samples = Resources.FindObjectsOfTypeAll(); + for (int i = 0; i < samples.Length; i++) + { + long sampleSize = 0L; +#if UNITY_5_6_OR_NEWER + sampleSize = Profiler.GetRuntimeMemorySizeLong(samples[i]); +#else + sampleSize = Profiler.GetRuntimeMemorySize(samples[i]); +#endif + m_SampleSize += sampleSize; + m_Samples.Add(new Sample(samples[i].name, samples[i].GetType().Name, sampleSize)); + } + + m_Samples.Sort(m_SampleComparer); + + for (int i = 1; i < m_Samples.Count; i++) + { + if (m_Samples[i].Name == m_Samples[i - 1].Name && m_Samples[i].Type == m_Samples[i - 1].Type && m_Samples[i].Size == m_Samples[i - 1].Size) + { + m_Samples[i].Highlight = true; + m_DuplicateSampleSize += m_Samples[i].Size; + m_DuplicateSimpleCount++; + } + } + } + + private static int SampleComparer(Sample a, Sample b) + { + int result = b.Size.CompareTo(a.Size); + if (result != 0) + { + return result; + } + + result = a.Type.CompareTo(b.Type); + if (result != 0) + { + return result; + } + + return a.Name.CompareTo(b.Name); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.cs.meta new file mode 100644 index 0000000..a8bb068 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.RuntimeMemoryInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 21007b3290d97754ea3ab4d9f46900e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.Record.cs b/Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.Record.cs new file mode 100644 index 0000000..25ed557 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.Record.cs @@ -0,0 +1,56 @@ +using AlicizaX; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed partial class RuntimeMemorySummaryWindow : ScrollableDebuggerWindowBase + { + private sealed class Record + { + private readonly string m_Name; + private int m_Count; + private long m_Size; + + public Record(string name) + { + m_Name = name; + m_Count = 0; + m_Size = 0L; + } + + public string Name + { + get + { + return m_Name; + } + } + + public int Count + { + get + { + return m_Count; + } + set + { + m_Count = value; + } + } + + public long Size + { + get + { + return m_Size; + } + set + { + m_Size = value; + } + } + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.Record.cs.meta b/Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.Record.cs.meta new file mode 100644 index 0000000..6fc6cfb --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.Record.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 468bbdd114a04de429a6bd97b202c891 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.cs b/Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.cs new file mode 100644 index 0000000..aeb6e20 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.cs @@ -0,0 +1,124 @@ + +using System; +using System.Collections.Generic; +using AlicizaX; +using UnityEngine; +#if UNITY_5_5_OR_NEWER +using UnityEngine.Profiling; +#endif + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed partial class RuntimeMemorySummaryWindow : ScrollableDebuggerWindowBase + { + private readonly List m_Records = new List(); + private readonly Comparison m_RecordComparer = RecordComparer; + private DateTime m_SampleTime = DateTime.MinValue; + private int m_SampleCount = 0; + private long m_SampleSize = 0L; + + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Runtime Memory Summary"); + GUILayout.BeginVertical("box"); + { + if (GUILayout.Button("Take Sample", GUILayout.Height(30f))) + { + TakeSample(); + } + + if (m_SampleTime <= DateTime.MinValue) + { + GUILayout.Label("Please take sample first."); + } + else + { + GUILayout.Label(Utility.Text.Format("{0} Objects ({1}) obtained at {2:yyyy-MM-dd HH:mm:ss}.", m_SampleCount, GetByteLengthString(m_SampleSize), m_SampleTime.ToLocalTime())); + + GUILayout.BeginHorizontal(); + { + GUILayout.Label("Type"); + GUILayout.Label("Count", GUILayout.Width(120f)); + GUILayout.Label("Size", GUILayout.Width(120f)); + } + GUILayout.EndHorizontal(); + + for (int i = 0; i < m_Records.Count; i++) + { + GUILayout.BeginHorizontal(); + { + GUILayout.Label(m_Records[i].Name); + GUILayout.Label(m_Records[i].Count.ToString(), GUILayout.Width(120f)); + GUILayout.Label(GetByteLengthString(m_Records[i].Size), GUILayout.Width(120f)); + } + GUILayout.EndHorizontal(); + } + } + } + GUILayout.EndVertical(); + } + + private void TakeSample() + { + m_Records.Clear(); + m_SampleTime = DateTime.UtcNow; + m_SampleCount = 0; + m_SampleSize = 0L; + + UnityEngine.Object[] samples = Resources.FindObjectsOfTypeAll(); + for (int i = 0; i < samples.Length; i++) + { + long sampleSize = 0L; +#if UNITY_5_6_OR_NEWER + sampleSize = Profiler.GetRuntimeMemorySizeLong(samples[i]); +#else + sampleSize = Profiler.GetRuntimeMemorySize(samples[i]); +#endif + string name = samples[i].GetType().Name; + m_SampleCount++; + m_SampleSize += sampleSize; + + Record record = null; + foreach (Record r in m_Records) + { + if (r.Name == name) + { + record = r; + break; + } + } + + if (record == null) + { + record = new Record(name); + m_Records.Add(record); + } + + record.Count++; + record.Size += sampleSize; + } + + m_Records.Sort(m_RecordComparer); + } + + private static int RecordComparer(Record a, Record b) + { + int result = b.Size.CompareTo(a.Size); + if (result != 0) + { + return result; + } + + result = a.Count.CompareTo(b.Count); + if (result != 0) + { + return result; + } + + return a.Name.CompareTo(b.Name); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.cs.meta new file mode 100644 index 0000000..df2dac1 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.RuntimeMemorySummaryWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 45a9f76b48e192d44b0de0eac60975b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.SceneInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.SceneInformationWindow.cs new file mode 100644 index 0000000..038b6d4 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.SceneInformationWindow.cs @@ -0,0 +1,38 @@ +using AlicizaX; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class SceneInformationWindow : ScrollableDebuggerWindowBase + { + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Scene Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Scene Count", SceneManager.sceneCount.ToString()); + DrawItem("Scene Count In Build Settings", SceneManager.sceneCountInBuildSettings.ToString()); + + UnityEngine.SceneManagement.Scene activeScene = SceneManager.GetActiveScene(); +#if UNITY_2018_3_OR_NEWER + DrawItem("Active Scene Handle", activeScene.handle.ToString()); +#endif + DrawItem("Active Scene Name", activeScene.name); + DrawItem("Active Scene Path", activeScene.path); + DrawItem("Active Scene Build Index", activeScene.buildIndex.ToString()); + DrawItem("Active Scene Is Dirty", activeScene.isDirty.ToString()); + DrawItem("Active Scene Is Loaded", activeScene.isLoaded.ToString()); + DrawItem("Active Scene Is Valid", activeScene.IsValid().ToString()); + DrawItem("Active Scene Root Count", activeScene.rootCount.ToString()); +#if UNITY_2019_1_OR_NEWER + DrawItem("Active Scene Is Sub Scene", activeScene.isSubScene.ToString()); +#endif + } + GUILayout.EndVertical(); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.SceneInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.SceneInformationWindow.cs.meta new file mode 100644 index 0000000..9a51a2d --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.SceneInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0c5471e8b14eef444a96f1e1cb4b6987 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.ScreenInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.ScreenInformationWindow.cs new file mode 100644 index 0000000..44cff7e --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.ScreenInformationWindow.cs @@ -0,0 +1,92 @@ +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class ScreenInformationWindow : ScrollableDebuggerWindowBase + { + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Screen Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Current Resolution", GetResolutionString(Screen.currentResolution)); + DrawItem("Screen Width", Utility.Text.Format("{0} px / {1:F2} in / {2:F2} cm", Screen.width, Utility.Converter.GetInchesFromPixels(Screen.width), Utility.Converter.GetCentimetersFromPixels(Screen.width))); + DrawItem("Screen Height", Utility.Text.Format("{0} px / {1:F2} in / {2:F2} cm", Screen.height, Utility.Converter.GetInchesFromPixels(Screen.height), Utility.Converter.GetCentimetersFromPixels(Screen.height))); + DrawItem("Screen DPI", Screen.dpi.ToString("F2")); + DrawItem("Screen Orientation", Screen.orientation.ToString()); + DrawItem("Is Full Screen", Screen.fullScreen.ToString()); +#if UNITY_2018_1_OR_NEWER + DrawItem("Full Screen Mode", Screen.fullScreenMode.ToString()); +#endif + DrawItem("Sleep Timeout", GetSleepTimeoutDescription(Screen.sleepTimeout)); +#if UNITY_2019_2_OR_NEWER + DrawItem("Brightness", Screen.brightness.ToString("F2")); +#endif + DrawItem("Cursor Visible", Cursor.visible.ToString()); + DrawItem("Cursor Lock State", Cursor.lockState.ToString()); + DrawItem("Auto Landscape Left", Screen.autorotateToLandscapeLeft.ToString()); + DrawItem("Auto Landscape Right", Screen.autorotateToLandscapeRight.ToString()); + DrawItem("Auto Portrait", Screen.autorotateToPortrait.ToString()); + DrawItem("Auto Portrait Upside Down", Screen.autorotateToPortraitUpsideDown.ToString()); +#if UNITY_2017_2_OR_NEWER && !UNITY_2017_2_0 + DrawItem("Safe Area", Screen.safeArea.ToString()); +#endif +#if UNITY_2019_2_OR_NEWER + DrawItem("Cutouts", GetCutoutsString(Screen.cutouts)); +#endif + DrawItem("Support Resolutions", GetResolutionsString(Screen.resolutions)); + } + GUILayout.EndVertical(); + } + + private string GetSleepTimeoutDescription(int sleepTimeout) + { + if (sleepTimeout == SleepTimeout.NeverSleep) + { + return "Never Sleep"; + } + + if (sleepTimeout == SleepTimeout.SystemSetting) + { + return "System Setting"; + } + + return sleepTimeout.ToString(); + } + + private string GetResolutionString(Resolution resolution) + { +#if UNITY_6000_0_OR_NEWER + return Utility.Text.Format("{0} x {1} @ {2}Hz", resolution.width, resolution.height, resolution.refreshRateRatio); +#else + return Utility.Text.Format("{0} x {1} @ {2}Hz", resolution.width, resolution.height, resolution.refreshRate); +#endif + } + + private string GetCutoutsString(Rect[] cutouts) + { + string[] cutoutStrings = new string[cutouts.Length]; + for (int i = 0; i < cutouts.Length; i++) + { + cutoutStrings[i] = cutouts[i].ToString(); + } + + return string.Join("; ", cutoutStrings); + } + + private string GetResolutionsString(Resolution[] resolutions) + { + string[] resolutionStrings = new string[resolutions.Length]; + for (int i = 0; i < resolutions.Length; i++) + { + resolutionStrings[i] = GetResolutionString(resolutions[i]); + } + + return string.Join("; ", resolutionStrings); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.ScreenInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.ScreenInformationWindow.cs.meta new file mode 100644 index 0000000..d8bcd4e --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.ScreenInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc352756f31513048a46e56ad2900db7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.ScrollableDebuggerWindowBase.cs b/Runtime/Debugger/DebuggerComponent.ScrollableDebuggerWindowBase.cs new file mode 100644 index 0000000..b85c9f7 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.ScrollableDebuggerWindowBase.cs @@ -0,0 +1,95 @@ + +using AlicizaX; +using UnityEngine; + + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private abstract class ScrollableDebuggerWindowBase : IDebuggerWindow + { + private const float TitleWidth = 240f; + private Vector2 m_ScrollPosition = Vector2.zero; + + public virtual void Initialize(params object[] args) + { + } + + public virtual void Shutdown() + { + } + + public virtual void OnEnter() + { + } + + public virtual void OnLeave() + { + } + + public virtual void OnUpdate(float elapseSeconds, float realElapseSeconds) + { + } + + public void OnDraw() + { + m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition); + { + OnDrawScrollableWindow(); + } + GUILayout.EndScrollView(); + } + + protected abstract void OnDrawScrollableWindow(); + + protected static void DrawItem(string title, string content) + { + GUILayout.BeginHorizontal(); + { + GUILayout.Label(title, GUILayout.Width(TitleWidth)); + if (GUILayout.Button(content, "label")) + { + CopyToClipboard(content); + } + } + GUILayout.EndHorizontal(); + } + + protected static string GetByteLengthString(long byteLength) + { + if (byteLength < 1024L) // 2 ^ 10 + { + return Utility.Text.Format("{0} Bytes", byteLength); + } + + if (byteLength < 1048576L) // 2 ^ 20 + { + return Utility.Text.Format("{0:F2} KB", byteLength / 1024f); + } + + if (byteLength < 1073741824L) // 2 ^ 30 + { + return Utility.Text.Format("{0:F2} MB", byteLength / 1048576f); + } + + if (byteLength < 1099511627776L) // 2 ^ 40 + { + return Utility.Text.Format("{0:F2} GB", byteLength / 1073741824f); + } + + if (byteLength < 1125899906842624L) // 2 ^ 50 + { + return Utility.Text.Format("{0:F2} TB", byteLength / 1099511627776f); + } + + if (byteLength < 1152921504606846976L) // 2 ^ 60 + { + return Utility.Text.Format("{0:F2} PB", byteLength / 1125899906842624f); + } + + return Utility.Text.Format("{0:F2} EB", byteLength / 1152921504606846976f); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.ScrollableDebuggerWindowBase.cs.meta b/Runtime/Debugger/DebuggerComponent.ScrollableDebuggerWindowBase.cs.meta new file mode 100644 index 0000000..c00e15b --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.ScrollableDebuggerWindowBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1af7b258c93df0341a163adfdd378f8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.SettingsWindow.cs b/Runtime/Debugger/DebuggerComponent.SettingsWindow.cs new file mode 100644 index 0000000..677abb1 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.SettingsWindow.cs @@ -0,0 +1,219 @@ +using UnityEngine; + + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class SettingsWindow : ScrollableDebuggerWindowBase + { + private DebuggerComponent m_DebuggerComponent = null; + private float m_LastIconX = 0f; + private float m_LastIconY = 0f; + private float m_LastWindowX = 0f; + private float m_LastWindowY = 0f; + private float m_LastWindowWidth = 0f; + private float m_LastWindowHeight = 0f; + private float m_LastWindowScale = 0f; + + public override void Initialize(params object[] args) + { + m_DebuggerComponent = DebuggerComponent.Instance; + if (m_DebuggerComponent == null) + { + Log.Error("Debugger component is invalid."); + return; + } + + + m_LastIconX = Utility.PlayerPrefsX.GetFloat("Debugger.Icon.X", DefaultIconRect.x); + m_LastIconY = Utility.PlayerPrefsX.GetFloat("Debugger.Icon.Y", DefaultIconRect.y); + m_LastWindowX = Utility.PlayerPrefsX.GetFloat("Debugger.Window.X", DefaultWindowRect.x); + m_LastWindowY = Utility.PlayerPrefsX.GetFloat("Debugger.Window.Y", DefaultWindowRect.y); + m_LastWindowWidth = Utility.PlayerPrefsX.GetFloat("Debugger.Window.Width", DefaultWindowRect.width); + m_LastWindowHeight = Utility.PlayerPrefsX.GetFloat("Debugger.Window.Height", DefaultWindowRect.height); + m_DebuggerComponent.WindowScale = m_LastWindowScale = Utility.PlayerPrefsX.GetFloat("Debugger.Window.Scale", DefaultWindowScale); + m_DebuggerComponent.IconRect = new Rect(m_LastIconX, m_LastIconY, DefaultIconRect.width, DefaultIconRect.height); + m_DebuggerComponent.WindowRect = new Rect(m_LastWindowX, m_LastWindowY, m_LastWindowWidth, m_LastWindowHeight); + } + + public override void OnUpdate(float elapseSeconds, float realElapseSeconds) + { + if (m_LastIconX != m_DebuggerComponent.IconRect.x) + { + m_LastIconX = m_DebuggerComponent.IconRect.x; + Utility.PlayerPrefsX.SetFloat("Debugger.Icon.X", m_DebuggerComponent.IconRect.x); + } + + if (m_LastIconY != m_DebuggerComponent.IconRect.y) + { + m_LastIconY = m_DebuggerComponent.IconRect.y; + Utility.PlayerPrefsX.SetFloat("Debugger.Icon.Y", m_DebuggerComponent.IconRect.y); + } + + if (m_LastWindowX != m_DebuggerComponent.WindowRect.x) + { + m_LastWindowX = m_DebuggerComponent.WindowRect.x; + Utility.PlayerPrefsX.SetFloat("Debugger.Window.X", m_DebuggerComponent.WindowRect.x); + } + + if (m_LastWindowY != m_DebuggerComponent.WindowRect.y) + { + m_LastWindowY = m_DebuggerComponent.WindowRect.y; + Utility.PlayerPrefsX.SetFloat("Debugger.Window.Y", m_DebuggerComponent.WindowRect.y); + } + + if (m_LastWindowWidth != m_DebuggerComponent.WindowRect.width) + { + m_LastWindowWidth = m_DebuggerComponent.WindowRect.width; + Utility.PlayerPrefsX.SetFloat("Debugger.Window.Width", m_DebuggerComponent.WindowRect.width); + } + + if (m_LastWindowHeight != m_DebuggerComponent.WindowRect.height) + { + m_LastWindowHeight = m_DebuggerComponent.WindowRect.height; + Utility.PlayerPrefsX.SetFloat("Debugger.Window.Height", m_DebuggerComponent.WindowRect.height); + } + + if (m_LastWindowScale != m_DebuggerComponent.WindowScale) + { + m_LastWindowScale = m_DebuggerComponent.WindowScale; + Utility.PlayerPrefsX.SetFloat("Debugger.Window.Scale", m_DebuggerComponent.WindowScale); + } + } + + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Window Settings"); + GUILayout.BeginVertical("box"); + { + GUILayout.BeginHorizontal(); + { + GUILayout.Label("Position:", GUILayout.Width(60f)); + GUILayout.Label("Drag window caption to move position."); + } + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + { + float width = m_DebuggerComponent.WindowRect.width; + GUILayout.Label("Width:", GUILayout.Width(60f)); + if (GUILayout.RepeatButton("-", GUILayout.Width(30f))) + { + width--; + } + + width = GUILayout.HorizontalSlider(width, 100f, Screen.width - 20f); + if (GUILayout.RepeatButton("+", GUILayout.Width(30f))) + { + width++; + } + + width = Mathf.Clamp(width, 100f, Screen.width - 20f); + if (width != m_DebuggerComponent.WindowRect.width) + { + m_DebuggerComponent.WindowRect = new Rect(m_DebuggerComponent.WindowRect.x, m_DebuggerComponent.WindowRect.y, width, m_DebuggerComponent.WindowRect.height); + } + } + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + { + float height = m_DebuggerComponent.WindowRect.height; + GUILayout.Label("Height:", GUILayout.Width(60f)); + if (GUILayout.RepeatButton("-", GUILayout.Width(30f))) + { + height--; + } + + height = GUILayout.HorizontalSlider(height, 100f, Screen.height - 20f); + if (GUILayout.RepeatButton("+", GUILayout.Width(30f))) + { + height++; + } + + height = Mathf.Clamp(height, 100f, Screen.height - 20f); + if (height != m_DebuggerComponent.WindowRect.height) + { + m_DebuggerComponent.WindowRect = new Rect(m_DebuggerComponent.WindowRect.x, m_DebuggerComponent.WindowRect.y, m_DebuggerComponent.WindowRect.width, height); + } + } + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + { + float scale = m_DebuggerComponent.WindowScale; + GUILayout.Label("Scale:", GUILayout.Width(60f)); + if (GUILayout.RepeatButton("-", GUILayout.Width(30f))) + { + scale -= 0.01f; + } + + scale = GUILayout.HorizontalSlider(scale, 0.5f, 4f); + if (GUILayout.RepeatButton("+", GUILayout.Width(30f))) + { + scale += 0.01f; + } + + scale = Mathf.Clamp(scale, 0.5f, 4f); + if (scale != m_DebuggerComponent.WindowScale) + { + m_DebuggerComponent.WindowScale = scale; + } + } + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + { + if (GUILayout.Button("0.5x", GUILayout.Height(60f))) + { + m_DebuggerComponent.WindowScale = 0.5f; + } + + if (GUILayout.Button("1.0x", GUILayout.Height(60f))) + { + m_DebuggerComponent.WindowScale = 1f; + } + + if (GUILayout.Button("1.5x", GUILayout.Height(60f))) + { + m_DebuggerComponent.WindowScale = 1.5f; + } + + if (GUILayout.Button("2.0x", GUILayout.Height(60f))) + { + m_DebuggerComponent.WindowScale = 2f; + } + + if (GUILayout.Button("2.5x", GUILayout.Height(60f))) + { + m_DebuggerComponent.WindowScale = 2.5f; + } + + if (GUILayout.Button("3.0x", GUILayout.Height(60f))) + { + m_DebuggerComponent.WindowScale = 3f; + } + + if (GUILayout.Button("3.5x", GUILayout.Height(60f))) + { + m_DebuggerComponent.WindowScale = 3.5f; + } + + if (GUILayout.Button("4.0x", GUILayout.Height(60f))) + { + m_DebuggerComponent.WindowScale = 4f; + } + } + GUILayout.EndHorizontal(); + + if (GUILayout.Button("Reset Layout", GUILayout.Height(30f))) + { + m_DebuggerComponent.ResetLayout(); + } + } + GUILayout.EndVertical(); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.SettingsWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.SettingsWindow.cs.meta new file mode 100644 index 0000000..e73c3d9 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.SettingsWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b4a36d102b4e1a648810a983c2101967 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.SystemInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.SystemInformationWindow.cs new file mode 100644 index 0000000..1dbaea2 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.SystemInformationWindow.cs @@ -0,0 +1,56 @@ + +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class SystemInformationWindow : ScrollableDebuggerWindowBase + { + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("System Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Device Unique ID", SystemInfo.deviceUniqueIdentifier); + DrawItem("Device Name", SystemInfo.deviceName); + DrawItem("Device Type", SystemInfo.deviceType.ToString()); + DrawItem("Device Model", SystemInfo.deviceModel); + DrawItem("Processor Type", SystemInfo.processorType); + DrawItem("Processor Count", SystemInfo.processorCount.ToString()); + DrawItem("Processor Frequency", Utility.Text.Format("{0} MHz", SystemInfo.processorFrequency)); + DrawItem("System Memory Size", Utility.Text.Format("{0} MB", SystemInfo.systemMemorySize)); +#if UNITY_5_5_OR_NEWER + DrawItem("Operating System Family", SystemInfo.operatingSystemFamily.ToString()); +#endif + DrawItem("Operating System", SystemInfo.operatingSystem); +#if UNITY_5_6_OR_NEWER + DrawItem("Battery Status", SystemInfo.batteryStatus.ToString()); + DrawItem("Battery Level", GetBatteryLevelString(SystemInfo.batteryLevel)); +#endif +#if UNITY_5_4_OR_NEWER + DrawItem("Supports Audio", SystemInfo.supportsAudio.ToString()); +#endif + DrawItem("Supports Location Service", SystemInfo.supportsLocationService.ToString()); + DrawItem("Supports Accelerometer", SystemInfo.supportsAccelerometer.ToString()); + DrawItem("Supports Gyroscope", SystemInfo.supportsGyroscope.ToString()); + DrawItem("Supports Vibration", SystemInfo.supportsVibration.ToString()); + DrawItem("Genuine", Application.genuine.ToString()); + DrawItem("Genuine Check Available", Application.genuineCheckAvailable.ToString()); + } + GUILayout.EndVertical(); + } + + private string GetBatteryLevelString(float batteryLevel) + { + if (batteryLevel < 0f) + { + return "Unavailable"; + } + + return batteryLevel.ToString("P0"); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.SystemInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.SystemInformationWindow.cs.meta new file mode 100644 index 0000000..b8505c5 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.SystemInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 788d38477d0fde741b8939db86e6a083 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.TimeInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.TimeInformationWindow.cs new file mode 100644 index 0000000..491ad83 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.TimeInformationWindow.cs @@ -0,0 +1,70 @@ + +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class TimeInformationWindow : ScrollableDebuggerWindowBase + { + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Time Information"); + GUILayout.BeginVertical("box"); + { + DrawItem("Time Scale", Utility.Text.Format("{0} [{1}]", Time.timeScale, GetTimeScaleDescription(Time.timeScale))); + DrawItem("Realtime Since Startup", Time.realtimeSinceStartup.ToString()); + DrawItem("Time Since Level Load", Time.timeSinceLevelLoad.ToString()); + DrawItem("Time", Time.time.ToString()); + DrawItem("Fixed Time", Time.fixedTime.ToString()); + DrawItem("Unscaled Time", Time.unscaledTime.ToString()); +#if UNITY_5_6_OR_NEWER + DrawItem("Fixed Unscaled Time", Time.fixedUnscaledTime.ToString()); +#endif + DrawItem("Delta Time", Time.deltaTime.ToString()); + DrawItem("Fixed Delta Time", Time.fixedDeltaTime.ToString()); + DrawItem("Unscaled Delta Time", Time.unscaledDeltaTime.ToString()); +#if UNITY_5_6_OR_NEWER + DrawItem("Fixed Unscaled Delta Time", Time.fixedUnscaledDeltaTime.ToString()); +#endif + DrawItem("Smooth Delta Time", Time.smoothDeltaTime.ToString()); + DrawItem("Maximum Delta Time", Time.maximumDeltaTime.ToString()); +#if UNITY_5_5_OR_NEWER + DrawItem("Maximum Particle Delta Time", Time.maximumParticleDeltaTime.ToString()); +#endif + DrawItem("Frame Count", Time.frameCount.ToString()); + DrawItem("Rendered Frame Count", Time.renderedFrameCount.ToString()); + DrawItem("Capture Framerate", Time.captureFramerate.ToString()); +#if UNITY_2019_2_OR_NEWER + DrawItem("Capture Delta Time", Time.captureDeltaTime.ToString()); +#endif +#if UNITY_5_6_OR_NEWER + DrawItem("In Fixed Time Step", Time.inFixedTimeStep.ToString()); +#endif + } + GUILayout.EndVertical(); + } + + private string GetTimeScaleDescription(float timeScale) + { + if (timeScale <= 0f) + { + return "Pause"; + } + + if (timeScale < 1f) + { + return "Slower"; + } + + if (timeScale > 1f) + { + return "Faster"; + } + + return "Normal"; + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.TimeInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.TimeInformationWindow.cs.meta new file mode 100644 index 0000000..fd67ff3 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.TimeInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e8bdf40da7ffdc94ab9e39ba8daeaa3a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.WebPlayerInformationWindow.cs b/Runtime/Debugger/DebuggerComponent.WebPlayerInformationWindow.cs new file mode 100644 index 0000000..851d9bb --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.WebPlayerInformationWindow.cs @@ -0,0 +1,34 @@ +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Debugger.Runtime +{ + public sealed partial class DebuggerComponent + { + private sealed class WebPlayerInformationWindow : ScrollableDebuggerWindowBase + { + protected override void OnDrawScrollableWindow() + { + GUILayout.Label("Web Player Information"); + GUILayout.BeginVertical("box"); + { +#if !UNITY_2017_2_OR_NEWER + DrawItem("Is Web Player", Application.isWebPlayer.ToString()); +#endif + DrawItem("Absolute URL", Application.absoluteURL); +#if !UNITY_2017_2_OR_NEWER + DrawItem("Source Value", Application.srcValue); +#endif +#if !UNITY_2018_2_OR_NEWER + DrawItem("Streamed Bytes", Application.streamedBytes.ToString()); +#endif +#if UNITY_5_3 || UNITY_5_4 + DrawItem("Web Security Enabled", Application.webSecurityEnabled.ToString()); + DrawItem("Web Security Host URL", Application.webSecurityHostUrl.ToString()); +#endif + } + GUILayout.EndVertical(); + } + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.WebPlayerInformationWindow.cs.meta b/Runtime/Debugger/DebuggerComponent.WebPlayerInformationWindow.cs.meta new file mode 100644 index 0000000..a194fac --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.WebPlayerInformationWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eea52d2ddd7a8804f956d4b9a2d67e8f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerComponent.cs b/Runtime/Debugger/DebuggerComponent.cs new file mode 100644 index 0000000..b9ba013 --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.cs @@ -0,0 +1,418 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace AlicizaX.Debugger.Runtime +{ + /// + /// 调试器组件。 + /// + [DisallowMultipleComponent] + [AddComponentMenu("Game Framework/Debugger")] + public sealed partial class DebuggerComponent : MonoBehaviour + { + private static DebuggerComponent _instance; + + public static DebuggerComponent Instance + { + get + { + if (_instance == null) + { + _instance = FindObjectOfType(); + } + + return _instance; + } + } + + /// + /// 默认调试器漂浮框大小。 + /// + internal static readonly Rect DefaultIconRect = new Rect(10f, 10f, 60f, 60f); + + /// + /// 默认调试器窗口大小。 + /// + internal static readonly Rect DefaultWindowRect = new Rect(10f, 10f, 640f, 480f); + + /// + /// 默认调试器窗口缩放比例。 + /// + internal static readonly float DefaultWindowScale = 1f; + + // private static readonly TextEditor s_TextEditor = new TextEditor(); + private IDebuggerModule _mDebuggerModule = null; + private Rect m_DragRect = new Rect(0f, 0f, float.MaxValue, 25f); + private Rect m_IconRect = DefaultIconRect; + private Rect m_WindowRect = DefaultWindowRect; + private float m_WindowScale = DefaultWindowScale; + + [SerializeField] private GUISkin m_Skin = null; + + [SerializeField] private DebuggerActiveWindowType m_ActiveWindow = DebuggerActiveWindowType.AlwaysOpen; + + [SerializeField] private bool m_ShowFullWindow = false; + + [SerializeField] private ConsoleWindow m_ConsoleWindow = new ConsoleWindow(); + + private SystemInformationWindow m_SystemInformationWindow = new SystemInformationWindow(); + private EnvironmentInformationWindow m_EnvironmentInformationWindow = new EnvironmentInformationWindow(); + private ScreenInformationWindow m_ScreenInformationWindow = new ScreenInformationWindow(); + private GraphicsInformationWindow m_GraphicsInformationWindow = new GraphicsInformationWindow(); + private InputSummaryInformationWindow m_InputSummaryInformationWindow = new InputSummaryInformationWindow(); + private InputTouchInformationWindow m_InputTouchInformationWindow = new InputTouchInformationWindow(); + private InputLocationInformationWindow m_InputLocationInformationWindow = new InputLocationInformationWindow(); + private InputAccelerationInformationWindow m_InputAccelerationInformationWindow = new InputAccelerationInformationWindow(); + private InputGyroscopeInformationWindow m_InputGyroscopeInformationWindow = new InputGyroscopeInformationWindow(); + private InputCompassInformationWindow m_InputCompassInformationWindow = new InputCompassInformationWindow(); + private PathInformationWindow m_PathInformationWindow = new PathInformationWindow(); + private SceneInformationWindow m_SceneInformationWindow = new SceneInformationWindow(); + private TimeInformationWindow m_TimeInformationWindow = new TimeInformationWindow(); + private QualityInformationWindow m_QualityInformationWindow = new QualityInformationWindow(); + private ProfilerInformationWindow m_ProfilerInformationWindow = new ProfilerInformationWindow(); + private WebPlayerInformationWindow m_WebPlayerInformationWindow = new WebPlayerInformationWindow(); + private RuntimeMemorySummaryWindow m_RuntimeMemorySummaryWindow = new RuntimeMemorySummaryWindow(); + private RuntimeMemoryInformationWindow m_RuntimeMemoryAllInformationWindow = new RuntimeMemoryInformationWindow(); + private RuntimeMemoryInformationWindow m_RuntimeMemoryTextureInformationWindow = new RuntimeMemoryInformationWindow(); + private RuntimeMemoryInformationWindow m_RuntimeMemoryMeshInformationWindow = new RuntimeMemoryInformationWindow(); + private RuntimeMemoryInformationWindow m_RuntimeMemoryMaterialInformationWindow = new RuntimeMemoryInformationWindow(); + private RuntimeMemoryInformationWindow m_RuntimeMemoryShaderInformationWindow = new RuntimeMemoryInformationWindow(); + private RuntimeMemoryInformationWindow m_RuntimeMemoryAnimationClipInformationWindow = new RuntimeMemoryInformationWindow(); + private RuntimeMemoryInformationWindow m_RuntimeMemoryAudioClipInformationWindow = new RuntimeMemoryInformationWindow(); + private RuntimeMemoryInformationWindow m_RuntimeMemoryFontInformationWindow = new RuntimeMemoryInformationWindow(); + private RuntimeMemoryInformationWindow m_RuntimeMemoryTextAssetInformationWindow = new RuntimeMemoryInformationWindow(); + private RuntimeMemoryInformationWindow m_RuntimeMemoryScriptableObjectInformationWindow = new RuntimeMemoryInformationWindow(); + private ObjectPoolInformationWindow m_ObjectPoolInformationWindow = new ObjectPoolInformationWindow(); + + private ReferencePoolInformationWindow m_ReferencePoolInformationWindow = new ReferencePoolInformationWindow(); + + //private NetworkInformationWindow m_NetworkInformationWindow = new NetworkInformationWindow(); + private SettingsWindow m_SettingsWindow = new SettingsWindow(); + //private OperationsWindow m_OperationsWindow = new OperationsWindow(); + + private FpsCounter m_FpsCounter = null; + + /// + /// 获取或设置调试器窗口是否激活。 + /// + public bool ActiveWindow + { + get { return _mDebuggerModule.ActiveWindow; } + set + { + _mDebuggerModule.ActiveWindow = value; + enabled = value; + } + } + + /// + /// 获取或设置是否显示完整调试器界面。 + /// + public bool ShowFullWindow + { + get { return m_ShowFullWindow; } + set { m_ShowFullWindow = value; } + } + + /// + /// 获取或设置调试器漂浮框大小。 + /// + public Rect IconRect + { + get { return m_IconRect; } + set { m_IconRect = value; } + } + + /// + /// 获取或设置调试器窗口大小。 + /// + public Rect WindowRect + { + get { return m_WindowRect; } + set { m_WindowRect = value; } + } + + /// + /// 获取或设置调试器窗口缩放比例。 + /// + public float WindowScale + { + get { return m_WindowScale; } + set { m_WindowScale = value; } + } + + /// + /// 游戏框架组件初始化。 + /// + private void Awake() + { + _instance = this; + _mDebuggerModule = ModuleSystem.RegisterModule(); + if (_mDebuggerModule == null) + { + Log.Error("Debugger manager is invalid."); + return; + } + + m_FpsCounter = new FpsCounter(0.5f); + } + + private void Start() + { + if (m_Skin == null) + { + Debug.LogWarning("DebugComponent GUI SKin Is Null!"); + } + + RegisterDebuggerWindow("Console", m_ConsoleWindow); + RegisterDebuggerWindow("Information/System", m_SystemInformationWindow); + RegisterDebuggerWindow("Information/Environment", m_EnvironmentInformationWindow); + RegisterDebuggerWindow("Information/Screen", m_ScreenInformationWindow); + RegisterDebuggerWindow("Information/Graphics", m_GraphicsInformationWindow); + RegisterDebuggerWindow("Information/Input/Summary", m_InputSummaryInformationWindow); + RegisterDebuggerWindow("Information/Input/Touch", m_InputTouchInformationWindow); + RegisterDebuggerWindow("Information/Input/Location", m_InputLocationInformationWindow); + RegisterDebuggerWindow("Information/Input/Acceleration", m_InputAccelerationInformationWindow); + RegisterDebuggerWindow("Information/Input/Gyroscope", m_InputGyroscopeInformationWindow); + RegisterDebuggerWindow("Information/Input/Compass", m_InputCompassInformationWindow); + RegisterDebuggerWindow("Information/Other/Scene", m_SceneInformationWindow); + RegisterDebuggerWindow("Information/Other/Path", m_PathInformationWindow); + RegisterDebuggerWindow("Information/Other/Time", m_TimeInformationWindow); + RegisterDebuggerWindow("Information/Other/Quality", m_QualityInformationWindow); + RegisterDebuggerWindow("Information/Other/Web Player", m_WebPlayerInformationWindow); + RegisterDebuggerWindow("Profiler/Summary", m_ProfilerInformationWindow); + RegisterDebuggerWindow("Profiler/Memory/Summary", m_RuntimeMemorySummaryWindow); + RegisterDebuggerWindow("Profiler/Memory/All", m_RuntimeMemoryAllInformationWindow); + RegisterDebuggerWindow("Profiler/Memory/Texture", m_RuntimeMemoryTextureInformationWindow); + RegisterDebuggerWindow("Profiler/Memory/Mesh", m_RuntimeMemoryMeshInformationWindow); + RegisterDebuggerWindow("Profiler/Memory/Material", m_RuntimeMemoryMaterialInformationWindow); + RegisterDebuggerWindow("Profiler/Memory/Shader", m_RuntimeMemoryShaderInformationWindow); + RegisterDebuggerWindow("Profiler/Memory/AnimationClip", m_RuntimeMemoryAnimationClipInformationWindow); + RegisterDebuggerWindow("Profiler/Memory/AudioClip", m_RuntimeMemoryAudioClipInformationWindow); + RegisterDebuggerWindow("Profiler/Memory/Font", m_RuntimeMemoryFontInformationWindow); + RegisterDebuggerWindow("Profiler/Memory/TextAsset", m_RuntimeMemoryTextAssetInformationWindow); + RegisterDebuggerWindow("Profiler/Memory/ScriptableObject", m_RuntimeMemoryScriptableObjectInformationWindow); + RegisterDebuggerWindow("Profiler/Object Pool", m_ObjectPoolInformationWindow); + RegisterDebuggerWindow("Profiler/Reference Pool", m_ReferencePoolInformationWindow); + //RegisterDebuggerWindow("Profiler/Network", m_NetworkInformationWindow); + RegisterDebuggerWindow("Other/Settings", m_SettingsWindow); + //RegisterDebuggerWindow("Other/Operations", m_OperationsWindow); + + switch (m_ActiveWindow) + { + case DebuggerActiveWindowType.AlwaysOpen: + ActiveWindow = true; + break; + + case DebuggerActiveWindowType.OnlyOpenWhenDevelopment: + ActiveWindow = Debug.isDebugBuild; + break; + + case DebuggerActiveWindowType.OnlyOpenInEditor: + ActiveWindow = Application.isEditor; + break; + + default: + ActiveWindow = false; + break; + } +#if !UNITY_EDITOR + ActiveWindow = AppBuilderSetting.Instance.DebugMode; +#endif + } + + private void Update() + { + m_FpsCounter.Update(Time.deltaTime, Time.unscaledDeltaTime); + } + + private void OnGUI() + { + if (_mDebuggerModule == null || !_mDebuggerModule.ActiveWindow) + { + return; + } + + GUISkin cachedGuiSkin = GUI.skin; + Matrix4x4 cachedMatrix = GUI.matrix; + + GUI.skin = m_Skin; + GUI.matrix = Matrix4x4.Scale(new Vector3(m_WindowScale, m_WindowScale, 1f)); + + if (m_ShowFullWindow) + { + m_WindowRect = GUILayout.Window(0, m_WindowRect, DrawWindow, "GAME FRAMEWORK DEBUGGER"); + } + else + { + m_IconRect = GUILayout.Window(0, m_IconRect, DrawDebuggerWindowIcon, "DEBUGGER"); + } + + GUI.matrix = cachedMatrix; + GUI.skin = cachedGuiSkin; + } + + /// + /// 注册调试器窗口。 + /// + /// 调试器窗口路径。 + /// 要注册的调试器窗口。 + /// 初始化调试器窗口参数。 + public void RegisterDebuggerWindow(string path, IDebuggerWindow debuggerWindow, params object[] args) + { + _mDebuggerModule.RegisterDebuggerWindow(path, debuggerWindow, args); + } + + /// + /// 解除注册调试器窗口。 + /// + /// 调试器窗口路径。 + /// 是否解除注册调试器窗口成功。 + public bool UnregisterDebuggerWindow(string path) + { + return _mDebuggerModule.UnregisterDebuggerWindow(path); + } + + /// + /// 获取调试器窗口。 + /// + /// 调试器窗口路径。 + /// 要获取的调试器窗口。 + public IDebuggerWindow GetDebuggerWindow(string path) + { + return _mDebuggerModule.GetDebuggerWindow(path); + } + + /// + /// 选中调试器窗口。 + /// + /// 调试器窗口路径。 + /// 是否成功选中调试器窗口。 + public bool SelectDebuggerWindow(string path) + { + return _mDebuggerModule.SelectDebuggerWindow(path); + } + + /// + /// 还原调试器窗口布局。 + /// + public void ResetLayout() + { + IconRect = DefaultIconRect; + WindowRect = DefaultWindowRect; + WindowScale = DefaultWindowScale; + } + + /// + /// 获取记录的所有日志。 + /// + /// 要获取的日志。 + public void GetRecentLogs(List results) + { + m_ConsoleWindow.GetRecentLogs(results); + } + + /// + /// 获取记录的最近日志。 + /// + /// 要获取的日志。 + /// 要获取最近日志的数量。 + public void GetRecentLogs(List results, int count) + { + m_ConsoleWindow.GetRecentLogs(results, count); + } + + private void DrawWindow(int windowId) + { + GUI.DragWindow(m_DragRect); + DrawDebuggerWindowGroup(_mDebuggerModule.DebuggerWindowRoot); + } + + private void DrawDebuggerWindowGroup(IDebuggerWindowGroup debuggerWindowGroup) + { + if (debuggerWindowGroup == null) + { + return; + } + + List names = new List(); + string[] debuggerWindowNames = debuggerWindowGroup.GetDebuggerWindowNames(); + for (int i = 0; i < debuggerWindowNames.Length; i++) + { + names.Add(Utility.Text.Format("{0}", debuggerWindowNames[i])); + } + + if (debuggerWindowGroup == _mDebuggerModule.DebuggerWindowRoot) + { + names.Add("Close"); + } + + int toolbarIndex = GUILayout.Toolbar(debuggerWindowGroup.SelectedIndex, names.ToArray(), GUILayout.Height(30f), GUILayout.MaxWidth(Screen.width)); + if (toolbarIndex >= debuggerWindowGroup.DebuggerWindowCount) + { + m_ShowFullWindow = false; + return; + } + + if (debuggerWindowGroup.SelectedWindow == null) + { + return; + } + + if (debuggerWindowGroup.SelectedIndex != toolbarIndex) + { + debuggerWindowGroup.SelectedWindow.OnLeave(); + debuggerWindowGroup.SelectedIndex = toolbarIndex; + debuggerWindowGroup.SelectedWindow.OnEnter(); + } + + IDebuggerWindowGroup subDebuggerWindowGroup = debuggerWindowGroup.SelectedWindow as IDebuggerWindowGroup; + if (subDebuggerWindowGroup != null) + { + DrawDebuggerWindowGroup(subDebuggerWindowGroup); + } + + debuggerWindowGroup.SelectedWindow.OnDraw(); + } + + private void DrawDebuggerWindowIcon(int windowId) + { + GUI.DragWindow(m_DragRect); + GUILayout.Space(5); + Color32 color = Color.white; + m_ConsoleWindow.RefreshCount(); + if (m_ConsoleWindow.FatalCount > 0) + { + color = m_ConsoleWindow.GetLogStringColor(LogType.Exception); + } + else if (m_ConsoleWindow.ErrorCount > 0) + { + color = m_ConsoleWindow.GetLogStringColor(LogType.Error); + } + else if (m_ConsoleWindow.WarningCount > 0) + { + color = m_ConsoleWindow.GetLogStringColor(LogType.Warning); + } + else + { + color = m_ConsoleWindow.GetLogStringColor(LogType.Log); + } + + string title = Utility.Text.Format("FPS: {4:F2}", color.r, color.g, color.b, color.a, m_FpsCounter.CurrentFps); + if (GUILayout.Button(title, GUILayout.Width(100f), GUILayout.Height(40f))) + { + m_ShowFullWindow = true; + } + } + + private static void CopyToClipboard(string content) + { + // s_TextEditor.text = content; + // s_TextEditor.OnFocus(); + // s_TextEditor.Copy(); + // s_TextEditor.text = string.Empty; + } + } +} diff --git a/Runtime/Debugger/DebuggerComponent.cs.meta b/Runtime/Debugger/DebuggerComponent.cs.meta new file mode 100644 index 0000000..6288f5d --- /dev/null +++ b/Runtime/Debugger/DebuggerComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f05eaceeebe870a4595e51f998ed518b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerManager.DebuggerWindowGroup.cs b/Runtime/Debugger/DebuggerManager.DebuggerWindowGroup.cs new file mode 100644 index 0000000..673a1bc --- /dev/null +++ b/Runtime/Debugger/DebuggerManager.DebuggerWindowGroup.cs @@ -0,0 +1,301 @@ +using System.Collections.Generic; +using AlicizaX; + +namespace AlicizaX.Debugger.Runtime +{ + internal sealed partial class DebuggerModule : IDebuggerModule + { + /// + /// 调试器窗口组。 + /// + private sealed class DebuggerWindowGroup : IDebuggerWindowGroup + { + private readonly List> m_DebuggerWindows; + private int m_SelectedIndex; + private string[] m_DebuggerWindowNames; + + public DebuggerWindowGroup() + { + m_DebuggerWindows = new List>(); + m_SelectedIndex = 0; + m_DebuggerWindowNames = null; + } + + /// + /// 获取调试器窗口数量。 + /// + public int DebuggerWindowCount + { + get + { + return m_DebuggerWindows.Count; + } + } + + /// + /// 获取或设置当前选中的调试器窗口索引。 + /// + public int SelectedIndex + { + get + { + return m_SelectedIndex; + } + set + { + m_SelectedIndex = value; + } + } + + /// + /// 获取当前选中的调试器窗口。 + /// + public IDebuggerWindow SelectedWindow + { + get + { + if (m_SelectedIndex >= m_DebuggerWindows.Count) + { + return null; + } + + return m_DebuggerWindows[m_SelectedIndex].Value; + } + } + + /// + /// 初始化调试组。 + /// + /// 初始化调试组参数。 + public void Initialize(params object[] args) + { + } + + /// + /// 关闭调试组。 + /// + public void Shutdown() + { + foreach (KeyValuePair debuggerWindow in m_DebuggerWindows) + { + debuggerWindow.Value.Shutdown(); + } + + m_DebuggerWindows.Clear(); + } + + /// + /// 进入调试器窗口。 + /// + public void OnEnter() + { + SelectedWindow.OnEnter(); + } + + /// + /// 离开调试器窗口。 + /// + public void OnLeave() + { + SelectedWindow.OnLeave(); + } + + /// + /// 调试组轮询。 + /// + /// 逻辑流逝时间,以秒为单位。 + /// 真实流逝时间,以秒为单位。 + public void OnUpdate(float elapseSeconds, float realElapseSeconds) + { + SelectedWindow.OnUpdate(elapseSeconds, realElapseSeconds); + } + + /// + /// 调试器窗口绘制。 + /// + public void OnDraw() + { + } + + private void RefreshDebuggerWindowNames() + { + int index = 0; + m_DebuggerWindowNames = new string[m_DebuggerWindows.Count]; + foreach (KeyValuePair debuggerWindow in m_DebuggerWindows) + { + m_DebuggerWindowNames[index++] = debuggerWindow.Key; + } + } + + /// + /// 获取调试组的调试器窗口名称集合。 + /// + public string[] GetDebuggerWindowNames() + { + return m_DebuggerWindowNames; + } + + /// + /// 获取调试器窗口。 + /// + /// 调试器窗口路径。 + /// 要获取的调试器窗口。 + public IDebuggerWindow GetDebuggerWindow(string path) + { + if (string.IsNullOrEmpty(path)) + { + return null; + } + + int pos = path.IndexOf('/'); + if (pos < 0 || pos >= path.Length - 1) + { + return InternalGetDebuggerWindow(path); + } + + string debuggerWindowGroupName = path.Substring(0, pos); + string leftPath = path.Substring(pos + 1); + DebuggerWindowGroup debuggerWindowGroup = (DebuggerWindowGroup)InternalGetDebuggerWindow(debuggerWindowGroupName); + if (debuggerWindowGroup == null) + { + return null; + } + + return debuggerWindowGroup.GetDebuggerWindow(leftPath); + } + + /// + /// 选中调试器窗口。 + /// + /// 调试器窗口路径。 + /// 是否成功选中调试器窗口。 + public bool SelectDebuggerWindow(string path) + { + if (string.IsNullOrEmpty(path)) + { + return false; + } + + int pos = path.IndexOf('/'); + if (pos < 0 || pos >= path.Length - 1) + { + return InternalSelectDebuggerWindow(path); + } + + string debuggerWindowGroupName = path.Substring(0, pos); + string leftPath = path.Substring(pos + 1); + DebuggerWindowGroup debuggerWindowGroup = (DebuggerWindowGroup)InternalGetDebuggerWindow(debuggerWindowGroupName); + if (debuggerWindowGroup == null || !InternalSelectDebuggerWindow(debuggerWindowGroupName)) + { + return false; + } + + return debuggerWindowGroup.SelectDebuggerWindow(leftPath); + } + + /// + /// 注册调试器窗口。 + /// + /// 调试器窗口路径。 + /// 要注册的调试器窗口。 + public void RegisterDebuggerWindow(string path, IDebuggerWindow debuggerWindow) + { + if (string.IsNullOrEmpty(path)) + { + throw new GameFrameworkException("Path is invalid."); + } + + int pos = path.IndexOf('/'); + if (pos < 0 || pos >= path.Length - 1) + { + if (InternalGetDebuggerWindow(path) != null) + { + throw new GameFrameworkException("Debugger window has been registered."); + } + + m_DebuggerWindows.Add(new KeyValuePair(path, debuggerWindow)); + RefreshDebuggerWindowNames(); + } + else + { + string debuggerWindowGroupName = path.Substring(0, pos); + string leftPath = path.Substring(pos + 1); + DebuggerWindowGroup debuggerWindowGroup = (DebuggerWindowGroup)InternalGetDebuggerWindow(debuggerWindowGroupName); + if (debuggerWindowGroup == null) + { + if (InternalGetDebuggerWindow(debuggerWindowGroupName) != null) + { + throw new GameFrameworkException("Debugger window has been registered, can not create debugger window group."); + } + + debuggerWindowGroup = new DebuggerWindowGroup(); + m_DebuggerWindows.Add(new KeyValuePair(debuggerWindowGroupName, debuggerWindowGroup)); + RefreshDebuggerWindowNames(); + } + + debuggerWindowGroup.RegisterDebuggerWindow(leftPath, debuggerWindow); + } + } + + /// + /// 解除注册调试器窗口。 + /// + /// 调试器窗口路径。 + /// 是否解除注册调试器窗口成功。 + public bool UnregisterDebuggerWindow(string path) + { + if (string.IsNullOrEmpty(path)) + { + return false; + } + + int pos = path.IndexOf('/'); + if (pos < 0 || pos >= path.Length - 1) + { + IDebuggerWindow debuggerWindow = InternalGetDebuggerWindow(path); + bool result = m_DebuggerWindows.Remove(new KeyValuePair(path, debuggerWindow)); + debuggerWindow.Shutdown(); + RefreshDebuggerWindowNames(); + return result; + } + + string debuggerWindowGroupName = path.Substring(0, pos); + string leftPath = path.Substring(pos + 1); + DebuggerWindowGroup debuggerWindowGroup = (DebuggerWindowGroup)InternalGetDebuggerWindow(debuggerWindowGroupName); + if (debuggerWindowGroup == null) + { + return false; + } + + return debuggerWindowGroup.UnregisterDebuggerWindow(leftPath); + } + + private IDebuggerWindow InternalGetDebuggerWindow(string name) + { + foreach (KeyValuePair debuggerWindow in m_DebuggerWindows) + { + if (debuggerWindow.Key == name) + { + return debuggerWindow.Value; + } + } + + return null; + } + + private bool InternalSelectDebuggerWindow(string name) + { + for (int i = 0; i < m_DebuggerWindows.Count; i++) + { + if (m_DebuggerWindows[i].Key == name) + { + m_SelectedIndex = i; + return true; + } + } + + return false; + } + } + } +} diff --git a/Runtime/Debugger/DebuggerManager.DebuggerWindowGroup.cs.meta b/Runtime/Debugger/DebuggerManager.DebuggerWindowGroup.cs.meta new file mode 100644 index 0000000..8db814d --- /dev/null +++ b/Runtime/Debugger/DebuggerManager.DebuggerWindowGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9d7f466f8a97daa4ebcd3c7858fc8d43 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/DebuggerModule.cs b/Runtime/Debugger/DebuggerModule.cs new file mode 100644 index 0000000..2c1a94f --- /dev/null +++ b/Runtime/Debugger/DebuggerModule.cs @@ -0,0 +1,123 @@ +using AlicizaX; + +namespace AlicizaX.Debugger.Runtime +{ + /// + /// 调试器管理器。 + /// + [UnityEngine.Scripting.Preserve] + internal sealed partial class DebuggerModule : IDebuggerModule + { + private readonly DebuggerWindowGroup m_DebuggerWindowRoot; + private bool m_ActiveWindow; + + /// + /// 初始化调试器管理器的新实例。 + /// + [UnityEngine.Scripting.Preserve] + public DebuggerModule() + { + m_DebuggerWindowRoot = new DebuggerWindowGroup(); + m_ActiveWindow = false; + } + + public int Priority + { + get => -1; + } + + /// + /// 获取或设置调试器窗口是否激活。 + /// + public bool ActiveWindow + { + get { return m_ActiveWindow; } + set { m_ActiveWindow = value; } + } + + /// + /// 调试器窗口根结点。 + /// + public IDebuggerWindowGroup DebuggerWindowRoot + { + get { return m_DebuggerWindowRoot; } + } + + /// + /// 调试器管理器轮询。 + /// + /// 逻辑流逝时间,以秒为单位。 + /// 真实流逝时间,以秒为单位。 + void IModuleUpdate.Update(float elapseSeconds, float realElapseSeconds) + { + if (!m_ActiveWindow) + { + return; + } + + m_DebuggerWindowRoot.OnUpdate(elapseSeconds, realElapseSeconds); + } + + /// + /// 关闭并清理调试器管理器。 + /// + void IModule.Dispose() + { + m_ActiveWindow = false; + m_DebuggerWindowRoot.Shutdown(); + } + + + /// + /// 注册调试器窗口。 + /// + /// 调试器窗口路径。 + /// 要注册的调试器窗口。 + /// 初始化调试器窗口参数。 + public void RegisterDebuggerWindow(string path, IDebuggerWindow debuggerWindow, params object[] args) + { + if (string.IsNullOrEmpty(path)) + { + throw new GameFrameworkException("Path is invalid."); + } + + if (debuggerWindow == null) + { + throw new GameFrameworkException("Debugger window is invalid."); + } + + m_DebuggerWindowRoot.RegisterDebuggerWindow(path, debuggerWindow); + debuggerWindow.Initialize(args); + } + + /// + /// 解除注册调试器窗口。 + /// + /// 调试器窗口路径。 + /// 是否解除注册调试器窗口成功。 + public bool UnregisterDebuggerWindow(string path) + { + return m_DebuggerWindowRoot.UnregisterDebuggerWindow(path); + } + + /// + /// 获取调试器窗口。 + /// + /// 调试器窗口路径。 + /// 要获取的调试器窗口。 + public IDebuggerWindow GetDebuggerWindow(string path) + { + return m_DebuggerWindowRoot.GetDebuggerWindow(path); + } + + /// + /// 选中调试器窗口。 + /// + /// 调试器窗口路径。 + /// 是否成功选中调试器窗口。 + public bool SelectDebuggerWindow(string path) + { + return m_DebuggerWindowRoot.SelectDebuggerWindow(path); + } + } +} diff --git a/Runtime/Debugger/DebuggerModule.cs.meta b/Runtime/Debugger/DebuggerModule.cs.meta new file mode 100644 index 0000000..4698d6b --- /dev/null +++ b/Runtime/Debugger/DebuggerModule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 85d1d66f64f96534f93aa063e04902cd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/IDebuggerModule.cs b/Runtime/Debugger/IDebuggerModule.cs new file mode 100644 index 0000000..9852d47 --- /dev/null +++ b/Runtime/Debugger/IDebuggerModule.cs @@ -0,0 +1,38 @@ + + + +using AlicizaX; + +namespace AlicizaX.Debugger.Runtime +{ + /// 调试器管理器接口。 + public interface IDebuggerModule:IModule,IModuleUpdate + { + /// 获取或设置调试器窗口是否激活。 + bool ActiveWindow { get; set; } + + /// 调试器窗口根结点。 + IDebuggerWindowGroup DebuggerWindowRoot { get; } + + /// 注册调试器窗口。 + /// 调试器窗口路径。 + /// 要注册的调试器窗口。 + /// 初始化调试器窗口参数。 + void RegisterDebuggerWindow(string path, IDebuggerWindow debuggerWindow, params object[] args); + + /// 解除注册调试器窗口。 + /// 调试器窗口路径。 + /// 是否解除注册调试器窗口成功。 + bool UnregisterDebuggerWindow(string path); + + /// 获取调试器窗口。 + /// 调试器窗口路径。 + /// 要获取的调试器窗口。 + IDebuggerWindow GetDebuggerWindow(string path); + + /// 选中调试器窗口。 + /// 调试器窗口路径。 + /// 是否成功选中调试器窗口。 + bool SelectDebuggerWindow(string path); + } +} diff --git a/Runtime/Debugger/IDebuggerModule.cs.meta b/Runtime/Debugger/IDebuggerModule.cs.meta new file mode 100644 index 0000000..da9869c --- /dev/null +++ b/Runtime/Debugger/IDebuggerModule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 08bcc79e3a7a4c848e9f3fe85220de0f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/IDebuggerWindow.cs b/Runtime/Debugger/IDebuggerWindow.cs new file mode 100644 index 0000000..bb8825c --- /dev/null +++ b/Runtime/Debugger/IDebuggerWindow.cs @@ -0,0 +1,29 @@ + + +namespace AlicizaX.Debugger.Runtime +{ + /// 调试器窗口接口。 + public interface IDebuggerWindow + { + /// 初始化调试器窗口。 + /// 初始化调试器窗口参数。 + void Initialize(params object[] args); + + /// 关闭调试器窗口。 + void Shutdown(); + + /// 进入调试器窗口。 + void OnEnter(); + + /// 离开调试器窗口。 + void OnLeave(); + + /// 调试器窗口轮询。 + /// 逻辑流逝时间,以秒为单位。 + /// 真实流逝时间,以秒为单位。 + void OnUpdate(float elapseSeconds, float realElapseSeconds); + + /// 调试器窗口绘制。 + void OnDraw(); + } +} \ No newline at end of file diff --git a/Runtime/Debugger/IDebuggerWindow.cs.meta b/Runtime/Debugger/IDebuggerWindow.cs.meta new file mode 100644 index 0000000..3014fbd --- /dev/null +++ b/Runtime/Debugger/IDebuggerWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d31f64ecd68a44589ca5648355f82962 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Debugger/IDebuggerWindowGroup.cs b/Runtime/Debugger/IDebuggerWindowGroup.cs new file mode 100644 index 0000000..6b54d06 --- /dev/null +++ b/Runtime/Debugger/IDebuggerWindowGroup.cs @@ -0,0 +1,31 @@ + + + +namespace AlicizaX.Debugger.Runtime +{ + /// 调试器窗口组接口。 + public interface IDebuggerWindowGroup : IDebuggerWindow + { + /// 获取调试器窗口数量。 + int DebuggerWindowCount { get; } + + /// 获取或设置当前选中的调试器窗口索引。 + int SelectedIndex { get; set; } + + /// 获取当前选中的调试器窗口。 + IDebuggerWindow SelectedWindow { get; } + + /// 获取调试组的调试器窗口名称集合。 + string[] GetDebuggerWindowNames(); + + /// 获取调试器窗口。 + /// 调试器窗口路径。 + /// 要获取的调试器窗口。 + IDebuggerWindow GetDebuggerWindow(string path); + + /// 注册调试器窗口。 + /// 调试器窗口路径。 + /// 要注册的调试器窗口。 + void RegisterDebuggerWindow(string path, IDebuggerWindow debuggerWindow); + } +} \ No newline at end of file diff --git a/Runtime/Debugger/IDebuggerWindowGroup.cs.meta b/Runtime/Debugger/IDebuggerWindowGroup.cs.meta new file mode 100644 index 0000000..d51022f --- /dev/null +++ b/Runtime/Debugger/IDebuggerWindowGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 27ec3e3ea31c49789b9d9d6277f05c56 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/FSM.meta b/Runtime/FSM.meta new file mode 100644 index 0000000..352a3c9 --- /dev/null +++ b/Runtime/FSM.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4747608d66fafbe4ea9b37bd8941beb4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/FSM/FsmComponent.cs b/Runtime/FSM/FsmComponent.cs new file mode 100644 index 0000000..57a725c --- /dev/null +++ b/Runtime/FSM/FsmComponent.cs @@ -0,0 +1,28 @@ +using AlicizaX; +using AlicizaX.Fsm; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace AlicizaX.Fsm.Runtime +{ + /// + /// 有限状态机组件。 + /// + [DisallowMultipleComponent] + [AddComponentMenu("Game Framework/FSM")] + public sealed class FsmComponent : MonoBehaviour + { + private IFsmModule _mFsmModule = null; + + private void Awake() + { + _mFsmModule = ModuleSystem.RegisterModule(); + if (_mFsmModule == null) + { + Log.Error("FSM manager is invalid."); + return; + } + } + } +} diff --git a/Runtime/FSM/FsmComponent.cs.meta b/Runtime/FSM/FsmComponent.cs.meta new file mode 100644 index 0000000..f60b609 --- /dev/null +++ b/Runtime/FSM/FsmComponent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a69e41ab65d84e83a0468f1a2cc3926f +timeCreated: 1712641057 \ No newline at end of file diff --git a/Runtime/FSM/FsmModule.cs b/Runtime/FSM/FsmModule.cs new file mode 100644 index 0000000..6c349ab --- /dev/null +++ b/Runtime/FSM/FsmModule.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; +using UnityEngine.Scripting; +using Object = UnityEngine.Object; + +namespace AlicizaX.Fsm.Runtime +{ + [Preserve] + public sealed class FsmModule : IFsmModule + { + private readonly List _active = new List(256); + private readonly List _toRemove = new List(64); + + public int Priority => 0; + + void IModuleLateUpdate.LateUpdate() + { + RemoveFsm(); + } + + private void RemoveFsm() + { + if (_toRemove.Count > 0) + { + for (int i = 0; i < _toRemove.Count; i++) + { + var f = _toRemove[i]; + MemoryPool.Release(f); + f.Dispose(); + _active.Remove(f); + } + + _toRemove.Clear(); + } + } + + void IModuleUpdate.Update(float elapseSeconds, float realElapseSeconds) + { + float dt = Time.deltaTime; + for (int i = 0; i < _active.Count; i++) + { + _active[i].Tick(dt); + } + } + + void IModuleAwake.Awake() + { + } + + void IModule.Dispose() + { + for (int i = _active.Count - 1; _active.Count > 0; i--) + { + _toRemove.Add(_active[i]); + } + + RemoveFsm(); + } + + private Fsm CreatePooled(FsmConfig cfg, T blackboard, UnityEngine.Object owner = null, Func stateNameGetter = null) + where T : class, IMemory + { + var fsm = Fsm.Rent(cfg, blackboard, owner, stateNameGetter); + _active.Add(fsm); + return fsm; + } + + public void DestroyFsm(Fsm fsm) where T : class, IMemory + { + if (fsm == null) return; + _toRemove.Add(fsm); + } + + public Fsm Create(FsmConfig cfg, UnityEngine.Object owner = null, Func stateNameGetter = null) + where T : class, IMemory, new() + { + var bb = MemoryPool.Acquire(); + var fsm = CreatePooled(cfg, bb, owner, stateNameGetter); + return fsm; + } + } +} diff --git a/Runtime/FSM/FsmModule.cs.meta b/Runtime/FSM/FsmModule.cs.meta new file mode 100644 index 0000000..dcc1530 --- /dev/null +++ b/Runtime/FSM/FsmModule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9eb47e20b70b4505bc1ac91a2669c2db +timeCreated: 1745559879 \ No newline at end of file diff --git a/Runtime/FSM/HighPerfFSM.cs b/Runtime/FSM/HighPerfFSM.cs new file mode 100644 index 0000000..7ad8876 --- /dev/null +++ b/Runtime/FSM/HighPerfFSM.cs @@ -0,0 +1,410 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace AlicizaX.Fsm +{ + internal interface IFsmRunner : IDisposable, IMemory + { + int Id { get; } + string DisplayName { get; } + UnityEngine.Object Owner { get; } + void Tick(float deltaTime); + } + + + public delegate void StateEnter(T bb) where T : class, IMemory; + + public delegate int StateUpdate(T bb) where T : class, IMemory; // -1 = stay + + public delegate void StateExit(T bb) where T : class, IMemory; + + public delegate bool Condition(T bb) where T : class, IMemory; + + // ===================== StateFunc ===================== + public sealed class StateFunc where T : class, IMemory + { + public StateEnter OnEnter; + public StateUpdate OnUpdate; + public StateExit OnExit; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static StateFunc Make(StateEnter enter = null, StateUpdate update = null, StateExit exit = null) + { + return new StateFunc + { + OnEnter = enter ?? EmptyEnter, + OnUpdate = update ?? Stay, + OnExit = exit ?? EmptyExit, + }; + } + + private static void EmptyEnter(T bb) + { + } + + private static int Stay(T bb) => -1; + + private static void EmptyExit(T bb) + { + } + } + + // ===================== Transition ===================== + public struct Transition where T : class, IMemory + { + public int From; + public int To; + public Condition Cond; // may be null + public int Priority; // smaller first + public float Timeout; // >0 enables timeout + + public Transition(int from, int to, Condition cond, int priority = 0, float timeout = 0f) + { + From = from; + To = to; + Cond = cond; + Priority = priority; + Timeout = timeout; + } + } + + internal struct TransitionIndex + { + public int Start; // start index in _trans + public int Length; // number of entries + } + + public readonly struct FsmConfig where T : class, IMemory + { + public readonly StateFunc[] Funcs; // [state] -> funcs + public readonly Transition[] Transitions; // flat transitions list + public readonly int StateCount; // >0 + public readonly int DefaultState; // initial state + + public FsmConfig(StateFunc[] func, Transition[] transition, int defaultState = -1) + { + Funcs = func; + Transitions = transition; + StateCount = Funcs.Length; + DefaultState = defaultState; + if (Funcs == null || Funcs.Length == 0) + throw new ArgumentException("Funcs must not be null/empty"); + if (StateCount <= 0) StateCount = Funcs.Length; + if ((uint)DefaultState >= (uint)StateCount) + throw new ArgumentOutOfRangeException(nameof(DefaultState)); + } + } + + public sealed class Fsm : IFsmRunner where T : class, IMemory + { + private static int _nextId; + + public static Fsm Rent(FsmConfig cfg, T blackboard, UnityEngine.Object owner = null, Func stateNameGetter = null) + { + Fsm fsm = MemoryPool.Acquire>(); + fsm.Init(cfg, blackboard, owner, stateNameGetter); + return fsm; + } + + public static void Return(Fsm fsm) + { + if (fsm == null) return; + MemoryPool.Release(fsm); + } + + // ---- Instance ---- + private StateFunc[] _funcs; + private Transition[] _trans; + private TransitionIndex[] _index; + private int _stateCount; + + private T _bb; + private bool _disposed; + + public int Id { get; private set; } + public string DisplayName { get; private set; } + public int Current { get; private set; } + public float TimeInState { get; private set; } + public bool Initialized { get; private set; } + public UnityEngine.Object Owner { get; private set; } + private Func _stateNameGetter; + + public T Blackboard + { + get => _bb; + } + + public Fsm() + { + } + + private void Init(FsmConfig cfg, T blackboard, UnityEngine.Object owner, Func stateNameGetter) + { + Id = Interlocked.Increment(ref _nextId); + DisplayName = typeof(T).Name; + _funcs = cfg.Funcs; + _trans = cfg.Transitions ?? Array.Empty>(); + _index = BuildIndex(_trans, cfg.StateCount); + _stateCount = cfg.StateCount; + + _bb = blackboard; + Owner = owner; + _stateNameGetter = stateNameGetter; + + Current = cfg.DefaultState; + TimeInState = 0f; + Initialized = false; + _disposed = false; + +#if UNITY_EDITOR + FSMDebugger.Register(this, typeof(T)); + FSMDebugger.BindProvider(Id, owner, BlackboardSnapshot, _stateNameGetter); +#endif + } + + private object BlackboardSnapshot() => _bb; + + private static TransitionIndex[] BuildIndex(Transition[] t, int stateCount) + { + if (t.Length > 1) + { + Array.Sort(t, (a, b) => + { + int f = a.From.CompareTo(b.From); + return (f != 0) ? f : a.Priority.CompareTo(b.Priority); + }); + } + + var idx = new TransitionIndex[stateCount]; + int cur = 0; + while (cur < t.Length) + { + int from = t[cur].From; + int end = cur + 1; + while (end < t.Length && t[end].From == from) end++; + if ((uint)from < (uint)stateCount) + { + idx[from].Start = cur; + idx[from].Length = end - cur; + } + + cur = end; + } + + return idx; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset(int state) + { + if (_disposed) return; + if ((uint)state >= (uint)_stateCount) return; + + if (Initialized) _funcs[Current].OnExit(_bb); + Current = state; + TimeInState = 0f; + _funcs[Current].OnEnter(_bb); + Initialized = true; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Tick(float deltaTime) + { + if (_disposed) return; + + if (!Initialized) + { + Reset(Current); + goto EDITOR_TRACK; + } + + TimeInState += deltaTime; + + // 1) state internal update (may suggest next state) + int suggested = _funcs[Current].OnUpdate(_bb); + if (suggested >= 0 && suggested != Current) + { + ChangeState(suggested); + } + else + { + // 2) transitions (timeout first, then condition) + ref readonly TransitionIndex ti = ref _index[Current]; + for (int i = 0; i < ti.Length; i++) + { + ref readonly var tr = ref _trans[ti.Start + i]; + bool timeoutOk = (tr.Timeout > 0f && TimeInState >= tr.Timeout); + bool condOk = (!timeoutOk && tr.Cond != null && tr.Cond(_bb)); + if (timeoutOk || condOk) + { + ChangeState(tr.To); + break; + } + } + } + + EDITOR_TRACK: +#if UNITY_EDITOR + if (FSMDebugger.Enabled) + FSMDebugger.Track(this); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ChangeState(int next) + { + if ((uint)next >= (uint)_stateCount || next == Current) return; + _funcs[Current].OnExit(_bb); + Current = next; + TimeInState = 0f; + _funcs[Current].OnEnter(_bb); + } + + public void Dispose() + { + if (_disposed) return; + _disposed = true; +#if UNITY_EDITOR + FSMDebugger.Unregister(Id); +#endif + } + + + public void Clear() + { + _funcs = null; + _trans = null; + _index = null; + _stateCount = 0; + _stateNameGetter = null; + Owner = null; + Initialized = false; + TimeInState = 0f; + DisplayName = null; + MemoryPool.Release(_bb); + _bb = null; + } + } + +#if UNITY_EDITOR + // ===================== FSMDebugger (Editor Only) ===================== + public static class FSMDebugger + { + public sealed class Entry + { + public int Id; + public string Name; // Blackboard type name + public int StateIndex; + public double LastSeenEditorTime; + public float TimeInState; + public FieldInfo[] Fields; // cached fields of blackboard + public WeakReference Owner; // may be null + public Func BlackboardGetter; // snapshot getter (only queried when painting) + public Func StateNameGetter; // may be null + } + + private static readonly Dictionary _entries = new Dictionary(256); + + private const double PRUNE_INTERVAL_SEC = 3.0; + private const double STALE_SEC = 10.0; + private static double _lastPruneCheck; + public static bool Enabled { get; private set; } = true; + + static FSMDebugger() + { + UnityEditor.EditorApplication.update += PruneLoop; + } + + public static void SetEnabled(bool enabled) => Enabled = enabled; + + private static void PruneLoop() + { + double now = UnityEditor.EditorApplication.timeSinceStartup; + if (now - _lastPruneCheck < PRUNE_INTERVAL_SEC) return; + _lastPruneCheck = now; + + var toRemove = ListPool.Get(); + foreach (var kv in _entries) + { + var e = kv.Value; + bool deadOwner = false; + if (e.Owner != null && e.Owner.TryGetTarget(out var target)) + { + deadOwner = target == null; + } + else if (e.Owner != null) + { + deadOwner = true; + } + + bool stale = (now - e.LastSeenEditorTime) > STALE_SEC; + if (deadOwner || stale) + toRemove.Add(kv.Key); + } + + for (int i = 0; i < toRemove.Count; i++) + _entries.Remove(toRemove[i]); + + ListPool.Release(toRemove); + } + + internal static void Register(in Fsm fsm, Type blackboardType) where T : class, IMemory + { + var id = fsm.Id; + if (_entries.ContainsKey(id)) return; + + var fields = blackboardType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + + _entries[id] = new Entry + { + Id = id, + Name = blackboardType.Name, + StateIndex = -1, + LastSeenEditorTime = UnityEditor.EditorApplication.timeSinceStartup, + TimeInState = 0f, + Fields = fields, + Owner = null, + BlackboardGetter = null, + StateNameGetter = null + }; + } + + internal static void BindProvider(int id, UnityEngine.Object owner, Func bbGetter, Func stateNameGetter) + { + if (!_entries.TryGetValue(id, out var e)) + return; + e.Owner = owner != null ? new WeakReference(owner) : null; + e.BlackboardGetter = bbGetter; + e.StateNameGetter = stateNameGetter; + } + + internal static void Track(in Fsm fsm) where T : class, IMemory + { + if (!_entries.TryGetValue(fsm.Id, out var e)) + return; + e.StateIndex = fsm.Current; + e.TimeInState = fsm.TimeInState; + e.LastSeenEditorTime = UnityEditor.EditorApplication.timeSinceStartup; + } + + public static void Unregister(int id) => _entries.Remove(id); + public static IReadOnlyDictionary Entries => _entries; + + // --- tiny List pool --- + private static class ListPool + { + private static readonly Stack> _pool = new Stack>(8); + public static List Get() => _pool.Count > 0 ? _pool.Pop() : new List(16); + + public static void Release(List list) + { + list.Clear(); + _pool.Push(list); + } + } + } +#endif +} diff --git a/Runtime/FSM/HighPerfFSM.cs.meta b/Runtime/FSM/HighPerfFSM.cs.meta new file mode 100644 index 0000000..066ea86 --- /dev/null +++ b/Runtime/FSM/HighPerfFSM.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0e40b7705a0b45a3b1e46ad7e89a5ebd +timeCreated: 1745547602 \ No newline at end of file diff --git a/Runtime/FSM/IFsmModule.cs b/Runtime/FSM/IFsmModule.cs new file mode 100644 index 0000000..d5f3617 --- /dev/null +++ b/Runtime/FSM/IFsmModule.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace AlicizaX.Fsm.Runtime +{ + public interface IFsmModule : IModule, IModuleUpdate, IModuleAwake, IModuleLateUpdate + { + Fsm Create(FsmConfig cfg, UnityEngine.Object owner = null, Func stateNameGetter = null) + where T : class, IMemory, new(); + + void DestroyFsm(Fsm fsm) where T : class, IMemory; + } +} diff --git a/Runtime/FSM/IFsmModule.cs.meta b/Runtime/FSM/IFsmModule.cs.meta new file mode 100644 index 0000000..9106f0e --- /dev/null +++ b/Runtime/FSM/IFsmModule.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5710f02ccd444b9c9c5d02d8bb2c3368 \ No newline at end of file diff --git a/Runtime/FSM/SimpleFSM.cs b/Runtime/FSM/SimpleFSM.cs new file mode 100644 index 0000000..5e0dd12 --- /dev/null +++ b/Runtime/FSM/SimpleFSM.cs @@ -0,0 +1,259 @@ +using System; +using System.Runtime.CompilerServices; +using AlicizaX; + + +public interface IState : IMemory +{ + IUltraFSM Fsm { get; set; } + bool IsRegistered { get; set; } + void Init(); + void Enter(); + void Exit(); + void Update(float deltaTime); + void Destroy(); +} + +public abstract class StateBase : IState where TState : Enum +{ + public void Clear() + { + IsRegistered = false; + Fsm = null; + } + + public bool fromPool { get; set; } + public IUltraFSM Fsm { get; set; } + public bool IsRegistered { get; set; } + + protected virtual void OnInit() + { + } + + protected virtual void OnEnter() + { + } + + protected virtual void OnExit() + { + } + + protected virtual void OnUpdate(float deltaTime) + { + } + + protected virtual void OnDestroy() + { + } + + void IState.Init() + { + OnInit(); + } + + void IState.Enter() + { + OnEnter(); + } + + void IState.Exit() + { + OnExit(); + } + + void IState.Update(float deltaTime) + { + OnUpdate(deltaTime); + } + + void IState.Destroy() + { + IsRegistered = false; + OnDestroy(); + } + + protected void SwitchState(TState newStateId) + { + (Fsm as SimpleFSM).SwitchState(newStateId); + } +} + + +public interface IUltraFSM +{ + string Name { get; } + string StateName { get; } + void Update(float deltaTime); + void Dispose(); +} + +public sealed class SimpleFSM : IUltraFSM where TState : Enum +{ + private IState[] _states; + private string _name; + private int _currentIndex = -1; + private int _registeredCount; + private TState _currentState; + + public string Name + { + get => _name; + } + + + public string StateName + { + get + { + if (_currentIndex == -1) + { + return "None"; + } + + return CurrentState.ToString(); + } + } + + + public TState CurrentState + { + get + { + if (_currentIndex == -1) + throw new InvalidOperationException("No current state."); + return _currentState; + } + } + + public SimpleFSM() + { + Initialize("None", 16); + } + + internal int Capacity => _states?.Length ?? 0; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int CalculateCapacity(int requested) + { + if (requested < 16) return 16; + requested--; + requested |= requested >> 1; + requested |= requested >> 2; + requested |= requested >> 4; + requested |= requested >> 8; + requested |= requested >> 16; + return requested + 1; + } + + internal void Initialize(string name, int minCapacity) + { + _name = name; + int newCapacity = CalculateCapacity(minCapacity); + _states ??= new IState[newCapacity]; + } + + private void Resize(int minCapacity) + { + int newCapacity = CalculateCapacity(minCapacity); + if (_states != null && _states.Length >= newCapacity) return; + Array.Resize(ref _states, newCapacity); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Register(TState stateId) where T : class, IMemory, IState, new() + { + int stateIdValue = Convert.ToInt32(stateId); + if (stateIdValue < 0) + throw new ArgumentException("State ID must be non-negative."); + + if (stateIdValue >= _states.Length) + Resize(stateIdValue + 1); + + // 检查当前状态是否已注册,若存在则先释放 + var existingState = _states[stateIdValue]; + if (existingState != null && existingState.IsRegistered) + { + existingState.Destroy(); + MemoryPool.Release(existingState); + _registeredCount--; + } + + IState state = default; + state = new T(); + + _states[stateIdValue] = state; + + if (!state.IsRegistered) + { + state.Fsm = this; + state.Init(); + state.IsRegistered = true; + _registeredCount++; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SwitchState(TState newStateId) + { + int newStateIdValue = Convert.ToInt32(newStateId); + if (newStateIdValue < 0 || newStateIdValue >= _states.Length) + throw new ArgumentOutOfRangeException(nameof(newStateId), "State ID is out of range."); + int prevIndex = _currentIndex; + int newIndex = newStateIdValue; + + if (prevIndex == newIndex) return; + + ExitState(prevIndex); + EnterState(newIndex); + _currentIndex = newIndex; + _currentState = newStateId; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Update(float deltaTime) + { + if ((uint)_currentIndex < (uint)_states.Length) + _states[_currentIndex]?.Update(deltaTime); + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ExitState(int index) + { + if ((uint)index < (uint)_states.Length) + _states[index]?.Exit(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void EnterState(int index) + { + if ((uint)index < (uint)_states.Length) + { + IState state = _states[index]; + if (state.IsRegistered) + state.Enter(); + } + } + + + public void Dispose() + { + if (_states == null) return; + + for (int i = 0; i < _states.Length; i++) + { + IState state = _states[i]; + if (state != null && state.IsRegistered) + { + state.Destroy(); + } + } + + // 清空数组引用,帮助 GC 回收内存 + Array.Clear(_states, 0, _states.Length); + _states = null; + + _currentIndex = -1; + _registeredCount = 0; + } +} diff --git a/Runtime/FSM/SimpleFSM.cs.meta b/Runtime/FSM/SimpleFSM.cs.meta new file mode 100644 index 0000000..5470ab1 --- /dev/null +++ b/Runtime/FSM/SimpleFSM.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 064e59389d6a4e61816e12e5de6848ed +timeCreated: 1756985353 \ No newline at end of file diff --git a/Runtime/Localization.meta b/Runtime/Localization.meta new file mode 100644 index 0000000..b79aae1 --- /dev/null +++ b/Runtime/Localization.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 505ab8f34e8e464b9548e39f10952911 +timeCreated: 1737618593 \ No newline at end of file diff --git a/Runtime/Localization/Event.meta b/Runtime/Localization/Event.meta new file mode 100644 index 0000000..e9ce001 --- /dev/null +++ b/Runtime/Localization/Event.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b7d3c4d80753429da508a3ec547fe296 +timeCreated: 1754033363 \ No newline at end of file diff --git a/Runtime/Localization/Event/LocalizationChangeEvent.cs b/Runtime/Localization/Event/LocalizationChangeEvent.cs new file mode 100644 index 0000000..4f57dc6 --- /dev/null +++ b/Runtime/Localization/Event/LocalizationChangeEvent.cs @@ -0,0 +1,20 @@ +using AlicizaX.Framework.Runtime.Event.Generated; + +namespace AlicizaX.Localization +{ + [Prewarm(4)] + public readonly struct LocalizationChangeEvent : IEventArgs + { + public readonly Language ChangedLanguage; + + public LocalizationChangeEvent(Language language) + { + ChangedLanguage = language; + } + + public static void Publisher(Language language) + { + EventPublisher.Publish(new LocalizationChangeEvent(language)); + } + } +} diff --git a/Runtime/Localization/Event/LocalizationChangeEvent.cs.meta b/Runtime/Localization/Event/LocalizationChangeEvent.cs.meta new file mode 100644 index 0000000..9cda66a --- /dev/null +++ b/Runtime/Localization/Event/LocalizationChangeEvent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5cce7e849bfa4e21b16e9250b8def923 +timeCreated: 1754033406 \ No newline at end of file diff --git a/Runtime/Localization/LocalizationComponent.cs b/Runtime/Localization/LocalizationComponent.cs new file mode 100644 index 0000000..655210e --- /dev/null +++ b/Runtime/Localization/LocalizationComponent.cs @@ -0,0 +1,25 @@ +using UnityEngine; + +namespace AlicizaX.Localization.Runtime +{ + /// + /// 本地化组件。 + /// + [DisallowMultipleComponent] + [AddComponentMenu("Game Framework/Localization")] + public sealed class LocalizationComponent : MonoBehaviour + { + private ILocalizationModule _mLocalizationModule = null; + + public static string PrefsKey = Application.dataPath.GetHashCode() + "Language"; + + private void Awake() + { + _mLocalizationModule = ModuleSystem.RegisterModule(); + if (_mLocalizationModule == null) + { + Log.Info("Localization manager is invalid."); + } + } + } +} diff --git a/Runtime/Localization/LocalizationComponent.cs.meta b/Runtime/Localization/LocalizationComponent.cs.meta new file mode 100644 index 0000000..968e3fd --- /dev/null +++ b/Runtime/Localization/LocalizationComponent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 577f4666bd05453d9b779ce3050b9ec2 +timeCreated: 1712727478 \ No newline at end of file diff --git a/Runtime/Localization/Manager.meta b/Runtime/Localization/Manager.meta new file mode 100644 index 0000000..d635f4a --- /dev/null +++ b/Runtime/Localization/Manager.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f21bf0d39b424adaa2b9de2cd1e7797b +timeCreated: 1712727478 \ No newline at end of file diff --git a/Runtime/Localization/Manager/ILocalizationModule.cs b/Runtime/Localization/Manager/ILocalizationModule.cs new file mode 100644 index 0000000..a5c4268 --- /dev/null +++ b/Runtime/Localization/Manager/ILocalizationModule.cs @@ -0,0 +1,426 @@ +using System.Collections.Generic; +using AlicizaX; + +namespace AlicizaX.Localization.Runtime +{ + /// + /// 本地化管理器接口。 + /// + public interface ILocalizationModule : IModule, IModuleAwake + { + public Language Language { get; } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典主键。 + /// 要获取的字典内容字符串。 + string GetString(string key); + + void ChangedLanguage(Language language); + + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数的类型。 + /// 字典主键。 + /// 字典参数列表。 + /// 要获取的字典内容字符串。 + string GetString(string key, params object[] args); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数的类型。 + /// 字典主键。 + /// 字典参数。 + /// 要获取的字典内容字符串。 + string GetString(string key, T arg); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2, T3 arg3); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典参数 10 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 字典参数 10。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典参数 10 的类型。 + /// 字典参数 11 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 字典参数 10。 + /// 字典参数 11。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典参数 10 的类型。 + /// 字典参数 11 的类型。 + /// 字典参数 12 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 字典参数 10。 + /// 字典参数 11。 + /// 字典参数 12。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典参数 10 的类型。 + /// 字典参数 11 的类型。 + /// 字典参数 12 的类型。 + /// 字典参数 13 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 字典参数 10。 + /// 字典参数 11。 + /// 字典参数 12。 + /// 字典参数 13。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典参数 10 的类型。 + /// 字典参数 11 的类型。 + /// 字典参数 12 的类型。 + /// 字典参数 13 的类型。 + /// 字典参数 14 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 字典参数 10。 + /// 字典参数 11。 + /// 字典参数 12。 + /// 字典参数 13。 + /// 字典参数 14。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典参数 10 的类型。 + /// 字典参数 11 的类型。 + /// 字典参数 12 的类型。 + /// 字典参数 13 的类型。 + /// 字典参数 14 的类型。 + /// 字典参数 15 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 字典参数 10。 + /// 字典参数 11。 + /// 字典参数 12。 + /// 字典参数 13。 + /// 字典参数 14。 + /// 字典参数 15。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15); + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典参数 10 的类型。 + /// 字典参数 11 的类型。 + /// 字典参数 12 的类型。 + /// 字典参数 13 的类型。 + /// 字典参数 14 的类型。 + /// 字典参数 15 的类型。 + /// 字典参数 16 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 字典参数 10。 + /// 字典参数 11。 + /// 字典参数 12。 + /// 字典参数 13。 + /// 字典参数 14。 + /// 字典参数 15。 + /// 字典参数 16。 + /// 要获取的字典内容字符串。 + string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16); + + + /// + /// 根据字典主键获取字典值。 + /// + /// 字典主键。 + /// 字典值。 + string GetRawString(string key); + + void AddLocalizationConfig(Dictionary config); + } +} diff --git a/Runtime/Localization/Manager/ILocalizationModule.cs.meta b/Runtime/Localization/Manager/ILocalizationModule.cs.meta new file mode 100644 index 0000000..9517c66 --- /dev/null +++ b/Runtime/Localization/Manager/ILocalizationModule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 05600158150d4b108b0173acad47e0fe +timeCreated: 1712727478 \ No newline at end of file diff --git a/Runtime/Localization/Manager/LocalizationModule.cs b/Runtime/Localization/Manager/LocalizationModule.cs new file mode 100644 index 0000000..e5a2b55 --- /dev/null +++ b/Runtime/Localization/Manager/LocalizationModule.cs @@ -0,0 +1,737 @@ +using System; +using System.Collections.Generic; +using AlicizaX; + +namespace AlicizaX.Localization.Runtime +{ + /// + /// 本地化管理器。 + /// + [UnityEngine.Scripting.Preserve] + public sealed partial class LocalizationModule : ILocalizationModule + { + private readonly Dictionary Dic = new(); + private Language _language; + + public Language Language + { + get => _language; + } + + public void ChangedLanguage(Language language) + { + if (_language == language) return; + _language = language; + LocalizationChangeEvent.Publisher(_language); + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典主键。 + /// 要获取的字典内容字符串。 + public string GetString(string key) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + return value; + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典主键。 + /// 参数列表. + /// 要获取的字典内容字符串。 + public string GetString(string key, params object[] args) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + return Utility.Text.Format(value, args); + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数的类型。 + /// 字典主键。 + /// 字典参数。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T arg) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg); + } + catch (Exception exception) + { + return Utility.Text.Format("{0},{1},{2},{3}", key, value, arg, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2); + } + catch (Exception exception) + { + return Utility.Text.Format("{0},{1},{2},{3},{4}", key, value, arg1, arg2, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2, T3 arg3) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2, arg3); + } + catch (Exception exception) + { + return Utility.Text.Format("{0},{1},{2},{3},{4},{5}", key, value, arg1, arg2, arg3, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2, arg3, arg4); + } + catch (Exception exception) + { + return Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6}", key, value, arg1, arg2, arg3, arg4, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2, arg3, arg4, arg5); + } + catch (Exception exception) + { + return Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7}", key, value, arg1, arg2, arg3, arg4, arg5, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2, arg3, arg4, arg5, arg6); + } + catch (Exception exception) + { + return Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8}", key, value, arg1, arg2, arg3, arg4, arg5, arg6, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + } + catch (Exception exception) + { + return Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}", key, value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); + } + catch (Exception exception) + { + return Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10}", key, value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); + } + catch (Exception exception) + { + return Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11}", key, value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典参数 10 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 字典参数 10。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10); + } + catch (Exception exception) + { + return Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12}", key, value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典参数 10 的类型。 + /// 字典参数 11 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 字典参数 10。 + /// 字典参数 11。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11); + } + catch (Exception exception) + { + return Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13}", key, value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典参数 10 的类型。 + /// 字典参数 11 的类型。 + /// 字典参数 12 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 字典参数 10。 + /// 字典参数 11。 + /// 字典参数 12。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12); + } + catch (Exception exception) + { + return Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14}", key, value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典参数 10 的类型。 + /// 字典参数 11 的类型。 + /// 字典参数 12 的类型。 + /// 字典参数 13 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 字典参数 10。 + /// 字典参数 11。 + /// 字典参数 12。 + /// 字典参数 13。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13); + } + catch (Exception exception) + { + return Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15}", key, value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典参数 10 的类型。 + /// 字典参数 11 的类型。 + /// 字典参数 12 的类型。 + /// 字典参数 13 的类型。 + /// 字典参数 14 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 字典参数 10。 + /// 字典参数 11。 + /// 字典参数 12。 + /// 字典参数 13。 + /// 字典参数 14。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14); + } + catch (Exception exception) + { + var args = Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13}", arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14); + return Utility.Text.Format("{0},{1},{2},{3}", key, value, args, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典参数 10 的类型。 + /// 字典参数 11 的类型。 + /// 字典参数 12 的类型。 + /// 字典参数 13 的类型。 + /// 字典参数 14 的类型。 + /// 字典参数 15 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 字典参数 10。 + /// 字典参数 11。 + /// 字典参数 12。 + /// 字典参数 13。 + /// 字典参数 14。 + /// 字典参数 15。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15); + } + catch (Exception exception) + { + var args = Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14}", arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15); + return Utility.Text.Format("{0},{1},{2},{3}", key, value, args, exception); + } + } + + /// + /// 根据字典主键获取字典内容字符串。 + /// + /// 字典参数 1 的类型。 + /// 字典参数 2 的类型。 + /// 字典参数 3 的类型。 + /// 字典参数 4 的类型。 + /// 字典参数 5 的类型。 + /// 字典参数 6 的类型。 + /// 字典参数 7 的类型。 + /// 字典参数 8 的类型。 + /// 字典参数 9 的类型。 + /// 字典参数 10 的类型。 + /// 字典参数 11 的类型。 + /// 字典参数 12 的类型。 + /// 字典参数 13 的类型。 + /// 字典参数 14 的类型。 + /// 字典参数 15 的类型。 + /// 字典参数 16 的类型。 + /// 字典主键。 + /// 字典参数 1。 + /// 字典参数 2。 + /// 字典参数 3。 + /// 字典参数 4。 + /// 字典参数 5。 + /// 字典参数 6。 + /// 字典参数 7。 + /// 字典参数 8。 + /// 字典参数 9。 + /// 字典参数 10。 + /// 字典参数 11。 + /// 字典参数 12。 + /// 字典参数 13。 + /// 字典参数 14。 + /// 字典参数 15。 + /// 字典参数 16。 + /// 要获取的字典内容字符串。 + public string GetString(string key, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, + T15 arg15, T16 arg16) + { + var value = GetRawString(key); + if (value == null) + { + return Utility.Text.Format("{0}", key); + } + + try + { + return Utility.Text.Format(value, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16); + } + catch (Exception exception) + { + var args = Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15}", arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15, arg16); + return Utility.Text.Format("{0},{1},{2},{3}", key, value, args, exception); + } + } + + public string GetRawString(string key) + { + if (Dic.TryGetValue(key, out string value)) + { + return value; + } + + return key; + } + + public void AddLocalizationConfig(Dictionary config) + { + foreach (var item in config) + { + Dic.TryAdd(item.Key, item.Value); + } + } + + void IModule.Dispose() + { + Dic.Clear(); + } + + void IModuleAwake.Awake() + { +#if UNITY_EDITOR + _language = (Language)UnityEditor.EditorPrefs.GetInt(LocalizationComponent.PrefsKey, 1); +#else + _language = AppBuilderSetting.Instance.Language; +#endif + } + } +} diff --git a/Runtime/Localization/Manager/LocalizationModule.cs.meta b/Runtime/Localization/Manager/LocalizationModule.cs.meta new file mode 100644 index 0000000..cc0d6d4 --- /dev/null +++ b/Runtime/Localization/Manager/LocalizationModule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f881828389644d9ab16b6d084931ab49 +timeCreated: 1712727478 \ No newline at end of file diff --git a/Runtime/Resource.meta b/Runtime/Resource.meta new file mode 100644 index 0000000..f7a5778 --- /dev/null +++ b/Runtime/Resource.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c5a07118583316146ae02a7b85c96e94 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resource/EventArgs.meta b/Runtime/Resource/EventArgs.meta new file mode 100644 index 0000000..3f98f96 --- /dev/null +++ b/Runtime/Resource/EventArgs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 71638f92e5a94e70904d8cb30bd6073a +timeCreated: 1724175353 \ No newline at end of file diff --git a/Runtime/Resource/EventArgs/AssetDownloadProgressUpdateEventArgs.cs b/Runtime/Resource/EventArgs/AssetDownloadProgressUpdateEventArgs.cs new file mode 100644 index 0000000..dde68b4 --- /dev/null +++ b/Runtime/Resource/EventArgs/AssetDownloadProgressUpdateEventArgs.cs @@ -0,0 +1,55 @@ +namespace AlicizaX.Resource.Runtime +{ + [Prewarm(2)] + public readonly struct AssetDownloadProgressUpdateEventArgs : IEventArgs + { + /// + /// 包名称 + /// + public readonly string PackageName; + + /// + /// 总下载数量 + /// + public readonly int TotalDownloadCount; + + /// + /// 当前下载数量 + /// + public readonly int CurrentDownloadCount; + + /// + /// 总下载大小 + /// + public readonly long TotalDownloadSizeBytes; + + /// + /// 当前下载大小 + /// + public readonly long CurrentDownloadSizeBytes; + + + public AssetDownloadProgressUpdateEventArgs(string packageName, int totalDownloadCount, int currentDownloadCount, long totalDownloadSizeBytes, long currentDownloadSizeBytes) + { + PackageName = packageName; + TotalDownloadCount = totalDownloadCount; + CurrentDownloadCount = currentDownloadCount; + TotalDownloadSizeBytes = totalDownloadSizeBytes; + CurrentDownloadSizeBytes = currentDownloadSizeBytes; + } + + /// + /// 创建下载进度更新 + /// + /// 包名称 + /// 总下载数量 + /// 当前下载数量 + /// 总下载大小 + /// 当前下载大小 + /// + public static AssetDownloadProgressUpdateEventArgs Create(string packageName, int totalDownloadCount, int currentDownloadCount, long totalDownloadSizeBytes, long currentDownloadSizeBytes) + { + return new AssetDownloadProgressUpdateEventArgs(packageName, totalDownloadCount, currentDownloadCount, totalDownloadSizeBytes, currentDownloadSizeBytes); + } + } +} diff --git a/Runtime/Resource/EventArgs/AssetDownloadProgressUpdateEventArgs.cs.meta b/Runtime/Resource/EventArgs/AssetDownloadProgressUpdateEventArgs.cs.meta new file mode 100644 index 0000000..a155d1b --- /dev/null +++ b/Runtime/Resource/EventArgs/AssetDownloadProgressUpdateEventArgs.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 11de0907958a4c8fb9212ccf4ac00608 +timeCreated: 1722395043 \ No newline at end of file diff --git a/Runtime/Resource/Resource.meta b/Runtime/Resource/Resource.meta new file mode 100644 index 0000000..3078c2e --- /dev/null +++ b/Runtime/Resource/Resource.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d36eae39b96a4dabb4af2c34dc87995b +timeCreated: 1737098863 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Callback.meta b/Runtime/Resource/Resource/Callback.meta new file mode 100644 index 0000000..6723f38 --- /dev/null +++ b/Runtime/Resource/Resource/Callback.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 72b9d244a6457e144a16439aa475321f +timeCreated: 1679316822 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Callback/LoadAssetCallbacks.cs b/Runtime/Resource/Resource/Callback/LoadAssetCallbacks.cs new file mode 100644 index 0000000..c5c1907 --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadAssetCallbacks.cs @@ -0,0 +1,94 @@ +using AlicizaX; + +namespace AlicizaX.Resource.Runtime +{ + /// + /// 加载资源回调函数集。 + /// + public sealed class LoadAssetCallbacks + { + private readonly LoadAssetSuccessCallback m_LoadAssetSuccessCallback; + private readonly LoadAssetFailureCallback m_LoadAssetFailureCallback; + private readonly LoadAssetUpdateCallback m_LoadAssetUpdateCallback; + + /// + /// 初始化加载资源回调函数集的新实例。 + /// + /// 加载资源成功回调函数。 + public LoadAssetCallbacks(LoadAssetSuccessCallback loadAssetSuccessCallback) + : this(loadAssetSuccessCallback, null, null) + { + } + + /// + /// 初始化加载资源回调函数集的新实例。 + /// + /// 加载资源成功回调函数。 + /// 加载资源失败回调函数。 + public LoadAssetCallbacks(LoadAssetSuccessCallback loadAssetSuccessCallback, LoadAssetFailureCallback loadAssetFailureCallback) + : this(loadAssetSuccessCallback, loadAssetFailureCallback, null) + { + } + + /// + /// 初始化加载资源回调函数集的新实例。 + /// + /// 加载资源成功回调函数。 + /// 加载资源更新回调函数。 + public LoadAssetCallbacks(LoadAssetSuccessCallback loadAssetSuccessCallback, LoadAssetUpdateCallback loadAssetUpdateCallback) + : this(loadAssetSuccessCallback, null, loadAssetUpdateCallback) + { + } + + /// + /// 初始化加载资源回调函数集的新实例。 + /// + /// 加载资源成功回调函数。 + /// 加载资源失败回调函数。 + /// 加载资源更新回调函数。 + public LoadAssetCallbacks(LoadAssetSuccessCallback loadAssetSuccessCallback, LoadAssetFailureCallback loadAssetFailureCallback, LoadAssetUpdateCallback loadAssetUpdateCallback) + { + if (loadAssetSuccessCallback == null) + { + throw new GameFrameworkException("Load asset success callback is invalid."); + } + + m_LoadAssetSuccessCallback = loadAssetSuccessCallback; + m_LoadAssetFailureCallback = loadAssetFailureCallback; + m_LoadAssetUpdateCallback = loadAssetUpdateCallback; + } + + /// + /// 获取加载资源成功回调函数。 + /// + public LoadAssetSuccessCallback LoadAssetSuccessCallback + { + get + { + return m_LoadAssetSuccessCallback; + } + } + + /// + /// 获取加载资源失败回调函数。 + /// + public LoadAssetFailureCallback LoadAssetFailureCallback + { + get + { + return m_LoadAssetFailureCallback; + } + } + + /// + /// 获取加载资源更新回调函数。 + /// + public LoadAssetUpdateCallback LoadAssetUpdateCallback + { + get + { + return m_LoadAssetUpdateCallback; + } + } + } +} diff --git a/Runtime/Resource/Resource/Callback/LoadAssetCallbacks.cs.meta b/Runtime/Resource/Resource/Callback/LoadAssetCallbacks.cs.meta new file mode 100644 index 0000000..a2ea794 --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadAssetCallbacks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: befc5188f1e39ed47b5bfe03f7bb2bde +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resource/Resource/Callback/LoadAssetFailureCallback.cs b/Runtime/Resource/Resource/Callback/LoadAssetFailureCallback.cs new file mode 100644 index 0000000..0ef8a6c --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadAssetFailureCallback.cs @@ -0,0 +1,11 @@ +namespace AlicizaX.Resource.Runtime +{ + /// + /// 加载资源失败回调函数。 + /// + /// 要加载的资源名称。 + /// 加载资源状态。 + /// 错误信息。 + /// 用户自定义数据。 + public delegate void LoadAssetFailureCallback(string assetName, LoadResourceStatus status, string errorMessage, object userData); +} diff --git a/Runtime/Resource/Resource/Callback/LoadAssetFailureCallback.cs.meta b/Runtime/Resource/Resource/Callback/LoadAssetFailureCallback.cs.meta new file mode 100644 index 0000000..1495ade --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadAssetFailureCallback.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6785caaf731920345b1a4b0fac0b7bcf +timeCreated: 1679305076 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Callback/LoadAssetSuccessCallback.cs b/Runtime/Resource/Resource/Callback/LoadAssetSuccessCallback.cs new file mode 100644 index 0000000..e895742 --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadAssetSuccessCallback.cs @@ -0,0 +1,11 @@ +namespace AlicizaX.Resource.Runtime +{ + /// + /// 加载资源成功回调函数。 + /// + /// 要加载的资源名称。 + /// 已加载的资源。 + /// 加载持续时间。 + /// 用户自定义数据。 + public delegate void LoadAssetSuccessCallback(string assetName, object asset, float duration, object userData); +} diff --git a/Runtime/Resource/Resource/Callback/LoadAssetSuccessCallback.cs.meta b/Runtime/Resource/Resource/Callback/LoadAssetSuccessCallback.cs.meta new file mode 100644 index 0000000..3c7ee9f --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadAssetSuccessCallback.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d1f31fa2ca0d660499363a6b2e018e72 +timeCreated: 1679305063 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Callback/LoadAssetUpdateCallback.cs b/Runtime/Resource/Resource/Callback/LoadAssetUpdateCallback.cs new file mode 100644 index 0000000..27f509b --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadAssetUpdateCallback.cs @@ -0,0 +1,10 @@ +namespace AlicizaX.Resource.Runtime +{ + /// + /// 加载资源更新回调函数。 + /// + /// 要加载的资源名称。 + /// 加载资源进度。 + /// 用户自定义数据。 + public delegate void LoadAssetUpdateCallback(string assetName, float progress, object userData); +} diff --git a/Runtime/Resource/Resource/Callback/LoadAssetUpdateCallback.cs.meta b/Runtime/Resource/Resource/Callback/LoadAssetUpdateCallback.cs.meta new file mode 100644 index 0000000..0197362 --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadAssetUpdateCallback.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4a65bba89c2ae1d49a0cc964823f997a +timeCreated: 1679305085 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Callback/LoadResourceStatus.cs b/Runtime/Resource/Resource/Callback/LoadResourceStatus.cs new file mode 100644 index 0000000..abe354d --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadResourceStatus.cs @@ -0,0 +1,38 @@ +namespace AlicizaX.Resource.Runtime +{ + /// + /// 加载资源状态。 + /// + public enum LoadResourceStatus : byte + { + /// + /// 加载资源完成。 + /// + Success = 0, + + /// + /// 资源不存在。 + /// + NotExist, + + /// + /// 资源尚未准备完毕。 + /// + NotReady, + + /// + /// 依赖资源错误。 + /// + DependencyError, + + /// + /// 资源类型错误。 + /// + TypeError, + + /// + /// 加载资源错误。 + /// + AssetError + } +} diff --git a/Runtime/Resource/Resource/Callback/LoadResourceStatus.cs.meta b/Runtime/Resource/Resource/Callback/LoadResourceStatus.cs.meta new file mode 100644 index 0000000..bc7dd5d --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadResourceStatus.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f7b7ca23cc63576438527a69ccd6f803 +timeCreated: 1679305107 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Callback/LoadSceneCallbacks.cs b/Runtime/Resource/Resource/Callback/LoadSceneCallbacks.cs new file mode 100644 index 0000000..01bf141 --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadSceneCallbacks.cs @@ -0,0 +1,94 @@ +using AlicizaX; + +namespace AlicizaX.Resource.Runtime +{ + /// + /// 加载场景回调函数集。 + /// + public sealed class LoadSceneCallbacks + { + private readonly LoadSceneSuccessCallback m_LoadSceneSuccessCallback; + private readonly LoadSceneFailureCallback m_LoadSceneFailureCallback; + private readonly LoadSceneUpdateCallback m_LoadSceneUpdateCallback; + + /// + /// 初始化加载场景回调函数集的新实例。 + /// + /// 加载场景成功回调函数。 + public LoadSceneCallbacks(LoadSceneSuccessCallback loadSceneSuccessCallback) + : this(loadSceneSuccessCallback, null, null) + { + } + + /// + /// 初始化加载场景回调函数集的新实例。 + /// + /// 加载场景成功回调函数。 + /// 加载场景失败回调函数。 + public LoadSceneCallbacks(LoadSceneSuccessCallback loadSceneSuccessCallback, LoadSceneFailureCallback loadSceneFailureCallback) + : this(loadSceneSuccessCallback, loadSceneFailureCallback, null) + { + } + + /// + /// 初始化加载场景回调函数集的新实例。 + /// + /// 加载场景成功回调函数。 + /// 加载场景更新回调函数。 + public LoadSceneCallbacks(LoadSceneSuccessCallback loadSceneSuccessCallback, LoadSceneUpdateCallback loadSceneUpdateCallback) + : this(loadSceneSuccessCallback, null, loadSceneUpdateCallback) + { + } + + /// + /// 初始化加载场景回调函数集的新实例。 + /// + /// 加载场景成功回调函数。 + /// 加载场景失败回调函数。 + /// 加载场景更新回调函数。 + public LoadSceneCallbacks(LoadSceneSuccessCallback loadSceneSuccessCallback, LoadSceneFailureCallback loadSceneFailureCallback, LoadSceneUpdateCallback loadSceneUpdateCallback) + { + if (loadSceneSuccessCallback == null) + { + throw new GameFrameworkException("Load scene success callback is invalid."); + } + + m_LoadSceneSuccessCallback = loadSceneSuccessCallback; + m_LoadSceneFailureCallback = loadSceneFailureCallback; + m_LoadSceneUpdateCallback = loadSceneUpdateCallback; + } + + /// + /// 获取加载场景成功回调函数。 + /// + public LoadSceneSuccessCallback LoadSceneSuccessCallback + { + get + { + return m_LoadSceneSuccessCallback; + } + } + + /// + /// 获取加载场景失败回调函数。 + /// + public LoadSceneFailureCallback LoadSceneFailureCallback + { + get + { + return m_LoadSceneFailureCallback; + } + } + + /// + /// 获取加载场景更新回调函数。 + /// + public LoadSceneUpdateCallback LoadSceneUpdateCallback + { + get + { + return m_LoadSceneUpdateCallback; + } + } + } +} diff --git a/Runtime/Resource/Resource/Callback/LoadSceneCallbacks.cs.meta b/Runtime/Resource/Resource/Callback/LoadSceneCallbacks.cs.meta new file mode 100644 index 0000000..149b56c --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadSceneCallbacks.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d0973e9b5e0881745b165ebbb076c0de +timeCreated: 1679316813 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Callback/LoadSceneFailureCallback.cs b/Runtime/Resource/Resource/Callback/LoadSceneFailureCallback.cs new file mode 100644 index 0000000..ff89fea --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadSceneFailureCallback.cs @@ -0,0 +1,11 @@ +namespace AlicizaX.Resource.Runtime +{ + /// + /// 加载场景失败回调函数。 + /// + /// 要加载的场景资源名称。 + /// 加载场景状态。 + /// 错误信息。 + /// 用户自定义数据。 + public delegate void LoadSceneFailureCallback(string sceneAssetName, LoadResourceStatus status, string errorMessage, object userData); +} diff --git a/Runtime/Resource/Resource/Callback/LoadSceneFailureCallback.cs.meta b/Runtime/Resource/Resource/Callback/LoadSceneFailureCallback.cs.meta new file mode 100644 index 0000000..ec8a351 --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadSceneFailureCallback.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9f8f6071c75cc4341a784bab6e1771cc +timeCreated: 1679316813 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Callback/LoadSceneSuccessCallback.cs b/Runtime/Resource/Resource/Callback/LoadSceneSuccessCallback.cs new file mode 100644 index 0000000..a952539 --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadSceneSuccessCallback.cs @@ -0,0 +1,11 @@ +namespace AlicizaX.Resource.Runtime +{ + /// + /// 加载场景成功回调函数。 + /// + /// 要加载的场景资源名称。 + /// 场景对象。 + /// 加载持续时间。 + /// 用户自定义数据。 + public delegate void LoadSceneSuccessCallback(string sceneAssetName,UnityEngine.SceneManagement.Scene scene, float duration, object userData); +} diff --git a/Runtime/Resource/Resource/Callback/LoadSceneSuccessCallback.cs.meta b/Runtime/Resource/Resource/Callback/LoadSceneSuccessCallback.cs.meta new file mode 100644 index 0000000..4693ab3 --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadSceneSuccessCallback.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e325b66b297a77840ad71342a6b10d44 +timeCreated: 1679316813 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Callback/LoadSceneUpdateCallback.cs b/Runtime/Resource/Resource/Callback/LoadSceneUpdateCallback.cs new file mode 100644 index 0000000..90c479f --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadSceneUpdateCallback.cs @@ -0,0 +1,10 @@ +namespace AlicizaX.Resource.Runtime +{ + /// + /// 加载场景更新回调函数。 + /// + /// 要加载的场景资源名称。 + /// 加载场景进度。 + /// 用户自定义数据。 + public delegate void LoadSceneUpdateCallback(string sceneAssetName, float progress, object userData); +} diff --git a/Runtime/Resource/Resource/Callback/LoadSceneUpdateCallback.cs.meta b/Runtime/Resource/Resource/Callback/LoadSceneUpdateCallback.cs.meta new file mode 100644 index 0000000..179f27c --- /dev/null +++ b/Runtime/Resource/Resource/Callback/LoadSceneUpdateCallback.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 19c0eceaedcd88a408f3dd8f508aab8a +timeCreated: 1679316813 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Callback/UnloadSceneCallbacks.cs b/Runtime/Resource/Resource/Callback/UnloadSceneCallbacks.cs new file mode 100644 index 0000000..1314912 --- /dev/null +++ b/Runtime/Resource/Resource/Callback/UnloadSceneCallbacks.cs @@ -0,0 +1,60 @@ +using AlicizaX; + +namespace AlicizaX.Resource.Runtime +{ + /// + /// 卸载场景回调函数集。 + /// + public sealed class UnloadSceneCallbacks + { + private readonly UnloadSceneSuccessCallback m_UnloadSceneSuccessCallback; + private readonly UnloadSceneFailureCallback m_UnloadSceneFailureCallback; + + /// + /// 初始化卸载场景回调函数集的新实例。 + /// + /// 卸载场景成功回调函数。 + public UnloadSceneCallbacks(UnloadSceneSuccessCallback unloadSceneSuccessCallback) + : this(unloadSceneSuccessCallback, null) + { + } + + /// + /// 初始化卸载场景回调函数集的新实例。 + /// + /// 卸载场景成功回调函数。 + /// 卸载场景失败回调函数。 + public UnloadSceneCallbacks(UnloadSceneSuccessCallback unloadSceneSuccessCallback, UnloadSceneFailureCallback unloadSceneFailureCallback) + { + if (unloadSceneSuccessCallback == null) + { + throw new GameFrameworkException("Unload scene success callback is invalid."); + } + + m_UnloadSceneSuccessCallback = unloadSceneSuccessCallback; + m_UnloadSceneFailureCallback = unloadSceneFailureCallback; + } + + /// + /// 获取卸载场景成功回调函数。 + /// + public UnloadSceneSuccessCallback UnloadSceneSuccessCallback + { + get + { + return m_UnloadSceneSuccessCallback; + } + } + + /// + /// 获取卸载场景失败回调函数。 + /// + public UnloadSceneFailureCallback UnloadSceneFailureCallback + { + get + { + return m_UnloadSceneFailureCallback; + } + } + } +} diff --git a/Runtime/Resource/Resource/Callback/UnloadSceneCallbacks.cs.meta b/Runtime/Resource/Resource/Callback/UnloadSceneCallbacks.cs.meta new file mode 100644 index 0000000..21765de --- /dev/null +++ b/Runtime/Resource/Resource/Callback/UnloadSceneCallbacks.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1e5f0e0bc23d4ef41a66f85b776b647c +timeCreated: 1679316871 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Callback/UnloadSceneFailureCallback.cs b/Runtime/Resource/Resource/Callback/UnloadSceneFailureCallback.cs new file mode 100644 index 0000000..9f2f112 --- /dev/null +++ b/Runtime/Resource/Resource/Callback/UnloadSceneFailureCallback.cs @@ -0,0 +1,9 @@ +namespace AlicizaX.Resource.Runtime +{ + /// + /// 卸载场景失败回调函数。 + /// + /// 要卸载的场景资源名称。 + /// 用户自定义数据。 + public delegate void UnloadSceneFailureCallback(string sceneAssetName, object userData); +} diff --git a/Runtime/Resource/Resource/Callback/UnloadSceneFailureCallback.cs.meta b/Runtime/Resource/Resource/Callback/UnloadSceneFailureCallback.cs.meta new file mode 100644 index 0000000..6ea62e0 --- /dev/null +++ b/Runtime/Resource/Resource/Callback/UnloadSceneFailureCallback.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fe58952bc4299ac40b1e160ee8a02d59 +timeCreated: 1679316871 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Callback/UnloadSceneSuccessCallback.cs b/Runtime/Resource/Resource/Callback/UnloadSceneSuccessCallback.cs new file mode 100644 index 0000000..e0dc89a --- /dev/null +++ b/Runtime/Resource/Resource/Callback/UnloadSceneSuccessCallback.cs @@ -0,0 +1,9 @@ +namespace AlicizaX.Resource.Runtime +{ + /// + /// 卸载场景成功回调函数。 + /// + /// 要卸载的场景资源名称。 + /// 用户自定义数据。 + public delegate void UnloadSceneSuccessCallback(string sceneAssetName, object userData); +} diff --git a/Runtime/Resource/Resource/Callback/UnloadSceneSuccessCallback.cs.meta b/Runtime/Resource/Resource/Callback/UnloadSceneSuccessCallback.cs.meta new file mode 100644 index 0000000..c367419 --- /dev/null +++ b/Runtime/Resource/Resource/Callback/UnloadSceneSuccessCallback.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0643f4d56f17c204bbbdeda6b5178238 +timeCreated: 1679316871 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Extension.meta b/Runtime/Resource/Resource/Extension.meta new file mode 100644 index 0000000..81c1f53 --- /dev/null +++ b/Runtime/Resource/Resource/Extension.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0af334ee4abd3e340a57593c366861aa +timeCreated: 1710733578 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Extension/AssetItemObject.cs b/Runtime/Resource/Resource/Extension/AssetItemObject.cs new file mode 100644 index 0000000..8cb2104 --- /dev/null +++ b/Runtime/Resource/Resource/Extension/AssetItemObject.cs @@ -0,0 +1,26 @@ +using System.Buffers; +using AlicizaX.ObjectPool; +using AlicizaX; + +namespace AlicizaX.Resource.Runtime +{ + public class AssetItemObject : ObjectBase + { + public static AssetItemObject Create(string location, UnityEngine.Object target) + { + AssetItemObject item = MemoryPool.Acquire(); + item.Initialize(location, target); + return item; + } + + protected override void Release(bool isShutdown) + { + if (Target == null) + { + return; + } + + ModuleSystem.GetModule().UnloadAsset(Target); + } + } +} diff --git a/Runtime/Resource/Resource/Extension/AssetItemObject.cs.meta b/Runtime/Resource/Resource/Extension/AssetItemObject.cs.meta new file mode 100644 index 0000000..6fbb36f --- /dev/null +++ b/Runtime/Resource/Resource/Extension/AssetItemObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: fc6a5d28fbd449c4eb71ea36749609c9 +timeCreated: 1710733596 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Extension/ISetAssetObject.cs b/Runtime/Resource/Resource/Extension/ISetAssetObject.cs new file mode 100644 index 0000000..6ab7267 --- /dev/null +++ b/Runtime/Resource/Resource/Extension/ISetAssetObject.cs @@ -0,0 +1,23 @@ + +using AlicizaX; + +namespace AlicizaX.Resource.Runtime +{ + public interface ISetAssetObject : IMemory + { + /// + /// 资源定位地址。 + /// + string Location { get; } + + /// + /// 设置资源。 + /// + void SetAsset(UnityEngine.Object asset); + + /// + /// 是否可以回收。 + /// + bool IsCanRelease(); + } +} diff --git a/Runtime/Resource/Resource/Extension/ISetAssetObject.cs.meta b/Runtime/Resource/Resource/Extension/ISetAssetObject.cs.meta new file mode 100644 index 0000000..f9f4916 --- /dev/null +++ b/Runtime/Resource/Resource/Extension/ISetAssetObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 81b39df61eb7eef4792db847b42ca929 +timeCreated: 1710733596 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Extension/Implement.meta b/Runtime/Resource/Resource/Extension/Implement.meta new file mode 100644 index 0000000..9f9882b --- /dev/null +++ b/Runtime/Resource/Resource/Extension/Implement.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 96ecdbffc485c5a448bdbc05fcd13128 +timeCreated: 1710733596 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Extension/Implement/SetSpriteExtensions.cs b/Runtime/Resource/Resource/Extension/Implement/SetSpriteExtensions.cs new file mode 100644 index 0000000..bc0ebf7 --- /dev/null +++ b/Runtime/Resource/Resource/Extension/Implement/SetSpriteExtensions.cs @@ -0,0 +1,56 @@ +using System.Threading; +using AlicizaX.Resource.Runtime; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.UI; + +public static class SetSpriteExtensions +{ + /// + /// 设置图片。 + /// + /// UI/Image。 + /// 资源定位地址。 + /// 是否使用原始分辨率。 + /// 取消设置资源的Token。 + public static void SetSprite(this Image image, string location, bool setNativeSize = false, CancellationToken cancellationToken = default) + { + ResourceExtComponent.Instance.SetAssetByResources(SetSpriteObject.Create(image, location, setNativeSize, cancellationToken)).Forget(); + } + + /// + /// 设置图片。 + /// + /// 2D/SpriteRender。 + /// 资源定位地址。 + /// 取消设置资源的Token。 + public static void SetSprite(this SpriteRenderer spriteRenderer, string location, CancellationToken cancellationToken = default) + { + ResourceExtComponent.Instance.SetAssetByResources(SetSpriteObject.Create(spriteRenderer, location, cancellationToken)).Forget(); + } + + /// + /// 设置子图片。 + /// + /// UI/Image。 + /// 资源定位地址。 + /// 子图片名称。 + /// 是否使用原始分辨率。 + /// 取消设置资源的Token。 + public static void SetSubSprite(this Image image, string location, string spriteName, bool setNativeSize = false, CancellationToken cancellationToken = default) + { + ResourceExtComponent.Instance.SetSubSprite(image, location, spriteName, setNativeSize, cancellationToken).Forget(); + } + + /// + /// 设置子图片。 + /// + /// 2D/SpriteRender。 + /// 资源定位地址。 + /// 子图片名称。 + /// 取消设置资源的Token。 + public static void SetSubSprite(this SpriteRenderer spriteRenderer, string location, string spriteName, CancellationToken cancellationToken = default) + { + ResourceExtComponent.Instance.SetSubSprite(spriteRenderer, location, spriteName, cancellationToken).Forget(); + } +} diff --git a/Runtime/Resource/Resource/Extension/Implement/SetSpriteExtensions.cs.meta b/Runtime/Resource/Resource/Extension/Implement/SetSpriteExtensions.cs.meta new file mode 100644 index 0000000..67b6815 --- /dev/null +++ b/Runtime/Resource/Resource/Extension/Implement/SetSpriteExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2f5d4515f2285b948a326ba5e8e88dd4 +timeCreated: 1709017662 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Extension/Implement/SetSpriteObject.cs b/Runtime/Resource/Resource/Extension/Implement/SetSpriteObject.cs new file mode 100644 index 0000000..f9aea6b --- /dev/null +++ b/Runtime/Resource/Resource/Extension/Implement/SetSpriteObject.cs @@ -0,0 +1,116 @@ +using System; +using System.Threading; +using AlicizaX; +using UnityEngine; +using UnityEngine.UI; +using Object = UnityEngine.Object; +#if ODIN_INSPECTOR +using Sirenix.OdinInspector; +#endif + +namespace AlicizaX.Resource.Runtime +{ + [Serializable] + public class SetSpriteObject : ISetAssetObject + { + enum SetType + { + None, + Image, + SpriteRender, + } +#if ODIN_INSPECTOR + [ShowInInspector] +#endif + private SetType _setType; + +#if ODIN_INSPECTOR + [ShowInInspector] +#endif + private Image _image; + +#if ODIN_INSPECTOR + [ShowInInspector] +#endif + private SpriteRenderer _spriteRenderer; + +#if ODIN_INSPECTOR + [ShowInInspector] +#endif + private Sprite _sprite; + + public string Location { get; private set; } + + private bool _setNativeSize = false; + + private CancellationToken _cancellationToken; + + public void SetAsset(Object asset) + { + _sprite = (Sprite)asset; + + if (_cancellationToken.IsCancellationRequested) + return; + + if (_image != null) + { + _image.sprite = _sprite; + if (_setNativeSize) + { + _image.SetNativeSize(); + } + } + else if (_spriteRenderer != null) + { + _spriteRenderer.sprite = _sprite; + } + } + + public bool IsCanRelease() + { + if (_setType == SetType.Image) + { + return _image == null || _image.sprite == null || + (_sprite != null && _image.sprite != _sprite); + } + else if (_setType == SetType.SpriteRender) + { + return _spriteRenderer == null || _spriteRenderer.sprite == null || + (_sprite != null && _spriteRenderer.sprite != _sprite); + } + + return true; + } + + public void Clear() + { + _spriteRenderer = null; + _image = null; + Location = null; + _sprite = null; + _setType = SetType.None; + _setNativeSize = false; + } + + public static SetSpriteObject Create(Image image, string location, bool setNativeSize = false, CancellationToken cancellationToken = default) + { + SetSpriteObject item = MemoryPool.Acquire(); + item._image = image; + item._setNativeSize = setNativeSize; + item.Location = location; + item._cancellationToken = cancellationToken; + item._setType = SetType.Image; + return item; + } + + public static SetSpriteObject Create(SpriteRenderer spriteRenderer, string location, CancellationToken cancellationToken = default) + { + SetSpriteObject item = MemoryPool.Acquire(); + item._spriteRenderer = spriteRenderer; + item.Location = location; + item._cancellationToken = cancellationToken; + item._setType = SetType.SpriteRender; + return item; + } + } +} diff --git a/Runtime/Resource/Resource/Extension/Implement/SetSpriteObject.cs.meta b/Runtime/Resource/Resource/Extension/Implement/SetSpriteObject.cs.meta new file mode 100644 index 0000000..b87fc0e --- /dev/null +++ b/Runtime/Resource/Resource/Extension/Implement/SetSpriteObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c68b2196a3c2b8f4fba42c4df2f70e11 +timeCreated: 1709016956 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Extension/LoadAssetObject.cs b/Runtime/Resource/Resource/Extension/LoadAssetObject.cs new file mode 100644 index 0000000..1e72099 --- /dev/null +++ b/Runtime/Resource/Resource/Extension/LoadAssetObject.cs @@ -0,0 +1,28 @@ +using System; +#if ODIN_INSPECTOR +using Sirenix.OdinInspector; +#endif + +namespace AlicizaX.Resource.Runtime +{ + [Serializable] + public class LoadAssetObject + { +#if ODIN_INSPECTOR + [ShowInInspector] +#endif + public ISetAssetObject AssetObject { get; } +#if ODIN_INSPECTOR + [ShowInInspector] +#endif + public UnityEngine.Object AssetTarget { get; } +#if UNITY_EDITOR + public bool IsSelect { get; set; } +#endif + public LoadAssetObject(ISetAssetObject obj, UnityEngine.Object assetTarget) + { + AssetObject = obj; + AssetTarget = assetTarget; + } + } +} \ No newline at end of file diff --git a/Runtime/Resource/Resource/Extension/LoadAssetObject.cs.meta b/Runtime/Resource/Resource/Extension/LoadAssetObject.cs.meta new file mode 100644 index 0000000..c834831 --- /dev/null +++ b/Runtime/Resource/Resource/Extension/LoadAssetObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b25c6b1f257bf3445b8e2651e169e314 +timeCreated: 1710733596 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Extension/ResourceExtComponent.Resource.cs b/Runtime/Resource/Resource/Extension/ResourceExtComponent.Resource.cs new file mode 100644 index 0000000..3fb4d4e --- /dev/null +++ b/Runtime/Resource/Resource/Extension/ResourceExtComponent.Resource.cs @@ -0,0 +1,63 @@ +using AlicizaX; +using Cysharp.Threading.Tasks; + +namespace AlicizaX.Resource.Runtime +{ + public partial class ResourceExtComponent + { + /// + /// 资源组件。 + /// + private IResourceModule m_ResourceModule; + + private LoadAssetCallbacks m_LoadAssetCallbacks; + + private void InitializedResources() + { + m_ResourceModule = ModuleSystem.GetModule(); + m_LoadAssetCallbacks = new LoadAssetCallbacks(OnLoadAssetSuccess, OnLoadAssetFailure); + } + + private void OnLoadAssetFailure(string assetName, LoadResourceStatus status, string errormessage, object userdata) + { + _assetLoadingList.Remove(assetName); + Log.Error("Can not load asset from '{0}' with error message '{1}'.", assetName, errormessage); + } + + private void OnLoadAssetSuccess(string assetName, object asset, float duration, object userdata) + { + _assetLoadingList.Remove(assetName); + ISetAssetObject setAssetObject = (ISetAssetObject)userdata; + UnityEngine.Object assetObject = asset as UnityEngine.Object; + if (assetObject != null) + { + m_AssetItemPool.Register(AssetItemObject.Create(setAssetObject.Location, assetObject), true); + SetAsset(setAssetObject, assetObject); + } + else + { + Log.Error($"Load failure asset type is {asset.GetType()}."); + } + } + + /// + /// 通过资源系统设置资源。 + /// + /// 需要设置的对象。 + public async UniTaskVoid SetAssetByResources(ISetAssetObject setAssetObject) where T : UnityEngine.Object + { + await TryWaitingLoading(setAssetObject.Location); + + if (m_AssetItemPool.CanSpawn(setAssetObject.Location)) + { + var assetObject = (T)m_AssetItemPool.Spawn(setAssetObject.Location).Target; + SetAsset(setAssetObject, assetObject); + } + else + { + _assetLoadingList.Add(setAssetObject.Location); + m_ResourceModule.LoadAssetAsync(setAssetObject.Location, typeof(T), 0, m_LoadAssetCallbacks, setAssetObject); + } + } + } +} diff --git a/Runtime/Resource/Resource/Extension/ResourceExtComponent.Resource.cs.meta b/Runtime/Resource/Resource/Extension/ResourceExtComponent.Resource.cs.meta new file mode 100644 index 0000000..a98c9bc --- /dev/null +++ b/Runtime/Resource/Resource/Extension/ResourceExtComponent.Resource.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: da9ba664426df254f876fa9e2a424e8c +timeCreated: 1710733596 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Extension/ResourceExtComponent.SubSprite.cs b/Runtime/Resource/Resource/Extension/ResourceExtComponent.SubSprite.cs new file mode 100644 index 0000000..eb19e39 --- /dev/null +++ b/Runtime/Resource/Resource/Extension/ResourceExtComponent.SubSprite.cs @@ -0,0 +1,130 @@ +using System.Collections.Generic; +using System.Threading; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.UI; +using YooAsset; + +namespace AlicizaX.Resource.Runtime +{ + /// + /// 资源组件拓展。 + /// + public partial class ResourceExtComponent + { + private readonly Dictionary _subAssetsHandles = new Dictionary(); + private readonly Dictionary _subSpriteReferences = new Dictionary(); + + public async UniTask SetSubSprite(Image image, string location, string spriteName, bool setNativeSize = false, CancellationToken cancellationToken = default) + { + var subSprite = await GetSubSpriteImp(location, spriteName, cancellationToken); + + if (image == null) + { + Log.Warning($"SetSubAssets Image is null"); + return; + } + + image.sprite = subSprite; + if (setNativeSize) + { + image.SetNativeSize(); + } + + AddReference(image.gameObject, location); + } + + public async UniTask SetSubSprite(SpriteRenderer spriteRenderer, string location, string spriteName, CancellationToken cancellationToken = default) + { + var subSprite = await GetSubSpriteImp(location, spriteName, cancellationToken); + + if (spriteRenderer == null) + { + Log.Warning($"SetSubAssets Image is null"); + return; + } + + spriteRenderer.sprite = subSprite; + AddReference(spriteRenderer.gameObject, location); + } + + private async UniTask GetSubSpriteImp(string location, string spriteName, CancellationToken cancellationToken = default) + { + var assetInfo = YooAssets.GetAssetInfo(location); + if (assetInfo.IsInvalid) + { + throw new GameFrameworkException($"Invalid location: {location}"); + } + + await TryWaitingLoading(location); + + if (!_subAssetsHandles.TryGetValue(location, out var subAssetsHandle)) + { + subAssetsHandle = YooAssets.LoadSubAssetsAsync(location); + await subAssetsHandle.ToUniTask(cancellationToken: cancellationToken); + _subAssetsHandles[location] = subAssetsHandle; + } + + var subSprite = subAssetsHandle.GetSubAssetObject(spriteName); + if (subSprite == null) + { + throw new GameFrameworkException($"Invalid sprite name: {spriteName}"); + } + + return subSprite; + } + + private void AddReference(GameObject target, string location) + { + var subSpriteReference = target.GetComponent(); + if (subSpriteReference == null) + { + subSpriteReference = target.AddComponent(); + } + + _subSpriteReferences[location] = _subSpriteReferences.TryGetValue(location, out var count) ? count + 1 : 1; + subSpriteReference.Reference(location); + } + + internal void DeleteReference(string location) + { + if (string.IsNullOrEmpty(location)) + { + return; + } + + _subSpriteReferences[location] = _subSpriteReferences.TryGetValue(location, out var count) ? count - 1 : 0; + if (_subSpriteReferences[location] <= 0) + { + var subAssetsHandle = _subAssetsHandles[location]; + subAssetsHandle.Dispose(); + _subAssetsHandles.Remove(location); + _subSpriteReferences.Remove(location); + } + } + } + + [DisallowMultipleComponent] + public class SubSpriteReference : MonoBehaviour + { + private string _location; + + public void Reference(string location) + { + if (_location != null && _location != location) + { + ResourceExtComponent.Instance?.DeleteReference(_location); + } + + _location = location; + } + + private void OnDestroy() + { + if (_location != null) + { + ResourceExtComponent.Instance?.DeleteReference(_location); + } + } + } +} diff --git a/Runtime/Resource/Resource/Extension/ResourceExtComponent.SubSprite.cs.meta b/Runtime/Resource/Resource/Extension/ResourceExtComponent.SubSprite.cs.meta new file mode 100644 index 0000000..8993f6c --- /dev/null +++ b/Runtime/Resource/Resource/Extension/ResourceExtComponent.SubSprite.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3dfab359cdc74dc285864a0032cd06f6 +timeCreated: 1747103563 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Extension/ResourceExtComponent.cs b/Runtime/Resource/Resource/Extension/ResourceExtComponent.cs new file mode 100644 index 0000000..dcab63a --- /dev/null +++ b/Runtime/Resource/Resource/Extension/ResourceExtComponent.cs @@ -0,0 +1,150 @@ +using System; +using System.Buffers; +using System.Collections; +using System.Collections.Generic; +using AlicizaX.ObjectPool; +using AlicizaX.Resource.Runtime; +using AlicizaX; +using Cysharp.Threading.Tasks; +using UnityEngine; +using Object = UnityEngine.Object; +#if ODIN_INSPECTOR +using Sirenix.OdinInspector; +#endif + +namespace AlicizaX.Resource.Runtime +{ + public partial class ResourceExtComponent : MonoBehaviour + { + public static ResourceExtComponent Instance { private set; get; } + private readonly TimeoutController _timeoutController = new TimeoutController(); + + /// + /// 正在加载的资源列表。 + /// + private readonly HashSet _assetLoadingList = new HashSet(); + + /// + /// 检查是否可以释放间隔 + /// + [SerializeField] private float m_CheckCanReleaseInterval = 30f; + + private float m_CheckCanReleaseTime = 0.0f; + + /// + /// 对象池自动释放时间间隔 + /// + [SerializeField] private float m_AutoReleaseInterval = 60f; + + /// + /// 保存加载的图片对象 + /// +#if ODIN_INSPECTOR + [ShowInInspector] +#endif + private LinkedList m_LoadAssetObjectsLinkedList; + + /// + /// 散图集合对象池 + /// + private IObjectPool m_AssetItemPool; + + +#if UNITY_EDITOR + public LinkedList LoadAssetObjectsLinkedList + { + get => m_LoadAssetObjectsLinkedList; + set => m_LoadAssetObjectsLinkedList = value; + } +#endif + private void Awake() + { + Instance = this; + } + + private IEnumerator Start() + { + yield return new WaitForEndOfFrame(); + IObjectPoolModule objectPoolComponent = ModuleSystem.GetModule(); + m_AssetItemPool = objectPoolComponent.CreateMultiSpawnObjectPool( + "SetAssetPool", + m_AutoReleaseInterval, 16, 60, 0); + m_LoadAssetObjectsLinkedList = new LinkedList(); + + InitializedResources(); + } + + private void Update() + { + m_CheckCanReleaseTime += Time.unscaledDeltaTime; + if (m_CheckCanReleaseTime < (double)m_CheckCanReleaseInterval) + { + return; + } + + ReleaseUnused(); + } + + + /// + /// 回收无引用的缓存资产。 + /// +#if ODIN_INSPECTOR + [Button("Release Unused")] +#endif + public void ReleaseUnused() + { + if (m_LoadAssetObjectsLinkedList == null) + { + return; + } + + LinkedListNode current = m_LoadAssetObjectsLinkedList.First; + while (current != null) + { + var next = current.Next; + if (current.Value.AssetObject.IsCanRelease()) + { + m_AssetItemPool.Unspawn(current.Value.AssetTarget); + MemoryPool.Release(current.Value.AssetObject); + m_LoadAssetObjectsLinkedList.Remove(current); + } + + current = next; + } + + m_CheckCanReleaseTime = 0f; + } + + private void SetAsset(ISetAssetObject setAssetObject, Object assetObject) + { + m_LoadAssetObjectsLinkedList.AddLast(new LoadAssetObject(setAssetObject, assetObject)); + setAssetObject.SetAsset(assetObject); + } + + private async UniTask TryWaitingLoading(string assetObjectKey) + { + if (_assetLoadingList.Contains(assetObjectKey)) + { + try + { + await UniTask.WaitUntil( + () => !_assetLoadingList.Contains(assetObjectKey)) +#if UNITY_EDITOR + .AttachExternalCancellation(_timeoutController.Timeout(TimeSpan.FromSeconds(60))); + _timeoutController.Reset(); +#else + ; +#endif + } + catch (OperationCanceledException ex) + { + if (_timeoutController.IsTimeout()) + { + Log.Error($"LoadAssetAsync Waiting {assetObjectKey} timeout. reason:{ex.Message}"); + } + } + } + } + } +} diff --git a/Runtime/Resource/Resource/Extension/ResourceExtComponent.cs.meta b/Runtime/Resource/Resource/Extension/ResourceExtComponent.cs.meta new file mode 100644 index 0000000..da5c654 --- /dev/null +++ b/Runtime/Resource/Resource/Extension/ResourceExtComponent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e3adf1e69b12944448c9fe6ae10983cb +timeCreated: 1710733596 \ No newline at end of file diff --git a/Runtime/Resource/Resource/HasAssetResult.cs b/Runtime/Resource/Resource/HasAssetResult.cs new file mode 100644 index 0000000..9fd35b2 --- /dev/null +++ b/Runtime/Resource/Resource/HasAssetResult.cs @@ -0,0 +1,43 @@ +namespace AlicizaX.Resource.Runtime +{ + /// + /// 检查资源是否存在的结果。 + /// + public enum HasAssetResult : byte + { + /// + /// 资源不存在。 + /// + NotExist = 0, + + /// + /// 资源需要从远端更新下载。 + /// + AssetOnline, + + /// + /// 存在资源且存储在磁盘上。 + /// + AssetOnDisk, + + /// + /// 存在资源且存储在文件系统里。 + /// + AssetOnFileSystem, + + /// + /// 存在二进制资源且存储在磁盘上。 + /// + BinaryOnDisk, + + /// + /// 存在二进制资源且存储在文件系统里。 + /// + BinaryOnFileSystem, + + /// + /// 资源定位地址无效。 + /// + Valid, + } +} \ No newline at end of file diff --git a/Runtime/Resource/Resource/HasAssetResult.cs.meta b/Runtime/Resource/Resource/HasAssetResult.cs.meta new file mode 100644 index 0000000..e7abe3d --- /dev/null +++ b/Runtime/Resource/Resource/HasAssetResult.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 70385e0a8397ff64fae0cca1cca67db4 +timeCreated: 1679316902 \ No newline at end of file diff --git a/Runtime/Resource/Resource/IResourceModule.cs b/Runtime/Resource/Resource/IResourceModule.cs new file mode 100644 index 0000000..2c2e5e5 --- /dev/null +++ b/Runtime/Resource/Resource/IResourceModule.cs @@ -0,0 +1,290 @@ +using System; +using System.Threading; +using AlicizaX; +using Cysharp.Threading.Tasks; +using UnityEngine; +using YooAsset; + +namespace AlicizaX.Resource.Runtime +{ + /// + /// 资源管理器接口。 + /// + public interface IResourceModule:IModule + { + /// + /// 获取当前资源适用的游戏版本号。 + /// + string ApplicableGameVersion { get; } + + /// + /// 获取当前内部资源版本号。 + /// + int InternalResourceVersion { get; } + + /// + /// 获取或设置运行模式。 + /// + EPlayMode PlayMode { get; set; } + + string DecryptionServices { get; set; } + + + /// + /// 同时下载的最大数目。 + /// + int DownloadingMaxNum { get; set; } + + /// + /// 失败重试最大数目。 + /// + int FailedTryAgain { get; set; } + + /// + /// 初始化接口。 + /// + void Initialize(); + + + UniTask InitPackageAsync(string packageName="", string hostServerURL = "", string fallbackHostServerURL = ""); + + /// + /// 默认资源包名称。 + /// + string DefaultPackageName { get; set; } + + /// + /// 获取或设置异步系统参数,每帧执行消耗的最大时间切片(单位:毫秒)。 + /// + long Milliseconds { get; set; } + + /// + /// 获取或设置资源对象池自动释放可释放对象的间隔秒数。 + /// + float AssetAutoReleaseInterval { get; set; } + + /// + /// 获取或设置资源对象池的容量。 + /// + int AssetCapacity { get; set; } + + /// + /// 获取或设置资源对象池对象过期秒数。 + /// + float AssetExpireTime { get; set; } + + /// + /// 获取或设置资源对象池的优先级。 + /// + int AssetPriority { get; set; } + + /// + /// 卸载资源。 + /// + /// 要卸载的资源。 + void UnloadAsset(object asset); + + /// + /// 资源回收(卸载引用计数为零的资源) + /// + void UnloadUnusedAssets(); + + /// + /// 强制回收所有资源 + /// + void ForceUnloadAllAssets(); + + /// + /// 强制执行释放未被使用的资源。 + /// + /// 是否使用垃圾回收。 + void ForceUnloadUnusedAssets(bool performGCCollect); + + /// + /// 检查资源是否存在。 + /// + /// 资源定位地址。 + /// 资源包名称。 + /// 检查资源是否存在的结果。 + public HasAssetResult HasAsset(string location, string packageName = ""); + + /// + /// 检查资源定位地址是否有效。 + /// + /// 资源的定位地址 + /// 指定资源包的名称。不传使用默认资源包 + bool CheckLocationValid(string location, string packageName = ""); + + /// + /// 获取资源信息列表。 + /// + /// 资源标签。 + /// 指定资源包的名称。不传使用默认资源包 + /// 资源信息列表。 + AssetInfo[] GetAssetInfos(string resTag, string packageName = ""); + + /// + /// 获取资源信息列表。 + /// + /// 资源标签列表。 + /// 指定资源包的名称。不传使用默认资源包 + /// 资源信息列表。 + AssetInfo[] GetAssetInfos(string[] tags, string packageName = ""); + + /// + /// 获取资源信息。 + /// + /// 资源的定位地址。 + /// 指定资源包的名称。不传使用默认资源包 + /// 资源信息。 + AssetInfo GetAssetInfo(string location, string packageName = ""); + + /// + /// 异步加载资源。 + /// + /// 资源的定位地址。 + /// 加载资源的优先级。 + /// 加载资源回调函数集。 + /// 用户自定义数据。 + /// 指定资源包的名称。不传使用默认资源包。 + void LoadAssetAsync(string location, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData, string packageName = ""); + + /// + /// 异步加载资源。 + /// + /// 资源的定位地址。 + /// 要加载的资源类型。 + /// 加载资源的优先级。 + /// 加载资源回调函数集。 + /// 用户自定义数据。 + /// 指定资源包的名称。不传使用默认资源包。 + void LoadAssetAsync(string location, Type assetType, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData, string packageName = ""); + + /// + /// 同步加载资源。 + /// + /// 资源的定位地址。 + /// 指定资源包的名称。不传使用默认资源包 + /// 要加载资源的类型。 + /// 资源实例。 + T LoadAsset(string location, string packageName = "") where T : UnityEngine.Object; + + /// + /// 同步加载游戏物体并实例化。 + /// + /// 资源的定位地址。 + /// 资源实例父节点。 + /// 指定资源包的名称。不传使用默认资源包 + /// 资源实例。 + /// 会实例化资源到场景,无需主动UnloadAsset,Destroy时自动UnloadAsset。 + GameObject LoadGameObject(string location, Transform parent = null, string packageName = ""); + + /// + /// 异步加载资源。 + /// + /// 资源的定位地址。 + /// 回调函数。 + /// 指定资源包的名称。不传使用默认资源包 + /// 要加载资源的类型。 + UniTaskVoid LoadAsset(string location, Action callback, string packageName = "") where T : UnityEngine.Object; + + /// + /// 异步加载资源。 + /// + /// 资源定位地址。 + /// 取消操作Token。 + /// 指定资源包的名称。不传使用默认资源包 + /// 要加载资源的类型。 + /// 异步资源实例。 + UniTask LoadAssetAsync(string location, CancellationToken cancellationToken = default, string packageName = "") where T : UnityEngine.Object; + + /// + /// 异步加载游戏物体并实例化。 + /// + /// 资源定位地址。 + /// 资源实例父节点。 + /// 取消操作Token。 + /// 指定资源包的名称。不传使用默认资源包 + /// 异步游戏物体实例。 + /// 会实例化资源到场景,无需主动UnloadAsset,Destroy时自动UnloadAsset。 + UniTask LoadGameObjectAsync(string location, Transform parent = null, CancellationToken cancellationToken = default, string packageName = ""); + + /// + /// 获取同步加载的资源操作句柄。 + /// + /// 资源定位地址。 + /// 资源包名称。 + /// 资源类型。 + /// 资源操作句柄。 + AssetHandle LoadAssetSyncHandle(string location, string packageName = "") where T : UnityEngine.Object; + + /// + /// 获取异步加载的资源操作句柄。 + /// + /// 资源定位地址。 + /// 资源包名称。 + /// 资源类型。 + /// 资源操作句柄。 + AssetHandle LoadAssetAsyncHandle(string location, string packageName = "") where T : UnityEngine.Object; + + /// + /// 清理包裹未使用的缓存文件。 + /// + /// 文件清理方式。 + /// 指定资源包的名称。不传使用默认资源包 + ClearCacheFilesOperation ClearCacheFilesAsync( + EFileClearMode clearMode = EFileClearMode.ClearUnusedBundleFiles, string customPackageName = ""); + + /// + /// 清理沙盒路径。 + /// + /// 指定资源包的名称。不传使用默认资源包 + void ClearAllBundleFiles(string customPackageName = ""); + + /// + /// 创建资源下载器,用于下载当前资源版本所有的资源包文件。 + /// + /// 指定资源包的名称。不传使用默认资源包 + ResourceDownloaderOperation CreateResourceDownloader(string customPackageName = ""); + + /// + /// 当前最新的包裹版本。 + /// + string PackageVersion { set; get; } + + /// + /// 获取当前资源包版本。 + /// + /// 指定资源包的名称。不传使用默认资源包 + /// 资源包版本。 + string GetPackageVersion(string customPackageName = ""); + + /// + /// 异步更新最新包的版本。 + /// + /// 请求URL是否需要带时间戳。 + /// 超时时间。 + /// 指定资源包的名称。不传使用默认资源包 + /// 请求远端包裹的最新版本操作句柄。 + RequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks = false, int timeout = 60, string customPackageName = ""); + + /// + /// 向网络端请求并更新清单 + /// + /// 更新的包裹版本 + /// 超时时间(默认值:60秒) + /// 指定资源包的名称。不传使用默认资源包 + UpdatePackageManifestOperation UpdatePackageManifestAsync(string packageVersion, int timeout = 60, string customPackageName = ""); + + /// + /// 低内存行为。 + /// + void OnLowMemory(); + + /// + /// 低内存回调保护。 + /// + /// 低内存行为。 + void SetForceUnloadUnusedAssetsAction(Action action); + } +} diff --git a/Runtime/Resource/Resource/IResourceModule.cs.meta b/Runtime/Resource/Resource/IResourceModule.cs.meta new file mode 100644 index 0000000..2ee6e93 --- /dev/null +++ b/Runtime/Resource/Resource/IResourceModule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8f04be71778469a44825233ee76bfc78 +timeCreated: 1706852080 \ No newline at end of file diff --git a/Runtime/Resource/Resource/ReadWritePathType.cs b/Runtime/Resource/Resource/ReadWritePathType.cs new file mode 100644 index 0000000..3101445 --- /dev/null +++ b/Runtime/Resource/Resource/ReadWritePathType.cs @@ -0,0 +1,23 @@ +namespace AlicizaX.Resource.Runtime +{ + /// + /// 读写区路径类型。 + /// + public enum ReadWritePathType : byte + { + /// + /// 未指定。 + /// + Unspecified = 0, + + /// + /// 临时缓存。 + /// + TemporaryCache, + + /// + /// 持久化数据。 + /// + PersistentData, + } +} diff --git a/Runtime/Resource/Resource/ReadWritePathType.cs.meta b/Runtime/Resource/Resource/ReadWritePathType.cs.meta new file mode 100644 index 0000000..d4fc584 --- /dev/null +++ b/Runtime/Resource/Resource/ReadWritePathType.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: aa0694b21b1a2fd44840f72598d43624 +timeCreated: 1679327686 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Reference.meta b/Runtime/Resource/Resource/Reference.meta new file mode 100644 index 0000000..39a0a18 --- /dev/null +++ b/Runtime/Resource/Resource/Reference.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 81e7808fc22aadb4e983bd5ee639a65d +timeCreated: 1708489036 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Reference/AssetsReference.cs b/Runtime/Resource/Resource/Reference/AssetsReference.cs new file mode 100644 index 0000000..310765b --- /dev/null +++ b/Runtime/Resource/Resource/Reference/AssetsReference.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace AlicizaX.Resource.Runtime +{ + [Serializable] + public struct AssetsRefInfo + { + public int instanceId; + + public Object refAsset; + + public AssetsRefInfo(Object refAsset) + { + this.refAsset = refAsset; + instanceId = this.refAsset.GetInstanceID(); + } + } + + [DisallowMultipleComponent] + public sealed class AssetsReference : MonoBehaviour + { + [SerializeField] + private GameObject sourceGameObject; + + [SerializeField] + private List refAssetInfoList; + + private static IResourceModule _resourceModule; + + private static Dictionary _originalRefs = new(); + + + private void CheckInit() + { + if (_resourceModule != null) + { + return; + } + else + { + _resourceModule = ModuleSystem.GetModule(); + } + + if (_resourceModule == null) + { + throw new GameFrameworkException($"resourceModule is null."); + } + } + + private void CheckRelease() + { + if (sourceGameObject != null) + { + _resourceModule.UnloadAsset(sourceGameObject); + } + else + { + Log.Warning($"sourceGameObject is not invalid."); + } + } + + + private void Awake() + { + // If it is a clone, clear the reference records before cloning + if (!IsOriginalInstance()) + { + ClearCloneReferences(); + } + } + + private bool IsOriginalInstance() + { + return _originalRefs.TryGetValue(gameObject, out var originalComponent) && + originalComponent == this; + } + + private void ClearCloneReferences() + { + sourceGameObject = null; + refAssetInfoList?.Clear(); + } + + private void OnDestroy() + { + CheckInit(); + if (sourceGameObject != null) + { + CheckRelease(); + } + + ReleaseRefAssetInfoList(); + } + + private void ReleaseRefAssetInfoList() + { + if (refAssetInfoList != null) + { + foreach (var refInfo in refAssetInfoList) + { + _resourceModule.UnloadAsset(refInfo.refAsset); + } + + refAssetInfoList.Clear(); + } + } + + public AssetsReference Ref(GameObject source, IResourceModule resourceModule = null) + { + if (source == null) + { + throw new GameFrameworkException($"Source gameObject is null."); + } + + if (source.scene.name != null) + { + throw new GameFrameworkException($"Source gameObject is in scene."); + } + + _resourceModule = resourceModule; + sourceGameObject = source; + + if (!_originalRefs.ContainsKey(gameObject)) + { + _originalRefs.Add(gameObject, this); + } + + return this; + } + + public AssetsReference Ref(T source, IResourceModule resourceModule = null) where T : Object + { + if (source == null) + { + throw new GameFrameworkException($"Source gameObject is null."); + } + + _resourceModule = resourceModule; + if (refAssetInfoList == null) + { + refAssetInfoList = new List(); + } + + refAssetInfoList.Add(new AssetsRefInfo(source)); + return this; + } + + internal static AssetsReference Instantiate(GameObject source, Transform parent = null, IResourceModule resourceModule = null) + { + if (source == null) + { + throw new GameFrameworkException($"Source gameObject is null."); + } + + if (source.scene.name != null) + { + throw new GameFrameworkException($"Source gameObject is in scene."); + } + + GameObject instance = Object.Instantiate(source, parent); + return instance.AddComponent().Ref(source, resourceModule); + } + + public static AssetsReference Ref(GameObject source, GameObject instance, IResourceModule resourceModule = null) + { + if (source == null) + { + throw new GameFrameworkException($"Source gameObject is null."); + } + + if (source.scene.name != null) + { + throw new GameFrameworkException($"Source gameObject is in scene."); + } + + var comp = instance.GetComponent(); + return comp ? comp.Ref(source, resourceModule) : instance.AddComponent().Ref(source, resourceModule); + } + + public static AssetsReference Ref(T source, GameObject instance, IResourceModule resourceModule = null) where T : Object + { + if (source == null) + { + throw new GameFrameworkException($"Source gameObject is null."); + } + + var comp = instance.GetComponent(); + return comp ? comp.Ref(source, resourceModule) : instance.AddComponent().Ref(source, resourceModule); + } + } +} diff --git a/Runtime/Resource/Resource/Reference/AssetsReference.cs.meta b/Runtime/Resource/Resource/Reference/AssetsReference.cs.meta new file mode 100644 index 0000000..1e2c9ba --- /dev/null +++ b/Runtime/Resource/Resource/Reference/AssetsReference.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a6f29400f419efb48a8fad058094dead +timeCreated: 1708487319 \ No newline at end of file diff --git a/Runtime/Resource/Resource/Reference/AssetsSetHelper.cs b/Runtime/Resource/Resource/Reference/AssetsSetHelper.cs new file mode 100644 index 0000000..0e798c2 --- /dev/null +++ b/Runtime/Resource/Resource/Reference/AssetsSetHelper.cs @@ -0,0 +1,151 @@ +using AlicizaX; +using UnityEngine; +using UnityEngine.UI; + +namespace AlicizaX.Resource.Runtime +{ + public static class AssetsSetHelper + { + private static IResourceModule _resourceModule; + + private static void CheckResourceManager() + { + if (_resourceModule == null) + { + _resourceModule = ModuleSystem.GetModule(); + } + } + + #region SetMaterial + + public static void SetMaterial(this Image image, string location, bool isAsync = false, string packageName = "") + { + if (image == null) + { + throw new GameFrameworkException($"SetSprite failed. Because image is null."); + } + + CheckResourceManager(); + + if (!isAsync) + { + Material material = _resourceModule.LoadAsset(location, packageName); + image.material = material; + AssetsReference.Ref(material, image.gameObject); + } + else + { + _resourceModule.LoadAsset(location, material => + { + if (image == null || image.gameObject == null) + { + _resourceModule.UnloadAsset(material); + material = null; + return; + } + + image.material = material; + AssetsReference.Ref(material, image.gameObject); + }, packageName); + } + } + + public static void SetMaterial(this SpriteRenderer spriteRenderer, string location, bool isAsync = false, string packageName = "") + { + if (spriteRenderer == null) + { + throw new GameFrameworkException($"SetSprite failed. Because image is null."); + } + + CheckResourceManager(); + + if (!isAsync) + { + Material material = _resourceModule.LoadAsset(location, packageName); + spriteRenderer.material = material; + AssetsReference.Ref(material, spriteRenderer.gameObject); + } + else + { + _resourceModule.LoadAsset(location, material => + { + if (spriteRenderer == null || spriteRenderer.gameObject == null) + { + _resourceModule.UnloadAsset(material); + material = null; + return; + } + + spriteRenderer.material = material; + AssetsReference.Ref(material, spriteRenderer.gameObject); + }, packageName); + } + } + + public static void SetMaterial(this MeshRenderer meshRenderer, string location, bool needInstance = true, bool isAsync = false, string packageName = "") + { + if (meshRenderer == null) + { + throw new GameFrameworkException($"SetSprite failed. Because image is null."); + } + + CheckResourceManager(); + + if (!isAsync) + { + Material material = _resourceModule.LoadAsset(location, packageName); + meshRenderer.material = needInstance ? Object.Instantiate(material) : material; + AssetsReference.Ref(material, meshRenderer.gameObject); + } + else + { + _resourceModule.LoadAsset(location, material => + { + if (meshRenderer == null || meshRenderer.gameObject == null) + { + _resourceModule.UnloadAsset(material); + material = null; + return; + } + + meshRenderer.material = needInstance ? Object.Instantiate(material) : material; + AssetsReference.Ref(material, meshRenderer.gameObject); + }, packageName); + } + } + + public static void SetSharedMaterial(this MeshRenderer meshRenderer, string location, bool isAsync = false, string packageName = "") + { + if (meshRenderer == null) + { + throw new GameFrameworkException($"SetSprite failed. Because image is null."); + } + + CheckResourceManager(); + + if (!isAsync) + { + Material material = _resourceModule.LoadAsset(location, packageName); + meshRenderer.sharedMaterial = material; + AssetsReference.Ref(material, meshRenderer.gameObject); + } + else + { + _resourceModule.LoadAsset(location, material => + { + if (meshRenderer == null || meshRenderer.gameObject == null) + { + _resourceModule.UnloadAsset(material); + material = null; + return; + } + + meshRenderer.sharedMaterial = material; + AssetsReference.Ref(material, meshRenderer.gameObject); + }, packageName); + } + } + + #endregion + } +} diff --git a/Runtime/Resource/Resource/Reference/AssetsSetHelper.cs.meta b/Runtime/Resource/Resource/Reference/AssetsSetHelper.cs.meta new file mode 100644 index 0000000..8ba320a --- /dev/null +++ b/Runtime/Resource/Resource/Reference/AssetsSetHelper.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 370ab69f738b11b429fbcc1d9e7a2fb1 +timeCreated: 1708490345 \ No newline at end of file diff --git a/Runtime/Resource/Resource/ResourceComponent.cs b/Runtime/Resource/Resource/ResourceComponent.cs new file mode 100644 index 0000000..532e2d3 --- /dev/null +++ b/Runtime/Resource/Resource/ResourceComponent.cs @@ -0,0 +1,269 @@ +using System; +using AlicizaX; +using UnityEngine; +using UnityEngine.Serialization; +using YooAsset; + +namespace AlicizaX.Resource.Runtime +{ + /// + /// 资源组件。 + /// + [DisallowMultipleComponent] + public class ResourceComponent : MonoBehaviour + { + #region Propreties + + private const int DEFAULT_PRIORITY = 0; + + private IResourceModule _resourceModule; + + private bool _forceUnloadUnusedAssets = false; + + private bool _preorderUnloadUnusedAssets = false; + + private bool _performGCCollect = false; + + private AsyncOperation _asyncOperation = null; + + private float _lastUnloadUnusedAssetsOperationElapseSeconds = 0f; + + [SerializeField] private float minUnloadUnusedAssetsInterval = 60f; + + [SerializeField] private float maxUnloadUnusedAssetsInterval = 300f; + + [SerializeField] private bool useSystemUnloadUnusedAssets = true; + + [SerializeField] private string _decryptionServices = ""; + + /// + /// 当前最新的包裹版本。 + /// + public string PackageVersion { set; get; } + + /// + /// 资源包名称。 + /// + [SerializeField] private string packageName = "DefaultPackage"; + + /// + /// 资源包名称。 + /// + public string PackageName + { + get => packageName; + set => packageName = value; + } + + + /// + /// 设置异步系统参数,每帧执行消耗的最大时间切片(单位:毫秒) + /// + [SerializeField] public long milliseconds = 30; + + public int downloadingMaxNum = 10; + + /// + /// 获取或设置同时最大下载数目。 + /// + public int DownloadingMaxNum + { + get => downloadingMaxNum; + set => downloadingMaxNum = value; + } + + [SerializeField] public int failedTryAgain = 3; + + public int FailedTryAgain + { + get => failedTryAgain; + set => failedTryAgain = value; + } + + /// + /// 获取当前资源适用的游戏版本号。 + /// + public string ApplicableGameVersion => _resourceModule.ApplicableGameVersion; + + /// + /// 获取当前内部资源版本号。 + /// + public int InternalResourceVersion => _resourceModule.InternalResourceVersion; + + /// + /// 获取或设置无用资源释放的最小间隔时间,以秒为单位。 + /// + public float MinUnloadUnusedAssetsInterval + { + get => minUnloadUnusedAssetsInterval; + set => minUnloadUnusedAssetsInterval = value; + } + + /// + /// 获取或设置无用资源释放的最大间隔时间,以秒为单位。 + /// + public float MaxUnloadUnusedAssetsInterval + { + get => maxUnloadUnusedAssetsInterval; + set => maxUnloadUnusedAssetsInterval = value; + } + + /// + /// 使用系统释放无用资源策略。 + /// + public bool UseSystemUnloadUnusedAssets + { + get => useSystemUnloadUnusedAssets; + set => useSystemUnloadUnusedAssets = value; + } + + /// + /// 获取无用资源释放的等待时长,以秒为单位。 + /// + public float LastUnloadUnusedAssetsOperationElapseSeconds => _lastUnloadUnusedAssetsOperationElapseSeconds; + + [SerializeField] private float assetAutoReleaseInterval = 60f; + + [SerializeField] private int assetCapacity = 64; + + [SerializeField] private float assetExpireTime = 60f; + + [SerializeField] private int assetPriority = 0; + + /// + /// 获取或设置资源对象池自动释放可释放对象的间隔秒数。 + /// + public float AssetAutoReleaseInterval + { + get => _resourceModule.AssetAutoReleaseInterval; + set => _resourceModule.AssetAutoReleaseInterval = assetAutoReleaseInterval = value; + } + + /// + /// 获取或设置资源对象池的容量。 + /// + public int AssetCapacity + { + get => _resourceModule.AssetCapacity; + set => _resourceModule.AssetCapacity = assetCapacity = value; + } + + /// + /// 获取或设置资源对象池对象过期秒数。 + /// + public float AssetExpireTime + { + get => _resourceModule.AssetExpireTime; + set => _resourceModule.AssetExpireTime = assetExpireTime = value; + } + + /// + /// 获取或设置资源对象池的优先级。 + /// + public int AssetPriority + { + get => _resourceModule.AssetPriority; + set => _resourceModule.AssetPriority = assetPriority = value; + } + + #endregion + + private void Awake() + { + _resourceModule = ModuleSystem.RegisterModule(); + if (_resourceModule == null) + { + Log.Error("Resource module is invalid."); + return; + } + + Application.lowMemory += OnLowMemory; + } + + public static string PrefsKey = Application.dataPath.GetHashCode() + "GamePlayMode"; + + private void Start() + { + EPlayMode playMode = EPlayMode.EditorSimulateMode; +#if UNITY_EDITOR + playMode = (EPlayMode)UnityEditor.EditorPrefs.GetInt(PrefsKey, 0); +#else + playMode = (EPlayMode)AppBuilderSetting.Instance.ResMode; +#endif + _resourceModule.DefaultPackageName = PackageName; + _resourceModule.DecryptionServices = _decryptionServices; + _resourceModule.PlayMode = playMode; + _resourceModule.Milliseconds = milliseconds; + _resourceModule.DownloadingMaxNum = DownloadingMaxNum; + _resourceModule.FailedTryAgain = FailedTryAgain; + _resourceModule.Initialize(); + _resourceModule.AssetAutoReleaseInterval = assetAutoReleaseInterval; + _resourceModule.AssetCapacity = assetCapacity; + _resourceModule.AssetExpireTime = assetExpireTime; + _resourceModule.AssetPriority = assetPriority; + _resourceModule.SetForceUnloadUnusedAssetsAction(ForceUnloadUnusedAssets); + Log.Info($"ResourceModule Run Mode:{playMode}"); + } + + private void OnApplicationQuit() + { + Application.lowMemory -= OnLowMemory; + } + + #region 释放资源 + + /// + /// 强制执行释放未被使用的资源。 + /// + /// 是否使用垃圾回收。 + public void ForceUnloadUnusedAssets(bool performGCCollect) + { + _forceUnloadUnusedAssets = true; + if (performGCCollect) + { + _performGCCollect = true; + } + } + + + private void Update() + { + _lastUnloadUnusedAssetsOperationElapseSeconds += Time.unscaledDeltaTime; + if (_asyncOperation == null && (_forceUnloadUnusedAssets || _lastUnloadUnusedAssetsOperationElapseSeconds >= maxUnloadUnusedAssetsInterval || + _preorderUnloadUnusedAssets && _lastUnloadUnusedAssetsOperationElapseSeconds >= minUnloadUnusedAssetsInterval)) + { + Log.Info("Unload unused assets..."); + _forceUnloadUnusedAssets = false; + _preorderUnloadUnusedAssets = false; + _lastUnloadUnusedAssetsOperationElapseSeconds = 0f; + _asyncOperation = Resources.UnloadUnusedAssets(); + if (useSystemUnloadUnusedAssets) + { + _resourceModule.UnloadUnusedAssets(); + } + } + + if (_asyncOperation is { isDone: true }) + { + _asyncOperation = null; + if (_performGCCollect) + { + Log.Info("GC.Collect..."); + _performGCCollect = false; + GC.Collect(); + } + } + } + + private void OnLowMemory() + { + Log.Warning("Low memory reported..."); + if (_resourceModule != null) + { + _resourceModule.OnLowMemory(); + } + } + + #endregion + } +} diff --git a/Runtime/Resource/Resource/ResourceComponent.cs.meta b/Runtime/Resource/Resource/ResourceComponent.cs.meta new file mode 100644 index 0000000..c324ddb --- /dev/null +++ b/Runtime/Resource/Resource/ResourceComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 91e581bb79676824f8c04687f21ed727 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Resource/Resource/ResourceLogger.cs b/Runtime/Resource/Resource/ResourceLogger.cs new file mode 100644 index 0000000..c9a9f08 --- /dev/null +++ b/Runtime/Resource/Resource/ResourceLogger.cs @@ -0,0 +1,27 @@ +using System; + +namespace AlicizaX.Resource.Runtime +{ + public class ResourceLogger : YooAsset.ILogger + { + public void Log(string message) + { + AlicizaX.Log.Info(message); + } + + public void Warning(string message) + { + AlicizaX.Log.Warning(message); + } + + public void Error(string message) + { + AlicizaX.Log.Error(message); + } + + public void Exception(Exception exception) + { + AlicizaX.Log.Exception(exception); + } + } +} diff --git a/Runtime/Resource/Resource/ResourceLogger.cs.meta b/Runtime/Resource/Resource/ResourceLogger.cs.meta new file mode 100644 index 0000000..82d7d5c --- /dev/null +++ b/Runtime/Resource/Resource/ResourceLogger.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 63d2c4d96713460f85fbcc70fbf5e8e7 +timeCreated: 1737534595 \ No newline at end of file diff --git a/Runtime/Resource/Resource/ResourceModule.AssetObject.cs b/Runtime/Resource/Resource/ResourceModule.AssetObject.cs new file mode 100644 index 0000000..243454a --- /dev/null +++ b/Runtime/Resource/Resource/ResourceModule.AssetObject.cs @@ -0,0 +1,69 @@ +using System.Buffers; +using System.Collections.Generic; +using AlicizaX.ObjectPool; +using AlicizaX; +using YooAsset; + +namespace AlicizaX.Resource.Runtime +{ + internal partial class ResourceModule + { + /// + /// 资源对象。 + /// + private sealed class AssetObject : ObjectBase + { + private AssetHandle m_AssetHandle; + private ResourceModule _mResourceModule; + + + public AssetObject() + { + m_AssetHandle = null; + } + + public static AssetObject Create(string name, object target, object assetHandle, ResourceModule resourceModule) + { + if (assetHandle == null) + { + throw new GameFrameworkException("Resource is invalid."); + } + + if (resourceModule == null) + { + throw new GameFrameworkException("Resource Manager is invalid."); + } + + AssetObject assetObject = MemoryPool.Acquire(); + assetObject.Initialize(name, target); + assetObject.m_AssetHandle = (AssetHandle)assetHandle; + assetObject._mResourceModule = resourceModule; + return assetObject; + } + + public override void Clear() + { + base.Clear(); + m_AssetHandle = null; + } + + protected override void OnUnspawn() + { + base.OnUnspawn(); + } + + protected override void Release(bool isShutdown) + { + if (!isShutdown) + { + AssetHandle handle = m_AssetHandle; + if (handle is { IsValid: true }) + { + handle.Dispose(); + } + handle = null; + } + } + } + } +} diff --git a/Runtime/Resource/Resource/ResourceModule.AssetObject.cs.meta b/Runtime/Resource/Resource/ResourceModule.AssetObject.cs.meta new file mode 100644 index 0000000..22e4996 --- /dev/null +++ b/Runtime/Resource/Resource/ResourceModule.AssetObject.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9d617a91ca3eacc41863c3950aa38f0e +timeCreated: 1706867174 \ No newline at end of file diff --git a/Runtime/Resource/Resource/ResourceModule.Initialization.cs b/Runtime/Resource/Resource/ResourceModule.Initialization.cs new file mode 100644 index 0000000..9d6d4ab --- /dev/null +++ b/Runtime/Resource/Resource/ResourceModule.Initialization.cs @@ -0,0 +1,123 @@ +using System; +using UnityEngine; +using YooAsset; + +namespace AlicizaX.Resource.Runtime +{ + internal partial class ResourceModule + { + /// + /// 根据运行模式创建初始化操作数据 + /// + /// + private InitializationOperation CreateInitializationOperationHandler(ResourcePackage resourcePackage, string hostServerURL, string fallbackHostServerURL, string decryptionServicesName) + { + IDecryptionServices decryptionServices = CreateDecryptionServices(decryptionServicesName); + switch (PlayMode) + { + case EPlayMode.EditorSimulateMode: + { + // 编辑器下的模拟模式 + return InitializeYooAssetEditorSimulateMode(resourcePackage, DefaultPackageName); + } + case EPlayMode.OfflinePlayMode: + { + // 单机运行模式 + return InitializeYooAssetOfflinePlayMode(resourcePackage, decryptionServices); + } + case EPlayMode.HostPlayMode: + { + // 联机运行模式 + return InitializeYooAssetHostPlayMode(resourcePackage, hostServerURL, fallbackHostServerURL, decryptionServices); + } + case EPlayMode.WebPlayMode: + { + // WebGL运行模式 + return InitializeYooAssetWebPlayMode(resourcePackage, hostServerURL, fallbackHostServerURL); + } + default: + { + return null; + } + } + } + + private IDecryptionServices CreateDecryptionServices(string decryptionServicesName) + { + IDecryptionServices decryptionServices = null; + if (!string.IsNullOrEmpty(decryptionServicesName)) + { + var decryptionServicesType = AlicizaX.Utility.Assembly.GetType(decryptionServicesName); + decryptionServices = (IDecryptionServices)Activator.CreateInstance(decryptionServicesType); + } + + return decryptionServices; + } + + private InitializationOperation InitializeYooAssetEditorSimulateMode(ResourcePackage resourcePackage, string packageName) + { + var buildResult = EditorSimulateModeHelper.SimulateBuild(packageName); + var packageRoot = buildResult.PackageRootDirectory; + var createParameters = new EditorSimulateModeParameters(); + createParameters.EditorFileSystemParameters = FileSystemParameters.CreateDefaultEditorFileSystemParameters(packageRoot); + // createParameters.EditorFileSystemParameters.AddParameter(FileSystemParametersDefine.DECRYPTION_SERVICES,new FileOffsetEncryption()); + return resourcePackage.InitializeAsync(createParameters); + } + + private InitializationOperation InitializeYooAssetOfflinePlayMode(ResourcePackage resourcePackage, IDecryptionServices decryptionServices = null) + { + var buildinFileSystem = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(decryptionServices); + var initParameters = new OfflinePlayModeParameters(); + initParameters.BuildinFileSystemParameters = buildinFileSystem; + return resourcePackage.InitializeAsync(initParameters); + } + + private InitializationOperation InitializeYooAssetWebPlayMode(ResourcePackage resourcePackage, string hostServerURL, string fallbackHostServerURL) + { + var initParameters = new WebPlayModeParameters(); + FileSystemParameters webFileSystem = null; +#if UNITY_WEBGL +#if ENABLE_DOUYIN_MINI_GAME + // 创建字节小游戏文件系统 + if (hostServerURL.IsNullOrWhiteSpace()) + { + webFileSystem = ByteGameFileSystemCreater.CreateByteGameFileSystemParameters(); + } + else + { + webFileSystem = ByteGameFileSystemCreater.CreateByteGameFileSystemParameters(hostServerURL); + } +#elif ENABLE_WECHAT_MINI_GAME + WeChatWASM.WXBase.PreloadConcurrent(10); + // 创建微信小游戏文件系统 + if (hostServerURL.IsNullOrWhiteSpace()) + { + webFileSystem = WechatFileSystemCreater.CreateWechatFileSystemParameters(); + } + else + { + webFileSystem = WechatFileSystemCreater.CreateWechatPathFileSystemParameters(hostServerURL); + } +#else + // 创建默认WebGL文件系统 + webFileSystem = FileSystemParameters.CreateDefaultWebFileSystemParameters(); +#endif +#else + webFileSystem = FileSystemParameters.CreateDefaultWebServerFileSystemParameters(); +#endif + initParameters.WebServerFileSystemParameters = webFileSystem; + return resourcePackage.InitializeAsync(initParameters); + } + + private InitializationOperation InitializeYooAssetHostPlayMode(ResourcePackage resourcePackage, string hostServerURL, string fallbackHostServerURL, IDecryptionServices decryptionServices = null) + { + IRemoteServices remoteServices = new RemoteServices(hostServerURL, fallbackHostServerURL); + var cacheFileSystem = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices); + var buildinFileSystem = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(decryptionServices); + var initParameters = new HostPlayModeParameters(); + initParameters.BuildinFileSystemParameters = buildinFileSystem; + initParameters.CacheFileSystemParameters = cacheFileSystem; + return resourcePackage.InitializeAsync(initParameters); + } + } +} diff --git a/Runtime/Resource/Resource/ResourceModule.Initialization.cs.meta b/Runtime/Resource/Resource/ResourceModule.Initialization.cs.meta new file mode 100644 index 0000000..01b852d --- /dev/null +++ b/Runtime/Resource/Resource/ResourceModule.Initialization.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 27ac1161024349ffb2be580dffce0510 +timeCreated: 1737343028 \ No newline at end of file diff --git a/Runtime/Resource/Resource/ResourceModule.Pool.cs b/Runtime/Resource/Resource/ResourceModule.Pool.cs new file mode 100644 index 0000000..b526c5e --- /dev/null +++ b/Runtime/Resource/Resource/ResourceModule.Pool.cs @@ -0,0 +1,71 @@ +using AlicizaX.ObjectPool; +using AlicizaX; + +namespace AlicizaX.Resource.Runtime +{ + internal partial class ResourceModule + { + private IObjectPool _assetPool; + + /// + /// 获取或设置资源对象池自动释放可释放对象的间隔秒数。 + /// + public float AssetAutoReleaseInterval + { + get => _assetPool.AutoReleaseInterval; + set => _assetPool.AutoReleaseInterval = value; + } + + /// + /// 获取或设置资源对象池的容量。 + /// + public int AssetCapacity + { + get => _assetPool.Capacity; + set => _assetPool.Capacity = value; + } + + /// + /// 获取或设置资源对象池对象过期秒数。 + /// + public float AssetExpireTime + { + get => _assetPool.ExpireTime; + set => _assetPool.ExpireTime = value; + } + + /// + /// 获取或设置资源对象池的优先级。 + /// + public int AssetPriority + { + get => _assetPool.Priority; + set => _assetPool.Priority = value; + } + + /// + /// 卸载资源。 + /// + /// 要卸载的资源。 + public void UnloadAsset(object asset) + { + if (_assetPool != null) + { + _assetPool.Unspawn(asset); + } + } + + /// + /// 设置对象池管理器。 + /// + /// 对象池管理器。 + public void SetObjectPoolModule(IObjectPoolModule objectPoolModule) + { + if (objectPoolModule == null) + { + throw new GameFrameworkException("Object pool manager is invalid."); + } + _assetPool = objectPoolModule.CreateMultiSpawnObjectPool("Asset Pool"); + } + } +} diff --git a/Runtime/Resource/Resource/ResourceModule.Pool.cs.meta b/Runtime/Resource/Resource/ResourceModule.Pool.cs.meta new file mode 100644 index 0000000..77caba9 --- /dev/null +++ b/Runtime/Resource/Resource/ResourceModule.Pool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 68d64211ccdc47846b6588e4c855e99e +timeCreated: 1706867111 \ No newline at end of file diff --git a/Runtime/Resource/Resource/ResourceModule.Services.cs b/Runtime/Resource/Resource/ResourceModule.Services.cs new file mode 100644 index 0000000..8cedd1b --- /dev/null +++ b/Runtime/Resource/Resource/ResourceModule.Services.cs @@ -0,0 +1,178 @@ +using System.IO; +using UnityEngine; +using YooAsset; + +namespace AlicizaX.Resource.Runtime +{ + /// + /// 远端资源地址查询服务类 + /// + class RemoteServices : IRemoteServices + { + private readonly string _defaultHostServer; + private readonly string _fallbackHostServer; + + public RemoteServices(string defaultHostServer, string fallbackHostServer) + { + _defaultHostServer = defaultHostServer; + _fallbackHostServer = fallbackHostServer; + } + + string IRemoteServices.GetRemoteMainURL(string fileName) + { + return $"{_defaultHostServer}/{fileName}"; + } + + string IRemoteServices.GetRemoteFallbackURL(string fileName) + { + return $"{_fallbackHostServer}/{fileName}"; + } + } + + /// + /// 资源文件流加载解密类 + /// + class FileStreamDecryption : IDecryptionServices + { + /// + /// 同步方式获取解密的资源包对象 + /// + DecryptResult IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo) + { + BundleStream bundleStream = new BundleStream(fileInfo.FileLoadPath, FileMode.Open, FileAccess.Read, FileShare.Read); + DecryptResult decryptResult = new DecryptResult(); + decryptResult.ManagedStream = bundleStream; + decryptResult.Result = AssetBundle.LoadFromStream(bundleStream, fileInfo.FileLoadCRC, GetManagedReadBufferSize()); + return decryptResult; + } + + /// + /// 异步方式获取解密的资源包对象 + /// + DecryptResult IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo) + { + BundleStream bundleStream = new BundleStream(fileInfo.FileLoadPath, FileMode.Open, FileAccess.Read, FileShare.Read); + DecryptResult decryptResult = new DecryptResult(); + decryptResult.ManagedStream = bundleStream; + decryptResult.CreateRequest = AssetBundle.LoadFromStreamAsync(bundleStream, fileInfo.FileLoadCRC, GetManagedReadBufferSize()); + return decryptResult; + } + + /// + /// 后备方式获取解密的资源包 + /// 注意:当正常解密方法失败后,会触发后备加载! + /// 说明:建议通过LoadFromMemory()方法加载资源包作为保底机制。 + /// + DecryptResult IDecryptionServices.LoadAssetBundleFallback(DecryptFileInfo fileInfo) + { + byte[] fileData = File.ReadAllBytes(fileInfo.FileLoadPath); + var assetBundle = AssetBundle.LoadFromMemory(fileData); + DecryptResult decryptResult = new DecryptResult(); + decryptResult.Result = assetBundle; + return decryptResult; + } + + /// + /// 获取解密的字节数据 + /// + byte[] IDecryptionServices.ReadFileData(DecryptFileInfo fileInfo) + { + throw new System.NotImplementedException(); + } + + /// + /// 获取解密的文本数据 + /// + string IDecryptionServices.ReadFileText(DecryptFileInfo fileInfo) + { + throw new System.NotImplementedException(); + } + + private static uint GetManagedReadBufferSize() + { + return 1024; + } + } + + /// + /// 资源文件偏移加载解密类 + /// + class FileOffsetDecryption : IDecryptionServices + { + /// + /// 同步方式获取解密的资源包对象 + /// 注意:加载流对象在资源包对象释放的时候会自动释放 + /// + DecryptResult IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo) + { + DecryptResult decryptResult = new DecryptResult(); + decryptResult.ManagedStream = null; + decryptResult.Result = AssetBundle.LoadFromFile(fileInfo.FileLoadPath, fileInfo.FileLoadCRC, GetFileOffset()); + return decryptResult; + } + + /// + /// 异步方式获取解密的资源包对象 + /// 注意:加载流对象在资源包对象释放的时候会自动释放 + /// + DecryptResult IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo) + { + DecryptResult decryptResult = new DecryptResult(); + decryptResult.ManagedStream = null; + decryptResult.CreateRequest = AssetBundle.LoadFromFileAsync(fileInfo.FileLoadPath, fileInfo.FileLoadCRC, GetFileOffset()); + return decryptResult; + } + + /// + /// 后备方式获取解密的资源包对象 + /// + DecryptResult IDecryptionServices.LoadAssetBundleFallback(DecryptFileInfo fileInfo) + { + return new DecryptResult(); + } + + /// + /// 获取解密的字节数据 + /// + byte[] IDecryptionServices.ReadFileData(DecryptFileInfo fileInfo) + { + throw new System.NotImplementedException(); + } + + /// + /// 获取解密的文本数据 + /// + string IDecryptionServices.ReadFileText(DecryptFileInfo fileInfo) + { + throw new System.NotImplementedException(); + } + + private static ulong GetFileOffset() + { + return 32; + } + } + + + public class BundleStream : FileStream + { + public const byte KEY = 64; + + public BundleStream(string path, FileMode mode, FileAccess access, FileShare share) : base(path, mode, access, share) + { + } + public BundleStream(string path, FileMode mode) : base(path, mode) + { + } + + public override int Read(byte[] array, int offset, int count) + { + var index = base.Read(array, offset, count); + for (int i = 0; i < array.Length; i++) + { + array[i] ^= KEY; + } + return index; + } + } +} diff --git a/Runtime/Resource/Resource/ResourceModule.Services.cs.meta b/Runtime/Resource/Resource/ResourceModule.Services.cs.meta new file mode 100644 index 0000000..02e227c --- /dev/null +++ b/Runtime/Resource/Resource/ResourceModule.Services.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 23f82b31c7eb84345b86abf347084ac2 +timeCreated: 1679030837 \ No newline at end of file diff --git a/Runtime/Resource/Resource/ResourceModule.cs b/Runtime/Resource/Resource/ResourceModule.cs new file mode 100644 index 0000000..8a388b1 --- /dev/null +++ b/Runtime/Resource/Resource/ResourceModule.cs @@ -0,0 +1,1096 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using AlicizaX.ObjectPool; +using AlicizaX; +using Cysharp.Threading.Tasks; +using UnityEngine; +using YooAsset; + +namespace AlicizaX.Resource.Runtime +{ + /// + /// 资源管理器。 + /// + internal sealed partial class ResourceModule : IResourceModule + { + /// + /// 默认资源包名称。 + /// + public string DefaultPackageName { get; set; } = "DefaultPackage"; + + /// + /// 资源系统运行模式。 + /// + public EPlayMode PlayMode { get; set; } = EPlayMode.OfflinePlayMode; + + public string DecryptionServices { get; set; } + + /// + /// 设置异步系统参数,每帧执行消耗的最大时间切片(单位:毫秒) + /// + public long Milliseconds { get; set; } = 30; + + + public int Priority + { + get => 2; + } + + private string _applicableGameVersion; + + private int _internalResourceVersion; + + /// + /// 获取当前资源适用的游戏版本号。 + /// + public string ApplicableGameVersion => _applicableGameVersion; + + /// + /// 获取当前内部资源版本号。 + /// + public int InternalResourceVersion => _internalResourceVersion; + + /// + /// 当前最新的包裹版本。 + /// + public string PackageVersion { set; get; } + + public int DownloadingMaxNum { get; set; } + + public int FailedTryAgain { get; set; } + + + #region internal + + /// + /// 默认资源包。 + /// + internal ResourcePackage DefaultPackage { private set; get; } + + /// + /// 资源包列表。 + /// + private Dictionary PackageMap { get; } = new Dictionary(); + + /// + /// 资源信息列表。 + /// + private readonly Dictionary _assetInfoMap = new Dictionary(); + + /// + /// 正在加载的资源列表。 + /// + private readonly HashSet _assetLoadingList = new HashSet(); + + #endregion + + public void Initialize() + { + // 初始化资源系统 + YooAssets.Initialize(new ResourceLogger()); + YooAssets.SetOperationSystemMaxTimeSlice(Milliseconds); + + // 创建默认的资源包 + string packageName = DefaultPackageName; + var defaultPackage = YooAssets.TryGetPackage(packageName); + if (defaultPackage == null) + { + defaultPackage = YooAssets.CreatePackage(packageName); + YooAssets.SetDefaultPackage(defaultPackage); + } + + DefaultPackage = defaultPackage; + + IObjectPoolModule objectPoolModule = ModuleSystem.GetModule(); + SetObjectPoolModule(objectPoolModule); + } + + + void IModule.Dispose() + { + PackageMap.Clear(); + _assetPool = null; + _assetLoadingList.Clear(); + _assetInfoMap.Clear(); + } + + public UniTask InitPackageAsync(string packageName = "", string hostServerURL = "", string fallbackHostServerURL = "") + { + if (string.IsNullOrEmpty(packageName)) + { + packageName = DefaultPackageName; + } + + if (PackageMap.TryGetValue(packageName, out var resPackage)) + { + if (resPackage.InitializeStatus is EOperationStatus.Processing or EOperationStatus.Succeed) + { + Log.Error($"ResourceSystem has already init package : {packageName}"); + return new UniTask(false); + } + else + { + PackageMap.Remove(packageName); + } + } + + var taskCompletionSource = new UniTaskCompletionSource(); + GameFrameworkGuard.NotNull(packageName, nameof(packageName)); + GameFrameworkGuard.NotNull(hostServerURL, nameof(hostServerURL)); + GameFrameworkGuard.NotNull(fallbackHostServerURL, nameof(fallbackHostServerURL)); + + // 创建默认的资源包 + var resourcePackage = YooAssets.TryGetPackage(packageName); + if (resourcePackage == null) + { + resourcePackage = YooAssets.CreatePackage(packageName); + } + + PackageMap[packageName] = resourcePackage; + + var initializationOperationHandler = CreateInitializationOperationHandler(resourcePackage, hostServerURL, fallbackHostServerURL, DecryptionServices); + initializationOperationHandler.Completed += asyncOperationBase => + { + if (asyncOperationBase.Error == null && asyncOperationBase.Status == EOperationStatus.Succeed && asyncOperationBase.IsDone) + { + taskCompletionSource.TrySetResult(true); + } + else + { + taskCompletionSource.TrySetException(new Exception(asyncOperationBase.Error)); + } + }; + return taskCompletionSource.Task; + } + + + /// + /// 获取当前资源包版本。 + /// + /// 指定资源包的名称。不传使用默认资源包 + /// 资源包版本。 + public string GetPackageVersion(string customPackageName = "") + { + var package = string.IsNullOrEmpty(customPackageName) + ? YooAssets.GetPackage(DefaultPackageName) + : YooAssets.GetPackage(customPackageName); + if (package == null) + { + return string.Empty; + } + + return package.GetPackageVersion(); + } + + /// + /// 异步更新最新包的版本。 + /// + /// 请求URL是否需要带时间戳。 + /// 超时时间。 + /// 指定资源包的名称。不传使用默认资源包 + /// 请求远端包裹的最新版本操作句柄。 + public RequestPackageVersionOperation RequestPackageVersionAsync(bool appendTimeTicks = false, int timeout = 60, + string customPackageName = "") + { + var package = string.IsNullOrEmpty(customPackageName) + ? YooAssets.GetPackage(DefaultPackageName) + : YooAssets.GetPackage(customPackageName); + return package.RequestPackageVersionAsync(appendTimeTicks, timeout); + } + + + /// + /// 向网络端请求并更新清单 + /// + /// 更新的包裹版本 + /// 超时时间(默认值:60秒) + /// 指定资源包的名称。不传使用默认资源包 + public UpdatePackageManifestOperation UpdatePackageManifestAsync(string packageVersion, int timeout = 60, string customPackageName = "") + { + var package = string.IsNullOrEmpty(customPackageName) + ? YooAssets.GetPackage(this.DefaultPackageName) + : YooAssets.GetPackage(customPackageName); + return package.UpdatePackageManifestAsync(packageVersion, timeout); + } + + + /// + /// 创建资源下载器,用于下载当前资源版本所有的资源包文件。 + /// + /// 指定资源包的名称。不传使用默认资源包 + public ResourceDownloaderOperation CreateResourceDownloader(string customPackageName = "") + { + ResourcePackage package = null; + if (string.IsNullOrEmpty(customPackageName)) + { + package = YooAssets.GetPackage(this.DefaultPackageName); + } + else + { + package = YooAssets.GetPackage(customPackageName); + } + + return package.CreateResourceDownloader(DownloadingMaxNum, FailedTryAgain); + } + + /// + /// 清理包裹未使用的缓存文件。 + /// + /// 文件清理方式。 + /// 指定资源包的名称。不传使用默认资源包 + public ClearCacheFilesOperation ClearCacheFilesAsync( + EFileClearMode clearMode = EFileClearMode.ClearUnusedBundleFiles, + string customPackageName = "") + { + var package = string.IsNullOrEmpty(customPackageName) + ? YooAssets.GetPackage(DefaultPackageName) + : YooAssets.GetPackage(customPackageName); + return package.ClearCacheFilesAsync(EFileClearMode.ClearUnusedBundleFiles); + } + + /// + /// 清理沙盒路径。 + /// + /// 指定资源包的名称。不传使用默认资源包 + public void ClearAllBundleFiles(string customPackageName = "") + { + var package = string.IsNullOrEmpty(customPackageName) + ? YooAssets.GetPackage(DefaultPackageName) + : YooAssets.GetPackage(customPackageName); + package.ClearCacheFilesAsync(EFileClearMode.ClearAllBundleFiles); + } + + #region 资源回收 + + public void OnLowMemory() + { + Log.Warning("Low memory reported..."); + _forceUnloadUnusedAssetsAction?.Invoke(true); + } + + private Action _forceUnloadUnusedAssetsAction; + + /// + /// 低内存回调保护。 + /// + /// 低内存行为。 + public void SetForceUnloadUnusedAssetsAction(Action action) + { + _forceUnloadUnusedAssetsAction = action; + } + + /// + /// 资源回收(卸载引用计数为零的资源)。 + /// + public void UnloadUnusedAssets() + { + _assetPool.ReleaseAllUnused(); + foreach (var package in PackageMap.Values) + { + if (package is { InitializeStatus: EOperationStatus.Succeed }) + { + package.UnloadUnusedAssetsAsync(); + } + } + } + + /// + /// 强制回收所有资源。 + /// + public void ForceUnloadAllAssets() + { +#if UNITY_WEBGL + Log.Warning($"WebGL not support invoke {nameof(ForceUnloadAllAssets)}"); + return; +#else + + foreach (var package in PackageMap.Values) + { + if (package is { InitializeStatus: EOperationStatus.Succeed }) + { + package.UnloadAllAssetsAsync(); + } + } +#endif + } + + public void ForceUnloadUnusedAssets(bool performGCCollect) + { + _forceUnloadUnusedAssetsAction?.Invoke(performGCCollect); + } + + #region Public Methods + + #region 获取资源信息 + + /// + /// 是否需要从远端更新下载。 + /// + /// 资源的定位地址。 + /// 资源包名称。 + public bool IsNeedDownloadFromRemote(string location, string packageName = "") + { + if (string.IsNullOrEmpty(packageName)) + { + return YooAssets.IsNeedDownloadFromRemote(location); + } + else + { + var package = YooAssets.GetPackage(packageName); + return package.IsNeedDownloadFromRemote(location); + } + } + + /// + /// 是否需要从远端更新下载。 + /// + /// 资源信息。 + /// 资源包名称。 + public bool IsNeedDownloadFromRemote(AssetInfo assetInfo, string packageName = "") + { + if (string.IsNullOrEmpty(packageName)) + { + return YooAssets.IsNeedDownloadFromRemote(assetInfo); + } + else + { + var package = YooAssets.GetPackage(packageName); + return package.IsNeedDownloadFromRemote(assetInfo); + } + } + + /// + /// 获取资源信息列表。 + /// + /// 资源标签。 + /// 资源包名称。 + /// 资源信息列表。 + public AssetInfo[] GetAssetInfos(string tag, string packageName = "") + { + if (string.IsNullOrEmpty(packageName)) + { + return YooAssets.GetAssetInfos(tag); + } + else + { + var package = YooAssets.GetPackage(packageName); + return package.GetAssetInfos(tag); + } + } + + /// + /// 获取资源信息列表。 + /// + /// 资源标签列表。 + /// 资源包名称。 + /// 资源信息列表。 + public AssetInfo[] GetAssetInfos(string[] tags, string packageName = "") + { + if (string.IsNullOrEmpty(packageName)) + { + return YooAssets.GetAssetInfos(tags); + } + else + { + var package = YooAssets.GetPackage(packageName); + return package.GetAssetInfos(tags); + } + } + + /// + /// 获取资源信息。 + /// + /// 资源的定位地址。 + /// 资源包名称。 + /// 资源信息。 + public AssetInfo GetAssetInfo(string location, string packageName = "") + { + if (string.IsNullOrEmpty(location)) + { + throw new GameFrameworkException("Asset name is invalid."); + } + + if (string.IsNullOrEmpty(packageName)) + { + if (_assetInfoMap.TryGetValue(location, out AssetInfo assetInfo)) + { + return assetInfo; + } + + assetInfo = YooAssets.GetAssetInfo(location); + _assetInfoMap[location] = assetInfo; + return assetInfo; + } + else + { + string key = $"{packageName}/{location}"; + if (_assetInfoMap.TryGetValue(key, out AssetInfo assetInfo)) + { + return assetInfo; + } + + var package = YooAssets.GetPackage(packageName); + if (package == null) + { + throw new GameFrameworkException($"The package does not exist. Package Name :{packageName}"); + } + + assetInfo = package.GetAssetInfo(location); + _assetInfoMap[key] = assetInfo; + return assetInfo; + } + } + + /// + /// 检查资源是否存在。 + /// + /// 资源定位地址。 + /// 资源包名称。 + /// 检查资源是否存在的结果。 + public HasAssetResult HasAsset(string location, string packageName = "") + { + if (string.IsNullOrEmpty(location)) + { + throw new GameFrameworkException("Asset name is invalid."); + } + + AssetInfo assetInfo = GetAssetInfo(location, packageName); + + if (!CheckLocationValid(location)) + { + return HasAssetResult.Valid; + } + + if (assetInfo == null) + { + return HasAssetResult.NotExist; + } + + if (IsNeedDownloadFromRemote(assetInfo)) + { + return HasAssetResult.AssetOnline; + } + + return HasAssetResult.AssetOnDisk; + } + + /// + /// 检查资源定位地址是否有效。 + /// + /// 资源的定位地址 + /// 资源包名称。 + public bool CheckLocationValid(string location, string packageName = "") + { + if (string.IsNullOrEmpty(packageName)) + { + return YooAssets.CheckLocationValid(location); + } + else + { + var package = YooAssets.GetPackage(packageName); + return package.CheckLocationValid(location); + } + } + + #endregion + + #region 资源加载 + + #region 获取资源句柄 + + /// + /// 获取同步资源句柄。 + /// + /// 资源定位地址。 + /// 指定资源包的名称。不传使用默认资源包 + /// 资源类型。 + /// 资源句柄。 + private AssetHandle GetHandleSync(string location, string packageName = "") where T : UnityEngine.Object + { + return GetHandleSync(location, typeof(T), packageName); + } + + private AssetHandle GetHandleSync(string location, Type assetType, string packageName = "") + { + if (string.IsNullOrEmpty(packageName)) + { + return YooAssets.LoadAssetSync(location, assetType); + } + + var package = YooAssets.GetPackage(packageName); + return package.LoadAssetSync(location, assetType); + } + + /// + /// 获取异步资源句柄。 + /// + /// 资源定位地址。 + /// 指定资源包的名称。不传使用默认资源包 + /// 资源类型。 + /// 资源句柄。 + private AssetHandle GetHandleAsync(string location, string packageName = "") where T : UnityEngine.Object + { + return GetHandleAsync(location, typeof(T), packageName); + } + + private AssetHandle GetHandleAsync(string location, Type assetType, string packageName = "") + { + if (string.IsNullOrEmpty(packageName)) + { + return YooAssets.LoadAssetAsync(location, assetType); + } + + var package = YooAssets.GetPackage(packageName); + return package.LoadAssetAsync(location, assetType); + } + + #endregion + + /// + /// 获取资源定位地址的缓存Key。 + /// + /// 资源定位地址。 + /// 资源包名称。 + /// 资源定位地址的缓存Key。 + private string GetCacheKey(string location, string packageName = "") + { + if (string.IsNullOrEmpty(packageName) || packageName.Equals(DefaultPackageName)) + { + return location; + } + + return $"{packageName}/{location}"; + } + + public T LoadAsset(string location, string packageName = "") where T : UnityEngine.Object + { + if (string.IsNullOrEmpty(location)) + { + throw new GameFrameworkException("Asset name is invalid."); + } + + if (!CheckLocationValid(location, packageName)) + { + Log.Error($"Could not found location [{location}]."); + return null; + } + + string assetObjectKey = GetCacheKey(location, packageName); + AssetObject assetObject = _assetPool.Spawn(assetObjectKey); + if (assetObject != null) + { + return assetObject.Target as T; + } + + AssetHandle handle = GetHandleSync(location, packageName: packageName); + + T ret = handle.AssetObject as T; + + assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); + _assetPool.Register(assetObject, true); + + return ret; + } + + public GameObject LoadGameObject(string location, Transform parent = null, string packageName = "") + { + if (string.IsNullOrEmpty(location)) + { + throw new GameFrameworkException("Asset name is invalid."); + } + + if (!CheckLocationValid(location, packageName)) + { + Log.Error($"Could not found location [{location}]."); + return null; + } + + string assetObjectKey = GetCacheKey(location, packageName); + AssetObject assetObject = _assetPool.Spawn(assetObjectKey); + if (assetObject != null) + { + return AssetsReference.Instantiate(assetObject.Target as GameObject, parent, this).gameObject; + } + + AssetHandle handle = GetHandleSync(location, packageName: packageName); + + GameObject gameObject = AssetsReference.Instantiate(handle.AssetObject as GameObject, parent, this).gameObject; + + assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); + _assetPool.Register(assetObject, true); + + return gameObject; + } + + /// + /// 异步加载资源。 + /// + /// 资源的定位地址。 + /// 回调函数。 + /// 指定资源包的名称。不传使用默认资源包 + /// 要加载资源的类型。 + public async UniTaskVoid LoadAsset(string location, Action callback, string packageName = "") where T : UnityEngine.Object + { + if (string.IsNullOrEmpty(location)) + { + Log.Error("Asset name is invalid."); + return; + } + + if (string.IsNullOrEmpty(location)) + { + throw new GameFrameworkException("Asset name is invalid."); + } + + if (!CheckLocationValid(location, packageName)) + { + Log.Error($"Could not found location [{location}]."); + callback?.Invoke(null); + return; + } + + string assetObjectKey = GetCacheKey(location, packageName); + + await TryWaitingLoading(assetObjectKey); + + AssetObject assetObject = _assetPool.Spawn(assetObjectKey); + if (assetObject != null) + { + await UniTask.Yield(); + callback?.Invoke(assetObject.Target as T); + return; + } + + _assetLoadingList.Add(assetObjectKey); + + AssetHandle handle = GetHandleAsync(location, packageName: packageName); + + handle.Completed += assetHandle => + { + _assetLoadingList.Remove(assetObjectKey); + + if (assetHandle.AssetObject != null) + { + assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); + _assetPool.Register(assetObject, true); + + callback?.Invoke(assetObject.Target as T); + } + else + { + callback?.Invoke(null); + } + }; + } + + public async UniTask LoadAssetAsync(string location, CancellationToken cancellationToken = default, string packageName = "") where T : UnityEngine.Object + { + if (string.IsNullOrEmpty(location)) + { + throw new GameFrameworkException("Asset name is invalid."); + } + + if (!CheckLocationValid(location, packageName)) + { + Log.Error($"Could not found location [{location}]."); + return null; + } + + + string assetObjectKey = GetCacheKey(location, packageName); + + await TryWaitingLoading(assetObjectKey); + + AssetObject assetObject = _assetPool.Spawn(assetObjectKey); + if (assetObject != null) + { + await UniTask.Yield(); + return assetObject.Target as T; + } + + _assetLoadingList.Add(assetObjectKey); + + AssetHandle handle = GetHandleAsync(location, packageName: packageName); + + bool cancelOrFailed = await handle.ToUniTask().AttachExternalCancellation(cancellationToken).SuppressCancellationThrow(); + + if (cancelOrFailed) + { + _assetLoadingList.Remove(assetObjectKey); + return null; + } + + assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); + _assetPool.Register(assetObject, true); + + _assetLoadingList.Remove(assetObjectKey); + + return handle.AssetObject as T; + } + + public async UniTask LoadGameObjectAsync(string location, Transform parent = null, CancellationToken cancellationToken = default, string packageName = "") + { + if (string.IsNullOrEmpty(location)) + { + throw new GameFrameworkException("Asset name is invalid."); + } + + if (!CheckLocationValid(location, packageName)) + { + Log.Error($"Could not found location [{location}]."); + return null; + } + + string assetObjectKey = GetCacheKey(location, packageName); + + await TryWaitingLoading(assetObjectKey); + + AssetObject assetObject = _assetPool.Spawn(assetObjectKey); + if (assetObject != null) + { + await UniTask.Yield(); + return AssetsReference.Instantiate(assetObject.Target as GameObject, parent, this).gameObject; + } + + _assetLoadingList.Add(assetObjectKey); + + AssetHandle handle = GetHandleAsync(location, packageName: packageName); + + bool cancelOrFailed = await handle.ToUniTask().AttachExternalCancellation(cancellationToken).SuppressCancellationThrow(); + + if (cancelOrFailed) + { + _assetLoadingList.Remove(assetObjectKey); + return null; + } + + GameObject gameObject = AssetsReference.Instantiate(handle.AssetObject as GameObject, parent, this).gameObject; + + assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); + _assetPool.Register(assetObject, true); + + _assetLoadingList.Remove(assetObjectKey); + + return gameObject; + } + + #endregion + + /// + /// 异步加载资源。 + /// + /// 资源的定位地址。 + /// 要加载资源的类型。 + /// 加载资源的优先级。 + /// 加载资源回调函数集。 + /// 用户自定义数据。 + /// 指定资源包的名称。不传使用默认资源包。 + public async void LoadAssetAsync(string location, Type assetType, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData, string packageName = "") + { + if (string.IsNullOrEmpty(location)) + { + throw new GameFrameworkException("Asset name is invalid."); + } + + if (loadAssetCallbacks == null) + { + throw new GameFrameworkException("Load asset callbacks is invalid."); + } + + if (!CheckLocationValid(location, packageName)) + { + string errorMessage = Utility.Text.Format("Could not found location [{0}].", location); + Log.Error(errorMessage); + if (loadAssetCallbacks.LoadAssetFailureCallback != null) + { + loadAssetCallbacks.LoadAssetFailureCallback(location, LoadResourceStatus.NotExist, errorMessage, userData); + } + return; + } + + string assetObjectKey = GetCacheKey(location, packageName); + + await TryWaitingLoading(assetObjectKey); + + float duration = Time.time; + + AssetObject assetObject = _assetPool.Spawn(assetObjectKey); + if (assetObject != null) + { + await UniTask.Yield(); + loadAssetCallbacks.LoadAssetSuccessCallback(location, assetObject.Target, Time.time - duration, userData); + return; + } + + _assetLoadingList.Add(assetObjectKey); + + AssetInfo assetInfo = GetAssetInfo(location, packageName); + + if (!string.IsNullOrEmpty(assetInfo.Error)) + { + _assetLoadingList.Remove(assetObjectKey); + + string errorMessage = Utility.Text.Format("Can not load asset '{0}' because :'{1}'.", location, assetInfo.Error); + if (loadAssetCallbacks.LoadAssetFailureCallback != null) + { + loadAssetCallbacks.LoadAssetFailureCallback(location, LoadResourceStatus.NotExist, errorMessage, userData); + return; + } + + throw new GameFrameworkException(errorMessage); + } + + AssetHandle handle = GetHandleAsync(location, assetType, packageName: packageName); + + if (loadAssetCallbacks.LoadAssetUpdateCallback != null) + { + InvokeProgress(location, handle, loadAssetCallbacks.LoadAssetUpdateCallback, userData).Forget(); + } + + await handle.ToUniTask(); + + if (handle.AssetObject == null || handle.Status == EOperationStatus.Failed) + { + _assetLoadingList.Remove(assetObjectKey); + + string errorMessage = Utility.Text.Format("Can not load asset '{0}'.", location); + if (loadAssetCallbacks.LoadAssetFailureCallback != null) + { + loadAssetCallbacks.LoadAssetFailureCallback(location, LoadResourceStatus.NotReady, errorMessage, userData); + return; + } + + throw new GameFrameworkException(errorMessage); + } + else + { + assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); + _assetPool.Register(assetObject, true); + + _assetLoadingList.Remove(assetObjectKey); + + if (loadAssetCallbacks.LoadAssetSuccessCallback != null) + { + duration = Time.time - duration; + + loadAssetCallbacks.LoadAssetSuccessCallback(location, handle.AssetObject, duration, userData); + } + } + } + + /// + /// 异步加载资源。 + /// + /// 资源的定位地址。 + /// 加载资源的优先级。 + /// 加载资源回调函数集。 + /// 用户自定义数据。 + /// 指定资源包的名称。不传使用默认资源包。 + public async void LoadAssetAsync(string location, int priority, LoadAssetCallbacks loadAssetCallbacks, object userData, string packageName = "") + { + if (string.IsNullOrEmpty(location)) + { + throw new GameFrameworkException("Asset name is invalid."); + } + + if (loadAssetCallbacks == null) + { + throw new GameFrameworkException("Load asset callbacks is invalid."); + } + + if (!CheckLocationValid(location, packageName)) + { + string errorMessage = Utility.Text.Format("Could not found location [{0}].", location); + Log.Error(errorMessage); + if (loadAssetCallbacks.LoadAssetFailureCallback != null) + { + loadAssetCallbacks.LoadAssetFailureCallback(location, LoadResourceStatus.NotExist, errorMessage, userData); + } + return; + } + + string assetObjectKey = GetCacheKey(location, packageName); + + await TryWaitingLoading(assetObjectKey); + + float duration = Time.time; + + AssetObject assetObject = _assetPool.Spawn(assetObjectKey); + if (assetObject != null) + { + await UniTask.Yield(); + loadAssetCallbacks.LoadAssetSuccessCallback(location, assetObject.Target, Time.time - duration, userData); + return; + } + + _assetLoadingList.Add(assetObjectKey); + + AssetInfo assetInfo = GetAssetInfo(location, packageName); + + if (!string.IsNullOrEmpty(assetInfo.Error)) + { + _assetLoadingList.Remove(assetObjectKey); + + string errorMessage = Utility.Text.Format("Can not load asset '{0}' because :'{1}'.", location, assetInfo.Error); + if (loadAssetCallbacks.LoadAssetFailureCallback != null) + { + loadAssetCallbacks.LoadAssetFailureCallback(location, LoadResourceStatus.NotExist, errorMessage, userData); + return; + } + + throw new GameFrameworkException(errorMessage); + } + + AssetHandle handle = GetHandleAsync(location, assetInfo.AssetType, packageName: packageName); + + if (loadAssetCallbacks.LoadAssetUpdateCallback != null) + { + InvokeProgress(location, handle, loadAssetCallbacks.LoadAssetUpdateCallback, userData).Forget(); + } + + await handle.ToUniTask(); + + if (handle.AssetObject == null || handle.Status == EOperationStatus.Failed) + { + _assetLoadingList.Remove(assetObjectKey); + + string errorMessage = Utility.Text.Format("Can not load asset '{0}'.", location); + if (loadAssetCallbacks.LoadAssetFailureCallback != null) + { + loadAssetCallbacks.LoadAssetFailureCallback(location, LoadResourceStatus.NotReady, errorMessage, userData); + return; + } + + throw new GameFrameworkException(errorMessage); + } + else + { + assetObject = AssetObject.Create(assetObjectKey, handle.AssetObject, handle, this); + _assetPool.Register(assetObject, true); + + _assetLoadingList.Remove(assetObjectKey); + + if (loadAssetCallbacks.LoadAssetSuccessCallback != null) + { + duration = Time.time - duration; + + loadAssetCallbacks.LoadAssetSuccessCallback(location, handle.AssetObject, duration, userData); + } + } + } + + private async UniTaskVoid InvokeProgress(string location, AssetHandle assetHandle, LoadAssetUpdateCallback loadAssetUpdateCallback, object userData) + { + if (string.IsNullOrEmpty(location)) + { + throw new GameFrameworkException("Asset name is invalid."); + } + + if (loadAssetUpdateCallback != null) + { + while (assetHandle is { IsValid: true, IsDone: false }) + { + await UniTask.Yield(); + + loadAssetUpdateCallback.Invoke(location, assetHandle.Progress, userData); + } + } + } + + /// + /// 获取同步加载的资源操作句柄。 + /// + /// 资源定位地址。 + /// 资源包名称。 + /// 资源类型。 + /// 资源操作句柄。 + public AssetHandle LoadAssetSyncHandle(string location, string packageName = "") where T : UnityEngine.Object + { + if (string.IsNullOrEmpty(packageName)) + { + return YooAssets.LoadAssetSync(location); + } + + var package = YooAssets.GetPackage(packageName); + return package.LoadAssetSync(location); + } + + /// + /// 获取异步加载的资源操作句柄。 + /// + /// 资源定位地址。 + /// 资源包名称。 + /// 资源类型。 + /// 资源操作句柄。 + public AssetHandle LoadAssetAsyncHandle(string location, string packageName = "") where T : UnityEngine.Object + { + if (string.IsNullOrEmpty(packageName)) + { + return YooAssets.LoadAssetAsync(location); + } + + var package = YooAssets.GetPackage(packageName); + return package.LoadAssetAsync(location); + } + + #endregion + + private readonly TimeoutController _timeoutController = new TimeoutController(); + + private async UniTask TryWaitingLoading(string assetObjectKey) + { + if (_assetLoadingList.Contains(assetObjectKey)) + { + try + { + await UniTask.WaitUntil(() => !_assetLoadingList.Contains(assetObjectKey)) +#if UNITY_EDITOR + .AttachExternalCancellation(_timeoutController.Timeout(TimeSpan.FromSeconds(60))); + _timeoutController.Reset(); +#else + ; +#endif + } + catch (OperationCanceledException ex) + { + if (_timeoutController.IsTimeout()) + { + Log.Error($"LoadAssetAsync Waiting {assetObjectKey} timeout. reason:{ex.Message}"); + } + } + } + } + + #endregion + + #region 设置下载系统参数,自定义下载请求 + + /// + /// 设置下载系统参数,自定义下载请求。 + /// + /// 自定义下载器的请求委托。 + public void SetDownloadSystemUnityWebRequest(UnityWebRequestDelegate downloadSystemUnityWebRequest) + { + YooAssets.SetDownloadSystemUnityWebRequest(downloadSystemUnityWebRequest); + } + + public UnityEngine.Networking.UnityWebRequest CustomWebRequester(string url) + { + var request = new UnityEngine.Networking.UnityWebRequest(url, UnityEngine.Networking.UnityWebRequest.kHttpVerbGET); + var authorization = GetAuthorization("Admin", "12345"); + request.SetRequestHeader("AUTHORIZATION", authorization); + return request; + } + + private string GetAuthorization(string userName, string password) + { + string auth = $"{userName}:{password}"; + var bytes = System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(auth); + return $"Basic {Convert.ToBase64String(bytes)}"; + } + + #endregion + } +} diff --git a/Runtime/Resource/Resource/ResourceModule.cs.meta b/Runtime/Resource/Resource/ResourceModule.cs.meta new file mode 100644 index 0000000..82f4410 --- /dev/null +++ b/Runtime/Resource/Resource/ResourceModule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 132ce4e1754bf78429c5f36e298ac40f +timeCreated: 1678969240 \ No newline at end of file diff --git a/Runtime/Scene.meta b/Runtime/Scene.meta new file mode 100644 index 0000000..bc292df --- /dev/null +++ b/Runtime/Scene.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a904919137204c579e29194387a084d6 +timeCreated: 1737381345 \ No newline at end of file diff --git a/Runtime/Scene/ISceneModule.cs b/Runtime/Scene/ISceneModule.cs new file mode 100644 index 0000000..9011a24 --- /dev/null +++ b/Runtime/Scene/ISceneModule.cs @@ -0,0 +1,90 @@ +using System; +using AlicizaX.Resource.Runtime; +using AlicizaX; +using Cysharp.Threading.Tasks; +using UnityEngine.SceneManagement; +using YooAsset; + +namespace AlicizaX.Scene.Runtime +{ + public interface ISceneModule : IModule,IModuleAwake + { + /// + /// 当前主场景名称。 + /// + public string CurrentMainSceneName { get; } + + /// + /// 加载场景。 + /// + /// 场景的定位地址 + /// 场景加载模式 + /// 加载完毕时是否主动挂起 + /// 优先级 + /// 加载主场景是否回收垃圾。 + /// 加载进度回调。 + public UniTask LoadSceneAsync(string location, LoadSceneMode sceneMode = LoadSceneMode.Single, bool suspendLoad = false, uint priority = 100, bool gcCollect = true, + Action progressCallBack = null); + + /// + /// 加载场景。 + /// + /// 场景的定位地址 + /// 场景加载模式 + /// 加载完毕时是否主动挂起 + /// 优先级 + /// 加载回调。 + /// 加载主场景是否回收垃圾。 + /// 加载进度回调。 + public void LoadScene(string location, + LoadSceneMode sceneMode = LoadSceneMode.Single, + bool suspendLoad = false, + uint priority = 100, + Action callBack = null, + bool gcCollect = true, + Action progressCallBack = null); + + /// + /// 激活场景(当同时存在多个场景时用于切换激活场景)。 + /// + /// 场景资源定位地址。 + /// 是否操作成功。 + public bool ActivateScene(string location); + + /// + /// 解除场景加载挂起操作。 + /// + /// 场景资源定位地址。 + /// 是否操作成功。 + public bool UnSuspend(string location); + + /// + /// 是否为主场景。 + /// + /// 场景资源定位地址。 + /// 是否主场景。 + public bool IsMainScene(string location); + + /// + /// 异步卸载子场景。 + /// + /// 场景资源定位地址。 + /// 进度回调。 + public UniTask UnloadAsync(string location, Action progressCallBack = null); + + /// + /// 异步卸载子场景。 + /// + /// 场景资源定位地址。 + /// 卸载完成回调。 + /// 进度回调。 + public void Unload(string location, Action callBack = null, Action progressCallBack = null); + + /// + /// 是否包含场景。 + /// + /// 场景资源定位地址。 + /// 是否包含场景。 + public bool IsContainScene(string location); + } +} diff --git a/Runtime/Scene/ISceneModule.cs.meta b/Runtime/Scene/ISceneModule.cs.meta new file mode 100644 index 0000000..6fa734c --- /dev/null +++ b/Runtime/Scene/ISceneModule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 861177fef6ea4c02a3aae34fa55b7ab0 +timeCreated: 1738733678 \ No newline at end of file diff --git a/Runtime/Scene/SceneComponent.cs b/Runtime/Scene/SceneComponent.cs new file mode 100644 index 0000000..3964062 --- /dev/null +++ b/Runtime/Scene/SceneComponent.cs @@ -0,0 +1,16 @@ +using System; +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Scene.Runtime +{ + [DisallowMultipleComponent] + [AddComponentMenu("Game Framework/Scene")] + public sealed class SceneComponent : MonoBehaviour + { + private void Awake() + { + ModuleSystem.RegisterModule(); + } + } +} diff --git a/Runtime/Scene/SceneComponent.cs.meta b/Runtime/Scene/SceneComponent.cs.meta new file mode 100644 index 0000000..28f77fd --- /dev/null +++ b/Runtime/Scene/SceneComponent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a21ca977c2264066ab0da2058aaecb24 +timeCreated: 1742461878 \ No newline at end of file diff --git a/Runtime/Scene/SceneModule.cs b/Runtime/Scene/SceneModule.cs new file mode 100644 index 0000000..4ca667e --- /dev/null +++ b/Runtime/Scene/SceneModule.cs @@ -0,0 +1,408 @@ +using System; +using System.Collections.Generic; +using AlicizaX.Resource.Runtime; +using Cysharp.Threading.Tasks; +using UnityEngine.SceneManagement; +using YooAsset; + +namespace AlicizaX.Scene.Runtime +{ + internal class SceneModule : ISceneModule + { + private string _currentMainSceneName = string.Empty; + + private SceneHandle _currentMainScene; + + private readonly Dictionary _subScenes = new Dictionary(); + + private readonly HashSet _handlingScene = new HashSet(); + + /// + /// 当前主场景名称。 + /// + public string CurrentMainSceneName => _currentMainSceneName; + + void IModuleAwake.Awake() + { + _currentMainScene = null; + _currentMainSceneName = SceneManager.GetSceneByBuildIndex(0).name; + } + + void IModule.Dispose() + { + var iter = _subScenes.Values.GetEnumerator(); + while (iter.MoveNext()) + { + SceneHandle subScene = iter.Current; + if (subScene != null) + { + subScene.UnloadAsync(); + } + } + + iter.Dispose(); + _subScenes.Clear(); + _handlingScene.Clear(); + _currentMainSceneName = string.Empty; + } + + + /// + /// 加载场景。 + /// + /// 场景的定位地址 + /// 场景加载模式 + /// 加载完毕时是否主动挂起 + /// 优先级 + /// 加载主场景是否回收垃圾。 + /// 加载进度回调。 + public async UniTask LoadSceneAsync(string location, LoadSceneMode sceneMode = LoadSceneMode.Single, bool suspendLoad = false, uint priority = 100, bool gcCollect = true, Action progressCallBack = null) + { + if (!_handlingScene.Add(location)) + { + Log.Error($"Could not load scene while loading. Scene: {location}"); + return default; + } + + if (sceneMode == LoadSceneMode.Additive) + { + if (_subScenes.TryGetValue(location, out SceneHandle subScene)) + { + throw new Exception($"Could not load subScene while already loaded. Scene: {location}"); + } + + subScene = YooAssets.LoadSceneAsync(location, sceneMode, LocalPhysicsMode.None, suspendLoad, priority); + + //Fix 这里前置,subScene.IsDone在UnSupendLoad之后才会是true + _subScenes.Add(location, subScene); + + if (progressCallBack != null) + { + while (!subScene.IsDone && subScene.IsValid) + { + progressCallBack.Invoke(subScene.Progress); + await UniTask.Yield(); + } + } + else + { + await subScene.ToUniTask(); + } + + _handlingScene.Remove(location); + + return subScene.SceneObject; + } + else + { + if (_currentMainScene is { IsDone: false }) + { + throw new Exception($"Could not load MainScene while loading. CurrentMainScene: {_currentMainSceneName}."); + } + + _currentMainSceneName = location; + + _currentMainScene = YooAssets.LoadSceneAsync(location, sceneMode, LocalPhysicsMode.None, suspendLoad, priority); + + if (progressCallBack != null) + { + while (!_currentMainScene.IsDone && _currentMainScene.IsValid) + { + progressCallBack.Invoke(_currentMainScene.Progress); + await UniTask.Yield(); + } + } + else + { + await _currentMainScene.ToUniTask(); + } + + ModuleSystem.GetModule().ForceUnloadUnusedAssets(gcCollect); + + _handlingScene.Remove(location); + + return _currentMainScene.SceneObject; + } + } + + /// + /// 加载场景。 + /// + /// 场景的定位地址 + /// 场景加载模式 + /// 加载完毕时是否主动挂起 + /// 优先级 + /// 加载回调。 + /// 加载主场景是否回收垃圾。 + /// 加载进度回调。 + public void LoadScene(string location, LoadSceneMode sceneMode = LoadSceneMode.Single, bool suspendLoad = false, uint priority = 100, + Action callBack = null, + bool gcCollect = true, Action progressCallBack = null) + { + if (!_handlingScene.Add(location)) + { + Log.Error($"Could not load scene while loading. Scene: {location}"); + return; + } + + if (sceneMode == LoadSceneMode.Additive) + { + if (_subScenes.TryGetValue(location, out SceneHandle subScene)) + { + Log.Warning($"Could not load subScene while already loaded. Scene: {location}"); + return; + } + + subScene = YooAssets.LoadSceneAsync(location, sceneMode, LocalPhysicsMode.None, suspendLoad, priority); + + subScene.Completed += handle => + { + _handlingScene.Remove(location); + callBack?.Invoke(handle.SceneObject); + }; + + if (progressCallBack != null) + { + InvokeProgress(subScene, progressCallBack).Forget(); + } + + _subScenes.Add(location, subScene); + } + else + { + if (_currentMainScene is { IsDone: false }) + { + Log.Warning($"Could not load MainScene while loading. CurrentMainScene: {_currentMainSceneName}."); + return; + } + + _currentMainSceneName = location; + + _currentMainScene = YooAssets.LoadSceneAsync(location, sceneMode, LocalPhysicsMode.None, suspendLoad, priority); + + _currentMainScene.Completed += handle => + { + _handlingScene.Remove(location); + callBack?.Invoke(handle.SceneObject); + }; + + if (progressCallBack != null) + { + InvokeProgress(_currentMainScene, progressCallBack).Forget(); + } + + ModuleSystem.GetModule().ForceUnloadUnusedAssets(gcCollect); + } + } + + private async UniTaskVoid InvokeProgress(SceneHandle sceneHandle, Action progress) + { + if (sceneHandle == null) + { + return; + } + + while (!sceneHandle.IsDone && sceneHandle.IsValid) + { + await UniTask.Yield(); + + progress?.Invoke(sceneHandle.Progress); + } + } + + /// + /// 激活场景(当同时存在多个场景时用于切换激活场景)。 + /// + /// 场景资源定位地址。 + /// 是否操作成功。 + public bool ActivateScene(string location) + { + if (_currentMainSceneName.Equals(location)) + { + if (_currentMainScene != null) + { + return _currentMainScene.ActivateScene(); + } + + return false; + } + + _subScenes.TryGetValue(location, out SceneHandle subScene); + if (subScene != null) + { + return subScene.ActivateScene(); + } + + Log.Warning($"IsMainScene invalid location:{location}"); + return false; + } + + /// + /// 解除场景加载挂起操作。 + /// + /// 场景资源定位地址。 + /// 是否操作成功。 + public bool UnSuspend(string location) + { + if (_currentMainSceneName.Equals(location)) + { + if (_currentMainScene != null) + { + return _currentMainScene.UnSuspend(); + } + + return false; + } + + _subScenes.TryGetValue(location, out SceneHandle subScene); + if (subScene != null) + { + return subScene.UnSuspend(); + } + + Log.Warning($"IsMainScene invalid location:{location}"); + return false; + } + + /// + /// 是否为主场景。 + /// + /// 场景资源定位地址。 + /// 是否主场景。 + public bool IsMainScene(string location) + { + // 获取当前激活的场景 + UnityEngine.SceneManagement.Scene currentScene = SceneManager.GetActiveScene(); + + if (_currentMainSceneName.Equals(location)) + { + if (_currentMainScene == null) + { + return false; + } + + // 判断当前场景是否是主场景 + if (currentScene.name == _currentMainScene.SceneName) + { + return true; + } + + return _currentMainScene.SceneName == currentScene.name; + } + + // 判断当前场景是否是主场景 + if (currentScene.name == _currentMainScene?.SceneName) + { + return true; + } + + Log.Warning($"IsMainScene invalid location:{location}"); + return false; + } + + /// + /// 异步卸载子场景。 + /// + /// 场景资源定位地址。 + /// 进度回调。 + public async UniTask UnloadAsync(string location, Action progressCallBack = null) + { + _subScenes.TryGetValue(location, out SceneHandle subScene); + if (subScene != null) + { + if (subScene.SceneObject == default) + { + Log.Error($"Could not unload Scene while not loaded. Scene: {location}"); + return false; + } + + if (!_handlingScene.Add(location)) + { + Log.Warning($"Could not unload Scene while loading. Scene: {location}"); + return false; + } + + var unloadOperation = subScene.UnloadAsync(); + + if (progressCallBack != null) + { + while (!unloadOperation.IsDone && unloadOperation.Status != EOperationStatus.Failed) + { + progressCallBack.Invoke(unloadOperation.Progress); + await UniTask.Yield(); + } + } + else + { + await unloadOperation.ToUniTask(); + } + + _subScenes.Remove(location); + + _handlingScene.Remove(location); + + return true; + } + + Log.Warning($"UnloadAsync invalid location:{location}"); + return false; + } + + /// + /// 异步卸载子场景。 + /// + /// 场景资源定位地址。 + /// 卸载完成回调。 + /// 进度回调。 + public void Unload(string location, Action callBack = null, Action progressCallBack = null) + { + _subScenes.TryGetValue(location, out SceneHandle subScene); + if (subScene != null) + { + if (subScene.SceneObject == default) + { + Log.Error($"Could not unload Scene while not loaded. Scene: {location}"); + return; + } + + if (!_handlingScene.Add(location)) + { + Log.Warning($"Could not unload Scene while loading. Scene: {location}"); + return; + } + + subScene.UnloadAsync(); + subScene.UnloadAsync().Completed += @base => + { + _subScenes.Remove(location); + _handlingScene.Remove(location); + callBack?.Invoke(); + }; + + if (progressCallBack != null) + { + InvokeProgress(subScene, progressCallBack).Forget(); + } + + return; + } + + Log.Warning($"UnloadAsync invalid location:{location}"); + } + + /// + /// 是否包含场景。 + /// + /// 场景资源定位地址。 + /// 是否包含场景。 + public bool IsContainScene(string location) + { + if (_currentMainSceneName.Equals(location)) + { + return true; + } + + return _subScenes.TryGetValue(location, out var _); + } + } +} diff --git a/Runtime/Scene/SceneModule.cs.meta b/Runtime/Scene/SceneModule.cs.meta new file mode 100644 index 0000000..62c1fab --- /dev/null +++ b/Runtime/Scene/SceneModule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: df58bc01666e4f05abdd6a6b8fc87e78 +timeCreated: 1738733705 \ No newline at end of file diff --git a/Runtime/Timer.meta b/Runtime/Timer.meta new file mode 100644 index 0000000..1eb8073 --- /dev/null +++ b/Runtime/Timer.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ae644371f69948a8b433ea56bee320b6 +timeCreated: 1737098228 \ No newline at end of file diff --git a/Runtime/Timer/ITimerModule.cs b/Runtime/Timer/ITimerModule.cs new file mode 100644 index 0000000..fa95b80 --- /dev/null +++ b/Runtime/Timer/ITimerModule.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using AlicizaX; + +namespace AlicizaX.Timer.Runtime +{ + /// + /// 定时器接口 + /// + [UnityEngine.Scripting.Preserve] + public interface ITimerModule : IModule, IModuleUpdate + { + /// + /// 添加计时器。 + /// + /// 计时器回调。 + /// 计时器间隔。 + /// 是否循环。 + /// 是否不收时间缩放影响。 + /// 传参。(避免闭包) + /// 计时器Id。 + public int AddTimer(TimerHandler callback, float time, bool isLoop = false, bool isUnscaled = false, params object[] args); + + /// + /// 暂停计时器。 + /// + /// 计时器Id。 + public void Stop(int timerId); + + /// + /// 恢复计时器。 + /// + /// 计时器Id。 + public void Resume(int timerId); + + /// + /// 计时器是否在运行中。 + /// + /// 计时器Id。 + /// 否在运行中。 + public bool IsRunning(int timerId); + + /// + /// 获得计时器剩余时间。 + /// + public float GetLeftTime(int timerId); + + /// + /// 重置计时器,恢复到开始状态。 + /// + public void Restart(int timerId); + + /// + /// 移除计时器。 + /// + /// 计时器Id。 + public void RemoveTimer(int timerId); + + /// + /// 移除所有计时器。 + /// + public void RemoveAllTimer(); + } +} diff --git a/Runtime/Timer/ITimerModule.cs.meta b/Runtime/Timer/ITimerModule.cs.meta new file mode 100644 index 0000000..2a798a3 --- /dev/null +++ b/Runtime/Timer/ITimerModule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 89c3f76bd6f04ecca4b90909318b07c1 +timeCreated: 1737084351 \ No newline at end of file diff --git a/Runtime/Timer/TimerComponent.cs b/Runtime/Timer/TimerComponent.cs new file mode 100644 index 0000000..4fec6ed --- /dev/null +++ b/Runtime/Timer/TimerComponent.cs @@ -0,0 +1,17 @@ +using System; +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Timer.Runtime +{ + [DisallowMultipleComponent] + [AddComponentMenu("Game Framework/Timer")] + [UnityEngine.Scripting.Preserve] + public sealed class TimerComponent:MonoBehaviour + { + private void Awake() + { + ModuleSystem.RegisterModule(); + } + } +} diff --git a/Runtime/Timer/TimerComponent.cs.meta b/Runtime/Timer/TimerComponent.cs.meta new file mode 100644 index 0000000..014fe0d --- /dev/null +++ b/Runtime/Timer/TimerComponent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 40c9991bf8984e2d89a8536bc8557abd +timeCreated: 1742474413 \ No newline at end of file diff --git a/Runtime/Timer/TimerModule.cs b/Runtime/Timer/TimerModule.cs new file mode 100644 index 0000000..7546e78 --- /dev/null +++ b/Runtime/Timer/TimerModule.cs @@ -0,0 +1,235 @@ +using System; +using System.Collections.Generic; +using AlicizaX; +using UnityEngine; + +namespace AlicizaX.Timer.Runtime +{ + public delegate void TimerHandler(params object[] args); + + [Serializable] + public class Timer : IMemory + { + public int TimerId; + public float TriggerTime; + public float Interval; + public TimerHandler Handler; + public bool IsLoop; + public bool IsRunning; + public bool IsUnscaled; + public bool IsActive; // 标记定时器是否有效 + public object[] Args; + public LinkedListNode Node; // 用于时间轮中的链表节点 + + public void Clear() + { + Handler = null; + Args = null; + Node = null; + IsActive = false; + } + } + + [UnityEngine.Scripting.Preserve] + internal sealed class TimerModule : ITimerModule + { + private int _curTimerId; + private TimeWheel _scaledTimeWheel; + private TimeWheel _unscaledTimeWheel; + private readonly Dictionary _activeTimers = new Dictionary(); + private readonly Action _processTimerDelegate; + private class TimeWheel + { + private readonly float _slotInterval; // 每个槽的时间间隔(秒) + private readonly int _slotCount; // 槽的数量 + private readonly LinkedList[] _slots; + private float _currentTime; + private int _currentSlotIndex; + + public TimeWheel(float slotInterval, int slotCount) + { + _slotInterval = slotInterval; + _slotCount = slotCount; + _slots = new LinkedList[slotCount]; + for (int i = 0; i < slotCount; i++) + _slots[i] = new LinkedList(); + _currentTime = 0f; + _currentSlotIndex = 0; + } + + public void AddTimer(Timer timer, float currentTime) + { + if (!timer.IsActive) return; + + float triggerTime = timer.TriggerTime; + float delta = triggerTime - currentTime; + + if (delta < 0) + { + // 立即触发 + delta = 0; + } + + int slotsToAdvance = Mathf.FloorToInt(delta / _slotInterval); + int targetSlot = (_currentSlotIndex + slotsToAdvance) % _slotCount; + + timer.Node = _slots[targetSlot].AddLast(timer); + timer.IsRunning = true; + } + + public void Advance(float currentTime, Action processTimer) + { + float timeDelta = currentTime - _currentTime; + if (timeDelta <= 0) return; + + int steps = Mathf.FloorToInt(timeDelta / _slotInterval); + for (int i = 0; i < steps; i++) + { + _currentSlotIndex = (_currentSlotIndex + 1) % _slotCount; + _currentTime += _slotInterval; + + LinkedList currentSlot = _slots[_currentSlotIndex]; + LinkedListNode currentNode = currentSlot.First; + while (currentNode != null) + { + LinkedListNode nextNode = currentNode.Next; + Timer timer = currentNode.Value; + currentSlot.Remove(currentNode); + + if (timer.IsActive && timer.IsRunning) + { + processTimer(timer); + } + + currentNode = nextNode; + } + } + } + } + + public TimerModule() + { + _scaledTimeWheel = new TimeWheel(1f, 60); + _unscaledTimeWheel = new TimeWheel(1f, 60); + _processTimerDelegate = ProcessTimer; // 初始化时创建委托一次 + } + + + public int AddTimer(TimerHandler callback, float time, bool isLoop = false, + bool isUnscaled = false, params object[] args) + { + Timer timer = GetTimerFromPool(); + timer.TimerId = ++_curTimerId; + timer.TriggerTime = (isUnscaled ? Time.unscaledTime : Time.time) + time; + timer.Interval = isLoop ? time : 0f; + timer.Handler = callback; + timer.IsLoop = isLoop; + timer.IsRunning = true; + timer.IsUnscaled = isUnscaled; + timer.Args = args; + timer.IsActive = true; + + _activeTimers.Add(timer.TimerId, timer); + TimeWheel targetWheel = isUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; + targetWheel.AddTimer(timer, isUnscaled ? Time.unscaledTime : Time.time); + return timer.TimerId; + } + + public void Stop(int timerId) + { + if (_activeTimers.TryGetValue(timerId, out Timer timer)) + timer.IsRunning = false; + } + + public void Resume(int timerId) + { + if (_activeTimers.TryGetValue(timerId, out Timer timer)) + timer.IsRunning = true; + } + + public bool IsRunning(int timerId) => + _activeTimers.TryGetValue(timerId, out Timer timer) && timer.IsRunning; + + public float GetLeftTime(int timerId) => + _activeTimers.TryGetValue(timerId, out Timer timer) + ? Mathf.Max(timer.TriggerTime - (timer.IsUnscaled ? Time.unscaledTime : Time.time), 0) + : 0; + + public void Restart(int timerId) + { + if (_activeTimers.TryGetValue(timerId, out Timer timer)) + { + timer.TriggerTime = (timer.IsUnscaled ? Time.unscaledTime : Time.time) + timer.Interval; + TimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; + targetWheel.AddTimer(timer, timer.IsUnscaled ? Time.unscaledTime : Time.time); + } + } + + public void RemoveTimer(int timerId) + { + if (_activeTimers.TryGetValue(timerId, out Timer timer)) + { + timer.IsActive = false; // 标记为无效 + _activeTimers.Remove(timerId); + ReturnTimerToPool(timer); + } + } + + public void RemoveAllTimer() + { + foreach (var timer in _activeTimers.Values) + { + timer.IsActive = false; + ReturnTimerToPool(timer); + } + + _activeTimers.Clear(); + } + + private Timer GetTimerFromPool() => MemoryPool.Acquire(); + + + private void ReturnTimerToPool(Timer timer) + { + MemoryPool.Release(timer); + } + + void IModuleUpdate.Update(float elapseSeconds, float realElapseSeconds) + { + float scaledTime = Time.time; + _scaledTimeWheel.Advance(scaledTime, _processTimerDelegate); + + float unscaledTime = Time.unscaledTime; + _unscaledTimeWheel.Advance(unscaledTime, _processTimerDelegate); + } + + private void ProcessTimer(Timer timer) + { + if (!timer.IsActive || !timer.IsRunning) return; + + try + { + timer.Handler?.Invoke(timer.Args); + } + catch (Exception e) + { + Log.Error($"Timer callback error: {e}"); + } + + if (timer.IsLoop) + { + timer.TriggerTime += timer.Interval; + TimeWheel targetWheel = timer.IsUnscaled ? _unscaledTimeWheel : _scaledTimeWheel; + targetWheel.AddTimer(timer, timer.IsUnscaled ? Time.unscaledTime : Time.time); + } + else + { + RemoveTimer(timer.TimerId); + } + } + + void IModule.Dispose() => RemoveAllTimer(); + + public int Priority => 0; + } +} diff --git a/Runtime/Timer/TimerModule.cs.meta b/Runtime/Timer/TimerModule.cs.meta new file mode 100644 index 0000000..c85d8fd --- /dev/null +++ b/Runtime/Timer/TimerModule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 205d0803930745d7825f89aa604530a5 +timeCreated: 1741683842 \ No newline at end of file diff --git a/Runtime/UI.meta b/Runtime/UI.meta new file mode 100644 index 0000000..c6795c5 --- /dev/null +++ b/Runtime/UI.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 003872fc9d1944708cac097b45a8382a +timeCreated: 1737703086 \ No newline at end of file diff --git a/Runtime/UI/Constant.meta b/Runtime/UI/Constant.meta new file mode 100644 index 0000000..0265b1b --- /dev/null +++ b/Runtime/UI/Constant.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f783b53bbb3de8e48a9127ffd6463454 +timeCreated: 1724221459 \ No newline at end of file diff --git a/Runtime/UI/Constant/EPanelLayer.cs b/Runtime/UI/Constant/EPanelLayer.cs new file mode 100644 index 0000000..e0ac901 --- /dev/null +++ b/Runtime/UI/Constant/EPanelLayer.cs @@ -0,0 +1,44 @@ +namespace AlicizaX.UI.Runtime +{ + public enum UILayer + { + /// + /// 最低层 + /// 要显示的这个就是最低的层级 + /// + Background = 0, + + /// + /// 场景层 + /// 比如 血条飘字不是做在3D时 用2D实现时的层 + /// 比如 头像 ... + /// + Scene = 1, + + /// + /// 普通面板层 + /// 全屏界面 + /// + UI = 2, + + /// + /// 弹窗层 + /// 一般是非全屏界面,可同时存在的 + /// + Popup = 3, + + /// + /// 提示层 + /// 一般 提示飘字 确认弹窗 跑马灯之类的 + /// + Tips = 4, + + /// + /// 最高层 + /// 一般新手引导之类的 + /// + Top = 5, + + All = 6, + } +} diff --git a/Runtime/UI/Constant/EPanelLayer.cs.meta b/Runtime/UI/Constant/EPanelLayer.cs.meta new file mode 100644 index 0000000..3ec6f3c --- /dev/null +++ b/Runtime/UI/Constant/EPanelLayer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2d4172fa9d8337340899edb34207e108 +timeCreated: 1724221472 \ No newline at end of file diff --git a/Runtime/UI/Constant/Em.cs b/Runtime/UI/Constant/Em.cs new file mode 100644 index 0000000..b5a1d14 --- /dev/null +++ b/Runtime/UI/Constant/Em.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; + +public static class InstanceFactory +{ + private static readonly Dictionary> _constructorCache = new(); + + public static object CreateInstanceOptimized(Type type) + { + if (!_constructorCache.TryGetValue(type, out var constructor)) + { + // 验证是否存在公共无参构造函数 + var ctor = type.GetConstructor(Type.EmptyTypes); + if (ctor == null) + { + throw new MissingMethodException($"类型 {type.Name} 缺少公共无参构造函数"); + } + + // 构建表达式树:new T() + var newExpr = Expression.New(ctor); + var lambda = Expression.Lambda>(newExpr); + constructor = lambda.Compile(); + + // 缓存委托 + _constructorCache[type] = constructor; + } + + return constructor(); + } +} diff --git a/Runtime/UI/Constant/Em.cs.meta b/Runtime/UI/Constant/Em.cs.meta new file mode 100644 index 0000000..eb8b4a1 --- /dev/null +++ b/Runtime/UI/Constant/Em.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 195eee24a3e6423fbe4b9b537099a2d9 +timeCreated: 1744176921 \ No newline at end of file diff --git a/Runtime/UI/Constant/UIHolderFactory.cs b/Runtime/UI/Constant/UIHolderFactory.cs new file mode 100644 index 0000000..954cca6 --- /dev/null +++ b/Runtime/UI/Constant/UIHolderFactory.cs @@ -0,0 +1,78 @@ +using System; +using System.Linq; +using AlicizaX.Resource.Runtime; +using AlicizaX; +using Cysharp.Threading.Tasks; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace AlicizaX.UI.Runtime +{ + public static class UIHolderFactory + { + private static readonly IResourceModule ResourceModule; + + static UIHolderFactory() + { + ResourceModule = ModuleSystem.GetModule(); + } + + public static async UniTask CreateUIHolder(Transform parent) where T : UIHolderObjectBase + { + if (UIResRegistry.TryGet(typeof(T).TypeHandle, out UIResRegistry.UIResInfo resInfo)) + { + GameObject obj = await LoadUIResourcesAsync(resInfo, parent); + + return obj.GetComponent(); + } + + return null; + } + + internal static async UniTask LoadUIResourcesAsync(UIResRegistry.UIResInfo resInfo, Transform parent) + { + return resInfo.LoadType == EUIResLoadType.AssetBundle + ? await ResourceModule.LoadGameObjectAsync(resInfo.Location, parent) + : await InstantiateResourceAsync(resInfo.Location, parent); + } + + internal static async UniTask CreateUIResource(UIMetadata meta, Transform parent, UIBase owner = null) + { + if (meta.State != UIState.CreatedUI) return; + GameObject obj = await LoadUIResourcesAsync(meta.ResInfo, parent); + ValidateAndBind(meta, obj, owner); + } + + internal static void LoadUIResourcesSync(UIMetadata meta, Transform parent, UIBase owner = null) + { + if (meta.State != UIState.CreatedUI) return; + + GameObject obj = meta.ResInfo.LoadType == EUIResLoadType.AssetBundle + ? ResourceModule.LoadGameObject(meta.ResInfo.Location, parent) + : Object.Instantiate(Resources.Load(meta.ResInfo.Location), parent); + + ValidateAndBind(meta, obj, owner); + } + + + private static void ValidateAndBind(UIMetadata meta, GameObject holderObject, UIBase owner) + { + if (!holderObject) throw new NullReferenceException($"UI resource load failed: {meta.ResInfo.Location}"); + + var holder = (UIHolderObjectBase)holderObject.GetComponent(meta.View.UIHolderType); + + if (holder == null) + { + throw new InvalidCastException($"资源{holderObject.name}上不存在{meta.View.UIHolderType.FullName}"); + } + + meta.View?.BindUIHolder(holder, owner); + } + + private static async UniTask InstantiateResourceAsync(string location, Transform parent) + { + GameObject prefab = (GameObject)await Resources.LoadAsync(location); + return Object.Instantiate(prefab, parent); + } + } +} diff --git a/Runtime/UI/Constant/UIHolderFactory.cs.meta b/Runtime/UI/Constant/UIHolderFactory.cs.meta new file mode 100644 index 0000000..e8504e3 --- /dev/null +++ b/Runtime/UI/Constant/UIHolderFactory.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d3cb20524d8440198708e90c56c76e38 +timeCreated: 1739847047 \ No newline at end of file diff --git a/Runtime/UI/Constant/UIMetaRegistry.cs b/Runtime/UI/Constant/UIMetaRegistry.cs new file mode 100644 index 0000000..9f92a54 --- /dev/null +++ b/Runtime/UI/Constant/UIMetaRegistry.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace AlicizaX.UI.Runtime +{ + public static class UIMetaRegistry + { + public readonly struct UIMetaInfo + { + public readonly RuntimeTypeHandle RuntimeTypeHandle; + public readonly RuntimeTypeHandle HolderRuntimeTypeHandle; + public readonly int UILayer; + public readonly bool FullScreen; + public readonly int CacheTime; + + public UIMetaInfo(RuntimeTypeHandle runtimeTypeHandle, RuntimeTypeHandle holderRuntimeTypeHandle, UILayer windowLayer, bool fullScreen, int cacheTime) + { + RuntimeTypeHandle = runtimeTypeHandle; + HolderRuntimeTypeHandle = holderRuntimeTypeHandle; + UILayer = (int)windowLayer; + FullScreen = fullScreen; + CacheTime = cacheTime; + } + } + + private static readonly Dictionary _typeHandleMap = new(); + private static readonly Dictionary _stringHandleMap = new(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Register(Type uiType, Type holderType, UILayer layer = UILayer.UI, bool fullScreen = false, int cacheTime = 0) + { + var holderHandle = holderType.TypeHandle; + var uiHandle = uiType.TypeHandle; + _typeHandleMap[uiHandle] = new UIMetaInfo(uiHandle, holderHandle, layer, fullScreen, cacheTime); + _stringHandleMap[uiType.Name] = uiHandle; + } + + + public static bool TryGet(RuntimeTypeHandle handle, out UIMetaInfo info) + { + return _typeHandleMap.TryGetValue(handle, out info); + } + + public static bool TryGet(string type, out UIMetaInfo info) + { + RuntimeTypeHandle typeHandle; + if (_stringHandleMap.TryGetValue(type, out typeHandle)) + { + } + + return _typeHandleMap.TryGetValue(typeHandle, out info); + } + } +} diff --git a/Runtime/UI/Constant/UIMetaRegistry.cs.meta b/Runtime/UI/Constant/UIMetaRegistry.cs.meta new file mode 100644 index 0000000..743dbcc --- /dev/null +++ b/Runtime/UI/Constant/UIMetaRegistry.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a9872b7a45da4d28b699bdf36ad792ca +timeCreated: 1744116849 \ No newline at end of file diff --git a/Runtime/UI/Constant/UIMetadata.cs b/Runtime/UI/Constant/UIMetadata.cs new file mode 100644 index 0000000..7257137 --- /dev/null +++ b/Runtime/UI/Constant/UIMetadata.cs @@ -0,0 +1,57 @@ +using System; +using System.Runtime.CompilerServices; +using AlicizaX; +using Cysharp.Threading.Tasks; + +namespace AlicizaX.UI.Runtime +{ + internal sealed class UIMetadata + { + public UIBase View { get; private set; } + public readonly UIMetaRegistry.UIMetaInfo MetaInfo; + public readonly UIResRegistry.UIResInfo ResInfo; + public readonly Type UILogicType; + public bool InCache = false; + + public UIState State + { + get + { + if (View == null) return UIState.Uninitialized; + return View.State; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void CreateUI() + { + if (View is null) + { + View = (UIBase)InstanceFactory.CreateInstanceOptimized(UILogicType); + } + } + + public void Dispose() + { + DisposeAsync().Forget(); + } + + private async UniTask DisposeAsync() + { + if (State != UIState.Uninitialized && State != UIState.Destroying) + { + await View.InternalDestroy(); + View = null; + } + } + + public UIMetadata(Type uiType) + { + UILogicType = uiType; + + UIMetaRegistry.TryGet(UILogicType.TypeHandle, out MetaInfo); + + UIResRegistry.TryGet(MetaInfo.HolderRuntimeTypeHandle, out ResInfo); + } + } +} diff --git a/Runtime/UI/Constant/UIMetadata.cs.meta b/Runtime/UI/Constant/UIMetadata.cs.meta new file mode 100644 index 0000000..8493c17 --- /dev/null +++ b/Runtime/UI/Constant/UIMetadata.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9f48f37976fb440479f1d76953347817 +timeCreated: 1724230882 \ No newline at end of file diff --git a/Runtime/UI/Constant/UIMetadataFactory.cs b/Runtime/UI/Constant/UIMetadataFactory.cs new file mode 100644 index 0000000..3c3f944 --- /dev/null +++ b/Runtime/UI/Constant/UIMetadataFactory.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using AlicizaX; + +namespace AlicizaX.UI.Runtime +{ + internal static class MetaTypeCache where T : UIBase + { + public static readonly UIMetadata Metadata; + + static MetaTypeCache() + { + var type = typeof(T); + Metadata = UIMetadataFactory.GetMetadata(type.TypeHandle); + } + } + + internal static class UIMetadataFactory + { + private static readonly Dictionary UIWindowMetadata = new(); + + internal static UIMetadata GetMetadata(RuntimeTypeHandle handle) + { + if (!UIWindowMetadata.TryGetValue(handle, out var meta)) + { + meta = new UIMetadata(Type.GetTypeFromHandle(handle)); + UIWindowMetadata[handle] = meta; + } + + return meta; + } + } +} diff --git a/Runtime/UI/Constant/UIMetadataFactory.cs.meta b/Runtime/UI/Constant/UIMetadataFactory.cs.meta new file mode 100644 index 0000000..3528577 --- /dev/null +++ b/Runtime/UI/Constant/UIMetadataFactory.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 908f3c05a3c74cc3aa0c35ec046e934d +timeCreated: 1739845718 \ No newline at end of file diff --git a/Runtime/UI/Constant/UIResRegistry.cs b/Runtime/UI/Constant/UIResRegistry.cs new file mode 100644 index 0000000..0d9213a --- /dev/null +++ b/Runtime/UI/Constant/UIResRegistry.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEditor; +using UnityEngine; + +namespace AlicizaX.UI.Runtime +{ + public static class UIResRegistry + { + private static readonly Dictionary _typeHandleMap = new(); + + public readonly struct UIResInfo + { + public readonly string Location; + public readonly EUIResLoadType LoadType; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UIResInfo(string location, EUIResLoadType loadType) + { + Location = location; + LoadType = loadType; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Register(Type holderType, string location, EUIResLoadType loadType) + { + var handle = holderType.TypeHandle; + _typeHandleMap[handle] = new UIResInfo(location, loadType); + } + + public static bool TryGet(RuntimeTypeHandle handle, out UIResInfo info) + => _typeHandleMap.TryGetValue(handle, out info); + } +} diff --git a/Runtime/UI/Constant/UIResRegistry.cs.meta b/Runtime/UI/Constant/UIResRegistry.cs.meta new file mode 100644 index 0000000..c7a529d --- /dev/null +++ b/Runtime/UI/Constant/UIResRegistry.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0132d28e3a4943a4acd10fbc7d35ad51 +timeCreated: 1744113525 \ No newline at end of file diff --git a/Runtime/UI/Constant/WindowAttribute.cs b/Runtime/UI/Constant/WindowAttribute.cs new file mode 100644 index 0000000..4abd175 --- /dev/null +++ b/Runtime/UI/Constant/WindowAttribute.cs @@ -0,0 +1,57 @@ +using System; +using UnityEngine; + +namespace AlicizaX.UI.Runtime +{ + + [AttributeUsage(AttributeTargets.Class)] + public class WindowAttribute : Attribute + { + /// + /// 窗口层级 + /// + public readonly UILayer WindowLayer; + + /// + /// 全屏窗口标记。 + /// + public readonly bool FullScreen; + + /// + /// 延时关闭 + /// + public readonly int CacheTime; + + /// + /// + /// + /// 显示层级 + /// 是否全屏遮挡 + /// 缓存时间/s -1永久 0不 >=1生效 + public WindowAttribute(UILayer windowLayer, bool fullScreen = false, int cacheTime = 0) + { + WindowLayer = windowLayer; + FullScreen = fullScreen; + CacheTime = cacheTime; + } + } + + [AttributeUsage(AttributeTargets.Class)] + public class UIResAttribute : Attribute + { + public readonly string ResLocation; + public readonly EUIResLoadType ResLoadType; + + public UIResAttribute(string location, EUIResLoadType loadType) + { + ResLocation = location; + ResLoadType = loadType; + } + } + + public enum EUIResLoadType:byte + { + Resources, + AssetBundle + } +} diff --git a/Runtime/UI/Constant/WindowAttribute.cs.meta b/Runtime/UI/Constant/WindowAttribute.cs.meta new file mode 100644 index 0000000..7cd8f5f --- /dev/null +++ b/Runtime/UI/Constant/WindowAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8144120881e676f4cbdb6209550c7097 +timeCreated: 1680511434 \ No newline at end of file diff --git a/Runtime/UI/EventListenerProxy.cs b/Runtime/UI/EventListenerProxy.cs new file mode 100644 index 0000000..f08689d --- /dev/null +++ b/Runtime/UI/EventListenerProxy.cs @@ -0,0 +1,32 @@ + +using System.Collections.Generic; + +namespace AlicizaX +{ + /// + /// 游戏事件代理:UI 层订阅 -> Proxy 自动回收。 + /// + public class EventListenerProxy : IMemory + { + private readonly List _eventHandles = new(); + + public EventRuntimeHandle Subscribe + { + set => _eventHandles.Add(value); + } + + public EventListenerProxy Add(EventRuntimeHandle handle) + { + _eventHandles.Add(handle); + return this; + } + + public void Clear() + { + for (int i = _eventHandles.Count - 1; i >= 0; i--) + _eventHandles[i].Dispose(); + _eventHandles.Clear(); + } + + } +} diff --git a/Runtime/UI/EventListenerProxy.cs.meta b/Runtime/UI/EventListenerProxy.cs.meta new file mode 100644 index 0000000..ddcd802 --- /dev/null +++ b/Runtime/UI/EventListenerProxy.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: eb346bc3be28f89468e2c071e7ab9594 +timeCreated: 1680273952 \ No newline at end of file diff --git a/Runtime/UI/Manager.meta b/Runtime/UI/Manager.meta new file mode 100644 index 0000000..f32c19d --- /dev/null +++ b/Runtime/UI/Manager.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0510a64b7f93de943ae13f097faf50d8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/UI/Manager/IUIModule.cs b/Runtime/UI/Manager/IUIModule.cs new file mode 100644 index 0000000..1b96006 --- /dev/null +++ b/Runtime/UI/Manager/IUIModule.cs @@ -0,0 +1,24 @@ +using System; +using AlicizaX.Resource.Runtime; +using AlicizaX; +using AlicizaX.Timer.Runtime; +using Cysharp.Threading.Tasks; +using UnityEngine; + +namespace AlicizaX.UI.Runtime +{ + public interface IUIModule : IModule, IModuleUpdate + { + void Initlize(Transform root,bool isOrthographic); + Camera UICamera { get; set; } + Transform UICanvasRoot { get; set; } + UniTask ShowUI(params System.Object[] userDatas) where T : UIBase; + UniTask? ShowUI(string type, params object[] userDatas); + UniTask ShowUIAsync(params System.Object[] userDatas) where T : UIBase; + void CloseUI(bool force = false) where T : UIBase; + T GetUI() where T : UIBase; + + void CloseUI(RuntimeTypeHandle handle, bool force = false); + protected internal void SetTimerManager(ITimerModule timerModule); + } +} diff --git a/Runtime/UI/Manager/IUIModule.cs.meta b/Runtime/UI/Manager/IUIModule.cs.meta new file mode 100644 index 0000000..dd1804f --- /dev/null +++ b/Runtime/UI/Manager/IUIModule.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4fc14a8ed26f89443b0844936e492514 \ No newline at end of file diff --git a/Runtime/UI/Manager/UIModule.Block.cs b/Runtime/UI/Manager/UIModule.Block.cs new file mode 100644 index 0000000..1f5c03d --- /dev/null +++ b/Runtime/UI/Manager/UIModule.Block.cs @@ -0,0 +1,80 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace AlicizaX.UI.Runtime +{ + internal sealed partial class UIModule + { + private GameObject m_LayerBlock; //内部屏蔽对象 显示时之下的所有UI将不可操作 + + private int m_LastCountDownGuid; //倒计时的唯一ID + + private void InitUIBlock() + { + m_LayerBlock = new GameObject("LayerBlock"); + var rect = m_LayerBlock.AddComponent(); + m_LayerBlock.AddComponent(); + m_LayerBlock.AddComponent(); + rect.SetParent(UICanvasRoot); + rect.SetAsLastSibling(); + rect.ResetToFullScreen(); + SetLayerBlockOption(false); + } + + /// + /// 设置UI遮挡 + /// + /// 倒计时/s + public void SetUIBlock(float timeDuration) + { + if (m_LastCountDownGuid != 0) + { + _timerModule.RemoveTimer(m_LastCountDownGuid); + } + + SetLayerBlockOption(true); + m_LastCountDownGuid = _timerModule.AddTimer(OnBlockCountDown, timeDuration); + } + + /// + /// 强制退出UI遮挡 + /// + public void ForceExitBlock() + { + if (m_LastCountDownGuid != 0) + { + _timerModule.RemoveTimer(m_LastCountDownGuid); + } + + RecoverLayerOptionAll(); + } + + private void OnBlockCountDown(object[] args) + { + RecoverLayerOptionAll(); + } + + /// + /// 设置UI是否可以操作 + /// 不能提供此API对外操作 + /// 因为有人设置过后就会忘记恢复 + /// 如果你确实需要你可以设置 禁止无限时间 + /// 之后调用恢复操作也可以做到 + /// + /// true = 可以操作 = 屏蔽层会被隐藏 + private void SetLayerBlockOption(bool value) + { + m_LayerBlock.SetActive(value); + } + + /// + /// 强制恢复层级到可操作状态 + /// 此方法会强制打断倒计时 根据需求调用 + /// + public void RecoverLayerOptionAll() + { + SetLayerBlockOption(false); + m_LastCountDownGuid = 0; + } + } +} diff --git a/Runtime/UI/Manager/UIModule.Block.cs.meta b/Runtime/UI/Manager/UIModule.Block.cs.meta new file mode 100644 index 0000000..90db693 --- /dev/null +++ b/Runtime/UI/Manager/UIModule.Block.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e880f39de986cc142a161d678fd7fc84 +timeCreated: 1724223150 \ No newline at end of file diff --git a/Runtime/UI/Manager/UIModule.Cache.cs b/Runtime/UI/Manager/UIModule.Cache.cs new file mode 100644 index 0000000..ee16859 --- /dev/null +++ b/Runtime/UI/Manager/UIModule.Cache.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using AlicizaX; + +namespace AlicizaX.UI.Runtime +{ + internal sealed partial class UIModule + { + private readonly Dictionary m_CacheWindow = new(); + + private void CacheWindow(UIMetadata uiMetadata, bool force) + { + if (uiMetadata == null || uiMetadata.View == null) + { + Log.Error(" ui not exist!"); + return; + } + + if (force || uiMetadata.MetaInfo.CacheTime == 0) + { + uiMetadata.Dispose(); + return; + } + + RemoveFromCache(uiMetadata.MetaInfo.RuntimeTypeHandle); + int tiemrId = -1; + + uiMetadata.View.Holder.transform.SetParent(UICacheLayer); + if (uiMetadata.MetaInfo.CacheTime > 0) + { + tiemrId = _timerModule.AddTimer(OnTimerDiposeWindow, uiMetadata.MetaInfo.CacheTime, false, true, uiMetadata); + } + + uiMetadata.InCache = true; + m_CacheWindow.Add(uiMetadata.MetaInfo.RuntimeTypeHandle, (uiMetadata, tiemrId)); + } + + private void OnTimerDiposeWindow(object[] args) + { + UIMetadata meta = args[0] as UIMetadata; + meta?.Dispose(); + RemoveFromCache(meta.MetaInfo.RuntimeTypeHandle); + } + + private void RemoveFromCache(RuntimeTypeHandle typeHandle) + { + if (m_CacheWindow.TryGetValue(typeHandle, out var result)) + { + m_CacheWindow.Remove(typeHandle); + result.Item1.InCache = false; + if (result.Item2 > 0) + { + _timerModule.RemoveTimer(result.Item2); + } + } + } + } +} diff --git a/Runtime/UI/Manager/UIModule.Cache.cs.meta b/Runtime/UI/Manager/UIModule.Cache.cs.meta new file mode 100644 index 0000000..2d00f8b --- /dev/null +++ b/Runtime/UI/Manager/UIModule.Cache.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ae149a329f7523044a804e3fe6769cd6 +timeCreated: 1724230178 \ No newline at end of file diff --git a/Runtime/UI/Manager/UIModule.Initlize.cs b/Runtime/UI/Manager/UIModule.Initlize.cs new file mode 100644 index 0000000..415fa02 --- /dev/null +++ b/Runtime/UI/Manager/UIModule.Initlize.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace AlicizaX.UI.Runtime +{ + internal sealed partial class UIModule + { + public Camera UICamera { get; set; } + public Canvas UICanvas; + public Transform UICanvasRoot { get; set; } + public Transform UIRoot; + + private const int UI_ROOT_OFFSET = 1000; + private const int LAYER_DISTANCE = 1000; + + private const int LAYER_DEEP = 2000; + private const int WINDOW_DEEP = 100; + + private readonly RectTransform[] m_AllWindowLayer = new RectTransform[(int)UILayer.All]; + + private RectTransform UICacheLayer; + private bool _isOrthographic; + + public void Initlize(Transform root, bool isOrthographic) + { + UIRoot = root; + Object.DontDestroyOnLoad(root.gameObject); + + UIRoot.transform.position = new Vector3(UI_ROOT_OFFSET, UI_ROOT_OFFSET, 0); + + UICanvas = UIRoot.GetComponentInChildren(); + UICamera = UICanvas.worldCamera; + UICanvasRoot = UICanvas.transform; + + _isOrthographic = isOrthographic; + UICamera.orthographic = isOrthographic; + if (!isOrthographic) + { + UICamera.nearClipPlane = 10; + UICamera.farClipPlane = 1000; + } + + const int len = (int)UILayer.All; + for (var i = len - 1; i >= 0; i--) + { + AddLayer(i); + } + + AddLayer((int)UILayer.All); + InitUIBlock(); + } + + private void AddLayer(int layer) + { + var layerObject = new GameObject($"Layer{layer}-{(UILayer)layer}"); + var rect = layerObject.AddComponent(); + rect.SetParent(UICanvasRoot); + rect.localScale = Vector3.one; + rect.pivot = new Vector2(0.5f, 0.5f); + rect.anchorMax = Vector2.one; + rect.anchorMin = Vector2.zero; + rect.sizeDelta = Vector2.zero; + rect.localRotation = Quaternion.identity; + rect.localPosition = new Vector3(0, 0, layer * (_isOrthographic ? LAYER_DISTANCE : 0)); + if (layer == (int)UILayer.All) + { + UICacheLayer = rect; + return; + } + + m_AllWindowLayer[layer] = rect; + _openUI[layer] = new LayerData(16); + } + + + public RectTransform GetLayerRect(int layer) + { + return m_AllWindowLayer[layer]; + } + } +} diff --git a/Runtime/UI/Manager/UIModule.Initlize.cs.meta b/Runtime/UI/Manager/UIModule.Initlize.cs.meta new file mode 100644 index 0000000..31728a7 --- /dev/null +++ b/Runtime/UI/Manager/UIModule.Initlize.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a857b714234e34545ac83be63c96147b +timeCreated: 1724154426 \ No newline at end of file diff --git a/Runtime/UI/Manager/UIModule.Open.cs b/Runtime/UI/Manager/UIModule.Open.cs new file mode 100644 index 0000000..5302871 --- /dev/null +++ b/Runtime/UI/Manager/UIModule.Open.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using AlicizaX; +using Cysharp.Threading.Tasks; + +namespace AlicizaX.UI.Runtime +{ + readonly struct LayerData + { + public readonly List OrderList; // 维护插入顺序 + public readonly HashSet HandleSet; // O(1)存在性检查 + + public LayerData(int initialCapacity) + { + OrderList = new List(initialCapacity); + HandleSet = new HashSet(); + } + } + + internal sealed partial class UIModule + { + private readonly LayerData[] _openUI = new LayerData[(int)UILayer.All]; + + private async UniTask ShowUIImplAsync(UIMetadata meta, params object[] userDatas) + { + var metaInfo = GetOrCreateMeta(meta); + await UIHolderFactory.CreateUIResource(metaInfo, UICacheLayer); + return await FinalizeShow(metaInfo, userDatas); + } + + private async UniTask CloseUIImpl(UIMetadata meta, bool force) + { + if (meta.State == UIState.Uninitialized || meta.State == UIState.CreatedUI) + { + return; + } + await meta.View.InternalClose(); + Pop(meta); + SortWindowVisible(meta.MetaInfo.UILayer); + SortWindowDepth(meta.MetaInfo.UILayer); + CacheWindow(meta, force); + } + + + private UIBase GetUIImpl(UIMetadata meta) + { + return meta.View; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private UIMetadata GetOrCreateMeta(UIMetadata meta) + { + if (meta.State == UIState.Uninitialized) meta.CreateUI(); + return meta; + } + + + private async UniTask FinalizeShow(UIMetadata meta, object[] userDatas) + { + if (meta.InCache) + { + RemoveFromCache(meta.MetaInfo.RuntimeTypeHandle); + Push(meta); + } + else + { + switch (meta.State) + { + case UIState.Loaded: + Push(meta); + break; + case UIState.Opened: + MoveToTop(meta); + break; + } + } + + meta.View.RefreshParams(userDatas); + await UpdateVisualState(meta); + return meta.View; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Push(UIMetadata meta) + { + ref var layer = ref _openUI[meta.MetaInfo.UILayer]; + if (layer.HandleSet.Add(meta.MetaInfo.RuntimeTypeHandle)) + { + layer.OrderList.Add(meta); + UpdateLayerParent(meta); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Pop(UIMetadata meta) + { + ref var layer = ref _openUI[meta.MetaInfo.UILayer]; + if (layer.HandleSet.Remove(meta.MetaInfo.RuntimeTypeHandle)) + { + layer.OrderList.Remove(meta); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void UpdateLayerParent(UIMetadata meta) + { + if (meta.View?.Holder) + { + var layerRect = GetLayerRect(meta.MetaInfo.UILayer); + meta.View.Holder.transform.SetParent(layerRect); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void MoveToTop(UIMetadata meta) + { + ref var layer = ref _openUI[meta.MetaInfo.UILayer]; + int lastIdx = layer.OrderList.Count - 1; + int currentIdx = layer.OrderList.IndexOf(meta); + + if (currentIdx != lastIdx && currentIdx >= 0) + { + layer.OrderList.RemoveAt(currentIdx); + layer.OrderList.Add(meta); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private async UniTask UpdateVisualState(UIMetadata meta) + { + SortWindowVisible(meta.MetaInfo.UILayer); + SortWindowDepth(meta.MetaInfo.UILayer); + if (meta.State == UIState.Loaded) + { + await meta.View.InternalInitlized(); + } + + await meta.View.InternalOpen(); + } + + private void SortWindowVisible(int layer) + { + var list = _openUI[layer].OrderList; + bool shouldHide = false; + + // 反向遍历避免GC分配 + for (int i = list.Count - 1; i >= 0; i--) + { + var meta = list[i]; + meta.View.Visible = !shouldHide; + shouldHide |= meta.MetaInfo.FullScreen && meta.State == UIState.Opened; + } + } + + private void SortWindowDepth(int layer) + { + var list = _openUI[layer].OrderList; + int baseDepth = layer * LAYER_DEEP; + + for (int i = 0; i < list.Count; i++) + { + list[i].View.Depth = baseDepth + i * WINDOW_DEEP; + } + } + } +} diff --git a/Runtime/UI/Manager/UIModule.Open.cs.meta b/Runtime/UI/Manager/UIModule.Open.cs.meta new file mode 100644 index 0000000..11ca815 --- /dev/null +++ b/Runtime/UI/Manager/UIModule.Open.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5374bef7b6c357f4ba05ae372ff262f6 +timeCreated: 1724224258 \ No newline at end of file diff --git a/Runtime/UI/Manager/UIModule.cs b/Runtime/UI/Manager/UIModule.cs new file mode 100644 index 0000000..5085d30 --- /dev/null +++ b/Runtime/UI/Manager/UIModule.cs @@ -0,0 +1,107 @@ +using System; +using AlicizaX.Resource.Runtime; +using AlicizaX; +using AlicizaX.Timer.Runtime; +using Cysharp.Threading.Tasks; +using UnityEngine; + +namespace AlicizaX.UI.Runtime +{ + internal sealed partial class UIModule : IUIModule + { + public int Priority { get; } + + private ITimerModule _timerModule; + + void IModule.Dispose() + { + } + + void IModuleUpdate.Update(float elapseSeconds, float realElapseSeconds) + { + // 遍历所有层级 + for (int layerIndex = 0; layerIndex < _openUI.Length; layerIndex++) + { + var layer = _openUI[layerIndex]; + int count = layer.OrderList.Count; + if (count == 0) continue; // 跳过空层级 + for (int i = 0; i < count; i++) + { + if (layer.OrderList.Count != count) + { + break; + } + + var window = layer.OrderList[i]; + window.View.InternalUpdate(); + } + } + } + + + public UniTask? ShowUI(string type, params object[] userDatas) + { + if (UIMetaRegistry.TryGet(type, out var metaRegistry)) + { + UIMetadata metadata = UIMetadataFactory.GetMetadata(metaRegistry.RuntimeTypeHandle); + return ShowUI(metadata, userDatas); + } + + return null; + } + + + public UniTask ShowUI(params System.Object[] userDatas) where T : UIBase + { + return ShowUI(MetaTypeCache.Metadata, userDatas); + } + + public async UniTask ShowUIAsync(params System.Object[] userDatas) where T : UIBase + { + return (T)await ShowUIAsync(MetaTypeCache.Metadata, userDatas); + } + + + public void CloseUI(bool force = false) where T : UIBase + { + CloseUIImpl(MetaTypeCache.Metadata, force).Forget(); + } + + public T GetUI() where T : UIBase + { + return (T)GetUI(MetaTypeCache.Metadata); + } + + + private UniTask ShowUI(UIMetadata meta, params System.Object[] userDatas) + { + return ShowUIImplAsync(meta, userDatas); + } + + private async UniTask ShowUIAsync(UIMetadata meta, params System.Object[] userDatas) + { + return await ShowUIImplAsync(meta, userDatas); + } + + + public void CloseUI(RuntimeTypeHandle handle, bool force = false) + { + var metadata = UIMetadataFactory.GetMetadata(handle); + if (metadata.State != UIState.Uninitialized && metadata.State != UIState.Destroying) + { + CloseUIImpl(metadata, force).Forget(); + } + } + + private UIBase GetUI(UIMetadata meta) + { + return (UIBase)GetUIImpl(meta); + } + + + void IUIModule.SetTimerManager(ITimerModule timerModule) + { + _timerModule = timerModule; + } + } +} diff --git a/Runtime/UI/Manager/UIModule.cs.meta b/Runtime/UI/Manager/UIModule.cs.meta new file mode 100644 index 0000000..d61571e --- /dev/null +++ b/Runtime/UI/Manager/UIModule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ae16df1037186a64b922d60f431440ba +timeCreated: 1724154184 \ No newline at end of file diff --git a/Runtime/UI/Other.meta b/Runtime/UI/Other.meta new file mode 100644 index 0000000..921a540 --- /dev/null +++ b/Runtime/UI/Other.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b963d3fe706c8a04cb734b28bac7f23a +timeCreated: 1724223096 \ No newline at end of file diff --git a/Runtime/UI/Other/UIBlock.cs b/Runtime/UI/Other/UIBlock.cs new file mode 100644 index 0000000..58e4005 --- /dev/null +++ b/Runtime/UI/Other/UIBlock.cs @@ -0,0 +1,62 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace AlicizaX.UI.Runtime +{ + /// + /// 不可见的一个图,用来阻挡UI的投射。 + /// + public class UIBlock : Graphic, ICanvasRaycastFilter + { + public override bool raycastTarget + { + get => true; + set { } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "StyleCop.CSharp.NamingRules", + "SA1300:ElementMustBeginWithUpperCaseLetter", + Justification = "Reviewed. Suppression is OK here.")] + public override Texture mainTexture + { + get { return null; } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage( + "StyleCop.CSharp.NamingRules", + "SA1300:ElementMustBeginWithUpperCaseLetter", + Justification = "Reviewed. Suppression is OK here.")] + public override Material materialForRendering + { + get { return null; } + } + + public bool IsRaycastLocationValid( + Vector2 screenPoint, Camera eventCamera) + { + return true; + } + + protected override void OnPopulateMesh(VertexHelper vh) + { + vh.Clear(); + } + + public override void SetAllDirty() + { + } + + public override void SetLayoutDirty() + { + } + + public override void SetVerticesDirty() + { + } + + public override void SetMaterialDirty() + { + } + } +} diff --git a/Runtime/UI/Other/UIBlock.cs.meta b/Runtime/UI/Other/UIBlock.cs.meta new file mode 100644 index 0000000..6c65c9f --- /dev/null +++ b/Runtime/UI/Other/UIBlock.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b95fd72d0c129af4fb87ef9802656ce1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/UI/UIBase.meta b/Runtime/UI/UIBase.meta new file mode 100644 index 0000000..b95fe5e --- /dev/null +++ b/Runtime/UI/UIBase.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1a64352099e341a0bedaf1a456d028a6 +timeCreated: 1732264981 \ No newline at end of file diff --git a/Runtime/UI/UIBase/UIBase.Widget.cs b/Runtime/UI/UIBase/UIBase.Widget.cs new file mode 100644 index 0000000..1455fa6 --- /dev/null +++ b/Runtime/UI/UIBase/UIBase.Widget.cs @@ -0,0 +1,124 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using AlicizaX; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.Pool; + +namespace AlicizaX.UI.Runtime +{ + public abstract partial class UIBase + { + private readonly Dictionary _children = new(); + + private void UpdateChildren() + { + var values = _children.Values; + foreach (var meta in values) + { + if (meta.View.State == UIState.Opened) + { + meta.View.InternalUpdate(); + } + } + } + + private async UniTask DestroyAllChildren() + { + var temp = ArrayPool.Shared.Rent(_children.Count); + try + { + int i = 0; + foreach (var kvp in _children) + { + temp[i++] = kvp.Value; + } + + for (int j = 0; j < i; j++) + { + if (temp[j].View.Visible) await temp[j].View.InternalClose(); + temp[j].Dispose(); + } + } + finally + { + ArrayPool.Shared.Return(temp); + } + + _children.Clear(); + } + + private void ChildVisible(bool value) + { + foreach (var meta in _children.Values) + { + var view = meta.View; + if (view.State == UIState.Opened) + { + view.Visible = value; + } + } + } + + internal async UniTask CreateWidget(UIMetadata metadata, Transform parent, bool visible) + { + metadata.CreateUI(); + await UIHolderFactory.CreateUIResource(metadata, parent, this); + await ProcessWidget(metadata, visible); + return (UIBase)metadata.View; + } + + protected async UniTask CreateWidget(string typeName, Transform parent, bool visible = true) + { + UIMetaRegistry.TryGet(typeName, out var metaRegistry); + UIMetadata metadata = UIMetadataFactory.GetMetadata(metaRegistry.RuntimeTypeHandle); + return await CreateWidget(metadata, parent, visible); + } + + protected async UniTask CreateWidget(Transform parent, bool visible = true) where T : UIBase + { + UIMetadata metadata = MetaTypeCache.Metadata; + return (T)await CreateWidget(metadata, parent, visible); + } + + protected async UniTask CreateWidget(UIHolderObjectBase holder) where T : UIBase + { + UIMetadata metadata = MetaTypeCache.Metadata; + metadata.CreateUI(); + UIBase widget = (UIBase)metadata.View; + widget.BindUIHolder(holder, this); + await ProcessWidget(metadata, true); + return (T)widget; + } + + private async UniTask ProcessWidget(UIMetadata meta, bool visible) + { + AddWidget(meta); + await meta.View.InternalInitlized(); + meta.View.Visible = visible; + if (meta.View.Visible) + { + await meta.View.InternalOpen(); + } + } + + private void AddWidget(UIMetadata meta) + { + if (!_children.TryAdd(meta.View, meta)) + { + Log.Warning("Already has widget:{0}", meta.View); + meta.Dispose(); + } + } + + public async UniTask RemoveWidget(UIBase widget) + { + if (_children.Remove(widget, out var meta)) + { + await widget.InternalClose(); + meta.Dispose(); + } + } + } +} diff --git a/Runtime/UI/UIBase/UIBase.Widget.cs.meta b/Runtime/UI/UIBase/UIBase.Widget.cs.meta new file mode 100644 index 0000000..875e3ce --- /dev/null +++ b/Runtime/UI/UIBase/UIBase.Widget.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5f25caf536134515b80acdc6709ad14d +timeCreated: 1739429626 \ No newline at end of file diff --git a/Runtime/UI/UIBase/UIBase.cs b/Runtime/UI/UIBase/UIBase.cs new file mode 100644 index 0000000..e3f4138 --- /dev/null +++ b/Runtime/UI/UIBase/UIBase.cs @@ -0,0 +1,260 @@ +using System; +using System.Collections.Generic; +using AlicizaX; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.UI; +using Object = UnityEngine.Object; + +namespace AlicizaX.UI.Runtime +{ + public abstract partial class UIBase : IDisposable + { + protected UIBase() + { + _state = UIState.CreatedUI; + } + + ~UIBase() => Dispose(false); + private bool _disposed; + + internal Canvas _canvas; + + internal GraphicRaycaster _raycaster; + + internal UIState _state = UIState.Uninitialized; + internal UIState State => _state; + + + private System.Object[] _userDatas; + protected System.Object UserData => _userDatas != null && _userDatas.Length >= 1 ? _userDatas[0] : null; + protected System.Object[] UserDatas => _userDatas; + + private RuntimeTypeHandle _runtimeTypeHandle; + + internal RuntimeTypeHandle RuntimeTypeHandler + { + get + { + if (_runtimeTypeHandle.Value == IntPtr.Zero) + { + _runtimeTypeHandle = GetType().TypeHandle; + } + + return _runtimeTypeHandle; + } + } + + protected virtual void OnInitialize() + { + } + + protected virtual void OnDestroy() + { + } + + protected virtual void OnOpen() + { + } + + protected virtual void OnClose() + { + } + + protected virtual void OnUpdate() + { + } + + /// + /// 如果重写当前方法 则同步OnInitialize不会调用 + /// + protected virtual async UniTask OnInitializeAsync() + { + await UniTask.CompletedTask; + OnInitialize(); + } + + /// + /// 如果重写当前方法 则同步OnOpen不会调用 + /// + protected virtual async UniTask OnOpenAsync() + { + await UniTask.CompletedTask; + OnOpen(); + } + + /// + /// 如果重写当前方法 则同步OnClose不会调用 + /// + protected virtual async UniTask OnCloseAsync() + { + await UniTask.CompletedTask; + OnClose(); + } + + /// + /// 事件在窗口销毁后会自动移除 + /// + /// + protected virtual void OnRegisterEvent(EventListenerProxy proxy) + { + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_disposed) return; + + if (disposing) + { + // 托管资源释放 + _canvas = null; + _raycaster = null; + } + + _userDatas = null; + + // 非托管资源释放 + if (Holder != null) + { + if (Application.isPlaying) + Object.Destroy(Holder.gameObject); + else + Object.DestroyImmediate(Holder.gameObject); + } + + _disposed = true; + } + + internal bool Visible + { + get => _canvas != null && _canvas.gameObject.layer == UIComponent.UIShowLayer; + + set + { + if (_canvas != null) + { + int setLayer = value ? UIComponent.UIShowLayer : UIComponent.UIHideLayer; + if (_canvas.gameObject.layer == setLayer) + return; + + _canvas.gameObject.layer = setLayer; + ChildVisible(value); + Interactable = value; + } + } + } + + private bool Interactable + { + get => _raycaster.enabled && _raycaster != null; + + set + { + if (_raycaster != null && _raycaster.enabled != value) + { + _raycaster.enabled = value; + } + } + } + + /// + /// 窗口深度值。 + /// + internal int Depth + { + get => _canvas != null ? _canvas.sortingOrder : 0; + + set + { + if (_canvas != null && _canvas.sortingOrder != value) + { + // 设置父类 + _canvas.sortingOrder = value; + } + } + } + + #region Event + + private EventListenerProxy _eventListenerProxy; + + private EventListenerProxy EventListenerProxy => _eventListenerProxy ??= MemoryPool.Acquire(); + + private void ReleaseEventListenerProxy() + { + if (!_eventListenerProxy.IsNull()) + { + MemoryPool.Release(_eventListenerProxy); + } + } + + #endregion + + #region 管理器内部调用 + + internal UIHolderObjectBase Holder; + internal abstract Type UIHolderType { get; } + + internal abstract void BindUIHolder(UIHolderObjectBase holder, UIBase owner); + + internal async UniTask InternalInitlized() + { + _state = UIState.Initialized; + await OnInitializeAsync(); + OnRegisterEvent(EventListenerProxy); + } + + internal async UniTask InternalOpen() + { + _state = UIState.Opened; + Visible = true; +#if ALICIZAX_UI_EXTENSION_SUPPORT + Holder.BindHotKeys(); +#endif + await OnOpenAsync(); + } + + internal async UniTask InternalClose() + { + if (_state == UIState.Opened) + { +#if ALICIZAX_UI_EXTENSION_SUPPORT + Holder.UnBindHotKeys(); +#endif + await OnCloseAsync(); + _state = UIState.Closed; + Visible = false; + } + } + + internal void InternalUpdate() + { + if (_state != UIState.Opened) return; + OnUpdate(); + UpdateChildren(); + } + + internal async UniTask InternalDestroy() + { + _state = UIState.Destroying; + await DestroyAllChildren(); + OnDestroy(); + ReleaseEventListenerProxy(); + Dispose(); + _state = UIState.Destroyed; + } + + internal void RefreshParams(params System.Object[] userDatas) + { + this._userDatas = userDatas; + } + + #endregion + } +} diff --git a/Runtime/UI/UIBase/UIBase.cs.meta b/Runtime/UI/UIBase/UIBase.cs.meta new file mode 100644 index 0000000..c2fc0c2 --- /dev/null +++ b/Runtime/UI/UIBase/UIBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 478527286cb74364a452b02eee2059e6 +timeCreated: 1739272697 \ No newline at end of file diff --git a/Runtime/UI/UIBase/UIHolderObjectBase.cs b/Runtime/UI/UIBase/UIHolderObjectBase.cs new file mode 100644 index 0000000..6de22bc --- /dev/null +++ b/Runtime/UI/UIBase/UIHolderObjectBase.cs @@ -0,0 +1,117 @@ +using Sirenix.OdinInspector; +using System; +using System.Collections.Generic; +using Cysharp.Threading.Tasks; +using Sirenix.Utilities; +using UnityEngine; +using UnityEngine.UI; + +namespace AlicizaX.UI.Runtime +{ + [DisallowMultipleComponent] + [HideMonoScript] + public abstract class UIHolderObjectBase : UnityEngine.MonoBehaviour + { +#if UNITY_EDITOR && ALICIZAX_UI_ANIMATION_SUPPORT + private void SetAnimtionFlow() + { + // 编辑器模式下自动重置状态 + AnimationFlow = GetComponent(); + } +#endif + +#if UNITY_EDITOR && ALICIZAX_UI_EXTENSION_SUPPORT + [InlineButton("SetHotKeyButtons")] + private void SetHotKeyButtons() + { + hotButtons = transform.GetComponentsInChildren(true); + } +#endif + +#if ALICIZAX_UI_EXTENSION_SUPPORT + [SerializeField] [HideLabel] [ReadOnly] + internal UXButton[] hotButtons; + + internal void BindHotKeys() + { + for (int i = 0; i < hotButtons.Length; i++) + { + hotButtons[i].BindHotKey(); + } + } + + internal void UnBindHotKeys() + { + for (int i = 0; i < hotButtons.Length; i++) + { + hotButtons[i].UnBindHotKey(); + } + } + +#endif + +#if ALICIZAX_UI_ANIMATION_SUPPORT + public async UniTask PlayAnimtion(string name) + { + await AnimationFlow.PlayAsync(name); + } +#endif + + + private GameObject _target; + + /// + /// UI实例资源对象。 + /// + public GameObject Target => _target ??= gameObject; + + + private RectTransform _rectTransform; + + /// + /// 窗口矩阵位置组件。 + /// + public RectTransform RectTransform => _rectTransform ??= _target.transform as RectTransform; + + /// + /// 可见性 + /// + public bool Visible + { + get => Target.activeSelf; + + internal set { _target.SetActive(value); } + } + +#if UNITY_EDITOR && ALICIZAX_UI_ANIMATION_SUPPORT + [InlineButton("SetAnimtionFlow")] +#endif + +#if ALICIZAX_UI_ANIMATION_SUPPORT + [SerializeField] + [HideLabel] + private AnimationFlow.Runtime.AnimationFlow AnimationFlow; +#endif + + + private void Awake() + { + _target = gameObject; + } + + private bool IsAlive = true; + + public static implicit operator bool(UIHolderObjectBase exists) + { + // 先检查Unity对象是否被销毁 + if (exists == null) return false; + // 再返回自定义的生命状态 + return exists.IsAlive; + } + + private void OnDestroy() + { + IsAlive = false; + } + } +} diff --git a/Runtime/UI/UIBase/UIHolderObjectBase.cs.meta b/Runtime/UI/UIBase/UIHolderObjectBase.cs.meta new file mode 100644 index 0000000..9179ecb --- /dev/null +++ b/Runtime/UI/UIBase/UIHolderObjectBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f8df68ea1a364571a8c7b9ae2e2cb29b +timeCreated: 1732171853 \ No newline at end of file diff --git a/Runtime/UI/UIBase/UIState.cs b/Runtime/UI/UIBase/UIState.cs new file mode 100644 index 0000000..da5b703 --- /dev/null +++ b/Runtime/UI/UIBase/UIState.cs @@ -0,0 +1,14 @@ +namespace AlicizaX.UI.Runtime +{ + public enum UIState:byte + { + Uninitialized, + CreatedUI, + Loaded, + Initialized, + Opened, + Closed, + Destroying, + Destroyed, + } +} diff --git a/Runtime/UI/UIBase/UIState.cs.meta b/Runtime/UI/UIBase/UIState.cs.meta new file mode 100644 index 0000000..bbf0891 --- /dev/null +++ b/Runtime/UI/UIBase/UIState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cadb58b2c31543a0aa9db1e995ab6dfe +timeCreated: 1739450978 \ No newline at end of file diff --git a/Runtime/UI/UIBase/UITabWindow.cs b/Runtime/UI/UIBase/UITabWindow.cs new file mode 100644 index 0000000..abbe286 --- /dev/null +++ b/Runtime/UI/UIBase/UITabWindow.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using AlicizaX; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.UI; + +namespace AlicizaX.UI.Runtime +{ + public abstract class UITabWindow : UIBase where T : UIHolderObjectBase + { + // 当前激活的Tab页 + private UIWidget _activeTab; + + // 类型顺序索引(根据初始化顺序) + private readonly List _typeOrder = new(); + + // 页面缓存字典(类型 - 父节点) + private readonly Dictionary _tabCache = new(); + + // 已加载的Tab实例缓存 + private readonly Dictionary _loadedTabs = new(); + + // 加载状态字典 + private readonly Dictionary _loadingFlags = new(); + + protected T baseui => (T)Holder; + + internal sealed override Type UIHolderType => typeof(T); + + protected void CloseSelf(bool forceClose = false) + { + ModuleSystem.GetModule().CloseUI(RuntimeTypeHandler, forceClose); + } + + internal sealed override void BindUIHolder(UIHolderObjectBase holder, UIBase owner) + { + if (_state != UIState.CreatedUI) + throw new InvalidOperationException("UI already Created"); + + Holder = holder; + _canvas = Holder.transform.GetComponent(); + _canvas.overrideSorting = true; + _raycaster = Holder.transform.GetComponent(); + Holder.RectTransform.localPosition = Vector3.zero; + Holder.RectTransform.pivot = new Vector2(0.5f, 0.5f); + Holder.RectTransform.anchorMin = Vector2.zero; + Holder.RectTransform.anchorMax = Vector2.one; + Holder.RectTransform.offsetMin = Vector2.zero; + Holder.RectTransform.offsetMax = Vector2.zero; + Holder.RectTransform.localScale = Vector3.one; + _state = UIState.Loaded; + } + + // 初始化方法(泛型版本) + protected void InitTabVirtuallyView(Transform parent = null) where TTab : UIWidget + { + var metadata = MetaTypeCache.Metadata; + CacheTabMetadata(metadata, parent); + } + + // 初始化方法(类型名版本) + protected void InitTabVirtuallyView(string typeName, Transform parent = null) + { + if (UIMetaRegistry.TryGet(typeName, out var metaRegistry)) + { + var metadata = UIMetadataFactory.GetMetadata(metaRegistry.RuntimeTypeHandle); + CacheTabMetadata(metadata, parent); + } + } + + private void CacheTabMetadata(UIMetadata metadata, Transform parent) + { + var typeHandle = metadata.MetaInfo.RuntimeTypeHandle; + + if (!_tabCache.ContainsKey(typeHandle)) + { + _typeOrder.Add(typeHandle); + _tabCache[typeHandle] = parent ?? baseui.RectTransform; + } + } + + public void SwitchTab(int index, params System.Object[] userDatas) + { + if (!ValidateIndex(index)) return; + + var typeHandle = _typeOrder[index]; + if (_loadingFlags.TryGetValue(typeHandle, out var isLoading) && isLoading) return; + + if (_loadedTabs.TryGetValue(typeHandle, out var loadedTab)) + { + SwitchToLoadedTab(loadedTab, userDatas); + return; + } + + StartAsyncLoading(typeHandle, userDatas).Forget(); + } + + private async UniTaskVoid StartAsyncLoading(RuntimeTypeHandle typeHandle, params System.Object[] userDatas) + { + _loadingFlags[typeHandle] = true; + + try + { + var metadata = UIMetadataFactory.GetMetadata(typeHandle); + var parent = _tabCache[typeHandle]; + + var widget = await CreateWidget(metadata, parent, false); + if (widget is not UIWidget tabWidget) return; + + _loadedTabs[typeHandle] = tabWidget; + SwitchToLoadedTab(tabWidget, userDatas); + } + catch (Exception e) + { + Debug.LogError($"Tab load failed: {e}"); + } + finally + { + _loadingFlags.Remove(typeHandle); + } + } + + private void SwitchToLoadedTab(UIWidget targetTab, params System.Object[] userDatas) + { + if (_activeTab == targetTab) return; + + _activeTab?.Close(); + _activeTab = targetTab; + targetTab.Open(userDatas); + } + + private bool ValidateIndex(int index) + { + if (index >= 0 && index < _typeOrder.Count) return true; + + Debug.LogError($"Invalid tab index: {index}"); + return false; + } + + } +} diff --git a/Runtime/UI/UIBase/UITabWindow.cs.meta b/Runtime/UI/UIBase/UITabWindow.cs.meta new file mode 100644 index 0000000..48cab35 --- /dev/null +++ b/Runtime/UI/UIBase/UITabWindow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8e10ab6cf87043638060ea5f933f6d80 +timeCreated: 1744200054 \ No newline at end of file diff --git a/Runtime/UI/UIBase/UIWidget.cs b/Runtime/UI/UIBase/UIWidget.cs new file mode 100644 index 0000000..0c55cf5 --- /dev/null +++ b/Runtime/UI/UIBase/UIWidget.cs @@ -0,0 +1,48 @@ +using System; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.UI; + +namespace AlicizaX.UI.Runtime +{ + public abstract class UIWidget : UIBase + { + internal UIBase _parent; + internal UIBase Parent => _parent; + + public void Open(params System.Object[] userDatas) + { + RefreshParams(userDatas); + InternalOpen().Forget(); + } + + public void Close() + { + InternalClose().Forget(); + } + + public void Destroy() + { + Parent.RemoveWidget(this).Forget(); + } + } + + public abstract class UIWidget : UIWidget where T : UIHolderObjectBase + { + protected T baseui => (T)Holder; + internal sealed override Type UIHolderType => typeof(T); + + internal sealed override void BindUIHolder(UIHolderObjectBase holder, UIBase owner) + { + if (_state != UIState.CreatedUI) + throw new InvalidOperationException("UI already Created"); + + Holder = holder; + _parent = owner; + _canvas = Holder.transform.GetComponent(); + _raycaster = Holder.transform.GetComponent(); + Depth = owner.Depth + 5; + _state = UIState.Loaded; + } + } +} diff --git a/Runtime/UI/UIBase/UIWidget.cs.meta b/Runtime/UI/UIBase/UIWidget.cs.meta new file mode 100644 index 0000000..40d5da1 --- /dev/null +++ b/Runtime/UI/UIBase/UIWidget.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ed07f96c90414e28bb7ed820e88157ca +timeCreated: 1732240802 \ No newline at end of file diff --git a/Runtime/UI/UIBase/UIWindow.cs b/Runtime/UI/UIBase/UIWindow.cs new file mode 100644 index 0000000..b6ebe14 --- /dev/null +++ b/Runtime/UI/UIBase/UIWindow.cs @@ -0,0 +1,46 @@ +using System; +using System.Reflection; +using AlicizaX; +using UnityEngine; +using UnityEngine.UI; + +namespace AlicizaX.UI.Runtime +{ + public abstract class UIWindow : UIBase where T : UIHolderObjectBase + { + protected T baseui => (T)Holder; + + internal sealed override Type UIHolderType => typeof(T); + + /// + /// 关闭自身 如果存在缓存 则会强制从缓存中移除 + /// + protected void ForceCloseSlef() + { + ModuleSystem.GetModule().CloseUI(RuntimeTypeHandler, true); + } + + protected void CloseSelf() + { + ModuleSystem.GetModule().CloseUI(RuntimeTypeHandler, false); + } + + internal sealed override void BindUIHolder(UIHolderObjectBase holder, UIBase owner) + { + if (_state != UIState.CreatedUI) + throw new InvalidOperationException("UI already Created"); + Holder = holder; + _canvas = Holder.transform.GetComponent(); + _canvas.overrideSorting = true; + _raycaster = Holder.transform.GetComponent(); + Holder.RectTransform.localPosition = Vector3.zero; + Holder.RectTransform.pivot = new Vector2(0.5f, 0.5f); + Holder.RectTransform.anchorMin = Vector2.zero; + Holder.RectTransform.anchorMax = Vector2.one; + Holder.RectTransform.offsetMin = Vector2.zero; + Holder.RectTransform.offsetMax = Vector2.zero; + Holder.RectTransform.localScale = Vector3.one; + _state = UIState.Loaded; + } + } +} diff --git a/Runtime/UI/UIBase/UIWindow.cs.meta b/Runtime/UI/UIBase/UIWindow.cs.meta new file mode 100644 index 0000000..983e29d --- /dev/null +++ b/Runtime/UI/UIBase/UIWindow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 43a865d7e0cd461aa06c458815dce483 +timeCreated: 1732172584 \ No newline at end of file diff --git a/Runtime/UI/UIComponent.cs b/Runtime/UI/UIComponent.cs new file mode 100644 index 0000000..b588eef --- /dev/null +++ b/Runtime/UI/UIComponent.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using AlicizaX.Resource.Runtime; +using AlicizaX; +using AlicizaX.Timer.Runtime; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.UI; +using Object = UnityEngine.Object; + +namespace AlicizaX.UI.Runtime +{ + [DisallowMultipleComponent] + [AddComponentMenu("Game Framework/UI")] + [UnityEngine.Scripting.Preserve] + public sealed partial class UIComponent : MonoBehaviour + { + [SerializeField] private GameObject uiRoot = null; + [SerializeField] private bool _isOrthographic = true; + private Transform _instanceRoot = null; + + private IUIModule _uiModule; + + public const int UIHideLayer = 2; // Ignore Raycast + public const int UIShowLayer = 5; // UI + + + private void Awake() + { + _uiModule = ModuleSystem.RegisterModule(); + if (uiRoot == null) + { + throw new GameFrameworkException("UIRoot Prefab is invalid."); + } + GameObject obj = Instantiate(uiRoot, Vector3.zero, Quaternion.identity); + obj.name = "------UI Root------"; + _instanceRoot = obj.transform; + Object.DontDestroyOnLoad(_instanceRoot); + _uiModule.Initlize(_instanceRoot,_isOrthographic); + } + + private void Start() + { + _uiModule.SetTimerManager(ModuleSystem.GetModule()); + } + + + #region 设置安全区域 + + /// + /// 设置屏幕安全区域(异形屏支持)。 + /// + /// 安全区域 + public void ApplyScreenSafeRect(Rect safeRect) + { + CanvasScaler scaler = _uiModule.UICanvasRoot.GetComponent(); + if (scaler == null) + { + Log.Error($"Not found {nameof(CanvasScaler)} !"); + return; + } + + // Convert safe area rectangle from absolute pixels to UGUI coordinates + float rateX = scaler.referenceResolution.x / Screen.width; + float rateY = scaler.referenceResolution.y / Screen.height; + float posX = (int)(safeRect.position.x * rateX); + float posY = (int)(safeRect.position.y * rateY); + float width = (int)(safeRect.size.x * rateX); + float height = (int)(safeRect.size.y * rateY); + + float offsetMaxX = scaler.referenceResolution.x - width - posX; + float offsetMaxY = scaler.referenceResolution.y - height - posY; + + // 注意:安全区坐标系的原点为左下角 + var rectTrans = _uiModule.UICanvasRoot.transform as RectTransform; + if (rectTrans != null) + { + rectTrans.offsetMin = new Vector2(posX, posY); //锚框状态下的屏幕左下角偏移向量 + rectTrans.offsetMax = new Vector2(-offsetMaxX, -offsetMaxY); //锚框状态下的屏幕右上角偏移向量 + } + } + + /// + /// 模拟IPhoneX异形屏 + /// + public void SimulateIPhoneXNotchScreen() + { + Rect rect; + if (Screen.height > Screen.width) + { + // 竖屏Portrait + float deviceWidth = 1125; + float deviceHeight = 2436; + rect = new Rect(0f / deviceWidth, 102f / deviceHeight, 1125f / deviceWidth, 2202f / deviceHeight); + } + else + { + // 横屏Landscape + float deviceWidth = 2436; + float deviceHeight = 1125; + rect = new Rect(132f / deviceWidth, 63f / deviceHeight, 2172f / deviceWidth, 1062f / deviceHeight); + } + + Rect safeArea = new Rect(Screen.width * rect.x, Screen.height * rect.y, Screen.width * rect.width, Screen.height * rect.height); + ApplyScreenSafeRect(safeArea); + } + + #endregion + } +} diff --git a/Runtime/UI/UIComponent.cs.meta b/Runtime/UI/UIComponent.cs.meta new file mode 100644 index 0000000..86f4dff --- /dev/null +++ b/Runtime/UI/UIComponent.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 028204b1d2277bd4782816ee91aeed81 +timeCreated: 1724156799 \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..39b1150 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "com.alicizax.unity.framework", + "displayName": "Aliciza X Framework", + "category": "Aliciza X", + "description": "Aliciza X Core Framework", + "version": "1.0.1", + "unity": "2025.1", + "keywords": [ + "Aliciza X" + ], + "repository": { + "name": "com.alicizax.unity.framework", + "url": "http://101.34.252.46:3000/AlicizaX/", + "type": "git" + }, + "author": { + "name": "Yuliuren", + "email": "yuliuren00@gmail.com" + }, + "dependencies": { + } +} \ No newline at end of file diff --git a/package.json.meta b/package.json.meta new file mode 100644 index 0000000..fe7bf6f --- /dev/null +++ b/package.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: f580e5a3a2f47b441a1529dd4e9cd17b +PackageManifestImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: