From fbc1cc8d3e84053a3ea8921f277b1543d3f75992 Mon Sep 17 00:00:00 2001 From: ThomasCharester Date: Thu, 26 Feb 2026 11:21:27 +0300 Subject: [PATCH] Init --- .gitattributes | 43 + .gitignore | 185 +++ Assets/Code.meta | 3 + Assets/Code/Infrastructure.meta | 3 + Assets/Code/Infrastructure/App.meta | 3 + .../Infrastructure/App/AppStateMachine.cs | 35 + .../App/AppStateMachine.cs.meta | 3 + .../Code/Infrastructure/App/PapaInstaller.cs | 213 ++++ .../Infrastructure/App/PapaInstaller.cs.meta | 3 + Assets/Code/Infrastructure/App/States.meta | 3 + .../App/States/BootstrapState.cs | 48 + .../App/States/BootstrapState.cs.meta | 3 + Assets/Code/Infrastructure/Audio.meta | 3 + .../Code/Infrastructure/Audio/AudioFactory.cs | 106 ++ .../Infrastructure/Audio/AudioFactory.cs.meta | 3 + .../Infrastructure/Audio/AudioGroupType.cs | 11 + .../Audio/AudioGroupType.cs.meta | 3 + Assets/Code/Infrastructure/Audio/AudioInfo.cs | 19 + .../Infrastructure/Audio/AudioInfo.cs.meta | 3 + .../Code/Infrastructure/Audio/AudioNames.cs | 8 + .../Infrastructure/Audio/AudioNames.cs.meta | 3 + .../Code/Infrastructure/Audio/AudioService.cs | 133 +++ .../Infrastructure/Audio/AudioService.cs.meta | 3 + .../Infrastructure/Audio/AudioSourcePool.cs | 129 ++ .../Audio/AudioSourcePool.cs.meta | 3 + .../Audio/AudioSourceSettings.cs | 14 + .../Audio/AudioSourceSettings.cs.meta | 3 + .../Infrastructure/Audio/AudioSourceType.cs | 8 + .../Audio/AudioSourceType.cs.meta | 3 + Assets/Code/Infrastructure/Configs.meta | 3 + .../Infrastructure/Configs/ConfigLocator.cs | 35 + .../Configs/ConfigLocator.cs.meta | 3 + .../Infrastructure/Configs/LayerMaskType.cs | 7 + .../Configs/LayerMaskType.cs.meta | 3 + .../Configs/LayerMasksConfig.cs | 53 + .../Configs/LayerMasksConfig.cs.meta | 3 + .../Configs/SystemConfigsReferences.cs | 11 + .../Configs/SystemConfigsReferences.cs.meta | 3 + Assets/Code/Infrastructure/EventBus.meta | 8 + .../Code/Infrastructure/EventBus/EventBus.cs | 68 ++ .../Infrastructure/EventBus/EventBus.cs.meta | 2 + .../Infrastructure/EventBus/EventBusHelper.cs | 31 + .../EventBus/EventBusHelper.cs.meta | 2 + .../EventBus/IGlobalSubscriber.cs | 5 + .../EventBus/IGlobalSubscriber.cs.meta | 2 + .../EventBus/SubscribersList.cs | 46 + .../EventBus/SubscribersList.cs.meta | 2 + Assets/Code/Infrastructure/Generators.meta | 3 + .../Infrastructure/Generators/Attributes.cs | 52 + .../Generators/Attributes.cs.meta | 3 + .../Generators/AudioMixerGenerator.cs | 73 ++ .../Generators/AudioMixerGenerator.cs.meta | 3 + .../Generators/ConfigGenerator.cs | 111 ++ .../Generators/ConfigGenerator.cs.meta | 3 + .../Generators/SceneLoaderGenerator.cs | 149 +++ .../Generators/SceneLoaderGenerator.cs.meta | 3 + .../Generators/StateMachineGenerator.cs | 133 +++ .../Generators/StateMachineGenerator.cs.meta | 3 + Assets/Code/Infrastructure/Input.meta | 3 + .../Infrastructure/Input/ActionMappings.cs | 24 + .../Input/ActionMappings.cs.meta | 3 + .../Infrastructure/Input/IInputService.cs | 15 + .../Input/IInputService.cs.meta | 3 + .../Code/Infrastructure/Input/InputService.cs | 62 + .../Infrastructure/Input/InputService.cs.meta | 3 + Assets/Code/Infrastructure/Loadings.meta | 3 + .../Code/Infrastructure/Loadings/Loading.cs | 36 + .../Infrastructure/Loadings/Loading.cs.meta | 3 + Assets/Code/Infrastructure/SceneLoader.meta | 3 + .../Infrastructure/SceneLoader/SceneId.cs | 13 + .../SceneLoader/SceneId.cs.meta | 3 + .../Infrastructure/SceneLoader/SceneLoader.cs | 97 ++ .../SceneLoader/SceneLoader.cs.meta | 3 + .../SceneLoader/ScenesAssetReferences.cs | 11 + .../SceneLoader/ScenesAssetReferences.cs.meta | 3 + Assets/Code/Infrastructure/StateMachine.meta | 3 + .../StateMachine/AbstractStateMachine.cs | 62 + .../StateMachine/AbstractStateMachine.cs.meta | 3 + .../Infrastructure/StateMachine/BaseState.cs | 37 + .../StateMachine/BaseState.cs.meta | 3 + Assets/Code/Infrastructure/UI.meta | 3 + Assets/Code/Infrastructure/UI/UIFactory.cs | 94 ++ .../Code/Infrastructure/UI/UIFactory.cs.meta | 3 + Assets/Code/Infrastructure/UI/UIPanel.cs | 13 + Assets/Code/Infrastructure/UI/UIPanel.cs.meta | 3 + .../Code/Infrastructure/UI/UIPanelSettings.cs | 18 + .../Infrastructure/UI/UIPanelSettings.cs.meta | 3 + Assets/Code/Infrastructure/UI/UIPanelType.cs | 15 + .../Infrastructure/UI/UIPanelType.cs.meta | 3 + Assets/Code/Infrastructure/UI/UIService.cs | 88 ++ .../Code/Infrastructure/UI/UIService.cs.meta | 3 + Assets/DefaultVolumeProfile.asset | 798 +++++++++++++ Assets/DefaultVolumeProfile.asset.meta | 8 + Assets/InputSystem_Actions.inputactions | 1057 +++++++++++++++++ Assets/InputSystem_Actions.inputactions.meta | 14 + Assets/Plugins.meta | 8 + Assets/Plugins/PrimeTween.meta | 8 + .../PrimeTween/PrimeTweenInstaller.asset | 19 + .../PrimeTween/PrimeTweenInstaller.asset.meta | 15 + Assets/Plugins/PrimeTween/internal.meta | 8 + .../internal/PrimeTween.Installer.asmdef | 29 + .../internal/PrimeTween.Installer.asmdef.meta | 14 + .../internal/PrimeTweenInstaller.cs | 548 +++++++++ .../internal/PrimeTweenInstaller.cs.meta | 18 + .../internal/com.kyrylokuzyk.primetween.tgz | Bin 0 -> 384502 bytes .../com.kyrylokuzyk.primetween.tgz.meta | 7 + Assets/Resources.meta | 8 + Assets/Resources/ProjectScope.prefab | 59 + Assets/Resources/ProjectScope.prefab.meta | 7 + Assets/Resources/ReflexSettings.asset | 17 + Assets/Resources/ReflexSettings.asset.meta | 8 + .../Resources/SystemConfigsReferences.asset | 52 + .../SystemConfigsReferences.asset.meta | 8 + Assets/Resources_moved.meta | 8 + Assets/Resources_moved/Configs.meta | 8 + Assets/Resources_moved/Configs/Audio.meta | 8 + .../Configs/Audio/Audio Source.prefab | 131 ++ .../Configs/Audio/Audio Source.prefab.meta | 7 + .../Audio/AudioSourcePrefabConfig.asset | 22 + .../Audio/AudioSourcePrefabConfig.asset.meta | 8 + .../Configs/Audio/Settings.meta | 8 + .../Configs/Audio/Settings/Full3D.asset | 16 + .../Configs/Audio/Settings/Full3D.asset.meta | 8 + .../Configs/Audio/Settings/NoBlend.asset | 16 + .../Configs/Audio/Settings/NoBlend.asset.meta | 8 + .../Configs/InputServiceConfig.asset | 15 + .../Configs/InputServiceConfig.asset.meta | 8 + Assets/Resources_moved/Configs/UI.meta | 8 + .../Configs/UI/CanvasSettingsConfig.asset | 20 + .../UI/CanvasSettingsConfig.asset.meta | 8 + Assets/Resources_moved/UI.meta | 8 + Assets/Resources_moved/UI/Canvas.prefab | 183 +++ Assets/Resources_moved/UI/Canvas.prefab.meta | 7 + Assets/Scenes.meta | 8 + Assets/Scenes/SampleScene.unity | 352 ++++++ Assets/Scenes/SampleScene.unity.meta | 7 + Assets/Settings.meta | 8 + .../Settings/Lit2DSceneTemplate.scenetemplate | 71 ++ .../Lit2DSceneTemplate.scenetemplate.meta | 8 + Assets/Settings/Renderer2D.asset | 69 ++ Assets/Settings/Renderer2D.asset.meta | 8 + Assets/Settings/Scenes.meta | 8 + .../Settings/Scenes/URP2DSceneTemplate.unity | 350 ++++++ .../Scenes/URP2DSceneTemplate.unity.meta | 7 + Assets/Settings/UniversalRP.asset | 143 +++ Assets/Settings/UniversalRP.asset.meta | 8 + Assets/UnityCodeGen.Generated.meta | 8 + .../AudioInfoConfig.Generated.cs | 20 + .../AudioInfoConfig.Generated.cs.meta | 2 + .../AudioMixer.Generated.cs | 17 + .../AudioMixer.Generated.cs.meta | 2 + .../AudioSourcePrefabConfig.Generated.cs | 16 + .../AudioSourcePrefabConfig.Generated.cs.meta | 2 + .../AudioSourceSettingsConfig.Generated.cs | 18 + ...udioSourceSettingsConfig.Generated.cs.meta | 2 + .../CanvasSettingsConfig.Generated.cs | 16 + .../CanvasSettingsConfig.Generated.cs.meta | 2 + ...structure.App.AppStateMachine.Generated.cs | 54 + ...ture.App.AppStateMachine.Generated.cs.meta | 2 + ...ure.App.States.BootstrapState.Generated.cs | 23 + ...pp.States.BootstrapState.Generated.cs.meta | 2 + .../ConfigLocator.Generated.cs | 59 + .../ConfigLocator.Generated.cs.meta | 2 + .../UnityCodeGen.Generated/IConfigLocator.cs | 10 + .../IConfigLocator.cs.meta | 3 + .../InputServiceConfig.Generated.cs | 16 + .../InputServiceConfig.Generated.cs.meta | 2 + .../LayerMasksConfigConfig.Generated.cs | 18 + .../LayerMasksConfigConfig.Generated.cs.meta | 2 + .../SceneInfoConfig.Generated.cs | 18 + .../SceneInfoConfig.Generated.cs.meta | 2 + .../SceneLoader.Generated.cs | 52 + .../SceneLoader.Generated.cs.meta | 2 + .../SystemConfigsReferences.Generated.cs | 38 + .../SystemConfigsReferences.Generated.cs.meta | 2 + .../UIPanelSettingsConfig.Generated.cs | 18 + .../UIPanelSettingsConfig.Generated.cs.meta | 2 + ...niversalRenderPipelineGlobalSettings.asset | 394 ++++++ ...salRenderPipelineGlobalSettings.asset.meta | 8 + Packages/manifest.json | 57 + Packages/packages-lock.json | 591 +++++++++ ProjectSettings/AudioManager.asset | 19 + ProjectSettings/ClusterInputManager.asset | 6 + ProjectSettings/DynamicsManager.asset | 36 + ProjectSettings/EditorBuildSettings.asset | 13 + ProjectSettings/EditorSettings.asset | 50 + ProjectSettings/GraphicsSettings.asset | 69 ++ ProjectSettings/InputManager.asset | 487 ++++++++ ProjectSettings/MemorySettings.asset | 35 + ProjectSettings/MultiplayerManager.asset | 7 + ProjectSettings/NavMeshAreas.asset | 91 ++ ProjectSettings/NetworkManager.asset | 8 + ProjectSettings/PackageManagerSettings.asset | 40 + ProjectSettings/Physics2DSettings.asset | 56 + ProjectSettings/PresetManager.asset | 7 + ProjectSettings/ProjectSettings.asset | 700 +++++++++++ ProjectSettings/ProjectVersion.txt | 2 + ProjectSettings/QualitySettings.asset | 336 ++++++ ProjectSettings/ShaderGraphSettings.asset | 19 + ProjectSettings/TagManager.asset | 43 + ProjectSettings/TimeManager.asset | 9 + ProjectSettings/URPProjectSettings.asset | 16 + ProjectSettings/UnityConnectSettings.asset | 40 + ProjectSettings/VFXManager.asset | 12 + ProjectSettings/VersionControlSettings.asset | 7 + ProjectSettings/XRSettings.asset | 10 + README.md | 2 - 207 files changed, 10327 insertions(+), 2 deletions(-) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Assets/Code.meta create mode 100644 Assets/Code/Infrastructure.meta create mode 100644 Assets/Code/Infrastructure/App.meta create mode 100644 Assets/Code/Infrastructure/App/AppStateMachine.cs create mode 100644 Assets/Code/Infrastructure/App/AppStateMachine.cs.meta create mode 100644 Assets/Code/Infrastructure/App/PapaInstaller.cs create mode 100644 Assets/Code/Infrastructure/App/PapaInstaller.cs.meta create mode 100644 Assets/Code/Infrastructure/App/States.meta create mode 100644 Assets/Code/Infrastructure/App/States/BootstrapState.cs create mode 100644 Assets/Code/Infrastructure/App/States/BootstrapState.cs.meta create mode 100644 Assets/Code/Infrastructure/Audio.meta create mode 100644 Assets/Code/Infrastructure/Audio/AudioFactory.cs create mode 100644 Assets/Code/Infrastructure/Audio/AudioFactory.cs.meta create mode 100644 Assets/Code/Infrastructure/Audio/AudioGroupType.cs create mode 100644 Assets/Code/Infrastructure/Audio/AudioGroupType.cs.meta create mode 100644 Assets/Code/Infrastructure/Audio/AudioInfo.cs create mode 100644 Assets/Code/Infrastructure/Audio/AudioInfo.cs.meta create mode 100644 Assets/Code/Infrastructure/Audio/AudioNames.cs create mode 100644 Assets/Code/Infrastructure/Audio/AudioNames.cs.meta create mode 100644 Assets/Code/Infrastructure/Audio/AudioService.cs create mode 100644 Assets/Code/Infrastructure/Audio/AudioService.cs.meta create mode 100644 Assets/Code/Infrastructure/Audio/AudioSourcePool.cs create mode 100644 Assets/Code/Infrastructure/Audio/AudioSourcePool.cs.meta create mode 100644 Assets/Code/Infrastructure/Audio/AudioSourceSettings.cs create mode 100644 Assets/Code/Infrastructure/Audio/AudioSourceSettings.cs.meta create mode 100644 Assets/Code/Infrastructure/Audio/AudioSourceType.cs create mode 100644 Assets/Code/Infrastructure/Audio/AudioSourceType.cs.meta create mode 100644 Assets/Code/Infrastructure/Configs.meta create mode 100644 Assets/Code/Infrastructure/Configs/ConfigLocator.cs create mode 100644 Assets/Code/Infrastructure/Configs/ConfigLocator.cs.meta create mode 100644 Assets/Code/Infrastructure/Configs/LayerMaskType.cs create mode 100644 Assets/Code/Infrastructure/Configs/LayerMaskType.cs.meta create mode 100644 Assets/Code/Infrastructure/Configs/LayerMasksConfig.cs create mode 100644 Assets/Code/Infrastructure/Configs/LayerMasksConfig.cs.meta create mode 100644 Assets/Code/Infrastructure/Configs/SystemConfigsReferences.cs create mode 100644 Assets/Code/Infrastructure/Configs/SystemConfigsReferences.cs.meta create mode 100644 Assets/Code/Infrastructure/EventBus.meta create mode 100644 Assets/Code/Infrastructure/EventBus/EventBus.cs create mode 100644 Assets/Code/Infrastructure/EventBus/EventBus.cs.meta create mode 100644 Assets/Code/Infrastructure/EventBus/EventBusHelper.cs create mode 100644 Assets/Code/Infrastructure/EventBus/EventBusHelper.cs.meta create mode 100644 Assets/Code/Infrastructure/EventBus/IGlobalSubscriber.cs create mode 100644 Assets/Code/Infrastructure/EventBus/IGlobalSubscriber.cs.meta create mode 100644 Assets/Code/Infrastructure/EventBus/SubscribersList.cs create mode 100644 Assets/Code/Infrastructure/EventBus/SubscribersList.cs.meta create mode 100644 Assets/Code/Infrastructure/Generators.meta create mode 100644 Assets/Code/Infrastructure/Generators/Attributes.cs create mode 100644 Assets/Code/Infrastructure/Generators/Attributes.cs.meta create mode 100644 Assets/Code/Infrastructure/Generators/AudioMixerGenerator.cs create mode 100644 Assets/Code/Infrastructure/Generators/AudioMixerGenerator.cs.meta create mode 100644 Assets/Code/Infrastructure/Generators/ConfigGenerator.cs create mode 100644 Assets/Code/Infrastructure/Generators/ConfigGenerator.cs.meta create mode 100644 Assets/Code/Infrastructure/Generators/SceneLoaderGenerator.cs create mode 100644 Assets/Code/Infrastructure/Generators/SceneLoaderGenerator.cs.meta create mode 100644 Assets/Code/Infrastructure/Generators/StateMachineGenerator.cs create mode 100644 Assets/Code/Infrastructure/Generators/StateMachineGenerator.cs.meta create mode 100644 Assets/Code/Infrastructure/Input.meta create mode 100644 Assets/Code/Infrastructure/Input/ActionMappings.cs create mode 100644 Assets/Code/Infrastructure/Input/ActionMappings.cs.meta create mode 100644 Assets/Code/Infrastructure/Input/IInputService.cs create mode 100644 Assets/Code/Infrastructure/Input/IInputService.cs.meta create mode 100644 Assets/Code/Infrastructure/Input/InputService.cs create mode 100644 Assets/Code/Infrastructure/Input/InputService.cs.meta create mode 100644 Assets/Code/Infrastructure/Loadings.meta create mode 100644 Assets/Code/Infrastructure/Loadings/Loading.cs create mode 100644 Assets/Code/Infrastructure/Loadings/Loading.cs.meta create mode 100644 Assets/Code/Infrastructure/SceneLoader.meta create mode 100644 Assets/Code/Infrastructure/SceneLoader/SceneId.cs create mode 100644 Assets/Code/Infrastructure/SceneLoader/SceneId.cs.meta create mode 100644 Assets/Code/Infrastructure/SceneLoader/SceneLoader.cs create mode 100644 Assets/Code/Infrastructure/SceneLoader/SceneLoader.cs.meta create mode 100644 Assets/Code/Infrastructure/SceneLoader/ScenesAssetReferences.cs create mode 100644 Assets/Code/Infrastructure/SceneLoader/ScenesAssetReferences.cs.meta create mode 100644 Assets/Code/Infrastructure/StateMachine.meta create mode 100644 Assets/Code/Infrastructure/StateMachine/AbstractStateMachine.cs create mode 100644 Assets/Code/Infrastructure/StateMachine/AbstractStateMachine.cs.meta create mode 100644 Assets/Code/Infrastructure/StateMachine/BaseState.cs create mode 100644 Assets/Code/Infrastructure/StateMachine/BaseState.cs.meta create mode 100644 Assets/Code/Infrastructure/UI.meta create mode 100644 Assets/Code/Infrastructure/UI/UIFactory.cs create mode 100644 Assets/Code/Infrastructure/UI/UIFactory.cs.meta create mode 100644 Assets/Code/Infrastructure/UI/UIPanel.cs create mode 100644 Assets/Code/Infrastructure/UI/UIPanel.cs.meta create mode 100644 Assets/Code/Infrastructure/UI/UIPanelSettings.cs create mode 100644 Assets/Code/Infrastructure/UI/UIPanelSettings.cs.meta create mode 100644 Assets/Code/Infrastructure/UI/UIPanelType.cs create mode 100644 Assets/Code/Infrastructure/UI/UIPanelType.cs.meta create mode 100644 Assets/Code/Infrastructure/UI/UIService.cs create mode 100644 Assets/Code/Infrastructure/UI/UIService.cs.meta create mode 100644 Assets/DefaultVolumeProfile.asset create mode 100644 Assets/DefaultVolumeProfile.asset.meta create mode 100644 Assets/InputSystem_Actions.inputactions create mode 100644 Assets/InputSystem_Actions.inputactions.meta create mode 100644 Assets/Plugins.meta create mode 100644 Assets/Plugins/PrimeTween.meta create mode 100644 Assets/Plugins/PrimeTween/PrimeTweenInstaller.asset create mode 100644 Assets/Plugins/PrimeTween/PrimeTweenInstaller.asset.meta create mode 100644 Assets/Plugins/PrimeTween/internal.meta create mode 100644 Assets/Plugins/PrimeTween/internal/PrimeTween.Installer.asmdef create mode 100644 Assets/Plugins/PrimeTween/internal/PrimeTween.Installer.asmdef.meta create mode 100644 Assets/Plugins/PrimeTween/internal/PrimeTweenInstaller.cs create mode 100644 Assets/Plugins/PrimeTween/internal/PrimeTweenInstaller.cs.meta create mode 100644 Assets/Plugins/PrimeTween/internal/com.kyrylokuzyk.primetween.tgz create mode 100644 Assets/Plugins/PrimeTween/internal/com.kyrylokuzyk.primetween.tgz.meta create mode 100644 Assets/Resources.meta create mode 100644 Assets/Resources/ProjectScope.prefab create mode 100644 Assets/Resources/ProjectScope.prefab.meta create mode 100644 Assets/Resources/ReflexSettings.asset create mode 100644 Assets/Resources/ReflexSettings.asset.meta create mode 100644 Assets/Resources/SystemConfigsReferences.asset create mode 100644 Assets/Resources/SystemConfigsReferences.asset.meta create mode 100644 Assets/Resources_moved.meta create mode 100644 Assets/Resources_moved/Configs.meta create mode 100644 Assets/Resources_moved/Configs/Audio.meta create mode 100644 Assets/Resources_moved/Configs/Audio/Audio Source.prefab create mode 100644 Assets/Resources_moved/Configs/Audio/Audio Source.prefab.meta create mode 100644 Assets/Resources_moved/Configs/Audio/AudioSourcePrefabConfig.asset create mode 100644 Assets/Resources_moved/Configs/Audio/AudioSourcePrefabConfig.asset.meta create mode 100644 Assets/Resources_moved/Configs/Audio/Settings.meta create mode 100644 Assets/Resources_moved/Configs/Audio/Settings/Full3D.asset create mode 100644 Assets/Resources_moved/Configs/Audio/Settings/Full3D.asset.meta create mode 100644 Assets/Resources_moved/Configs/Audio/Settings/NoBlend.asset create mode 100644 Assets/Resources_moved/Configs/Audio/Settings/NoBlend.asset.meta create mode 100644 Assets/Resources_moved/Configs/InputServiceConfig.asset create mode 100644 Assets/Resources_moved/Configs/InputServiceConfig.asset.meta create mode 100644 Assets/Resources_moved/Configs/UI.meta create mode 100644 Assets/Resources_moved/Configs/UI/CanvasSettingsConfig.asset create mode 100644 Assets/Resources_moved/Configs/UI/CanvasSettingsConfig.asset.meta create mode 100644 Assets/Resources_moved/UI.meta create mode 100644 Assets/Resources_moved/UI/Canvas.prefab create mode 100644 Assets/Resources_moved/UI/Canvas.prefab.meta create mode 100644 Assets/Scenes.meta create mode 100644 Assets/Scenes/SampleScene.unity create mode 100644 Assets/Scenes/SampleScene.unity.meta create mode 100644 Assets/Settings.meta create mode 100644 Assets/Settings/Lit2DSceneTemplate.scenetemplate create mode 100644 Assets/Settings/Lit2DSceneTemplate.scenetemplate.meta create mode 100644 Assets/Settings/Renderer2D.asset create mode 100644 Assets/Settings/Renderer2D.asset.meta create mode 100644 Assets/Settings/Scenes.meta create mode 100644 Assets/Settings/Scenes/URP2DSceneTemplate.unity create mode 100644 Assets/Settings/Scenes/URP2DSceneTemplate.unity.meta create mode 100644 Assets/Settings/UniversalRP.asset create mode 100644 Assets/Settings/UniversalRP.asset.meta create mode 100644 Assets/UnityCodeGen.Generated.meta create mode 100644 Assets/UnityCodeGen.Generated/AudioInfoConfig.Generated.cs create mode 100644 Assets/UnityCodeGen.Generated/AudioInfoConfig.Generated.cs.meta create mode 100644 Assets/UnityCodeGen.Generated/AudioMixer.Generated.cs create mode 100644 Assets/UnityCodeGen.Generated/AudioMixer.Generated.cs.meta create mode 100644 Assets/UnityCodeGen.Generated/AudioSourcePrefabConfig.Generated.cs create mode 100644 Assets/UnityCodeGen.Generated/AudioSourcePrefabConfig.Generated.cs.meta create mode 100644 Assets/UnityCodeGen.Generated/AudioSourceSettingsConfig.Generated.cs create mode 100644 Assets/UnityCodeGen.Generated/AudioSourceSettingsConfig.Generated.cs.meta create mode 100644 Assets/UnityCodeGen.Generated/CanvasSettingsConfig.Generated.cs create mode 100644 Assets/UnityCodeGen.Generated/CanvasSettingsConfig.Generated.cs.meta create mode 100644 Assets/UnityCodeGen.Generated/Code.Infrastructure.App.AppStateMachine.Generated.cs create mode 100644 Assets/UnityCodeGen.Generated/Code.Infrastructure.App.AppStateMachine.Generated.cs.meta create mode 100644 Assets/UnityCodeGen.Generated/Code.Infrastructure.App.States.BootstrapState.Generated.cs create mode 100644 Assets/UnityCodeGen.Generated/Code.Infrastructure.App.States.BootstrapState.Generated.cs.meta create mode 100644 Assets/UnityCodeGen.Generated/ConfigLocator.Generated.cs create mode 100644 Assets/UnityCodeGen.Generated/ConfigLocator.Generated.cs.meta create mode 100644 Assets/UnityCodeGen.Generated/IConfigLocator.cs create mode 100644 Assets/UnityCodeGen.Generated/IConfigLocator.cs.meta create mode 100644 Assets/UnityCodeGen.Generated/InputServiceConfig.Generated.cs create mode 100644 Assets/UnityCodeGen.Generated/InputServiceConfig.Generated.cs.meta create mode 100644 Assets/UnityCodeGen.Generated/LayerMasksConfigConfig.Generated.cs create mode 100644 Assets/UnityCodeGen.Generated/LayerMasksConfigConfig.Generated.cs.meta create mode 100644 Assets/UnityCodeGen.Generated/SceneInfoConfig.Generated.cs create mode 100644 Assets/UnityCodeGen.Generated/SceneInfoConfig.Generated.cs.meta create mode 100644 Assets/UnityCodeGen.Generated/SceneLoader.Generated.cs create mode 100644 Assets/UnityCodeGen.Generated/SceneLoader.Generated.cs.meta create mode 100644 Assets/UnityCodeGen.Generated/SystemConfigsReferences.Generated.cs create mode 100644 Assets/UnityCodeGen.Generated/SystemConfigsReferences.Generated.cs.meta create mode 100644 Assets/UnityCodeGen.Generated/UIPanelSettingsConfig.Generated.cs create mode 100644 Assets/UnityCodeGen.Generated/UIPanelSettingsConfig.Generated.cs.meta create mode 100644 Assets/UniversalRenderPipelineGlobalSettings.asset create mode 100644 Assets/UniversalRenderPipelineGlobalSettings.asset.meta create mode 100644 Packages/manifest.json create mode 100644 Packages/packages-lock.json create mode 100644 ProjectSettings/AudioManager.asset create mode 100644 ProjectSettings/ClusterInputManager.asset create mode 100644 ProjectSettings/DynamicsManager.asset create mode 100644 ProjectSettings/EditorBuildSettings.asset create mode 100644 ProjectSettings/EditorSettings.asset create mode 100644 ProjectSettings/GraphicsSettings.asset create mode 100644 ProjectSettings/InputManager.asset create mode 100644 ProjectSettings/MemorySettings.asset create mode 100644 ProjectSettings/MultiplayerManager.asset create mode 100644 ProjectSettings/NavMeshAreas.asset create mode 100644 ProjectSettings/NetworkManager.asset create mode 100644 ProjectSettings/PackageManagerSettings.asset create mode 100644 ProjectSettings/Physics2DSettings.asset create mode 100644 ProjectSettings/PresetManager.asset create mode 100644 ProjectSettings/ProjectSettings.asset create mode 100644 ProjectSettings/ProjectVersion.txt create mode 100644 ProjectSettings/QualitySettings.asset create mode 100644 ProjectSettings/ShaderGraphSettings.asset create mode 100644 ProjectSettings/TagManager.asset create mode 100644 ProjectSettings/TimeManager.asset create mode 100644 ProjectSettings/URPProjectSettings.asset create mode 100644 ProjectSettings/UnityConnectSettings.asset create mode 100644 ProjectSettings/VFXManager.asset create mode 100644 ProjectSettings/VersionControlSettings.asset create mode 100644 ProjectSettings/XRSettings.asset delete mode 100644 README.md diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7205e72 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,43 @@ +# В последующем может быть расширен или изменён, если посчитаем нужным + +* text=auto + +# ====================================== +# 3D МОДЕЛИ И АНИМАЦИИ +# ====================================== +*.blend filter=lfs diff=lfs merge=lfs -text +*.fbx filter=lfs diff=lfs merge=lfs -text +*.obj filter=lfs diff=lfs merge=lfs -text +*.3ds filter=lfs diff=lfs merge=lfs -text +*.dae filter=lfs diff=lfs merge=lfs -text +*.max filter=lfs diff=lfs merge=lfs -text +*.c4d filter=lfs diff=lfs merge=lfs -text + +# ====================================== +# ГРАФИКА (ИСХОДНИКИ) +# ====================================== +*.psd filter=lfs diff=lfs merge=lfs -text +*.ai filter=lfs diff=lfs merge=lfs -text +*.skp filter=lfs diff=lfs merge=lfs -text +*.tga filter=lfs diff=lfs merge=lfs -text +*.exr filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text + +# ====================================== +# АУДИО (ИСХОДНИКИ) +# ====================================== +*.wav filter=lfs diff=lfs merge=lfs -text +*.mp3 filter=lfs diff=lfs merge=lfs -text +*.ogg filter=lfs diff=lfs merge=lfs -text +*.aiff filter=lfs diff=lfs merge=lfs -text +*.flac filter=lfs diff=lfs merge=lfs -text + +# ====================================== +# ВИДЕО И КИНЕМАТИКА +# ====================================== +*.mp4 filter=lfs diff=lfs merge=lfs -text +*.mov filter=lfs diff=lfs merge=lfs -text +*.avi filter=lfs diff=lfs merge=lfs -text +*.webm filter=lfs diff=lfs merge=lfs -text \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17db35f --- /dev/null +++ b/.gitignore @@ -0,0 +1,185 @@ +# ====================================== +# UNITY - ОСНОВНЫЕ ИГНОРИРУЕМЫЕ ПАПКИ +# ====================================== + +# Автогенерируемые Unity папки + +# Кэш и временные файлы Unity +/[Ll]ibrary/ +# Временные файлы компиляции +/[Tt]emp/ +# Файлы объектов компиляции +/[Oo]bj/ +# Сборки проекта +/[Bb]uild/ +# Дополнительные сборки +/[Bb]uilds/ +# Логи Unity +/[Ll]ogs/ +# Пользовательские настройки +/[Uu]ser[Ss]ettings/ +# Временные файлы Unity +.utmp/ + +# Файлы логов +*.log + +# Файлы Blender (резервные копии) +*.blend1 +*.blend1.meta + +# Записи и дампы памяти (могут быть очень большими) + +# Дампы памяти (содержат чувствительные данные) +/[Mm]emoryCaptures/ +# Записи (могут занимать много места) +/[Rr]ecordings/ + +# Автогенерируемые плагины Rider +/[Aa]ssets/Plugins/Editor/JetBrains* + +# ====================================== +# ФАЙЛЫ СБОРОК И ПАКЕТОВ +# ====================================== + +# Готовые сборки и пакеты + +# Android приложение +*.apk + +# Android App Bundle +*.aab + +# Пакет для Asset Store +*.unitypackage + +# Meta файлы пакетов +*.unitypackage.meta + +# macOS приложение +*.app + +# Windows приложение +*.exe + +# Addressables +/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* +/ServerData +/[Aa]ssets/StreamingAssets/aa* +/[Aa]ssets/AddressableAssetsData/link.xml* +/[Aa]ssets/Addressables_Temp* + +# Временные Android Assets +/[Aa]ssets/[Ss]treamingAssets/aa.meta +/[Aa]ssets/[Ss]treamingAssets/aa/* + +# ====================================== +# VISUAL STUDIO / RIDER / VS CODE +# ====================================== + +# Visual Studio + +# Кэш Visual Studio +.vs/ +# Файлы проектов C# +*.csproj +# Файл решения +*.sln +# Пользовательские настройки решения +*.suo +# Пользовательские настройки проекта +*.user +# Пользовательские настройки +*.userprefs +*.userosscache +*.rsuser + +# JetBrains Rider + +# Все файлы Rider +.idea/ +# Файлы модулей +*.iml +# Файлы проектов +*.ipr +# Файлы рабочих пространств +*.iws +# Персональные настройки Rider +*.DotSettings.user + +# VS Code + +# Все настройки VS Code +.vscode/ +# Игнорировать рабочие пространства +*.code-workspace +# Игнорировать установленные расширения +*.vsix + +# ====================================== +# ВСПОМОГАТЕЛЬНЫЕ ФАЙЛЫ СРЕД РАЗРАБОТКИ +# ====================================== + +# Общие временные файлы + +# База данных отладки MonoDevelop +*.pidb +# База данных программ +*.pdb +# База данных Mono Debugging +*.mdb +# Открытая база данных +*.opendb +# База данных Visual C++ +*.VC.db +*.booproj +*.svd +*.tmp +*.unityproj + +# Mono crash файлы +mono_crash.* + +# Файлы отчетов и системной информации + +# Системная информация при крашах +sysinfo.txt + +# ====================================== +# META-ФАЙЛЫ +# ====================================== + +# Игнорировать только meta-файлы для временных файлов +*.pidb.meta +*.pdb.meta +*.mdb.meta + +# ====================================== +# ТЕСТЫ И ВИЗУАЛЬНЫЕ СКРИПТЫ +# ====================================== + +# Файлы TestRunner +InitTestScene*.unity* + +# Автогенерированные сцены тестов +/[Aa]ssets/[Ii]nit[Tt]est[Ss]cene*.unity* + +# Visual Scripting auto-generated files +/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Flow/UnitOptions.db +/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Flow/UnitOptions.db.meta +/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Core/Property Providers +/[Aa]ssets/Unity.VisualScripting.Generated/VisualScripting.Core/Property Providers.meta + +# ====================================== +# ДОПОЛНИТЕЛЬНЫЕ НАСТРОЙКИ +# ====================================== + +# Gradle (для Android сборок) +.gradle/ + +# Другие автосгенерированные файлы +ExportedObj/ +.consulo/ + +# Crashlytics +crashlytics-build.properties \ No newline at end of file diff --git a/Assets/Code.meta b/Assets/Code.meta new file mode 100644 index 0000000..f2650e3 --- /dev/null +++ b/Assets/Code.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: df98c273f7a040bf929b4f16a5c36b58 +timeCreated: 1765563475 \ No newline at end of file diff --git a/Assets/Code/Infrastructure.meta b/Assets/Code/Infrastructure.meta new file mode 100644 index 0000000..0ca0631 --- /dev/null +++ b/Assets/Code/Infrastructure.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8ae0afd22327405eb4098d4106118b68 +timeCreated: 1766056068 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/App.meta b/Assets/Code/Infrastructure/App.meta new file mode 100644 index 0000000..cabf772 --- /dev/null +++ b/Assets/Code/Infrastructure/App.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 378d32559e654d4b91e761f7f5d4cee6 +timeCreated: 1765572320 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/App/AppStateMachine.cs b/Assets/Code/Infrastructure/App/AppStateMachine.cs new file mode 100644 index 0000000..c7c06d5 --- /dev/null +++ b/Assets/Code/Infrastructure/App/AppStateMachine.cs @@ -0,0 +1,35 @@ +using Code.Infrastructure.Audio; +using Code.Infrastructure.Camera; +using Code.Infrastructure.Input; +using Code.Infrastructure.UI; +using Reflex.Attributes; + +namespace Code.Infrastructure.App +{ + public enum AppStates + { + Bootstrap, + Gameplay, + MainMenu, + Platformer + } + + public class AppStateMachineContext + { + [Inject] public SceneLoader.SceneLoader SceneLoader { get; set; } + [Inject] public CameraStateMachine Camera { get; set; } + [Inject] public CameraFactory CameraFactory { get; set; } + [Inject] public IUIService UIService { get; set; } + [Inject] public IAudioFactory AudioFactory { get; set; } + [Inject] public IAudioService AudioService { get; set; } + [Inject] public IInputService InputService { get; set; } + public AppStateMachine StateMachine { get; set; } + } + + [Generators.StateMachine(typeof(AppStateMachineContext),typeof(AppStates))] + public partial class AppStateMachine + { + + } + +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/App/AppStateMachine.cs.meta b/Assets/Code/Infrastructure/App/AppStateMachine.cs.meta new file mode 100644 index 0000000..605294e --- /dev/null +++ b/Assets/Code/Infrastructure/App/AppStateMachine.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5627248405434f028be921cb7916f823 +timeCreated: 1765572329 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/App/PapaInstaller.cs b/Assets/Code/Infrastructure/App/PapaInstaller.cs new file mode 100644 index 0000000..122d25d --- /dev/null +++ b/Assets/Code/Infrastructure/App/PapaInstaller.cs @@ -0,0 +1,213 @@ +using Code.Debug; +using Code.Infrastructure.Audio; +using Code.Infrastructure.Camera; +using Code.Infrastructure.Configs; +using Code.Infrastructure.Input; +using Code.Infrastructure.Loadings; +using Code.Infrastructure.SceneLoader; +using Code.Infrastructure.UI; +using Code.Infrastructure.Volume; +using Reflex.Core; +using Reflex.Enums; +using Reflex.Injectors; +using Unity.Cinemachine; +using UnityEngine; +using UnityEngine.Rendering.Universal; +using Resolution = Reflex.Enums.Resolution; + +namespace Code.Infrastructure.App +{ + public class PapaInstaller : MonoBehaviour, IInstaller, + ISceneConfigsLoadedHandle, + IInputServiceInitializedHandle, + ICameraInitializedHandle, + IUIInitializedHandle, + IUIPanelLoaded, + IVolumesInitializedHandle, + IVolumeLoaded, + IAudioSourcePoolInitializedHandle, + IAudioConfigsLoadedHandle, + ICinematicMachineHandle, + ILayerMasksInitializedHandle + { + public void InstallBindings(ContainerBuilder containerBuilder) + { + InitLoadings(); + + containerBuilder.RegisterType(typeof(EventBus.EventBus), Lifetime.Singleton, Resolution.Eager); + containerBuilder.RegisterType(typeof(ConfigLocator), new[] { typeof(IConfigLocator) }, Lifetime.Singleton, + Resolution.Eager); + containerBuilder.RegisterType(typeof(SceneLoader.SceneLoader), Lifetime.Singleton, Resolution.Eager); + containerBuilder.RegisterType(typeof(InputService), new[] { typeof(IInputService) }, Lifetime.Singleton, + Resolution.Eager); + + containerBuilder.OnContainerBuilt += Bootstrap; + + containerBuilder.RegisterType(typeof(AppStateMachine), Lifetime.Singleton, Resolution.Lazy); + containerBuilder.RegisterType(typeof(AppStateMachineContext), Lifetime.Singleton, Resolution.Lazy); + + containerBuilder.RegisterType(typeof(CameraFactory), Lifetime.Singleton, Resolution.Lazy); + containerBuilder.RegisterType(typeof(CameraStateMachine), Lifetime.Singleton, Resolution.Lazy); + containerBuilder.RegisterType(typeof(CameraStateMachineContext), Lifetime.Singleton, Resolution.Lazy); + + containerBuilder.RegisterType(typeof(UIFactory), new[] { typeof(IUIFactory) }, Lifetime.Singleton, + Resolution.Eager); + containerBuilder.RegisterType(typeof(UIService), new[] { typeof(IUIService) }, Lifetime.Singleton, + Resolution.Eager); + + containerBuilder.RegisterType(typeof(VolumeService), new[] { typeof(IVolumeService) }, Lifetime.Singleton, + Resolution.Lazy); + containerBuilder.RegisterType(typeof(VolumeFactory), new[] { typeof(IVolumeFactory) }, Lifetime.Singleton, + Resolution.Eager); + + containerBuilder.RegisterType(typeof(AudioFactory), new[] { typeof(IAudioFactory) }, Lifetime.Singleton, + Resolution.Eager); + containerBuilder.RegisterType(typeof(AudioSourcePool), new[] { typeof(IAudioSourcePool) }, Lifetime.Singleton, + Resolution.Eager); + containerBuilder.RegisterType(typeof(AudioService), new[] { typeof(IAudioService) }, Lifetime.Singleton, + Resolution.Eager); + } + + private void InitLoadings() + { + // UIFactory + // Logo + // Scene configs + // Input service + // Camera + // Volume Factory + // Loading volume + // Audio Factory + // Audio Pool + // Cinemachine Camera + // Layer masks + _initialLoading = new Loading(11, () => EssentialLoadEnded(_thatRightContainer)); + + _ui2CamLoading = new Loading(2, + () => + { + _thatRightContainer.Resolve().SetCanvasCamera(_thatRightContainer + .Resolve().cameraLoadedData.Camera); + + }); + } + + private Loading _initialLoading; + private Loading _ui2CamLoading; + private Container _thatRightContainer; + + private void Bootstrap(Container container) + { + _thatRightContainer = container; + + container.Resolve().Subscribe(this); + + ConstructorInjector.Construct(typeof(LayerMasks), container); + + var cameraFactory = container.Resolve(); + cameraFactory.CreateCameraStateMachine(container.Resolve()) + .Forget(); + } + + private void EssentialLoadEnded(Container container) + { + container.Resolve().Unsubscribe(this); + container.Resolve().SwitchState(AppStates.Bootstrap) + .AllowStateTransition(AppStates.Gameplay) + .AllowStateTransition(AppStates.Platformer); + container.Resolve().StateMachine = container.Resolve(); + } + + public void SceneConfigsLoaded() + { + _initialLoading.DoProgress(); + } + + public void InputServiceInitialized() + { + _initialLoading.DoProgress(); + } + + public void CameraInitialized(CameraStateMachineContext.CameraLoadedData data) + { + _thatRightContainer.Resolve().cameraLoadedData = data; + _thatRightContainer.Resolve().SwitchState(CameraMode.None) + .AllowStateTransition(CameraMode.ThirdPerson) + .AllowStateTransition(CameraMode.Platformer); + + data.Camera.GetUniversalAdditionalCameraData().renderPostProcessing = true; + + _ui2CamLoading.DoProgress(); + _initialLoading.DoProgress(); + + _thatRightContainer.Resolve().LoadCinemachineCamera(CameraMode.None).Forget(); + } + + public void UIInitialized() + { + _ui2CamLoading.DoProgress(); + _initialLoading.DoProgress(); + + _thatRightContainer.Resolve().TryLoadPanel(UIPanelType.Logo); + _thatRightContainer.Resolve().TryLoadPanel(UIPanelType.Loading); + } + + public void UILoaded(UIPanelType uiPanelType) + { + switch (uiPanelType) + { + case UIPanelType.Logo: + _thatRightContainer.Resolve().OpenPanel(UIPanelType.Logo); + + _initialLoading.DoProgress(); + break; + case UIPanelType.Loading: + // _thatRightContainer.Resolve().OpenPanel(UIPanelType.Logo); + break; + default: + Application.Quit(); + break; + } + } + + public void VolumesInitialized() + { + _initialLoading.DoProgress(); + + _thatRightContainer.Resolve().TryLoadVolume(VolumeId.MenuRetro); + } + + public void VolumeLoaded(VolumeId volumeId) + { + switch (volumeId) + { + case VolumeId.MenuRetro: + _initialLoading.DoProgress(); + break; + default: + Application.Quit(); + break; + } + } + + public void AudioSourcePoolInitialized() + { + _initialLoading.DoProgress(); + } + + public void AudioConfigsLoaded() + { + _initialLoading.DoProgress(); + } + + public void CinemachineLoaded(CinemachineCamera cinemachineCamera) + { + _initialLoading.DoProgress(); + } + + public void LayerMasksInitialized() + { + _initialLoading.DoProgress(); + } + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/App/PapaInstaller.cs.meta b/Assets/Code/Infrastructure/App/PapaInstaller.cs.meta new file mode 100644 index 0000000..0049cc1 --- /dev/null +++ b/Assets/Code/Infrastructure/App/PapaInstaller.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e62d9c9e01334727887a684e008440a6 +timeCreated: 1765572590 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/App/States.meta b/Assets/Code/Infrastructure/App/States.meta new file mode 100644 index 0000000..e39fceb --- /dev/null +++ b/Assets/Code/Infrastructure/App/States.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 778cb4118a914d9c8b1cac8bef4295a5 +timeCreated: 1765572459 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/App/States/BootstrapState.cs b/Assets/Code/Infrastructure/App/States/BootstrapState.cs new file mode 100644 index 0000000..17d99c0 --- /dev/null +++ b/Assets/Code/Infrastructure/App/States/BootstrapState.cs @@ -0,0 +1,48 @@ +using System.Threading; +using Code.Debug; +using Code.Infrastructure.Audio; +using Code.Infrastructure.Generators; +using Code.Infrastructure.UI; + +namespace Code.Infrastructure.App.States +{ + [State(typeof(AppStates),typeof(AppStateMachineContext))] + [StateRaisingEvent] + public partial class BootstrapState : IUIPanelLoaded, IReadyToPlayHandle + { + private EventBus.EventBus _eventBus; + public BootstrapState(AppStateMachineContext context, EventBus.EventBus eventBus) : base(context,eventBus) + { + _eventBus = eventBus; + } + + public override AppStates myState => AppStates.Bootstrap; + protected override void OnEnterState() + { + _eventBus.Subscribe(this); + + Context.UIService.TryRemovePanel(UIPanelType.Logo); + + Context.UIService.TryLoadPanel(UIPanelType.DebugSceneChooser); + Context.AudioFactory.LoadAudioClips(AudioNames.UISounds); + } + + protected override void OnExitState() + { + // Context.AudioFactory.ReleaseAudios(AudioNames.UISounds); + Context.UIService.TryRemovePanel(UIPanelType.DebugSceneChooser); + _eventBus.Unsubscribe(this); + } + + public void UILoaded(UIPanelType uiPanelType) + { + if (uiPanelType != UIPanelType.DebugSceneChooser) return; + + Context.UIService.OpenPanel(uiPanelType); + } + public void OnReadyToPlay(AudioNames groupName) + { + Context.AudioService.PlayRepeatedly(AudioNames.UISounds,0); + } + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/App/States/BootstrapState.cs.meta b/Assets/Code/Infrastructure/App/States/BootstrapState.cs.meta new file mode 100644 index 0000000..8f6c626 --- /dev/null +++ b/Assets/Code/Infrastructure/App/States/BootstrapState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 71df5370ea2e4819bfbdb9cbb8e6c0ab +timeCreated: 1765572469 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio.meta b/Assets/Code/Infrastructure/Audio.meta new file mode 100644 index 0000000..6394543 --- /dev/null +++ b/Assets/Code/Infrastructure/Audio.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 117e5c36ac3f47b2bff18075759dd50e +timeCreated: 1769344255 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioFactory.cs b/Assets/Code/Infrastructure/Audio/AudioFactory.cs new file mode 100644 index 0000000..eb0d599 --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioFactory.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Code.Infrastructure.Configs; +using Code.Infrastructure.EventBus; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; + +namespace Code.Infrastructure.Audio +{ + public interface IAudioFactory + { + UniTaskVoid LoadAudioClips(AudioNames groupName); + void ReleaseAudios(AudioNames groupName); + } + + public class AudioFactory : IAudioFactory, IDisposable + { + private string _label; + private Dictionary> _audioInfos = new(); + + private Dictionary>> _clipsHandles = new(); + private AsyncOperationHandle> _configsHandle; + private EventBus.EventBus _eventBus; + + public AudioFactory(IConfigLocator configLocator, EventBus.EventBus eventBus) + { + _eventBus = eventBus; + _label = configLocator.TryGetManyConfigAssetReferences(); + + for (var i = 0; i < typeof(AudioNames).GetEnumValues().Length; i++) + { + _audioInfos.Add((AudioNames)i, new Dictionary()); + _clipsHandles.Add((AudioNames)i, new Dictionary>()); + } + + LoadConfigs().Forget(); + } + + private async UniTaskVoid LoadConfigs() + { + _configsHandle = Addressables.LoadAssetsAsync(_label); + + await _configsHandle.ToUniTask(); + + foreach (AudioInfoConfig config in _configsHandle.Result) + _audioInfos[config.GroupName].Add(config.id, config.fileReference); + + _eventBus.RaiseEvent(ui => ui.AudioConfigsLoaded()); + } + + public async UniTaskVoid LoadAudioClips(AudioNames groupName) + { + UnityEngine.Debug.LogWarning($"Audios loaded {groupName}"); + + foreach (var audioInfo in _audioInfos[groupName]) + if (!_clipsHandles[groupName].ContainsKey(audioInfo.Key)) + _clipsHandles[groupName].Add(audioInfo.Key, Addressables.LoadAssetAsync(audioInfo.Value)); + else + _clipsHandles[groupName][audioInfo.Key] = Addressables.LoadAssetAsync(audioInfo.Value); + + foreach (var handle in _clipsHandles[groupName]) + await handle.Value.ToUniTask(); + + _eventBus.RaiseEvent(ui => + ui.AudioClipsLoaded(groupName, _clipsHandles[groupName].ToDictionary( + x => x.Key, + x => x.Value.Result))); + } + + public void ReleaseAudios(AudioNames groupName) + { + UnityEngine.Debug.LogWarning($"Audios unloaded {groupName}"); + _eventBus.RaiseEvent(x=> x.AudioClipsUnLoaded(groupName)); + + foreach (var handle in _clipsHandles) + Addressables.Release(handle.Value); + } + public void Dispose() + { + foreach (var handles in _clipsHandles) + { + foreach (var handle in handles.Value) + { + handle.Value.Release(); + } + } + + _configsHandle.Release(); + } + } + + internal interface IAudioConfigsLoadedHandle : IGlobalSubscriber + { + void AudioConfigsLoaded(); + } + + internal interface IAudioClipsEventHandle : IGlobalSubscriber + { + void AudioClipsLoaded(AudioNames groupName, Dictionary toDictionary); + void AudioClipsUnLoaded(AudioNames groupName); + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioFactory.cs.meta b/Assets/Code/Infrastructure/Audio/AudioFactory.cs.meta new file mode 100644 index 0000000..81da7af --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioFactory.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cc45c23918ed446681564b467668304b +timeCreated: 1769344269 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioGroupType.cs b/Assets/Code/Infrastructure/Audio/AudioGroupType.cs new file mode 100644 index 0000000..7337cbf --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioGroupType.cs @@ -0,0 +1,11 @@ +using Code.Infrastructure.Generators; + +namespace Code.Infrastructure.Audio +{ + [AudioMixerEnum] + public enum AudioGroupType + { + Master, + + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioGroupType.cs.meta b/Assets/Code/Infrastructure/Audio/AudioGroupType.cs.meta new file mode 100644 index 0000000..6aa3a81 --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioGroupType.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 754a72a6b2c140a49a23bf6d39654c94 +timeCreated: 1769609499 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioInfo.cs b/Assets/Code/Infrastructure/Audio/AudioInfo.cs new file mode 100644 index 0000000..86cafb4 --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioInfo.cs @@ -0,0 +1,19 @@ +using Code.Infrastructure.Generators; +using UnityEngine; +using UnityEngine.AddressableAssets; + +namespace Code.Infrastructure.Audio +{ + [Config("Configs/Audio/AudioInfo", true)] + public class AudioInfo + { + [ToConfig] public AudioNames GroupName; + [ToConfig] public int id; + [ToConfig] public AssetReference fileReference; + } + [Config("Configs/Audio/AudioSourceSettings")] + public class AudioSourcePrefab + { + [ToConfig] public AssetReference sourcePrefab; + } +} diff --git a/Assets/Code/Infrastructure/Audio/AudioInfo.cs.meta b/Assets/Code/Infrastructure/Audio/AudioInfo.cs.meta new file mode 100644 index 0000000..51c0be5 --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioInfo.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 821f8c193f8846568335e415bf1e7ced +timeCreated: 1769437653 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioNames.cs b/Assets/Code/Infrastructure/Audio/AudioNames.cs new file mode 100644 index 0000000..96c3f70 --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioNames.cs @@ -0,0 +1,8 @@ +namespace Code.Infrastructure.Audio +{ + public enum AudioNames + { + UISounds, + EnemySFX + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioNames.cs.meta b/Assets/Code/Infrastructure/Audio/AudioNames.cs.meta new file mode 100644 index 0000000..691f804 --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioNames.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 88f7326e5bec4c97b2a70228dcd9f899 +timeCreated: 1769437552 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioService.cs b/Assets/Code/Infrastructure/Audio/AudioService.cs new file mode 100644 index 0000000..7ba23f2 --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioService.cs @@ -0,0 +1,133 @@ +using System.Collections.Generic; +using System.Threading; +using Code.Infrastructure.EventBus; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.Audio; + +namespace Code.Infrastructure.Audio +{ + public interface IAudioService + { + AudioClip GetClip(AudioNames groupName, int id); + void PlayTest(); + + UniTaskVoid PlayOneShot(AudioNames groupName, int id, Vector3 position = new Vector3(), + AudioSourceType sourceType = AudioSourceType.NoBlend, + AudioSource audioSource = null); + + UniTaskVoid PlayRepeatedly(AudioNames groupName, int id, + AudioSourceType sourceType = AudioSourceType.NoBlend, AudioSource audioSource = null, CancellationTokenSource cancellationTokenSource = null); + + bool CheckClip(AudioNames groupName, int id); + void CancelMusic(AudioNames groupName, int id); + void ReleaseCancellationSource(AudioNames groupName, int id); + } + + public partial class AudioService : IAudioClipsEventHandle, IAudioService + { + private EventBus.EventBus _eventBus; + private IAudioFactory _audioFactory; + private IAudioSourcePool _pool; + private Dictionary> _audioClips = new(); + private AudioMixer _mixer; + + private Dictionary _audioMixerGroups = new(); + public AudioService(EventBus.EventBus eventBus, IAudioFactory audioFactory, IAudioSourcePool pool) + { + _eventBus = eventBus; + _audioFactory = audioFactory; + _pool = pool; + + _eventBus.Subscribe(this); + + for (int i = 0; i < typeof(AudioNames).GetEnumValues().Length; i++) + _audioClips.Add((AudioNames)i, new Dictionary()); + + //InitializeMixer(); + } + + private partial void InitializeMixer(); + public void PlayTest() + { + _pool.GetSource(AudioSourceType.NoBlend).PlayOneShot(GetClip(AudioNames.UISounds, 0)); + } + + public async UniTaskVoid PlayOneShot(AudioNames groupName, int id, Vector3 position = new Vector3(), + AudioSourceType sourceType = AudioSourceType.NoBlend, + AudioSource audioSource = null) + { + if (!CheckClip(groupName, id)) return; + + if (!audioSource) audioSource = _pool.GetSource(sourceType); + + audioSource.transform.position = position; + audioSource.PlayOneShot(GetClip(groupName, id)); + + await UniTask.NextFrame(); + await UniTask.WaitUntilValueChanged(audioSource, _ => !audioSource.isPlaying); + + _pool.ReleaseSource(audioSource); + } + + private Dictionary, CancellationTokenSource> _cancellationTokenSources = new(); + + public void CancelMusic(AudioNames groupName, int id) => _cancellationTokenSources[new KeyValuePair(groupName, id)].Cancel(); + public void ReleaseCancellationSource(AudioNames groupName, int id) => _cancellationTokenSources[new KeyValuePair(groupName, id)] = null; + public async UniTaskVoid PlayRepeatedly(AudioNames groupName, int id, + AudioSourceType sourceType = AudioSourceType.NoBlend, AudioSource audioSource = null, CancellationTokenSource cancellationTokenSource = null) + { + if (!CheckClip(groupName, id)) return; + + if(cancellationTokenSource == null) + { + cancellationTokenSource = new CancellationTokenSource(); + _cancellationTokenSources[new KeyValuePair(groupName, id)] = cancellationTokenSource; + } + + if (!audioSource) audioSource = _pool.GetSource(sourceType); + if(!audioSource.resource) audioSource.resource = GetClip(groupName, id); + + audioSource.Play(); + + await UniTask.NextFrame(); + var (isCanceled, _) = await UniTask.WaitUntilValueChanged(audioSource, + _ => !audioSource.isPlaying, + cancellationToken: cancellationTokenSource.Token).SuppressCancellationThrow(); + UnityEngine.Debug.LogError("Music skipping"); + + if (isCanceled) + { + cancellationTokenSource.Dispose(); + + _pool.ReleaseSource(audioSource); + + ReleaseCancellationSource(groupName,id); + + return; + } + + PlayRepeatedly(groupName, id, sourceType, audioSource, cancellationTokenSource).Forget(); + } + + public AudioClip GetClip(AudioNames groupName, int id) => _audioClips[groupName][id]; + public bool CheckClip(AudioNames groupName, int id) => _audioClips[groupName].ContainsKey(id); + + public void AudioClipsLoaded(AudioNames groupname, Dictionary clips) + { + _audioClips[groupname] = clips; + _eventBus.RaiseEvent(x => x.OnReadyToPlay(groupname)); + } + + public void AudioClipsUnLoaded(AudioNames groupName) + { + _audioClips[groupName].Clear(); + _audioClips[groupName] = null; + } + } + + public interface IReadyToPlayHandle : IGlobalSubscriber + { + void OnReadyToPlay(AudioNames groupName); + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioService.cs.meta b/Assets/Code/Infrastructure/Audio/AudioService.cs.meta new file mode 100644 index 0000000..19a5260 --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioService.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e8426c9e925a431aa73069c49d6a71c0 +timeCreated: 1769446252 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioSourcePool.cs b/Assets/Code/Infrastructure/Audio/AudioSourcePool.cs new file mode 100644 index 0000000..dc8116f --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioSourcePool.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Code.Infrastructure.Configs; +using Code.Infrastructure.EventBus; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.AddressableAssets; +using UnityEngine.Audio; +using UnityEngine.Pool; +using UnityEngine.ResourceManagement.AsyncOperations; +using Object = UnityEngine.Object; + +namespace Code.Infrastructure.Audio +{ + public interface IAudioSourcePool + { + AudioSource GetSource(AudioSourceType sourceType); + void ReleaseSource(AudioSource source); + } + + public class AudioSourcePool : IAudioSourcePool, IDisposable + { + // The pool holds plain GameObjects (you can swap this for any component type). + private IObjectPool _pool; + private GameObject _sourcePrefab; + + private Dictionary _settings; + + public AudioSourcePool(Dictionary settings) => _settings = settings; + + private string _configsLabel; + private AsyncOperationHandle> _handles; + private AssetReference _sourcePrefabConfigReference; + private AsyncOperationHandle _prefabConfigHandle; + private AsyncOperationHandle _prefabHandle; + private EventBus.EventBus _eventBus; + + public AudioSourcePool(IConfigLocator configLocator, EventBus.EventBus eventBus) + { + _eventBus = eventBus; + + _configsLabel = configLocator.TryGetManyConfigAssetReferences(); + _sourcePrefabConfigReference = configLocator.TryGetConfigAssetReference(); + + LoadConfigs().Forget(); + } + + private async UniTaskVoid LoadConfigs() + { + _prefabConfigHandle = Addressables.LoadAssetAsync(_sourcePrefabConfigReference); + _handles = Addressables.LoadAssetsAsync(_configsLabel); + + await UniTask.WhenAll( + _prefabConfigHandle.ToUniTask(), + _handles.ToUniTask()); + + _prefabHandle = Addressables.LoadAssetAsync(_prefabConfigHandle.Result.sourcePrefab); + + _settings = _handles.Result.ToDictionary(x => x.sourceType); + + await _prefabHandle.ToUniTask(); + + _pool = new ObjectPool( + createFunc: CreateItem, + // actionOnGet: OnGet, + actionOnRelease: OnRelease, + actionOnDestroy: OnDestroyItem, + collectionCheck: true, // helps catch double-release mistakes + defaultCapacity: 10, + maxSize: 20 + ); + + _eventBus.RaiseEvent(x => x.AudioSourcePoolInitialized()); + } + // Creates a new pooled GameObject the first time (and whenever the pool needs more). + private AudioSource CreateItem() + { + var audioSource = Object.Instantiate(_prefabHandle.Result).GetComponent(); + audioSource.name = "PooledAudioSource"; + Object.DontDestroyOnLoad(audioSource); + return audioSource; + } + + // Called when an item is taken from the pool. + private void OnGet(AudioSource source) + { + //source.gameObject.SetActive(true); + } + + // Called when an item is returned to the pool. + private void OnRelease(AudioSource source) + { + source.Stop(); + source.resource = null; + //source.gameObject.SetActive(false); + } + + // Called when the pool decides to destroy an item (e.g., above max size). + private void OnDestroyItem(AudioSource source) + { + Object.Destroy(source.gameObject); + } + + public AudioSource GetSource(AudioSourceType sourceType) + { + var source = _pool.Get(); + + source.spatialBlend = _settings[sourceType].spatialBlend; + + return source; + } + + public void ReleaseSource(AudioSource source) => _pool.Release(source); + + public void Dispose() + { + Addressables.Release(_handles); + Addressables.Release(_prefabConfigHandle); + Addressables.Release(_sourcePrefab); + } + } + + public interface IAudioSourcePoolInitializedHandle : IGlobalSubscriber + { + void AudioSourcePoolInitialized(); + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioSourcePool.cs.meta b/Assets/Code/Infrastructure/Audio/AudioSourcePool.cs.meta new file mode 100644 index 0000000..3cfdb3e --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioSourcePool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 246ae406fabc4ca791673d3f38cfe1c5 +timeCreated: 1769448041 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioSourceSettings.cs b/Assets/Code/Infrastructure/Audio/AudioSourceSettings.cs new file mode 100644 index 0000000..81809a7 --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioSourceSettings.cs @@ -0,0 +1,14 @@ +using Code.Infrastructure.Generators; + +namespace Code.Infrastructure.Audio +{ + [Config("Configs/Audio/AudioSourceSettings", true)] + public class AudioSourceSettings + { + // Дополнять по мере необходимости + [ToConfig] public float spatialBlend; + + [ToConfig] public AudioSourceType sourceType; + + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioSourceSettings.cs.meta b/Assets/Code/Infrastructure/Audio/AudioSourceSettings.cs.meta new file mode 100644 index 0000000..f936d0d --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioSourceSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 76d9dd39cbe641dab0cfa8ed1f2007f4 +timeCreated: 1769448198 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioSourceType.cs b/Assets/Code/Infrastructure/Audio/AudioSourceType.cs new file mode 100644 index 0000000..bc0794d --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioSourceType.cs @@ -0,0 +1,8 @@ +namespace Code.Infrastructure.Audio +{ + public enum AudioSourceType + { + Full3D, + NoBlend + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Audio/AudioSourceType.cs.meta b/Assets/Code/Infrastructure/Audio/AudioSourceType.cs.meta new file mode 100644 index 0000000..94398e8 --- /dev/null +++ b/Assets/Code/Infrastructure/Audio/AudioSourceType.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 85f9cb99599f4399a1fd66496b5184d0 +timeCreated: 1769448254 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Configs.meta b/Assets/Code/Infrastructure/Configs.meta new file mode 100644 index 0000000..c55d26d --- /dev/null +++ b/Assets/Code/Infrastructure/Configs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ef0499dec5324e12b82f8688c6d3b3d8 +timeCreated: 1765711304 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Configs/ConfigLocator.cs b/Assets/Code/Infrastructure/Configs/ConfigLocator.cs new file mode 100644 index 0000000..83914e3 --- /dev/null +++ b/Assets/Code/Infrastructure/Configs/ConfigLocator.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using Code.Infrastructure.SceneLoader; +using Cysharp.Threading.Tasks; +using Reflex.Core; +using UnityEngine; +using UnityEngine.AddressableAssets; + +namespace Code.Infrastructure.Configs +{ + public partial class ConfigLocator : IConfigLocator + { + private Dictionary _singleConfigReferences = new(); + private Dictionary _multipleConfigReferences = new(); + + public AssetReference TryGetConfigAssetReference() => _singleConfigReferences.GetValueOrDefault(typeof(T), null); + public string TryGetManyConfigAssetReferences() => _multipleConfigReferences.GetValueOrDefault(typeof(T), null); + + partial void LoadConfigs(); + + public ConfigLocator() + { + LoadSystemConfigs(); + + LoadConfigs(); + } + + private void LoadSystemConfigs() + { + SystemConfigsReferences systemConfigsReferences = Resources.Load("SystemConfigsReferences"); + + _singleConfigReferences.Add(typeof(ScenesAssetReferences), systemConfigsReferences.scenesAssetReferences); + } + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Configs/ConfigLocator.cs.meta b/Assets/Code/Infrastructure/Configs/ConfigLocator.cs.meta new file mode 100644 index 0000000..39f6c6c --- /dev/null +++ b/Assets/Code/Infrastructure/Configs/ConfigLocator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 42496064fd5d42fea1c889f91e6a7e98 +timeCreated: 1765711330 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Configs/LayerMaskType.cs b/Assets/Code/Infrastructure/Configs/LayerMaskType.cs new file mode 100644 index 0000000..0460f83 --- /dev/null +++ b/Assets/Code/Infrastructure/Configs/LayerMaskType.cs @@ -0,0 +1,7 @@ +namespace Code.Infrastructure.Configs +{ + public enum LayerMaskType + { + PlatformerGround + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Configs/LayerMaskType.cs.meta b/Assets/Code/Infrastructure/Configs/LayerMaskType.cs.meta new file mode 100644 index 0000000..db339ac --- /dev/null +++ b/Assets/Code/Infrastructure/Configs/LayerMaskType.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0aab84d976cf4045b47f347ad8d77c79 +timeCreated: 1770907660 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Configs/LayerMasksConfig.cs b/Assets/Code/Infrastructure/Configs/LayerMasksConfig.cs new file mode 100644 index 0000000..7ad7d9e --- /dev/null +++ b/Assets/Code/Infrastructure/Configs/LayerMasksConfig.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using System.Linq; +using Code.Infrastructure.EventBus; +using Code.Infrastructure.Generators; +using Cysharp.Threading.Tasks; +using Reflex.Attributes; +using UnityEngine; +using UnityEngine.AddressableAssets; + +namespace Code.Infrastructure.Configs +{ + [Config("Configs/LayerMasksConfig", true)] + public struct LayerMasksConfig + { + [ToConfig] public LayerMask mask; + [ToConfig] public LayerMaskType type; + } + + public class LayerMasks + { + public static Dictionary Masks { get; } = new(); + + private static void SetMasks(IList configs) + { + foreach (var config in configs.Where(config => !Masks.ContainsKey(config.type))) + Masks.Add(config.type, config.mask); + } + + public LayerMasks(IConfigLocator configLocator, EventBus.EventBus eventBus) + { + SetupMasks(configLocator, eventBus).Forget(); + } + private async UniTaskVoid SetupMasks(IConfigLocator configLocator, EventBus.EventBus eventBus) + { + var handle = + Addressables.LoadAssetsAsync( + configLocator.TryGetManyConfigAssetReferences(), null, Addressables.MergeMode.Union); + + await handle.ToUniTask(); + + SetMasks(handle.Result); + + Addressables.Release(handle); + + eventBus.RaiseEvent(x => x.LayerMasksInitialized()); + } + } + + public interface ILayerMasksInitializedHandle : IGlobalSubscriber + { + void LayerMasksInitialized(); + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Configs/LayerMasksConfig.cs.meta b/Assets/Code/Infrastructure/Configs/LayerMasksConfig.cs.meta new file mode 100644 index 0000000..a6dff24 --- /dev/null +++ b/Assets/Code/Infrastructure/Configs/LayerMasksConfig.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2fe6e42771fc4a48a37acbe4fa992e4a +timeCreated: 1770906616 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Configs/SystemConfigsReferences.cs b/Assets/Code/Infrastructure/Configs/SystemConfigsReferences.cs new file mode 100644 index 0000000..c8ee89f --- /dev/null +++ b/Assets/Code/Infrastructure/Configs/SystemConfigsReferences.cs @@ -0,0 +1,11 @@ +using UnityEngine; +using UnityEngine.AddressableAssets; + +namespace Code.Infrastructure.Configs +{ + [CreateAssetMenu(fileName = "SystemConfigsReferences", menuName = "Configs/SystemConfigsReferences")] + public partial class SystemConfigsReferences : ScriptableObject + { + public AssetReference scenesAssetReferences; + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Configs/SystemConfigsReferences.cs.meta b/Assets/Code/Infrastructure/Configs/SystemConfigsReferences.cs.meta new file mode 100644 index 0000000..434781c --- /dev/null +++ b/Assets/Code/Infrastructure/Configs/SystemConfigsReferences.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 728b0ad306a64bd28d47fc4d052c5e81 +timeCreated: 1766325607 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/EventBus.meta b/Assets/Code/Infrastructure/EventBus.meta new file mode 100644 index 0000000..797bb41 --- /dev/null +++ b/Assets/Code/Infrastructure/EventBus.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7b34014c0c80c914baed6d14ef39dada +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Code/Infrastructure/EventBus/EventBus.cs b/Assets/Code/Infrastructure/EventBus/EventBus.cs new file mode 100644 index 0000000..875e908 --- /dev/null +++ b/Assets/Code/Infrastructure/EventBus/EventBus.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using Cysharp.Threading.Tasks; +using UnityEngine; + +namespace Code.Infrastructure.EventBus +{ + public class EventBus + { + private Dictionary> s_Subscribers + = new(); + + public void Subscribe(IGlobalSubscriber subscriber) + { + List subscriberTypes = EventBusHelper.GetSubscriberTypes(subscriber); + foreach (Type t in subscriberTypes) + { + if (!s_Subscribers.ContainsKey(t)) + { + s_Subscribers[t] = new SubscribersList(); + } + s_Subscribers[t].Add(subscriber); + } + } + + public void Unsubscribe(IGlobalSubscriber subscriber) + { + List subscriberTypes = EventBusHelper.GetSubscriberTypes(subscriber); + foreach (Type t in subscriberTypes) + { + if (s_Subscribers.ContainsKey(t)) + s_Subscribers[t].Remove(subscriber); + } + } + + public void RaiseEvent(Action action) + where TSubscriber : class, IGlobalSubscriber + { + if (!s_Subscribers.ContainsKey(typeof(TSubscriber))) return; + + SubscribersList subscribers = s_Subscribers[typeof(TSubscriber)]; + + subscribers.Executing = true; + + InvokeSubscribers(action, subscribers); + + subscribers.Executing = false; + subscribers.Cleanup(); + } + + private void InvokeSubscribers(Action action, SubscribersList subscribers) + where TSubscriber : class, IGlobalSubscriber + { + for ( int i = 0; i < subscribers.List.Count; i++) + { + IGlobalSubscriber subscriber = subscribers.List[i]; + try + { + action.Invoke(subscriber as TSubscriber); + } + catch (Exception e) + { + UnityEngine.Debug.LogError(e); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/EventBus/EventBus.cs.meta b/Assets/Code/Infrastructure/EventBus/EventBus.cs.meta new file mode 100644 index 0000000..0da7eac --- /dev/null +++ b/Assets/Code/Infrastructure/EventBus/EventBus.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b0547f2543ccf5e47b9ec486f35aa2df \ No newline at end of file diff --git a/Assets/Code/Infrastructure/EventBus/EventBusHelper.cs b/Assets/Code/Infrastructure/EventBus/EventBusHelper.cs new file mode 100644 index 0000000..fc0d3c2 --- /dev/null +++ b/Assets/Code/Infrastructure/EventBus/EventBusHelper.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Code.Infrastructure.EventBus +{ + internal static class EventBusHelper + { + private static Dictionary> s_CashedSubscriberTypes = + new(); + + public static List GetSubscriberTypes( + IGlobalSubscriber globalSubscriber) + { + Type type = globalSubscriber.GetType(); + if (s_CashedSubscriberTypes.ContainsKey(type)) + { + return s_CashedSubscriberTypes[type]; + } + + List subscriberTypes = type + .GetInterfaces() + .Where(t => t.GetInterfaces() + .Contains(typeof(IGlobalSubscriber))) + .ToList(); + + s_CashedSubscriberTypes[type] = subscriberTypes; + return subscriberTypes; + } + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/EventBus/EventBusHelper.cs.meta b/Assets/Code/Infrastructure/EventBus/EventBusHelper.cs.meta new file mode 100644 index 0000000..b3bc2d2 --- /dev/null +++ b/Assets/Code/Infrastructure/EventBus/EventBusHelper.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ab2b1bcff76d5614dbda77a3c7b24158 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/EventBus/IGlobalSubscriber.cs b/Assets/Code/Infrastructure/EventBus/IGlobalSubscriber.cs new file mode 100644 index 0000000..19d882b --- /dev/null +++ b/Assets/Code/Infrastructure/EventBus/IGlobalSubscriber.cs @@ -0,0 +1,5 @@ +namespace Code.Infrastructure.EventBus +{ + public interface IGlobalSubscriber + { } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/EventBus/IGlobalSubscriber.cs.meta b/Assets/Code/Infrastructure/EventBus/IGlobalSubscriber.cs.meta new file mode 100644 index 0000000..e8d8c97 --- /dev/null +++ b/Assets/Code/Infrastructure/EventBus/IGlobalSubscriber.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: fe414b0de24841347b03eec77d1855c0 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/EventBus/SubscribersList.cs b/Assets/Code/Infrastructure/EventBus/SubscribersList.cs new file mode 100644 index 0000000..886359e --- /dev/null +++ b/Assets/Code/Infrastructure/EventBus/SubscribersList.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; + +namespace Code.Infrastructure.EventBus +{ + internal class SubscribersList where TSubscriber : class + { + private bool m_NeedsCleanUp; + + public bool Executing; + + public readonly List List = new(); // TODO Weakptr + + public void Add(TSubscriber subscriber) + { + List.Add(subscriber); + } + + public void Remove(TSubscriber subscriber) + { + if (Executing) + { + var i = List.IndexOf(subscriber); + if (i >= 0) + { + m_NeedsCleanUp = true; + List[i] = null; + } + } + else + { + List.Remove(subscriber); + } + } + + public void Cleanup() + { + if (!m_NeedsCleanUp) + { + return; + } + + List.RemoveAll(s => s == null); + m_NeedsCleanUp = false; + } + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/EventBus/SubscribersList.cs.meta b/Assets/Code/Infrastructure/EventBus/SubscribersList.cs.meta new file mode 100644 index 0000000..1ade1d1 --- /dev/null +++ b/Assets/Code/Infrastructure/EventBus/SubscribersList.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d364e9c8ac1c255428486d5892d23c1d \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Generators.meta b/Assets/Code/Infrastructure/Generators.meta new file mode 100644 index 0000000..7d1b381 --- /dev/null +++ b/Assets/Code/Infrastructure/Generators.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 56928494c304465d8dea6fe4bac75de2 +timeCreated: 1765563811 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Generators/Attributes.cs b/Assets/Code/Infrastructure/Generators/Attributes.cs new file mode 100644 index 0000000..1cfc309 --- /dev/null +++ b/Assets/Code/Infrastructure/Generators/Attributes.cs @@ -0,0 +1,52 @@ +using System; + +namespace Code.Infrastructure.Generators +{ + public class StateMachine : Attribute + { + public Type StateMachineContextType; + public Type StateMachineEnumType; + + public StateMachine(Type stateMachineContextType, Type stateMachineEnumType) + { + StateMachineContextType = stateMachineContextType; + StateMachineEnumType = stateMachineEnumType; + } + } + + public class State : Attribute + { + public Type StateEnum; + public Type StateMachineContext; + public bool SilentBitch; // Поэтому нейминг переменных очень важен + + public State(Type stateEnum,Type stateMachineContext, bool silentBitch = false) + { + StateEnum = stateEnum; + StateMachineContext = stateMachineContext; + SilentBitch = silentBitch; + } + } + public class StateRaisingEvent : Attribute + { + } + public class Config : Attribute + { + public bool Multiple; + public string MenuName; + public Config(string menuName, bool multiple = false) + { + Multiple = multiple; + MenuName = menuName; + } + } + public class ToConfig : Attribute + { + } + public class SceneIdEnum : Attribute + { + } + public class AudioMixerEnum : Attribute + { + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Generators/Attributes.cs.meta b/Assets/Code/Infrastructure/Generators/Attributes.cs.meta new file mode 100644 index 0000000..46c4706 --- /dev/null +++ b/Assets/Code/Infrastructure/Generators/Attributes.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 03e07712b53c4a3d8f6c8033af003369 +timeCreated: 1765563884 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Generators/AudioMixerGenerator.cs b/Assets/Code/Infrastructure/Generators/AudioMixerGenerator.cs new file mode 100644 index 0000000..63d7e54 --- /dev/null +++ b/Assets/Code/Infrastructure/Generators/AudioMixerGenerator.cs @@ -0,0 +1,73 @@ +#if UNITY_EDITOR +using UnityCodeGen; +using UnityEditor; +using UnityEngine; + +namespace Code.Infrastructure.Generators +{ + [Generator] + public class AudioMixerGenerator : ICodeGenerator + { + public void Execute(GeneratorContext context) + { + var Enum = TypeCache.GetTypesWithAttribute(); + + var initializedCode = ""; + var serviceCode = ""; + var interfacesUsingsCode = $@" +// using Code.Infrastructure.Audio; +"; + + foreach (var en in Enum) + { + if (!en.IsEnum) return; + + var members = en.GetFields(); + + foreach (var member in members) + { + if (member.FieldType.FullName != en.FullName) continue; + initializedCode += $@" + _audioMixerGroups.Add({en.FullName}.{member.Name}, _mixer.FindMatchingGroups(""{member.Name}"")[0]); + +"; +// dictionaryCode += $@" +// +// _scenesActions.Add(SceneId.{member.Name},() => _eventBus.RaiseEvent(h=> h.On{member.Name}SceneLoaded())); +// "; + } + + serviceCode += $@" +#pragma warning disable CS0105 +{interfacesUsingsCode} +#pragma warning restore CS0105 +namespace Code.Infrastructure.Audio{{ +public partial class AudioService + {{ +private partial void InitializeMixer(){{ + {initializedCode} +}} + }} +}}"; + } +// +// loaderCode = $@" +// #pragma warning disable CS0105 +// {interfacesUsingsCode} +// #pragma warning restore CS0105 +// +// namespace Code.Infrastructure.SceneLoader +// {{ +// public partial class SceneLoader{{ +// partial void SetupScenesInfo(){{ +// {dictionaryCode} +// }} +// }} +// {initializedCode} +// }} +// "; + context.AddCode($"AudioMixer.Generated.cs", serviceCode); + } + } +} +#endif \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Generators/AudioMixerGenerator.cs.meta b/Assets/Code/Infrastructure/Generators/AudioMixerGenerator.cs.meta new file mode 100644 index 0000000..2ec8ebe --- /dev/null +++ b/Assets/Code/Infrastructure/Generators/AudioMixerGenerator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c0e1349496af435c8bad4f3a409d8d58 +timeCreated: 1769608573 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Generators/ConfigGenerator.cs b/Assets/Code/Infrastructure/Generators/ConfigGenerator.cs new file mode 100644 index 0000000..37cc2aa --- /dev/null +++ b/Assets/Code/Infrastructure/Generators/ConfigGenerator.cs @@ -0,0 +1,111 @@ +#if UNITY_EDITOR +using System.Linq; +using Unity.VisualScripting; +using UnityCodeGen; +using UnityEditor; + +namespace Code.Infrastructure.Generators +{ + [Generator] + public class ConfigGenerator : ICodeGenerator + { + public void Execute(GeneratorContext context) + { + var configs = TypeCache.GetTypesWithAttribute(); + + var configsCode = ""; + var configAddCode = ""; + var configReferencesCode = ""; + var configReferencesUsingsCode = ""; + var configOfConfigsCode = ""; + + foreach (var cfg in configs) + { + var configFields = ""; + var configUsings = ""; + + var mem = cfg.GetFields(); + var members = mem.Where(m => m.HasAttribute()).ToList(); + foreach (var m in members) + { + configUsings += $"using {m.FieldType.Namespace};\n"; + configFields += $"public {m.FieldType.FullName} {m.Name};\n"; + } + + var attribute = cfg.GetAttribute(); + + configUsings += @" +using UnityEngine; +using UnityEngine.AddressableAssets; +"; + var configCode = $@" +#pragma warning disable CS0105 +{configUsings} +namespace {cfg.Namespace}{{ +#pragma warning restore CS0105 +[CreateAssetMenu(fileName = ""{cfg.Name}Config"", menuName = ""{attribute.MenuName}"")] +public class {cfg.Name}Config : ScriptableObject +{{ + {configFields} +}} +}} +"; + context.AddCode($"{cfg.Name}Config.Generated.cs", configCode); + + configReferencesUsingsCode += $"using {cfg.Namespace};\n"; + + if (attribute.Multiple) + { + configReferencesCode += $"public string {cfg.Name};\n"; + configAddCode += $@" + _multipleConfigReferences.Add(typeof({cfg.FullName}Config), systemConfigsReferences.{cfg.Name}); +"; + } + else + { + configReferencesCode += $"public AssetReference {cfg.Name};\n"; + configAddCode += $@" + _singleConfigReferences.Add(typeof({cfg.FullName}Config), systemConfigsReferences.{cfg.Name}); +"; + } + } + configsCode += $@" +#pragma warning disable CS0105 + {configReferencesUsingsCode} + using UnityEngine; +#pragma warning restore CS0105 + + namespace Code.Infrastructure.Configs + {{ + + public partial class ConfigLocator + {{ + partial void LoadConfigs() + {{ + SystemConfigsReferences systemConfigsReferences = Resources.Load(""SystemConfigsReferences""); + {configAddCode} + //_singleConfigReferences.Add(typeof(ScenesAssetReferences), systemConfigsReferences.scenesAssetReferences); + }} + }} + }} + "; + context.AddCode($"ConfigLocator.Generated.cs", configsCode); + + configOfConfigsCode += $@" +#pragma warning disable CS0105 + {configReferencesUsingsCode} + using UnityEngine.AddressableAssets; +#pragma warning restore CS0105 + + namespace Code.Infrastructure.Configs{{ + public partial class SystemConfigsReferences + {{ + {configReferencesCode} + }} +}} +"; + context.AddCode($"SystemConfigsReferences.Generated.cs", configOfConfigsCode); + } + } +} +#endif \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Generators/ConfigGenerator.cs.meta b/Assets/Code/Infrastructure/Generators/ConfigGenerator.cs.meta new file mode 100644 index 0000000..fa3eab1 --- /dev/null +++ b/Assets/Code/Infrastructure/Generators/ConfigGenerator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 026403be8eae4546adcb407308031c1d +timeCreated: 1765660396 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Generators/SceneLoaderGenerator.cs b/Assets/Code/Infrastructure/Generators/SceneLoaderGenerator.cs new file mode 100644 index 0000000..a915e5d --- /dev/null +++ b/Assets/Code/Infrastructure/Generators/SceneLoaderGenerator.cs @@ -0,0 +1,149 @@ +#if UNITY_EDITOR +using UnityCodeGen; +using UnityEditor; +using UnityEngine; + +namespace Code.Infrastructure.Generators +{ + [Generator] + public class SceneLoaderGenerator : ICodeGenerator + { + public void Execute(GeneratorContext context) + { + var Enum = TypeCache.GetTypesWithAttribute(); + + var loaderCode = ""; + var dictionaryCode = ""; + var interfacesCode = ""; + var interfacesUsingsCode = $@" +using Code.Infrastructure.EventBus; +"; + + foreach (var en in Enum) + { + if (!en.IsEnum) return; + + var members = en.GetFields(); + foreach (var member in members) + { + if(member.FieldType.FullName != en.FullName) continue; + interfacesCode += $@" + public interface IOn{member.Name}SceneLoadedHandler : IGlobalSubscriber + {{ + void On{member.Name}SceneLoaded(); + void On{member.Name}SceneInitialized(Reflex.Core.Container container); // Until its fixed + }} +"; + dictionaryCode += $@" + + _scenesActions.Add(SceneId.{member.Name},() => _eventBus.RaiseEvent(h=> h.On{member.Name}SceneLoaded())); +"; + } + } + + loaderCode = $@" +#pragma warning disable CS0105 +{interfacesUsingsCode} +#pragma warning restore CS0105 + +namespace Code.Infrastructure.SceneLoader +{{ +public partial class SceneLoader{{ +partial void SetupScenesInfo(){{ +{dictionaryCode} +}} +}} +{interfacesCode} +}} +"; + context.AddCode($"SceneLoader.Generated.cs", loaderCode); +// var configsCode = ""; +// var configCode = ""; +// var configUsings = ""; +// var configFields = ""; +// var configAddCode = ""; +// var configReferencesCode = ""; +// var configReferencesUsingsCode = ""; +// var configOfConfigsCode = ""; +// +// foreach (var cfg in configs) +// { +// var mem = cfg.GetFields(); +// var members = mem.Where(m => m.HasAttribute()).ToList(); +// foreach (var m in members) +// { +// configUsings += $"using {m.FieldType.Namespace};\n"; +// configFields += $"public {m.FieldType.FullName} {m.Name};\n"; +// } +// +// var attribute = cfg.GetAttribute(); +// +// configUsings += @" +// using UnityEngine; +// using UnityEngine.AddressableAssets; +// "; +// configCode = $@" +// {configUsings} +// namespace {cfg.Namespace}{{ +// [CreateAssetMenu(fileName = ""{cfg.Name}Config"", menuName = ""{attribute.MenuName}"")] +// public class {cfg.Name}Config : ScriptableObject +// {{ +// {configFields} +// }} +// }} +// "; +// context.AddCode($"{cfg.Name}Config.Generated.cs", configCode); +// +// configReferencesUsingsCode += $"using {cfg.Namespace};\n"; +// +// if (attribute.Multiple) +// { +// configReferencesCode += $"public String {cfg.Name};\n"; +// configAddCode += $@" +// _multipleConfigReferences.Add(typeof({cfg.FullName}), systemConfigsReferences.{cfg.Name}); +// "; +// } +// else +// { +// configReferencesCode += $"public AssetReference {cfg.Name};\n"; +// configAddCode += $@" +// _singleConfigReferences.Add(typeof({cfg.FullName}Config), systemConfigsReferences.{cfg.Name}); +// "; +// } +// } +// configsCode += $@" +// {configReferencesUsingsCode} +// using UnityEngine; +// +// namespace Code.Infrastructure.Configs +// {{ +// +// public partial class ConfigLocator +// {{ +// partial void LoadConfigs() +// {{ +// SystemConfigsReferences systemConfigsReferences = Resources.Load(""SystemConfigsReferences""); +// {configAddCode} +// //_singleConfigReferences.Add(typeof(ScenesAssetReferences), systemConfigsReferences.scenesAssetReferences); +// }} +// }} +// }} +// "; +// context.AddCode($"ConfigLocator.Generated.cs", configsCode); +// +// configOfConfigsCode += $@" +// {configReferencesUsingsCode} +// using UnityEngine.AddressableAssets; +// +// namespace Code.Infrastructure.Configs{{ +// public partial class SystemConfigsReferences +// {{ +// {configReferencesCode} +// }} +// }} +// "; +// context.AddCode($"SystemConfigsReferences.Generated.cs", configOfConfigsCode); + } + } +} +#endif \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Generators/SceneLoaderGenerator.cs.meta b/Assets/Code/Infrastructure/Generators/SceneLoaderGenerator.cs.meta new file mode 100644 index 0000000..ed17cad --- /dev/null +++ b/Assets/Code/Infrastructure/Generators/SceneLoaderGenerator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 92ec56ffd46d4a569bb9a7d436790439 +timeCreated: 1766423754 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Generators/StateMachineGenerator.cs b/Assets/Code/Infrastructure/Generators/StateMachineGenerator.cs new file mode 100644 index 0000000..648b56b --- /dev/null +++ b/Assets/Code/Infrastructure/Generators/StateMachineGenerator.cs @@ -0,0 +1,133 @@ +#if UNITY_EDITOR +using System.Linq; +using Unity.VisualScripting; +using UnityCodeGen; +using UnityEditor; + +namespace Code.Infrastructure.Generators +{ + [Generator] + public class StateMachineGenerator : ICodeGenerator + { + public void Execute(GeneratorContext context) + { + GenerateStates(context); + + GenerateStateMachine(context); + } + + private void GenerateStateMachine(GeneratorContext context) + { + var stateMachines = TypeCache.GetTypesWithAttribute(); + + foreach (var sm in stateMachines) + { + var attribute = sm.GetAttribute(); + + var states = TypeCache.GetTypesWithAttribute() + .Where(st => + st.GetAttribute().StateMachineContext.FullName == + attribute.StateMachineContextType.FullName); + + var statesCode = ""; + var statesNamespacesCode = ""; + + foreach (var st in states) + { + statesNamespacesCode += $@" +using {st.Namespace}; +"; + statesCode += $@" +var state{st.Name} = new {st.Name}(StateMachineContext,eventBus); +States.Add(state{st.Name}.myState, state{st.Name}); +"; + } + + var stateMachineCode = + $@" +// +#pragma warning disable CS0105 +using Code.Infrastructure.StateMachine; +{statesNamespacesCode} +#pragma warning restore CS0105 +namespace {sm.Namespace} +{{ + +public partial class {sm.Name} : AbstractStateMachine<{attribute.StateMachineEnumType.FullName},{attribute.StateMachineContextType.FullName}> +{{ + + public {sm.Name}({attribute.StateMachineContextType.FullName} context, EventBus.EventBus eventBus) : base(context) + {{ + {statesCode} + }} + + protected override bool CheckTransitionAllowed({attribute.StateMachineEnumType.FullName} nextState) => AllowedStates.HasFlag(nextState); + protected override void ResetAllowedTransitions() => AllowedStates = 0; +}} + + public static class {sm.Name}Extensions + {{ + public static AbstractStateMachine<{attribute.StateMachineEnumType.FullName}, {attribute.StateMachineContextType.FullName}> AllowStateTransition( + this AbstractStateMachine<{attribute.StateMachineEnumType.FullName}, {attribute.StateMachineContextType.FullName}> stateMachine, {attribute.StateMachineEnumType.FullName} state) + {{ + if (stateMachine == null) return null; + + stateMachine.AllowedStates |= state; + return stateMachine; + }} + }} +}} + +"; + + context.AddCode($"{sm.FullName}.Generated.cs", stateMachineCode); + } + } + + private void GenerateStates(GeneratorContext context) + { + var states = TypeCache.GetTypesWithAttribute(); + + var statesCode = ""; + foreach (var st in states) + { + var attribute = st.GetAttribute(); + + var fireStatesCode = ""; + if(st.HasAttribute()) fireStatesCode = $@" +protected override void _fireEnterState() + {{ + EventBus.RaiseEvent>(h => h.EnterState()); + }} + + protected override void _fireExitState() + {{ + EventBus.RaiseEvent>(h => h.ExitState()); + }} +"; + var genetics = ""; + if(!attribute.SilentBitch) genetics = $@" : BaseState<{attribute.StateEnum.FullName}, {attribute.StateMachineContext.FullName}>"; + + statesCode = $@" + +// retard-generated + +using Code.Infrastructure.StateMachine; + +namespace {st.Namespace} +{{ + public partial class {st.Name} {genetics} + {{ + {fireStatesCode} + }} +}} +"; + + + context.AddCode($"{st.FullName}.Generated.cs", statesCode); + + } + } + } +} +#endif \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Generators/StateMachineGenerator.cs.meta b/Assets/Code/Infrastructure/Generators/StateMachineGenerator.cs.meta new file mode 100644 index 0000000..b34c15d --- /dev/null +++ b/Assets/Code/Infrastructure/Generators/StateMachineGenerator.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9fb4b6961cd0471d8183511316c15000 +timeCreated: 1765563824 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Input.meta b/Assets/Code/Infrastructure/Input.meta new file mode 100644 index 0000000..f0365bf --- /dev/null +++ b/Assets/Code/Infrastructure/Input.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 629db0b5c4ac4624b5bde7022e167b3b +timeCreated: 1766612368 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Input/ActionMappings.cs b/Assets/Code/Infrastructure/Input/ActionMappings.cs new file mode 100644 index 0000000..736dbc6 --- /dev/null +++ b/Assets/Code/Infrastructure/Input/ActionMappings.cs @@ -0,0 +1,24 @@ +namespace Code.Infrastructure.Input +{ + public enum ActionMappings + { + MainMenu, + Gameplay, + Debug + } + public enum GameplayActions + { + Move, + Brake, + } + public enum DebugActions + { + Debug1, + Debug2, + Debug3, + Debug4, + Debug5, + Debug6, + Debug7 + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Input/ActionMappings.cs.meta b/Assets/Code/Infrastructure/Input/ActionMappings.cs.meta new file mode 100644 index 0000000..7ee92eb --- /dev/null +++ b/Assets/Code/Infrastructure/Input/ActionMappings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: dc62fec94fde411ab1420155a5087d64 +timeCreated: 1766613550 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Input/IInputService.cs b/Assets/Code/Infrastructure/Input/IInputService.cs new file mode 100644 index 0000000..c50e798 --- /dev/null +++ b/Assets/Code/Infrastructure/Input/IInputService.cs @@ -0,0 +1,15 @@ +using System; +using UnityEngine.InputSystem; + +namespace Code.Infrastructure.Input +{ + public interface IInputService + { + void EnableMap(ActionMappings actionMap); + void DisableMap(ActionMappings actionMap); + void BindActionPerformed(int mapId, int actionId, Action action); + void BindActionCancelled(int mapId, int actionId, Action action); + void UnbindActionPerformed(int mapId, int actionId, Action action); + void UnbindActionCancelled(int mapId, int actionId, Action action); + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Input/IInputService.cs.meta b/Assets/Code/Infrastructure/Input/IInputService.cs.meta new file mode 100644 index 0000000..eed0bc4 --- /dev/null +++ b/Assets/Code/Infrastructure/Input/IInputService.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9443ed0c0d76423f8ecebbd9398bacf3 +timeCreated: 1766614201 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Input/InputService.cs b/Assets/Code/Infrastructure/Input/InputService.cs new file mode 100644 index 0000000..8f23e47 --- /dev/null +++ b/Assets/Code/Infrastructure/Input/InputService.cs @@ -0,0 +1,62 @@ +using System; +using Code.Infrastructure.Configs; +using Code.Infrastructure.EventBus; +using Code.Infrastructure.Generators; +using Cysharp.Threading.Tasks; +using UnityEngine.AddressableAssets; +using UnityEngine.InputSystem; +using UnityEngine.ResourceManagement.AsyncOperations; + +namespace Code.Infrastructure.Input +{ + [Config("Configs/InputService")] + public class InputService : IInputService + { + [ToConfig] public InputActionAsset inputActionAsset; + private EventBus.EventBus _eventBus; + + public InputService(IConfigLocator configLocator, EventBus.EventBus eventBus) + { + _eventBus = eventBus; + LoadConfigs(configLocator.TryGetConfigAssetReference()).Forget(); + } + + private AsyncOperationHandle _configHandle; + + private async UniTaskVoid LoadConfigs(AssetReference configReference) + { + _configHandle = Addressables.LoadAssetAsync(configReference); + + await _configHandle.ToUniTask(); + + inputActionAsset = _configHandle.Result.inputActionAsset; + + // inputActionAsset.actionMaps[(int)ActionMappings.Debug].Enable(); + // inputActionAsset.actionMaps[(int)ActionMappings.Gameplay].Enable(); + + _eventBus.RaiseEvent(h => h.InputServiceInitialized()); + } + + public void EnableMap(ActionMappings actionMap) => inputActionAsset.actionMaps[(int)actionMap].Enable(); + public void DisableMap(ActionMappings actionMap) => inputActionAsset.actionMaps[(int)actionMap].Disable(); + + public void BindActionPerformed(int mapId, int actionId, Action action) => inputActionAsset + .actionMaps[mapId].actions[actionId].performed += action; + + public void BindActionCancelled(int mapId, int actionId, Action action) => inputActionAsset + .actionMaps[mapId].actions[actionId].canceled += action; + + public void UnbindActionPerformed(int mapId, int actionId, Action action) => + inputActionAsset + .actionMaps[mapId].actions[actionId].performed -= action; + + public void UnbindActionCancelled(int mapId, int actionId, Action action) => + inputActionAsset + .actionMaps[mapId].actions[actionId].canceled -= action; + } + + public interface IInputServiceInitializedHandle : IGlobalSubscriber + { + void InputServiceInitialized(); + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Input/InputService.cs.meta b/Assets/Code/Infrastructure/Input/InputService.cs.meta new file mode 100644 index 0000000..8bff0df --- /dev/null +++ b/Assets/Code/Infrastructure/Input/InputService.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2755e684d68a4f79994f0f92e8327a73 +timeCreated: 1766612399 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Loadings.meta b/Assets/Code/Infrastructure/Loadings.meta new file mode 100644 index 0000000..3ec2cf7 --- /dev/null +++ b/Assets/Code/Infrastructure/Loadings.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d78dfc7c05e54743bfa99dfeaa7dc064 +timeCreated: 1766483099 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Loadings/Loading.cs b/Assets/Code/Infrastructure/Loadings/Loading.cs new file mode 100644 index 0000000..1991beb --- /dev/null +++ b/Assets/Code/Infrastructure/Loadings/Loading.cs @@ -0,0 +1,36 @@ +using System; +using UnityEngine; + +namespace Code.Infrastructure.Loadings +{ + public class Loading + { + private int _progress; + public bool Completed { get; private set; } + private Action _completed; + private Action _loadProgress; + + public Loading(int maxProgress, Action completed, Action loadProgress = null) + { + _progress = maxProgress; + MaxProgress = maxProgress; + + _completed = completed; + _loadProgress = loadProgress; + } + public int CurrentProgress => _progress; + public int MaxProgress { get; } + + public void DoProgress() + { + _progress--; + _loadProgress?.Invoke(this); + + if(_progress <= 0) // Зачем инвертировать, если так читается лучше, райдер соси + { + Completed = true; + _completed(); + } + } + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/Loadings/Loading.cs.meta b/Assets/Code/Infrastructure/Loadings/Loading.cs.meta new file mode 100644 index 0000000..65a594b --- /dev/null +++ b/Assets/Code/Infrastructure/Loadings/Loading.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a1707a55b78d4218b1557d42adb7d961 +timeCreated: 1766483205 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/SceneLoader.meta b/Assets/Code/Infrastructure/SceneLoader.meta new file mode 100644 index 0000000..ec5b382 --- /dev/null +++ b/Assets/Code/Infrastructure/SceneLoader.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b7568293a2874477b2faeb918157ca94 +timeCreated: 1766325345 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/SceneLoader/SceneId.cs b/Assets/Code/Infrastructure/SceneLoader/SceneId.cs new file mode 100644 index 0000000..7f7d44f --- /dev/null +++ b/Assets/Code/Infrastructure/SceneLoader/SceneId.cs @@ -0,0 +1,13 @@ +using Code.Infrastructure.Generators; + +namespace Code.Infrastructure.SceneLoader +{ + [SceneIdEnum] + public enum SceneId + { + MainMenu, + Gameplay, + SoundTest, + Platformer + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/SceneLoader/SceneId.cs.meta b/Assets/Code/Infrastructure/SceneLoader/SceneId.cs.meta new file mode 100644 index 0000000..31b12b4 --- /dev/null +++ b/Assets/Code/Infrastructure/SceneLoader/SceneId.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: af1da0e870d542f2a864d7ddbbe9fb4c +timeCreated: 1766396924 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/SceneLoader/SceneLoader.cs b/Assets/Code/Infrastructure/SceneLoader/SceneLoader.cs new file mode 100644 index 0000000..af5acb9 --- /dev/null +++ b/Assets/Code/Infrastructure/SceneLoader/SceneLoader.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Code.Infrastructure.Configs; +using Code.Infrastructure.EventBus; +using Code.Infrastructure.Generators; +using Cysharp.Threading.Tasks; +using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; +using UnityEngine.ResourceManagement.ResourceProviders; +using UnityEngine.SceneManagement; + +namespace Code.Infrastructure.SceneLoader +{ + [Config("Configs/SceneConfig",true)] + public class SceneInfo + { + [ToConfig] public AssetReference SceneAsset; + [ToConfig] public SceneId SceneId; + } + public partial class SceneLoader : IDisposable + { + private Dictionary _sceneInfos = new(); + private Dictionary _scenesActions = new(); + + private EventBus.EventBus _eventBus; + + partial void SetupScenesInfo(); + + private AsyncOperationHandle> _loadHandle; + + public SceneLoader(EventBus.EventBus eventBus, IConfigLocator configLocator) + { + _eventBus = eventBus; + LoadConfigs(configLocator.TryGetManyConfigAssetReferences()).Forget(); + } + + // TODO типилище + public async UniTaskVoid LoadSceneAsync(SceneId id, LoadSceneMode loadMode) + { + if (loadMode == LoadSceneMode.Single) + for (var i = 0; i < _loadedScenes.Count; i++) + UnloadSceneAsync(_loadedScenes.Keys.ToList()[i]).Forget(); + + _loadedScenes.Add(id,Addressables.LoadSceneAsync(_sceneInfos[id].SceneAsset, loadMode)); + + await _loadedScenes[id]; + + _scenesActions[id](); + } + + private Dictionary> _loadedScenes = new(); + public Scene GetScene(SceneId id) => _loadedScenes[id].Result.Scene; + public async UniTaskVoid UnloadSceneAsync(SceneId id) + { + await Addressables.UnloadSceneAsync(_loadedScenes[id]); + _loadedScenes.Remove(id); + } + + private AsyncOperationHandle> _configHandle; + + private async UniTaskVoid LoadConfigs(string configReferences) + { + _configHandle = Addressables.LoadAssetsAsync(configReferences, OnSceneConfigLoaded, Addressables.MergeMode.Union); + + await _configHandle.ToUniTask(); + + SetupScenesInfo(); + _eventBus.RaiseEvent(h => h.SceneConfigsLoaded()); + } + + + private void OnSceneConfigLoaded(SceneInfoConfig sceneInfoConfig) + { + SceneInfo sceneInfo = new(); + sceneInfo.SceneId = sceneInfoConfig.SceneId; + sceneInfo.SceneAsset = sceneInfoConfig.SceneAsset; + + _sceneInfos.Add(sceneInfoConfig.SceneId, sceneInfo); + } + + public void Dispose() + { + Addressables.Release(_configHandle); + } + + } + + public interface ISceneConfigsLoadedHandle : IGlobalSubscriber + { + void SceneConfigsLoaded(); + } + public interface ILoadedSceneHandle : IGlobalSubscriber + { + void SceneLoaded(SceneId sceneId); + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/SceneLoader/SceneLoader.cs.meta b/Assets/Code/Infrastructure/SceneLoader/SceneLoader.cs.meta new file mode 100644 index 0000000..82e3e28 --- /dev/null +++ b/Assets/Code/Infrastructure/SceneLoader/SceneLoader.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 760817ac86164799ac58327509052257 +timeCreated: 1766325358 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/SceneLoader/ScenesAssetReferences.cs b/Assets/Code/Infrastructure/SceneLoader/ScenesAssetReferences.cs new file mode 100644 index 0000000..9196154 --- /dev/null +++ b/Assets/Code/Infrastructure/SceneLoader/ScenesAssetReferences.cs @@ -0,0 +1,11 @@ +using UnityEngine; +using UnityEngine.AddressableAssets; + +namespace Code.Infrastructure.SceneLoader +{ + [CreateAssetMenu(fileName = "ScenesAssetReferences", menuName = "Configs/ScenesAssetReferences")] + public class ScenesAssetReferences : ScriptableObject + { + public AssetReference gameplaySceneReference; + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/SceneLoader/ScenesAssetReferences.cs.meta b/Assets/Code/Infrastructure/SceneLoader/ScenesAssetReferences.cs.meta new file mode 100644 index 0000000..3900da4 --- /dev/null +++ b/Assets/Code/Infrastructure/SceneLoader/ScenesAssetReferences.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9759979f397044a5bcb03ded0d1c9561 +timeCreated: 1766325404 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/StateMachine.meta b/Assets/Code/Infrastructure/StateMachine.meta new file mode 100644 index 0000000..3e0fd41 --- /dev/null +++ b/Assets/Code/Infrastructure/StateMachine.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f1c4dc7e83794f978e68900a1be81bc2 +timeCreated: 1765563491 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/StateMachine/AbstractStateMachine.cs b/Assets/Code/Infrastructure/StateMachine/AbstractStateMachine.cs new file mode 100644 index 0000000..84e9903 --- /dev/null +++ b/Assets/Code/Infrastructure/StateMachine/AbstractStateMachine.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using Code.Infrastructure.EventBus; + +namespace Code.Infrastructure.StateMachine +{ + public abstract class AbstractStateMachine + where TStateMachineContext : new() + { + protected AbstractStateMachine(TStateMachineContext stateMachineContext ) + { + StateMachineContext = stateMachineContext; + } + + public TStateEnum CurrentState { get; protected set; } + public BaseState CurrentBaseState { get; private set; } + private BaseState _previousBaseState; + + protected Dictionary> States = new(); + protected Dictionary StatesTypes = new(); + public TStateEnum AllowedStates { get; set; } + + protected TStateMachineContext StateMachineContext { get; } + + // Костыли 2 шт. для флагового енама в шаблонах + protected abstract bool CheckTransitionAllowed(TStateEnum nextState); + protected abstract void ResetAllowedTransitions(); + + // TODO Если в EnterState вызывается ExitState, то будет гарантированная поломка + public AbstractStateMachine SwitchState(TStateEnum nextState) + { + if (!CheckTransitionAllowed(nextState) && CurrentBaseState != null) + { + return null; + } + + ResetAllowedTransitions(); + + _previousBaseState = CurrentBaseState; + + CurrentBaseState = States[nextState]; + + _previousBaseState?.ExitState(); + + CurrentBaseState.EnterState(); + + CurrentState = nextState; + + return this; + } + } + + public interface IEnterStateHandle : IGlobalSubscriber + { + void EnterState(); + } + + public interface IExitStateHandle : IGlobalSubscriber + { + void ExitState(); + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/StateMachine/AbstractStateMachine.cs.meta b/Assets/Code/Infrastructure/StateMachine/AbstractStateMachine.cs.meta new file mode 100644 index 0000000..bf02077 --- /dev/null +++ b/Assets/Code/Infrastructure/StateMachine/AbstractStateMachine.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8f6ce15c93464753a99851fc5d46440f +timeCreated: 1765563731 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/StateMachine/BaseState.cs b/Assets/Code/Infrastructure/StateMachine/BaseState.cs new file mode 100644 index 0000000..a9e9dd0 --- /dev/null +++ b/Assets/Code/Infrastructure/StateMachine/BaseState.cs @@ -0,0 +1,37 @@ +namespace Code.Infrastructure.StateMachine +{ + public abstract class BaseState + { + public abstract TEnum myState { get; } + + protected BaseState(TStateMachineContext context, EventBus.EventBus eventBus) + { + Context = context; + EventBus = eventBus; + } + + protected EventBus.EventBus EventBus; + protected TStateMachineContext Context { get; } + + protected virtual void _fireEnterState() + { + } + + protected virtual void _fireExitState() + { + } + + protected abstract void OnEnterState(); + protected abstract void OnExitState(); + public void EnterState() + { + OnEnterState(); + _fireEnterState(); + } + public void ExitState() + { + OnExitState(); + _fireExitState(); + } + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/StateMachine/BaseState.cs.meta b/Assets/Code/Infrastructure/StateMachine/BaseState.cs.meta new file mode 100644 index 0000000..779593c --- /dev/null +++ b/Assets/Code/Infrastructure/StateMachine/BaseState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0c348c251b644c3c941d2bd91bbb290c +timeCreated: 1765563748 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/UI.meta b/Assets/Code/Infrastructure/UI.meta new file mode 100644 index 0000000..e943801 --- /dev/null +++ b/Assets/Code/Infrastructure/UI.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f00c637597e248c9b6df168e80ab4ea2 +timeCreated: 1768978928 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/UI/UIFactory.cs b/Assets/Code/Infrastructure/UI/UIFactory.cs new file mode 100644 index 0000000..6be6254 --- /dev/null +++ b/Assets/Code/Infrastructure/UI/UIFactory.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Code.Infrastructure.Configs; +using Code.Infrastructure.EventBus; +using Cysharp.Threading.Tasks; +using UnityEngine; +using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; + +namespace Code.Infrastructure.UI +{ + public interface IUIFactory + { + UniTaskVoid LoadPanel(UIPanelType panelType); + void ReleasePanel(UIPanel panel); + Canvas Canvas { get; } + } + + public class UIFactory : IDisposable, IUIFactory + { + private Dictionary _configs; + private AssetReference _canvasConfigReference; + private string _configsLabel; + private EventBus.EventBus _eventBus; + + private AsyncOperationHandle> _configsHandle; + private AsyncOperationHandle _canvasSettingsHandle; + private CanvasSettingsConfig _canvasConfig; + + private GameObject _canvas; + + public Canvas Canvas { get; private set; } + + public UIFactory(IConfigLocator configLocator, EventBus.EventBus eventBus) + { + _eventBus = eventBus; + _configsLabel = configLocator.TryGetManyConfigAssetReferences(); + _canvasConfigReference = configLocator.TryGetConfigAssetReference(); + + LoadConfigs().Forget(); + } + + public async UniTaskVoid LoadPanel(UIPanelType panelType) + { + var panel = (await Addressables.InstantiateAsync(_configs[panelType].Prefab, _canvas.transform).ToUniTask()) + .GetComponent(); + + UnityEngine.Debug.LogWarning($"Panel loaded {panelType}"); + _eventBus.RaiseEvent(ui => ui.UIPanelLoaded(panel, panelType)); + } + + public void ReleasePanel(UIPanel panel) + { + UnityEngine.Debug.LogWarning($"Panel released {panel.gameObject.name}"); + Addressables.ReleaseInstance(panel.gameObject); + } + + private async UniTaskVoid LoadConfigs() + { + _configsHandle = Addressables.LoadAssetsAsync(_configsLabel); + _canvasSettingsHandle = Addressables.LoadAssetAsync(_canvasConfigReference); + + await _configsHandle.ToUniTask(); + await _canvasSettingsHandle.ToUniTask(); + + _canvasConfig = _canvasSettingsHandle.Result; + + _configs = _configsHandle.Result.ToDictionary(c => c.UIPanelType); + + _canvas = await Addressables.InstantiateAsync(_canvasConfig.Prefab).ToUniTask(); + UnityEngine.Object.DontDestroyOnLoad(_canvas); + + Canvas = _canvas.GetComponent(); + + _eventBus.RaiseEvent(ui => ui.UIInitialized()); + } + public void Dispose() + { + Addressables.Release(_configsHandle); + Addressables.Release(_canvasSettingsHandle); + Addressables.ReleaseInstance(_canvas); + } + } + + public interface IUIInitializedHandle : IGlobalSubscriber + { + void UIInitialized(); + } + public interface IUIPanelLoadedHandle : IGlobalSubscriber + { + void UIPanelLoaded(UIPanel panel, UIPanelType panelType); + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/UI/UIFactory.cs.meta b/Assets/Code/Infrastructure/UI/UIFactory.cs.meta new file mode 100644 index 0000000..41887d6 --- /dev/null +++ b/Assets/Code/Infrastructure/UI/UIFactory.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8447b81247b24f1b869b0067aeeff0b8 +timeCreated: 1768979324 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/UI/UIPanel.cs b/Assets/Code/Infrastructure/UI/UIPanel.cs new file mode 100644 index 0000000..f002c46 --- /dev/null +++ b/Assets/Code/Infrastructure/UI/UIPanel.cs @@ -0,0 +1,13 @@ +using System; +using UnityEngine; + +namespace Code.Infrastructure.UI +{ + public abstract class UIPanel : MonoBehaviour + { + [SerializeField] protected bool injectMeSenpai; + public bool InjectMeSenpai => injectMeSenpai; + public abstract void Open(Action callback = null); + public abstract void Close(Action callback = null); + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/UI/UIPanel.cs.meta b/Assets/Code/Infrastructure/UI/UIPanel.cs.meta new file mode 100644 index 0000000..5b618a0 --- /dev/null +++ b/Assets/Code/Infrastructure/UI/UIPanel.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4464e0158e044f5985c45cb73250cf43 +timeCreated: 1768980188 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/UI/UIPanelSettings.cs b/Assets/Code/Infrastructure/UI/UIPanelSettings.cs new file mode 100644 index 0000000..76021a1 --- /dev/null +++ b/Assets/Code/Infrastructure/UI/UIPanelSettings.cs @@ -0,0 +1,18 @@ +using Code.Infrastructure.Generators; +using UnityEngine.AddressableAssets; + +namespace Code.Infrastructure.UI +{ + [Config("Configs/UI/UIPanelSettings", true)] + public class UIPanelSettings + { + [ToConfig] public AssetReference Prefab; + [ToConfig] public UIPanelType UIPanelType; + } + + [Config("Configs/UI/CanvasSettings")] + public class CanvasSettings + { + [ToConfig] public AssetReference Prefab; + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/UI/UIPanelSettings.cs.meta b/Assets/Code/Infrastructure/UI/UIPanelSettings.cs.meta new file mode 100644 index 0000000..c49f1cd --- /dev/null +++ b/Assets/Code/Infrastructure/UI/UIPanelSettings.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 996c214bb8624a64b6c563f0c13320ac +timeCreated: 1768979468 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/UI/UIPanelType.cs b/Assets/Code/Infrastructure/UI/UIPanelType.cs new file mode 100644 index 0000000..a73cf8e --- /dev/null +++ b/Assets/Code/Infrastructure/UI/UIPanelType.cs @@ -0,0 +1,15 @@ +namespace Code.Infrastructure.UI +{ + public enum UIPanelType + { + Logo, + Loading, + DebugSceneChooser, + // Race + PlayerScoreCounter, + WinScreen, + PlayerScores, + // + DebugMenu + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/UI/UIPanelType.cs.meta b/Assets/Code/Infrastructure/UI/UIPanelType.cs.meta new file mode 100644 index 0000000..494d90a --- /dev/null +++ b/Assets/Code/Infrastructure/UI/UIPanelType.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e33d3007130a4cbbac8a5e962d5c4806 +timeCreated: 1768979170 \ No newline at end of file diff --git a/Assets/Code/Infrastructure/UI/UIService.cs b/Assets/Code/Infrastructure/UI/UIService.cs new file mode 100644 index 0000000..148b055 --- /dev/null +++ b/Assets/Code/Infrastructure/UI/UIService.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using Code.Infrastructure.EventBus; +using Reflex.Core; +using Reflex.Extensions; +using Reflex.Injectors; +using UnityEngine.SceneManagement; + +namespace Code.Infrastructure.UI +{ + public interface IUIService + { + void OpenPanel(UIPanelType uiPanelType); + void ClosePanel(UIPanelType uiPanelType); + void TryLoadPanel(UIPanelType uiPanelType); + void TryRemovePanel(UIPanelType uiPanelType); + void SetCanvasCamera(UnityEngine.Camera camera); + UIPanel TryGetPanel(UIPanelType panelType); + } + + public class UIService : IUIPanelLoadedHandle, IUIService + { + private Dictionary _loadedPanels = new(); + + private EventBus.EventBus _eventBus; + private IUIFactory _factory; + + public UIService(EventBus.EventBus eventBus, IUIFactory factory) + { + _eventBus = eventBus; + _factory = factory; + eventBus.Subscribe(this); + } + + public void SetCanvasCamera(UnityEngine.Camera camera) + { + _factory.Canvas.worldCamera = camera; + _factory.Canvas.planeDistance = 0.3f; + } + + public void OpenPanel(UIPanelType uiPanelType) + { + _loadedPanels[uiPanelType].gameObject.SetActive(true); + _loadedPanels[uiPanelType].Open(); + } + + // TODO Close event + public void ClosePanel(UIPanelType uiPanelType) + { + _loadedPanels[uiPanelType].Close(() => { _loadedPanels[uiPanelType].gameObject.SetActive(false); }); + } + + public void TryLoadPanel(UIPanelType uiPanelType) => _factory.LoadPanel(uiPanelType); + + public void UIPanelLoaded(UIPanel panel, UIPanelType panelType) + { + _loadedPanels.Add(panelType, panel); + + if (panel.InjectMeSenpai) + GameObjectInjector.InjectRecursive(panel.gameObject, Container.RootContainer); + // GameObjectInjector.InjectRecursive(panel.gameObject, SceneManager.GetActiveScene().GetSceneContainer()); // Скорее всего будут нужны компоненты текущей сцены + + _eventBus.RaiseEvent(ui => ui.UILoaded(panelType)); + } + + public void TryRemovePanel(UIPanelType uiPanelType) + { + _loadedPanels[uiPanelType].Close(() => + { + _loadedPanels[uiPanelType].gameObject.SetActive(false); + RemovePanel(uiPanelType); + }); + } + + private void RemovePanel(UIPanelType panelType) + { + _factory.ReleasePanel(_loadedPanels[panelType]); + _loadedPanels.Remove(panelType); + } + + public UIPanel TryGetPanel(UIPanelType panelType) => _loadedPanels.GetValueOrDefault(panelType); + } + + public interface IUIPanelLoaded : IGlobalSubscriber + { + void UILoaded(UIPanelType uiPanelType); + } +} \ No newline at end of file diff --git a/Assets/Code/Infrastructure/UI/UIService.cs.meta b/Assets/Code/Infrastructure/UI/UIService.cs.meta new file mode 100644 index 0000000..8be06ed --- /dev/null +++ b/Assets/Code/Infrastructure/UI/UIService.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 0bcb777d18014c428f24113d73787e38 +timeCreated: 1768980594 \ No newline at end of file diff --git a/Assets/DefaultVolumeProfile.asset b/Assets/DefaultVolumeProfile.asset new file mode 100644 index 0000000..8cea4d2 --- /dev/null +++ b/Assets/DefaultVolumeProfile.asset @@ -0,0 +1,798 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-8186968272690591729 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3eb4b772797da9440885e8bd939e9560, type: 3} + m_Name: ColorCurves + m_EditorClassIdentifier: + active: 1 + master: + m_OverrideState: 1 + m_Value: + k__BackingField: 2 + m_Loop: 0 + m_ZeroValue: 0 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + red: + m_OverrideState: 1 + m_Value: + k__BackingField: 2 + m_Loop: 0 + m_ZeroValue: 0 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + green: + m_OverrideState: 1 + m_Value: + k__BackingField: 2 + m_Loop: 0 + m_ZeroValue: 0 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + blue: + m_OverrideState: 1 + m_Value: + k__BackingField: 2 + m_Loop: 0 + m_ZeroValue: 0 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + hueVsHue: + m_OverrideState: 1 + m_Value: + k__BackingField: 0 + m_Loop: 1 + m_ZeroValue: 0.5 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: [] + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + hueVsSat: + m_OverrideState: 1 + m_Value: + k__BackingField: 0 + m_Loop: 1 + m_ZeroValue: 0.5 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: [] + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + satVsSat: + m_OverrideState: 1 + m_Value: + k__BackingField: 0 + m_Loop: 0 + m_ZeroValue: 0.5 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: [] + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + lumVsSat: + m_OverrideState: 1 + m_Value: + k__BackingField: 0 + m_Loop: 0 + m_ZeroValue: 0.5 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: [] + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 +--- !u!114 &-7645455725216806467 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 29fa0085f50d5e54f8144f766051a691, type: 3} + m_Name: FilmGrain + m_EditorClassIdentifier: + active: 1 + type: + m_OverrideState: 1 + m_Value: 0 + intensity: + m_OverrideState: 1 + m_Value: 0 + response: + m_OverrideState: 1 + m_Value: 0.8 + texture: + m_OverrideState: 1 + m_Value: {fileID: 0} +--- !u!114 &-6951754464447674219 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 558a8e2b6826cf840aae193990ba9f2e, type: 3} + m_Name: ShadowsMidtonesHighlights + m_EditorClassIdentifier: + active: 1 + shadows: + m_OverrideState: 1 + m_Value: {x: 1, y: 1, z: 1, w: 0} + midtones: + m_OverrideState: 1 + m_Value: {x: 1, y: 1, z: 1, w: 0} + highlights: + m_OverrideState: 1 + m_Value: {x: 1, y: 1, z: 1, w: 0} + shadowsStart: + m_OverrideState: 1 + m_Value: 0 + shadowsEnd: + m_OverrideState: 1 + m_Value: 0.3 + highlightsStart: + m_OverrideState: 1 + m_Value: 0.55 + highlightsEnd: + m_OverrideState: 1 + m_Value: 1 +--- !u!114 &-4210620904635426390 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: cdfbdbb87d3286943a057f7791b43141, type: 3} + m_Name: ChannelMixer + m_EditorClassIdentifier: + active: 1 + redOutRedIn: + m_OverrideState: 1 + m_Value: 100 + redOutGreenIn: + m_OverrideState: 1 + m_Value: 0 + redOutBlueIn: + m_OverrideState: 1 + m_Value: 0 + greenOutRedIn: + m_OverrideState: 1 + m_Value: 0 + greenOutGreenIn: + m_OverrideState: 1 + m_Value: 100 + greenOutBlueIn: + m_OverrideState: 1 + m_Value: 0 + blueOutRedIn: + m_OverrideState: 1 + m_Value: 0 + blueOutGreenIn: + m_OverrideState: 1 + m_Value: 0 + blueOutBlueIn: + m_OverrideState: 1 + m_Value: 100 +--- !u!114 &-3978695733392391637 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0b2db86121404754db890f4c8dfe81b2, type: 3} + m_Name: Bloom + m_EditorClassIdentifier: + active: 1 + skipIterations: + m_OverrideState: 1 + m_Value: 1 + threshold: + m_OverrideState: 1 + m_Value: 0.9 + intensity: + m_OverrideState: 1 + m_Value: 0 + scatter: + m_OverrideState: 1 + m_Value: 0.7 + clamp: + m_OverrideState: 1 + m_Value: 65472 + tint: + m_OverrideState: 1 + m_Value: {r: 1, g: 1, b: 1, a: 1} + highQualityFiltering: + m_OverrideState: 1 + m_Value: 0 + filter: + m_OverrideState: 1 + m_Value: 0 + downscale: + m_OverrideState: 1 + m_Value: 0 + maxIterations: + m_OverrideState: 1 + m_Value: 6 + dirtTexture: + m_OverrideState: 1 + m_Value: {fileID: 0} + dimension: 1 + dirtIntensity: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &-3757877592200609216 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 70afe9e12c7a7ed47911bb608a23a8ff, type: 3} + m_Name: SplitToning + m_EditorClassIdentifier: + active: 1 + shadows: + m_OverrideState: 1 + m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1} + highlights: + m_OverrideState: 1 + m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1} + balance: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &-3386560400091697801 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c01700fd266d6914ababb731e09af2eb, type: 3} + m_Name: DepthOfField + m_EditorClassIdentifier: + active: 1 + mode: + m_OverrideState: 1 + m_Value: 0 + gaussianStart: + m_OverrideState: 1 + m_Value: 10 + gaussianEnd: + m_OverrideState: 1 + m_Value: 30 + gaussianMaxRadius: + m_OverrideState: 1 + m_Value: 1 + highQualitySampling: + m_OverrideState: 1 + m_Value: 0 + focusDistance: + m_OverrideState: 1 + m_Value: 10 + aperture: + m_OverrideState: 1 + m_Value: 5.6 + focalLength: + m_OverrideState: 1 + m_Value: 50 + bladeCount: + m_OverrideState: 1 + m_Value: 5 + bladeCurvature: + m_OverrideState: 1 + m_Value: 1 + bladeRotation: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &-1083910812058648046 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6bd486065ce11414fa40e631affc4900, type: 3} + m_Name: ProbeVolumesOptions + m_EditorClassIdentifier: + active: 1 + normalBias: + m_OverrideState: 1 + m_Value: 0.05 + viewBias: + m_OverrideState: 1 + m_Value: 0.1 + scaleBiasWithMinProbeDistance: + m_OverrideState: 1 + m_Value: 0 + samplingNoise: + m_OverrideState: 1 + m_Value: 0.1 + animateSamplingNoise: + m_OverrideState: 1 + m_Value: 1 + leakReductionMode: + m_OverrideState: 1 + m_Value: 2 + minValidDotProductValue: + m_OverrideState: 1 + m_Value: 0.1 + occlusionOnlyReflectionNormalization: + m_OverrideState: 1 + m_Value: 1 + intensityMultiplier: + m_OverrideState: 1 + m_Value: 1 + skyOcclusionIntensityMultiplier: + m_OverrideState: 1 + m_Value: 1 + worldOffset: + m_OverrideState: 1 + m_Value: {x: 0, y: 0, z: 0} +--- !u!114 &-277880470225475626 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ccf1aba9553839d41ae37dd52e9ebcce, type: 3} + m_Name: MotionBlur + m_EditorClassIdentifier: + active: 1 + mode: + m_OverrideState: 1 + m_Value: 0 + quality: + m_OverrideState: 1 + m_Value: 0 + intensity: + m_OverrideState: 1 + m_Value: 0 + clamp: + m_OverrideState: 1 + m_Value: 0.05 +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d7fd9488000d3734a9e00ee676215985, type: 3} + m_Name: DefaultVolumeProfile + m_EditorClassIdentifier: + components: + - {fileID: 1247868777620691934} + - {fileID: -277880470225475626} + - {fileID: 2338690907228448291} + - {fileID: 2335868966179733374} + - {fileID: 3460444317416099510} + - {fileID: 7933203426823181213} + - {fileID: 5762530381802164056} + - {fileID: -3386560400091697801} + - {fileID: 156332030701414761} + - {fileID: 9099548931283147545} + - {fileID: -3757877592200609216} + - {fileID: 2332282131777942337} + - {fileID: -4210620904635426390} + - {fileID: -6951754464447674219} + - {fileID: 8549655192814258499} + - {fileID: -7645455725216806467} + - {fileID: -8186968272690591729} + - {fileID: -3978695733392391637} + - {fileID: -1083910812058648046} +--- !u!114 &156332030701414761 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5485954d14dfb9a4c8ead8edb0ded5b1, type: 3} + m_Name: LiftGammaGain + m_EditorClassIdentifier: + active: 1 + lift: + m_OverrideState: 1 + m_Value: {x: 1, y: 1, z: 1, w: 0} + gamma: + m_OverrideState: 1 + m_Value: {x: 1, y: 1, z: 1, w: 0} + gain: + m_OverrideState: 1 + m_Value: {x: 1, y: 1, z: 1, w: 0} +--- !u!114 &1247868777620691934 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 97c23e3b12dc18c42a140437e53d3951, type: 3} + m_Name: Tonemapping + m_EditorClassIdentifier: + active: 1 + mode: + m_OverrideState: 1 + m_Value: 0 + neutralHDRRangeReductionMode: + m_OverrideState: 1 + m_Value: 2 + acesPreset: + m_OverrideState: 1 + m_Value: 3 + hueShiftAmount: + m_OverrideState: 1 + m_Value: 0 + detectPaperWhite: + m_OverrideState: 1 + m_Value: 0 + paperWhite: + m_OverrideState: 1 + m_Value: 300 + detectBrightnessLimits: + m_OverrideState: 1 + m_Value: 1 + minNits: + m_OverrideState: 1 + m_Value: 0.005 + maxNits: + m_OverrideState: 1 + m_Value: 1000 +--- !u!114 &2332282131777942337 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e021b4c809a781e468c2988c016ebbea, type: 3} + m_Name: ColorLookup + m_EditorClassIdentifier: + active: 1 + texture: + m_OverrideState: 1 + m_Value: {fileID: 0} + dimension: 1 + contribution: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &2335868966179733374 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c5e1dc532bcb41949b58bc4f2abfbb7e, type: 3} + m_Name: LensDistortion + m_EditorClassIdentifier: + active: 1 + intensity: + m_OverrideState: 1 + m_Value: 0 + xMultiplier: + m_OverrideState: 1 + m_Value: 1 + yMultiplier: + m_OverrideState: 1 + m_Value: 1 + center: + m_OverrideState: 1 + m_Value: {x: 0.5, y: 0.5} + scale: + m_OverrideState: 1 + m_Value: 1 +--- !u!114 &2338690907228448291 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 221518ef91623a7438a71fef23660601, type: 3} + m_Name: WhiteBalance + m_EditorClassIdentifier: + active: 1 + temperature: + m_OverrideState: 1 + m_Value: 0 + tint: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &3460444317416099510 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 81180773991d8724ab7f2d216912b564, type: 3} + m_Name: ChromaticAberration + m_EditorClassIdentifier: + active: 1 + intensity: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &5762530381802164056 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 06437c1ff663d574d9447842ba0a72e4, type: 3} + m_Name: ScreenSpaceLensFlare + m_EditorClassIdentifier: + active: 1 + intensity: + m_OverrideState: 1 + m_Value: 0 + tintColor: + m_OverrideState: 1 + m_Value: {r: 1, g: 1, b: 1, a: 1} + bloomMip: + m_OverrideState: 1 + m_Value: 1 + firstFlareIntensity: + m_OverrideState: 1 + m_Value: 1 + secondaryFlareIntensity: + m_OverrideState: 1 + m_Value: 1 + warpedFlareIntensity: + m_OverrideState: 1 + m_Value: 1 + warpedFlareScale: + m_OverrideState: 1 + m_Value: {x: 1, y: 1} + samples: + m_OverrideState: 1 + m_Value: 1 + sampleDimmer: + m_OverrideState: 1 + m_Value: 0.5 + vignetteEffect: + m_OverrideState: 1 + m_Value: 1 + startingPosition: + m_OverrideState: 1 + m_Value: 1.25 + scale: + m_OverrideState: 1 + m_Value: 1.5 + streaksIntensity: + m_OverrideState: 1 + m_Value: 0 + streaksLength: + m_OverrideState: 1 + m_Value: 0.5 + streaksOrientation: + m_OverrideState: 1 + m_Value: 0 + streaksThreshold: + m_OverrideState: 1 + m_Value: 0.25 + resolution: + m_OverrideState: 1 + m_Value: 4 + chromaticAbberationIntensity: + m_OverrideState: 1 + m_Value: 0.5 +--- !u!114 &7933203426823181213 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 66f335fb1ffd8684294ad653bf1c7564, type: 3} + m_Name: ColorAdjustments + m_EditorClassIdentifier: + active: 1 + postExposure: + m_OverrideState: 1 + m_Value: 0 + contrast: + m_OverrideState: 1 + m_Value: 0 + colorFilter: + m_OverrideState: 1 + m_Value: {r: 1, g: 1, b: 1, a: 1} + hueShift: + m_OverrideState: 1 + m_Value: 0 + saturation: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &8549655192814258499 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 899c54efeace73346a0a16faa3afe726, type: 3} + m_Name: Vignette + m_EditorClassIdentifier: + active: 1 + color: + m_OverrideState: 1 + m_Value: {r: 0, g: 0, b: 0, a: 1} + center: + m_OverrideState: 1 + m_Value: {x: 0.5, y: 0.5} + intensity: + m_OverrideState: 1 + m_Value: 0 + smoothness: + m_OverrideState: 1 + m_Value: 0.2 + rounded: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &9099548931283147545 +MonoBehaviour: + m_ObjectHideFlags: 3 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fb60a22f311433c4c962b888d1393f88, type: 3} + m_Name: PaniniProjection + m_EditorClassIdentifier: + active: 1 + distance: + m_OverrideState: 1 + m_Value: 0 + cropToFit: + m_OverrideState: 1 + m_Value: 1 diff --git a/Assets/DefaultVolumeProfile.asset.meta b/Assets/DefaultVolumeProfile.asset.meta new file mode 100644 index 0000000..5383e5d --- /dev/null +++ b/Assets/DefaultVolumeProfile.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3f9215ea0144899419cfbc0957140d3f +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/InputSystem_Actions.inputactions b/Assets/InputSystem_Actions.inputactions new file mode 100644 index 0000000..1a12cb9 --- /dev/null +++ b/Assets/InputSystem_Actions.inputactions @@ -0,0 +1,1057 @@ +{ + "name": "InputSystem_Actions", + "maps": [ + { + "name": "Player", + "id": "df70fa95-8a34-4494-b137-73ab6b9c7d37", + "actions": [ + { + "name": "Move", + "type": "Value", + "id": "351f2ccd-1f9f-44bf-9bec-d62ac5c5f408", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Look", + "type": "Value", + "id": "6b444451-8a00-4d00-a97e-f47457f736a8", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Attack", + "type": "Button", + "id": "6c2ab1b8-8984-453a-af3d-a3c78ae1679a", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Interact", + "type": "Button", + "id": "852140f2-7766-474d-8707-702459ba45f3", + "expectedControlType": "Button", + "processors": "", + "interactions": "Hold", + "initialStateCheck": false + }, + { + "name": "Crouch", + "type": "Button", + "id": "27c5f898-bc57-4ee1-8800-db469aca5fe3", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Jump", + "type": "Button", + "id": "f1ba0d36-48eb-4cd5-b651-1c94a6531f70", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Previous", + "type": "Button", + "id": "2776c80d-3c14-4091-8c56-d04ced07a2b0", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Next", + "type": "Button", + "id": "b7230bb6-fc9b-4f52-8b25-f5e19cb2c2ba", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Sprint", + "type": "Button", + "id": "641cd816-40e6-41b4-8c3d-04687c349290", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + } + ], + "bindings": [ + { + "name": "", + "id": "978bfe49-cc26-4a3d-ab7b-7d7a29327403", + "path": "/leftStick", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Move", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "WASD", + "id": "00ca640b-d935-4593-8157-c05846ea39b3", + "path": "Dpad", + "interactions": "", + "processors": "", + "groups": "", + "action": "Move", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "e2062cb9-1b15-46a2-838c-2f8d72a0bdd9", + "path": "/w", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "up", + "id": "8180e8bd-4097-4f4e-ab88-4523101a6ce9", + "path": "/upArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "320bffee-a40b-4347-ac70-c210eb8bc73a", + "path": "/s", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "1c5327b5-f71c-4f60-99c7-4e737386f1d1", + "path": "/downArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "d2581a9b-1d11-4566-b27d-b92aff5fabbc", + "path": "/a", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "2e46982e-44cc-431b-9f0b-c11910bf467a", + "path": "/leftArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "fcfe95b8-67b9-4526-84b5-5d0bc98d6400", + "path": "/d", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "77bff152-3580-4b21-b6de-dcd0c7e41164", + "path": "/rightArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "", + "id": "1635d3fe-58b6-4ba9-a4e2-f4b964f6b5c8", + "path": "/{Primary2DAxis}", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Move", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "3ea4d645-4504-4529-b061-ab81934c3752", + "path": "/stick", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Move", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "c1f7a91b-d0fd-4a62-997e-7fb9b69bf235", + "path": "/rightStick", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Look", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8c8e490b-c610-4785-884f-f04217b23ca4", + "path": "/delta", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse;Touch", + "action": "Look", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "3e5f5442-8668-4b27-a940-df99bad7e831", + "path": "/{Hatswitch}", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Look", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "143bb1cd-cc10-4eca-a2f0-a3664166fe91", + "path": "/buttonWest", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "05f6913d-c316-48b2-a6bb-e225f14c7960", + "path": "/leftButton", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "886e731e-7071-4ae4-95c0-e61739dad6fd", + "path": "/primaryTouch/tap", + "interactions": "", + "processors": "", + "groups": ";Touch", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "ee3d0cd2-254e-47a7-a8cb-bc94d9658c54", + "path": "/trigger", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8255d333-5683-4943-a58a-ccb207ff1dce", + "path": "/{PrimaryAction}", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "b3c1c7f0-bd20-4ee7-a0f1-899b24bca6d7", + "path": "/enter", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "cbac6039-9c09-46a1-b5f2-4e5124ccb5ed", + "path": "/2", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Next", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "e15ca19d-e649-4852-97d5-7fe8ccc44e94", + "path": "/dpad/right", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Next", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "f2e9ba44-c423-42a7-ad56-f20975884794", + "path": "/leftShift", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Sprint", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8cbb2f4b-a784-49cc-8d5e-c010b8c7f4e6", + "path": "/leftStickPress", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Sprint", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "d8bf24bf-3f2f-4160-a97c-38ec1eb520ba", + "path": "/trigger", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Sprint", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "eb40bb66-4559-4dfa-9a2f-820438abb426", + "path": "/space", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Jump", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "daba33a1-ad0c-4742-a909-43ad1cdfbeb6", + "path": "/buttonSouth", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Jump", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "603f3daf-40bd-4854-8724-93e8017f59e3", + "path": "/secondaryButton", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Jump", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "1534dc16-a6aa-499d-9c3a-22b47347b52a", + "path": "/1", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Previous", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "25060bbd-a3a6-476e-8fba-45ae484aad05", + "path": "/dpad/left", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Previous", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "1c04ea5f-b012-41d1-a6f7-02e963b52893", + "path": "/e", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Interact", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "b3f66d0b-7751-423f-908b-a11c5bd95930", + "path": "/buttonNorth", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Interact", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "4f4649ac-64a8-4a73-af11-b3faef356a4d", + "path": "/buttonEast", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Crouch", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "36e52cba-0905-478e-a818-f4bfcb9f3b9a", + "path": "/c", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Crouch", + "isComposite": false, + "isPartOfComposite": false + } + ] + }, + { + "name": "UI", + "id": "272f6d14-89ba-496f-b7ff-215263d3219f", + "actions": [ + { + "name": "Navigate", + "type": "PassThrough", + "id": "c95b2375-e6d9-4b88-9c4c-c5e76515df4b", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Submit", + "type": "Button", + "id": "7607c7b6-cd76-4816-beef-bd0341cfe950", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Cancel", + "type": "Button", + "id": "15cef263-9014-4fd5-94d9-4e4a6234a6ef", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Point", + "type": "PassThrough", + "id": "32b35790-4ed0-4e9a-aa41-69ac6d629449", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Click", + "type": "PassThrough", + "id": "3c7022bf-7922-4f7c-a998-c437916075ad", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "RightClick", + "type": "PassThrough", + "id": "44b200b1-1557-4083-816c-b22cbdf77ddf", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "MiddleClick", + "type": "PassThrough", + "id": "dad70c86-b58c-4b17-88ad-f5e53adf419e", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "ScrollWheel", + "type": "PassThrough", + "id": "0489e84a-4833-4c40-bfae-cea84b696689", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "TrackedDevicePosition", + "type": "PassThrough", + "id": "24908448-c609-4bc3-a128-ea258674378a", + "expectedControlType": "Vector3", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "TrackedDeviceOrientation", + "type": "PassThrough", + "id": "9caa3d8a-6b2f-4e8e-8bad-6ede561bd9be", + "expectedControlType": "Quaternion", + "processors": "", + "interactions": "", + "initialStateCheck": false + } + ], + "bindings": [ + { + "name": "Gamepad", + "id": "809f371f-c5e2-4e7a-83a1-d867598f40dd", + "path": "2DVector", + "interactions": "", + "processors": "", + "groups": "", + "action": "Navigate", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "14a5d6e8-4aaf-4119-a9ef-34b8c2c548bf", + "path": "/leftStick/up", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "up", + "id": "9144cbe6-05e1-4687-a6d7-24f99d23dd81", + "path": "/rightStick/up", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "2db08d65-c5fb-421b-983f-c71163608d67", + "path": "/leftStick/down", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "58748904-2ea9-4a80-8579-b500e6a76df8", + "path": "/rightStick/down", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "8ba04515-75aa-45de-966d-393d9bbd1c14", + "path": "/leftStick/left", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "712e721c-bdfb-4b23-a86c-a0d9fcfea921", + "path": "/rightStick/left", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "fcd248ae-a788-4676-a12e-f4d81205600b", + "path": "/leftStick/right", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "1f04d9bc-c50b-41a1-bfcc-afb75475ec20", + "path": "/rightStick/right", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "", + "id": "fb8277d4-c5cd-4663-9dc7-ee3f0b506d90", + "path": "/dpad", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "Joystick", + "id": "e25d9774-381c-4a61-b47c-7b6b299ad9f9", + "path": "2DVector", + "interactions": "", + "processors": "", + "groups": "", + "action": "Navigate", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "3db53b26-6601-41be-9887-63ac74e79d19", + "path": "/stick/up", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "0cb3e13e-3d90-4178-8ae6-d9c5501d653f", + "path": "/stick/down", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "0392d399-f6dd-4c82-8062-c1e9c0d34835", + "path": "/stick/left", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "942a66d9-d42f-43d6-8d70-ecb4ba5363bc", + "path": "/stick/right", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "Keyboard", + "id": "ff527021-f211-4c02-933e-5976594c46ed", + "path": "2DVector", + "interactions": "", + "processors": "", + "groups": "", + "action": "Navigate", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "563fbfdd-0f09-408d-aa75-8642c4f08ef0", + "path": "/w", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "up", + "id": "eb480147-c587-4a33-85ed-eb0ab9942c43", + "path": "/upArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "2bf42165-60bc-42ca-8072-8c13ab40239b", + "path": "/s", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "85d264ad-e0a0-4565-b7ff-1a37edde51ac", + "path": "/downArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "74214943-c580-44e4-98eb-ad7eebe17902", + "path": "/a", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "cea9b045-a000-445b-95b8-0c171af70a3b", + "path": "/leftArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "8607c725-d935-4808-84b1-8354e29bab63", + "path": "/d", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "4cda81dc-9edd-4e03-9d7c-a71a14345d0b", + "path": "/rightArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "", + "id": "9e92bb26-7e3b-4ec4-b06b-3c8f8e498ddc", + "path": "*/{Submit}", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse;Gamepad;Touch;Joystick;XR", + "action": "Submit", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "82627dcc-3b13-4ba9-841d-e4b746d6553e", + "path": "*/{Cancel}", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse;Gamepad;Touch;Joystick;XR", + "action": "Cancel", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "c52c8e0b-8179-41d3-b8a1-d149033bbe86", + "path": "/position", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "e1394cbc-336e-44ce-9ea8-6007ed6193f7", + "path": "/position", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "5693e57a-238a-46ed-b5ae-e64e6e574302", + "path": "/touch*/position", + "interactions": "", + "processors": "", + "groups": "Touch", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "4faf7dc9-b979-4210-aa8c-e808e1ef89f5", + "path": "/leftButton", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8d66d5ba-88d7-48e6-b1cd-198bbfef7ace", + "path": "/tip", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "47c2a644-3ebc-4dae-a106-589b7ca75b59", + "path": "/touch*/press", + "interactions": "", + "processors": "", + "groups": "Touch", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "bb9e6b34-44bf-4381-ac63-5aa15d19f677", + "path": "/trigger", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "38c99815-14ea-4617-8627-164d27641299", + "path": "/scroll", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "ScrollWheel", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "4c191405-5738-4d4b-a523-c6a301dbf754", + "path": "/rightButton", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "RightClick", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "24066f69-da47-44f3-a07e-0015fb02eb2e", + "path": "/middleButton", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "MiddleClick", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "7236c0d9-6ca3-47cf-a6ee-a97f5b59ea77", + "path": "/devicePosition", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "TrackedDevicePosition", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "23e01e3a-f935-4948-8d8b-9bcac77714fb", + "path": "/deviceRotation", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "TrackedDeviceOrientation", + "isComposite": false, + "isPartOfComposite": false + } + ] + } + ], + "controlSchemes": [ + { + "name": "Keyboard&Mouse", + "bindingGroup": "Keyboard&Mouse", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + }, + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "Gamepad", + "bindingGroup": "Gamepad", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "Touch", + "bindingGroup": "Touch", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "Joystick", + "bindingGroup": "Joystick", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "XR", + "bindingGroup": "XR", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + } + ] +} \ No newline at end of file diff --git a/Assets/InputSystem_Actions.inputactions.meta b/Assets/InputSystem_Actions.inputactions.meta new file mode 100644 index 0000000..e786d3e --- /dev/null +++ b/Assets/InputSystem_Actions.inputactions.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 2bcd2660ca9b64942af0de543d8d7100 +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: 8404be70184654265930450def6a9037, type: 3} + generateWrapperCode: 0 + wrapperCodePath: + wrapperClassName: + wrapperCodeNamespace: diff --git a/Assets/Plugins.meta b/Assets/Plugins.meta new file mode 100644 index 0000000..9bf04d2 --- /dev/null +++ b/Assets/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: aede24c794a9a64168ef45392c38c9cd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PrimeTween.meta b/Assets/Plugins/PrimeTween.meta new file mode 100644 index 0000000..1421330 --- /dev/null +++ b/Assets/Plugins/PrimeTween.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: da2e19a1c5f8b4e2ca7c1785ea338b2e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PrimeTween/PrimeTweenInstaller.asset b/Assets/Plugins/PrimeTween/PrimeTweenInstaller.asset new file mode 100644 index 0000000..e3d412e --- /dev/null +++ b/Assets/Plugins/PrimeTween/PrimeTweenInstaller.asset @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 118450fad714b420684eb5f984268b5c, type: 3} + m_Name: PrimeTweenInstaller + m_EditorClassIdentifier: + demoScene: {fileID: 102900000, guid: 13170347bc506405f88d3a87bd0982b3, type: 3} + demoSceneUrp: {fileID: 102900000, guid: 15054139658a5445689224c71f805b10, type: 3} + demoScenePro: {fileID: 102900000, guid: e8086c8f95dba534ca9bf16d3ce9ef31, type: 3} + demoSceneProUrp: {fileID: 102900000, guid: 2992e8ebf85ed47489b777c4d27b9952, type: 3} + uninstallButtonColor: {r: 1, g: 0.23113209, b: 0.2762581, a: 1} diff --git a/Assets/Plugins/PrimeTween/PrimeTweenInstaller.asset.meta b/Assets/Plugins/PrimeTween/PrimeTweenInstaller.asset.meta new file mode 100644 index 0000000..0987fe9 --- /dev/null +++ b/Assets/Plugins/PrimeTween/PrimeTweenInstaller.asset.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: aafeeef745bb4406db1b2cd166853022 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 252960 + packageName: "PrimeTween \xB7 High-Performance Animations and Sequences" + packageVersion: 1.3.7 + assetPath: Assets/Plugins/PrimeTween/PrimeTweenInstaller.asset + uploadId: 860556 diff --git a/Assets/Plugins/PrimeTween/internal.meta b/Assets/Plugins/PrimeTween/internal.meta new file mode 100644 index 0000000..bd0e4c4 --- /dev/null +++ b/Assets/Plugins/PrimeTween/internal.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f164d11d6323e46b786f187ecd84eb1b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Plugins/PrimeTween/internal/PrimeTween.Installer.asmdef b/Assets/Plugins/PrimeTween/internal/PrimeTween.Installer.asmdef new file mode 100644 index 0000000..4bc81f0 --- /dev/null +++ b/Assets/Plugins/PrimeTween/internal/PrimeTween.Installer.asmdef @@ -0,0 +1,29 @@ +{ + "name": "PrimeTween.Installer", + "rootNamespace": "", + "references": [ + "PrimeTween.Runtime" + ], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.kyrylokuzyk.primetween", + "expression": "1.0.0", + "define": "PRIME_TWEEN_INSTALLED" + }, + { + "name": "com.kyrylokuzyk.primetween", + "expression": "[1.3.6-pro]", + "define": "PRIME_TWEEN_PRO" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/Plugins/PrimeTween/internal/PrimeTween.Installer.asmdef.meta b/Assets/Plugins/PrimeTween/internal/PrimeTween.Installer.asmdef.meta new file mode 100644 index 0000000..f8314f4 --- /dev/null +++ b/Assets/Plugins/PrimeTween/internal/PrimeTween.Installer.asmdef.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 090d70c1ebac0421985bd70fa7f561ef +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 252960 + packageName: "PrimeTween \xB7 High-Performance Animations and Sequences" + packageVersion: 1.3.7 + assetPath: Assets/Plugins/PrimeTween/internal/PrimeTween.Installer.asmdef + uploadId: 860556 diff --git a/Assets/Plugins/PrimeTween/internal/PrimeTweenInstaller.cs b/Assets/Plugins/PrimeTween/internal/PrimeTweenInstaller.cs new file mode 100644 index 0000000..48c7f27 --- /dev/null +++ b/Assets/Plugins/PrimeTween/internal/PrimeTweenInstaller.cs @@ -0,0 +1,548 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using System.Text.RegularExpressions; +using UnityEditor; +using UnityEditor.PackageManager; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.Assertions; +using UnityEngine.Rendering; +using static UnityEngine.GUILayout; +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("PrimeTween.Internal")] + +namespace PrimeTween { + internal class PrimeTweenInstaller : ScriptableObject { + [SerializeField] internal SceneAsset demoScene; + [SerializeField] internal SceneAsset demoSceneUrp; + [SerializeField] internal SceneAsset demoScenePro; + [SerializeField] internal SceneAsset demoSceneProUrp; + [SerializeField] internal Color uninstallButtonColor; + + [ContextMenu(nameof(ResetReviewRequest))] void ResetReviewRequest() => ReviewRequest.ResetReviewRequest(); + [ContextMenu(nameof(DebugReviewRequest))] void DebugReviewRequest() => ReviewRequest.DebugReviewRequest(); + } + + [CustomEditor(typeof(PrimeTweenInstaller), false)] + internal class InstallerInspector : Editor { + internal const string pluginName = "PrimeTween"; + internal const string pluginPackageId = "com.kyrylokuzyk.primetween"; + internal const string tgzPath = "Assets/Plugins/PrimeTween/internal/com.kyrylokuzyk.primetween.tgz"; + internal const string newTgzPath = "Assets/Plugins/PrimeTween/internal/com.kyrylokuzyk.primetween-" + version + ".tgz"; + const string documentationUrl = "https://github.com/KyryloKuzyk/PrimeTween"; + bool isInstalled; + bool hasNewTgz; + GUIStyle boldButtonStyle; + GUIStyle uninstallButtonStyle; + GUIStyle wordWrapLabelStyle; + + void OnEnable() { + isInstalled = CheckPluginInstalled(); + hasNewTgz = File.Exists(newTgzPath); + } + + /// Use Package Manager because Unity 2018 doesn't support version defines + static bool CheckPluginInstalled() { + var listRequest = Client.List(true); + while (!listRequest.IsCompleted) { + } + return listRequest.Result.Any(_ => _.name == pluginPackageId); + } + + public override void OnInspectorGUI() { + if (boldButtonStyle == null) { + boldButtonStyle = new GUIStyle(GUI.skin.button) { fontStyle = FontStyle.Bold }; + } + var installer = (PrimeTweenInstaller)target; + if (uninstallButtonStyle == null) { + uninstallButtonStyle = new GUIStyle(GUI.skin.button) { normal = { textColor = installer.uninstallButtonColor } }; + } + if (wordWrapLabelStyle == null) { + wordWrapLabelStyle = new GUIStyle(GUI.skin.label) { wordWrap = true, richText = true, margin = new RectOffset(4, 4, 8, 8) }; + } + EditorGUI.indentLevel = 5; + Space(8); + Label(pluginName, EditorStyles.boldLabel); + Space(4); + if (!isInstalled) { + if (Button("Install " + pluginName)) { + installPlugin(); + } + return; + } + if (hasNewTgz) { + if (Button($"Update to {version}", boldButtonStyle)) { + ReviewRequest.OnPackageUpdate(); + } + Space(8); + } + if (Button("Documentation", boldButtonStyle)) { + Application.OpenURL(documentationUrl); + } + + Space(8); + var rpAsset = GraphicsSettings. + #if UNITY_2019_3_OR_NEWER + defaultRenderPipeline; + #else + renderPipelineAsset; + #endif + bool isUrp = rpAsset != null && rpAsset.GetType().Name.Contains("Universal"); + if (Button("Open Demo", boldButtonStyle)) { + var demoScene = isUrp ? installer.demoSceneUrp : installer.demoScene; + if (demoScene == null) { + Debug.LogError("Please re-import the plugin from Asset Store and import the 'Demo' folder.\n"); + return; + } + string path = AssetDatabase.GetAssetPath(demoScene); + EditorSceneManager.OpenScene(path); + } + #if PRIME_TWEEN_PRO + if (Button("Open Demo (Pro)", boldButtonStyle)) { + var demoScene = isUrp ? installer.demoSceneProUrp : installer.demoScenePro; + if (demoScene == null) { + Debug.LogError("Please re-import the plugin from Asset Store and import the 'Demo' folder.\n"); + return; + } + string path = AssetDatabase.GetAssetPath(demoScene); + EditorSceneManager.OpenScene(path); + } + #endif + #if UNITY_2019_4_OR_NEWER + if (Button("Import Basic Examples")) { + EditorUtility.DisplayDialog(pluginName, $"Please select the '{pluginName}' package in 'Package Manager', then press the 'Samples/Import' button at the bottom of the plugin's description.", "Ok"); + UnityEditor.PackageManager.UI.Window.Open(pluginPackageId); + } + #endif + if (Button("Support")) { + Application.OpenURL("https://github.com/KyryloKuzyk/PrimeTween#support"); + } + + Space(8); + if (Button("Uninstall", uninstallButtonStyle)) { + Client.Remove(pluginPackageId); + isInstalled = false; + var msg = $"Please remove the folder manually to uninstall {pluginName} completely: 'Assets/Plugins/{pluginName}'"; + EditorUtility.DisplayDialog(pluginName, msg, "Ok"); + Debug.Log(msg); + } + + if (EditorPrefs.GetBool(InsertCallbackBug.showInsertCallbackBugUi, false)) { + Space(24); + Label("Updating from PrimeTween [1.1.10 - 1.1.22]", EditorStyles.boldLabel); + Label("The behaviour of 'Sequence.ChainCallback()' and 'InsertCallback()' was fixed in PrimeTween 1.2.0 so the code written with older versions may work differently in some cases.", wordWrapLabelStyle); + if (Button("Find potential issues")) { + InsertCallbackBug.Find(); + } + BeginHorizontal(); + if (Button("More info")) { + Application.OpenURL(InsertCallbackBug.moreInfoUrl); + } + if (Button("Download version 1.1.22")) { + Application.OpenURL("https://github.com/KyryloKuzyk/PrimeTween/blob/545dcc52769d52841e282c772e98c8984bfeb243/Benchmarks/Packages/com.kyrylokuzyk.primetween.tgz"); + } + EndHorizontal(); + } + + Space(24); + Label("Enjoying PrimeTween?", EditorStyles.boldLabel); + Label("Consider leaving an honest review and starring PrimeTween on GitHub!\n\n" + + "Honest reviews make PrimeTween better and help other developers discover it.", wordWrapLabelStyle); + if (Button("Leave review!", GUI.skin.button)) { + ReviewRequest.DisableReviewRequest(); + ReviewRequest.OpenReviewsURL(); + } + } + + static void installPlugin() { + if (File.Exists(newTgzPath)) { + MoveAndRenameTgzArchive(); + } + + ReviewRequest.OnBeforeInstall(); + var path = $"file:../{tgzPath}"; + var addRequest = Client.Add(path); + while (!addRequest.IsCompleted) { + } + if (addRequest.Status == StatusCode.Success) { + Debug.Log($"{pluginName} installed successfully.\n" + + $"Offline documentation is located at Packages/{pluginName}/Documentation.md.\n" + + $"Online documentation: {documentationUrl}\n"); + } else { + Debug.LogError($"Please re-import the plugin from the Asset Store and check that the file exists: [{path}].\n\n{addRequest.Error?.message}\n"); + } + } + + internal static void MoveAndRenameTgzArchive() { + Assert.IsTrue(File.Exists(newTgzPath)); + Assert.IsTrue(File.Exists(newTgzPath + ".meta")); + File.Delete(tgzPath); + File.Delete(tgzPath + ".meta"); + File.Move(newTgzPath, tgzPath); + File.Move(newTgzPath + ".meta", tgzPath + ".meta"); + RevertTgzMeta(); + } + + static void RevertTgzMeta() { + const string path = tgzPath + ".meta"; + Assert.IsTrue(File.Exists(path), path); + File.WriteAllText(path, @"fileFormatVersion: 2 +guid: cdd0c4b9889044d73bc958a922ada300 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +"); + } + + [InitializeOnLoadMethod] + static void InitOnLoad() { + AssetDatabase.importPackageCompleted += name => { + if (name.Contains(pluginName)) { + if (CheckPluginInstalled()) { + ReviewRequest.OnPackageUpdate(); + } else { + var installer = AssetDatabase.LoadAssetAtPath("Assets/Plugins/PrimeTween/PrimeTweenInstaller.asset"); + EditorUtility.FocusProjectWindow(); // this is important to show the installer object in the Project window + Selection.activeObject = installer; + EditorGUIUtility.PingObject(installer); + EditorApplication.update += InstallAndUnsubscribeFromUpdate; + void InstallAndUnsubscribeFromUpdate() { + EditorApplication.update -= InstallAndUnsubscribeFromUpdate; + installPlugin(); + } + } + } + }; + } + + internal const string version = "1.3.8"; + } + + internal static class FixedUpdateParameterMigration { + internal static string[] FindLocalScriptGuids() { + var listRequest = Client.List(true); + while (!listRequest.IsCompleted) { + } + Assert.AreEqual(StatusCode.Success, listRequest.Status); + string[] folders = listRequest.Result + .Where(x => x.source == PackageSource.Embedded || x.source == PackageSource.Local) + .Where(x => x.name != InstallerInspector.pluginPackageId) + .Select(x => x.assetPath) + .Append("Assets") + .ToArray(); + return AssetDatabase.FindAssets("t:Script", folders); + } + + internal static bool Process(string[] scripts, bool? fixAutomatically = null) { + var logSb = new StringBuilder(); + var fileSb = new StringBuilder(); + foreach (string guid in scripts) + { + string path = AssetDatabase.GUIDToAssetPath(guid); + var textAsset = AssetDatabase.LoadAssetAtPath(path); + string text = textAsset.text; + if (!Regex.IsMatch(text, @"using PrimeTween\s*;")) { + continue; + } + var parameterMatches = Regex.Matches(text, @"useFixedUpdate\s*:"); + if (parameterMatches.Count > 0) { + if (!fixAutomatically.HasValue) { + return true; + } + logSb.Clear(); + if (fixAutomatically.Value) { + fileSb.Clear(); + fileSb.Append(text); + for (int i = parameterMatches.Count - 1; i >= 0; i--) { + var paramMatch = parameterMatches[i]; + fileSb.Remove(paramMatch.Index, paramMatch.Length); + fileSb.Insert(paramMatch.Index, "updateType:"); + } + File.WriteAllText(path, fileSb.ToString()); + logSb.Append($"PrimeTween automatically renamed ({parameterMatches.Count}) occurrences of 'useFixedUpdate' to 'updateType' in file '{textAsset.name}.cs':\n"); + } else { + logSb.Append($"PrimeTween: please rename ({parameterMatches.Count}) occurrences of 'useFixedUpdate' to 'updateType' in file '{textAsset.name}.cs':\n"); + } + var lineMatches = Regex.Matches(text, @"(?m)^.*useFixedUpdate\s*:.*$", RegexOptions.Multiline); + foreach (Match match in lineMatches) { + logSb.Append($"{match.Value.Trim().Replace("useFixedUpdate", "useFixedUpdate")}\n"); + } + Debug.unityLogger.Log(fixAutomatically.Value ? LogType.Warning : LogType.Error, logSb.ToString()); + } + } + // no need to call AssetDatabase.Refresh() here because MoveAndRenameTgzArchive() already does that + return false; + } + } + + internal static class ReviewRequest { + const string version = InstallerInspector.version; + const string canAskKey = "PrimeTween.canAskForReview"; + const string versionKey = "PrimeTween.version"; + + internal static void OnPackageUpdate() { + log("OnPackageUpdate"); + if (!File.Exists(InstallerInspector.newTgzPath)) { + Debug.LogError($"The installation archive is missing: '{InstallerInspector.newTgzPath}'. Please re-import PrimeTween from Asset Store."); + return; + } + bool shouldAskForReview = true; + var scriptGuids = FixedUpdateParameterMigration.FindLocalScriptGuids(); + if (FixedUpdateParameterMigration.Process(scriptGuids)) { + shouldAskForReview = false; + const string msg = "'bool useFixedUpdate' parameter was changed to 'UpdateType updateType' in version 1.3.0, which will cause breaking changes in your current project.\n" + + "PrimeTween can fix the breaking changes automatically, or you can fix them manually after the update.\n"; + Debug.LogWarning($"PrimeTween: the {msg}"); + int response = EditorUtility.DisplayDialogComplex($"{InstallerInspector.pluginName} {version}", + $"The {msg}", + "Fix automatically", + "Cancel", + "Fix manually"); + string cancelMessage = $"PrimeTween: update to {version} was cancelled. You can trigger update manually from 'Assets/Plugins/PrimeTween/PrimeTweenInstaller'."; + if (response == 1) { + Debug.LogWarning(cancelMessage); + return; + } + if (!EditorUtility.DisplayDialog($"{InstallerInspector.pluginName} {version}", "Please back up your project before proceeding.", "OK", "Cancel")) { + Debug.LogWarning(cancelMessage); + return; + } + bool fixAutomatically = response == 0; + FixedUpdateParameterMigration.Process(scriptGuids, fixAutomatically); + } + InstallerInspector.MoveAndRenameTgzArchive(); + if (UNITY_2018) { + var removeRequest = Client.Remove(InstallerInspector.pluginPackageId); + while (!removeRequest.IsCompleted) { + } + string path = $"file:../{InstallerInspector.tgzPath}"; + Client.Add(path); + } else { + EditorApplication.ExecuteMenuItem("Assets/Refresh"); // AssetDatabase.Refresh() refreshes the project only partially + } + + string prevVersion = savedVersion; + if (savedVersion == version) { + log($"same version {version}"); + return; + } + savedVersion = version; + + if (InsertCallbackBug.IsUpdatingFromVersionWithBug(prevVersion)) { + InsertCallbackBug.Find(); + EditorPrefs.SetBool(InsertCallbackBug.showInsertCallbackBugUi, true); + shouldAskForReview = false; + } else { + EditorPrefs.SetBool(InsertCallbackBug.showInsertCallbackBugUi, false); + } + log($"updated from version {prevVersion} to {version}, {nameof(shouldAskForReview)}: {shouldAskForReview}"); + if (shouldAskForReview) { + TryAskForReview(); + } + } + + static bool UNITY_2018 { + get { + #if UNITY_2018 + return true; + #else + return false; + #endif + } + } + + static void TryAskForReview() { + if (!EditorPrefs.GetBool(canAskKey, true)) { + log("can't ask"); + return; + } + DisableReviewRequest(); + var response = EditorUtility.DisplayDialogComplex("Enjoying PrimeTween?", + "Would you mind to leave an honest review on Asset store? Honest reviews make PrimeTween better and help other developers discover it.", + "Sure, leave a review!", + "Never ask again", + ""); + if (response == 0) { + OpenReviewsURL(); + } + } + + internal static void OnBeforeInstall() { + log($"OnBeforeInstall {version}"); + if (string.IsNullOrEmpty(savedVersion)) { + savedVersion = version; + } + } + + static string savedVersion { + get => EditorPrefs.GetString(versionKey); + set => EditorPrefs.SetString(versionKey, value); + } + + internal static void DisableReviewRequest() => EditorPrefs.SetBool(canAskKey, false); + internal static void OpenReviewsURL() => Application.OpenURL("https://assetstore.unity.com/packages/slug/252960#reviews"); + + internal static void ResetReviewRequest() { + Debug.Log(nameof(ResetReviewRequest)); + EditorPrefs.DeleteKey(versionKey); + EditorPrefs.DeleteKey(canAskKey); + } + + internal static void DebugReviewRequest() { + Debug.Log(nameof(DebugReviewRequest)); + savedVersion = "1.1.22"; + EditorPrefs.SetBool(canAskKey, false); + // TryAskForReview(); + } + + [System.Diagnostics.Conditional("_")] + static void log(string msg) { + Debug.Log($"ReviewRequest: {msg}"); + } + } + + internal static class InsertCallbackBug { + internal const string moreInfoUrl = "https://github.com/KyryloKuzyk/PrimeTween/discussions/112"; + internal const string showInsertCallbackBugUi = "PrimeTween.showInsertCallbackBugUi"; + static Dictionary OpCodeDict; + static MethodInfo[] methodsWithBug; + static MethodInfo[] groupMethods; + + internal static bool IsUpdatingFromVersionWithBug(string prevVersionString) { + if (Version.TryParse(prevVersionString, out var prevVersion) + && new Version(1, 1, 10) <= prevVersion + && prevVersion <= new Version(1, 1, 22) + ) { + return true; + } + return false; + } + + internal static void Find() { + OpCodeDict = typeof(OpCodes) + .GetFields(BindingFlags.Public | BindingFlags.Static) + .Select(x => (OpCode)x.GetValue(null)) + .ToDictionary(x => x.Value, x => x); + #if PRIME_TWEEN_INSTALLED + methodsWithBug = typeof(Sequence).GetMethods(BindingFlags.Public | BindingFlags.Instance) + .Where(methodInfo => methodInfo.Name == nameof(Sequence.ChainCallback) || methodInfo.Name == nameof(Sequence.InsertCallback)) + .Select(methodInfo => methodInfo.IsGenericMethod ? methodInfo.GetGenericMethodDefinition() : methodInfo) + .ToArray(); + Assert.AreEqual(4, methodsWithBug.Length); + groupMethods = typeof(Sequence).GetMethods(BindingFlags.Public | BindingFlags.Instance) + .Where(methodInfo => methodInfo.Name == nameof(Sequence.Group)) + .ToArray(); + #endif + Assert.AreEqual(2, groupMethods.Length); + + string methodAssemblyName = methodsWithBug[0].Module.Assembly.FullName; + const BindingFlags findAll = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; + int numPotentialIssues = AppDomain.CurrentDomain.GetAssemblies() + .Where(assembly => assembly.GetReferencedAssemblies().Any(dependency => dependency.FullName == methodAssemblyName)) + .Where(assembly => !assembly.GetName().Name.StartsWith("PrimeTween.", StringComparison.Ordinal)) + .SelectMany(assembly => assembly.GetTypes()) + .SelectMany(type => type.GetMethods(findAll).Cast().Union(type.GetConstructors(findAll))) + .Count(method => FindInMethod(method)); + if (numPotentialIssues == 0) { + Debug.Log($"PrimeTween updated to version {InstallerInspector.version}: no potential issues found in ChainCallback() and InsertCallback() usages.\n" + + $"More info: {moreInfoUrl}\n"); + } + + int updateResponse = EditorUtility.DisplayDialogComplex($"{InstallerInspector.pluginName} {InstallerInspector.version}", + "PrimeTween 1.2.0 fixed a bug in ChainCallback() and InsertCallback() methods.\n" + + "This fix may introduce breaking changes in the existing projects. Please see the Console output for more details.", + "More info", + "Close", + ""); + if (updateResponse == 0) { + Application.OpenURL(moreInfoUrl); + } + } + + /// https://stackoverflow.com/a/33034906/1951038 + static bool FindInMethod(MethodBase method) { + byte[] il = method.GetMethodBody()?.GetILAsByteArray(); + if (il == null) { + return false; + } + bool bugFound = false; + using (var br = new BinaryReader(new MemoryStream(il))) { + while (br.BaseStream.Position < br.BaseStream.Length) { + byte firstByte = br.ReadByte(); + short opCodeValue = firstByte == 0xFE ? BitConverter.ToInt16(new[] { br.ReadByte(), firstByte }, 0) : firstByte; + OpCode opCode = OpCodeDict[opCodeValue]; + switch (opCode.OperandType) { + case OperandType.ShortInlineBrTarget: + case OperandType.ShortInlineVar: + case OperandType.ShortInlineI: + br.ReadByte(); + break; + case OperandType.InlineVar: + br.ReadInt16(); + break; + case OperandType.InlineField: + case OperandType.InlineType: + case OperandType.ShortInlineR: + case OperandType.InlineString: + case OperandType.InlineSig: + case OperandType.InlineI: + case OperandType.InlineBrTarget: + br.ReadInt32(); + break; + case OperandType.InlineI8: + case OperandType.InlineR: + br.ReadInt64(); + break; + case OperandType.InlineSwitch: + var size = (int)br.ReadUInt32(); + br.ReadBytes(size * 4); + break; + case OperandType.InlineTok: + br.ReadUInt32(); + break; + case OperandType.InlineMethod: + int token = (int)br.ReadUInt32(); + if (method.Module.ResolveMethod(token) is MethodInfo resolvedMethod) { + if (bugFound) { + if (groupMethods.Contains(resolvedMethod)) { + Debug.LogError($"PrimeTween updated to version {InstallerInspector.version}: potential breaking change found in the '{method.DeclaringType}.{method.Name}()' method.\n" + + "Please double-check the behavior if Group() is called immediately after the ChainCallback() or InsertCallback() and apply the fix manually if necessary.\n" + + "Or use ChainCallbackObsolete/InsertCallbackObsolete() instead to preserve the old incorrect behavior.\n" + + $"More info: {moreInfoUrl}\n"); + return true; + } + } else { + bugFound = isMethodWithBug(resolvedMethod); + } + } + break; + case OperandType.InlineNone: + break; + default: + throw new Exception(); + } + } + } + return false; + } + + static bool isMethodWithBug(MethodInfo method) { + foreach (var methodWithBug in methodsWithBug) { + if (methodWithBug.IsGenericMethodDefinition && method.IsGenericMethod) { + if (methodWithBug == method.GetGenericMethodDefinition()) { + return true; + } + } else if (methodWithBug == method) { + return true; + } + } + return false; + } + } +} diff --git a/Assets/Plugins/PrimeTween/internal/PrimeTweenInstaller.cs.meta b/Assets/Plugins/PrimeTween/internal/PrimeTweenInstaller.cs.meta new file mode 100644 index 0000000..7b878a7 --- /dev/null +++ b/Assets/Plugins/PrimeTween/internal/PrimeTweenInstaller.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 118450fad714b420684eb5f984268b5c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 252960 + packageName: "PrimeTween \xB7 High-Performance Animations and Sequences" + packageVersion: 1.3.7 + assetPath: Assets/Plugins/PrimeTween/internal/PrimeTweenInstaller.cs + uploadId: 860556 diff --git a/Assets/Plugins/PrimeTween/internal/com.kyrylokuzyk.primetween.tgz b/Assets/Plugins/PrimeTween/internal/com.kyrylokuzyk.primetween.tgz new file mode 100644 index 0000000000000000000000000000000000000000..9278ac0fd7cac150cea1f1dc24330249135e101a GIT binary patch literal 384502 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