Repository: VirtueSky/sunflower
Branch: main
Commit: 58d97baec1c0
Files: 2099
Total size: 3.6 MB
Directory structure:
gitextract_ddb6gc8r/
├── .github/
│ └── workflows/
│ └── notify-discord-on-release.yml
├── LICENSE
├── LICENSE.meta
├── README.md
├── README.md.meta
├── VirtueSky/
│ ├── Advertising/
│ │ ├── Editor/
│ │ │ ├── AdSettingEditor.cs
│ │ │ ├── AdSettingEditor.cs.meta
│ │ │ ├── AdsWindowEditor.cs
│ │ │ ├── AdsWindowEditor.cs.meta
│ │ │ ├── Virtuesky.Sunflower.Advertising.Editor.asmdef
│ │ │ └── Virtuesky.Sunflower.Advertising.Editor.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── Admob/
│ │ │ │ ├── AdmobAdClient.cs
│ │ │ │ ├── AdmobAdClient.cs.meta
│ │ │ │ ├── AdmobAdUnitVariable.cs
│ │ │ │ ├── AdmobAdUnitVariable.cs.meta
│ │ │ │ ├── AdmodUnitVariable/
│ │ │ │ │ ├── AdmobAppOpenVariable.cs
│ │ │ │ │ ├── AdmobAppOpenVariable.cs.meta
│ │ │ │ │ ├── AdmobBannerVariable.cs
│ │ │ │ │ ├── AdmobBannerVariable.cs.meta
│ │ │ │ │ ├── AdmobInterVariable.cs
│ │ │ │ │ ├── AdmobInterVariable.cs.meta
│ │ │ │ │ ├── AdmobNativeOverlayVariable.cs
│ │ │ │ │ ├── AdmobNativeOverlayVariable.cs.meta
│ │ │ │ │ ├── AdmobRewardInterVariable.cs
│ │ │ │ │ ├── AdmobRewardInterVariable.cs.meta
│ │ │ │ │ ├── AdmobRewardVariable.cs
│ │ │ │ │ └── AdmobRewardVariable.cs.meta
│ │ │ │ └── AdmodUnitVariable.meta
│ │ │ ├── Admob.meta
│ │ │ ├── General/
│ │ │ │ ├── AdClient.cs
│ │ │ │ ├── AdClient.cs.meta
│ │ │ │ ├── AdInfo.cs
│ │ │ │ ├── AdInfo.cs.meta
│ │ │ │ ├── AdSetting.cs
│ │ │ │ ├── AdSetting.cs.meta
│ │ │ │ ├── AdStatic.cs
│ │ │ │ ├── AdStatic.cs.meta
│ │ │ │ ├── AdUnitVariable.cs
│ │ │ │ ├── AdUnitVariable.cs.meta
│ │ │ │ ├── Advertising.cs
│ │ │ │ ├── Advertising.cs.meta
│ │ │ │ ├── IAdUnit.cs
│ │ │ │ └── IAdUnit.cs.meta
│ │ │ ├── General.meta
│ │ │ ├── LevelPlay/
│ │ │ │ ├── LevelPlayAdClient.cs
│ │ │ │ ├── LevelPlayAdClient.cs.meta
│ │ │ │ ├── LevelPlayAdUnitVariable.cs
│ │ │ │ ├── LevelPlayAdUnitVariable.cs.meta
│ │ │ │ ├── LevelPlayUnitVariable/
│ │ │ │ │ ├── LevelPlayBannerVariable.cs
│ │ │ │ │ ├── LevelPlayBannerVariable.cs.meta
│ │ │ │ │ ├── LevelPlayInterVariable.cs
│ │ │ │ │ ├── LevelPlayInterVariable.cs.meta
│ │ │ │ │ ├── LevelPlayRewardVariable.cs
│ │ │ │ │ └── LevelPlayRewardVariable.cs.meta
│ │ │ │ └── LevelPlayUnitVariable.meta
│ │ │ ├── LevelPlay.meta
│ │ │ ├── Max/
│ │ │ │ ├── MaxAdClient.cs
│ │ │ │ ├── MaxAdClient.cs.meta
│ │ │ │ ├── MaxAdUnitVariable.cs
│ │ │ │ ├── MaxAdUnitVariable.cs.meta
│ │ │ │ ├── MaxUnitVariable/
│ │ │ │ │ ├── MaxAppOpenVariable.cs
│ │ │ │ │ ├── MaxAppOpenVariable.cs.meta
│ │ │ │ │ ├── MaxBannerVariable.cs
│ │ │ │ │ ├── MaxBannerVariable.cs.meta
│ │ │ │ │ ├── MaxInterVariable.cs
│ │ │ │ │ ├── MaxInterVariable.cs.meta
│ │ │ │ │ ├── MaxRewardVariable.cs
│ │ │ │ │ └── MaxRewardVariable.cs.meta
│ │ │ │ └── MaxUnitVariable.meta
│ │ │ ├── Max.meta
│ │ │ ├── Virtuesky.Sunflower.Advertising.asmdef
│ │ │ └── Virtuesky.Sunflower.Advertising.asmdef.meta
│ │ └── Runtime.meta
│ ├── Advertising.meta
│ ├── AssetFinder/
│ │ ├── Editor/
│ │ │ ├── Script/
│ │ │ │ ├── AssetFinder.cs
│ │ │ │ ├── AssetFinder.cs.meta
│ │ │ │ ├── AssetFinderAssetType.cs
│ │ │ │ ├── AssetFinderAssetType.cs.meta
│ │ │ │ ├── AssetFinderCacheEditor.cs
│ │ │ │ ├── AssetFinderCacheEditor.cs.meta
│ │ │ │ ├── AssetFinderDuplicate.cs
│ │ │ │ ├── AssetFinderDuplicate.cs.meta
│ │ │ │ ├── AssetFinderRef.cs
│ │ │ │ ├── AssetFinderRef.cs.meta
│ │ │ │ ├── AssetFinderWindowExtension.cs
│ │ │ │ ├── AssetFinderWindowExtension.cs.meta
│ │ │ │ ├── Core/
│ │ │ │ │ ├── AssetFinderAsset.AssetState.cs
│ │ │ │ │ ├── AssetFinderAsset.AssetState.cs.meta
│ │ │ │ │ ├── AssetFinderAsset.AssetType.cs
│ │ │ │ │ ├── AssetFinderAsset.AssetType.cs.meta
│ │ │ │ │ ├── AssetFinderAsset.Classes.cs
│ │ │ │ │ ├── AssetFinderAsset.Classes.cs.meta
│ │ │ │ │ ├── AssetFinderAsset.Constants.cs
│ │ │ │ │ ├── AssetFinderAsset.Constants.cs.meta
│ │ │ │ │ ├── AssetFinderAsset.ContentLoader.cs
│ │ │ │ │ ├── AssetFinderAsset.ContentLoader.cs.meta
│ │ │ │ │ ├── AssetFinderAsset.Drawing.cs
│ │ │ │ │ ├── AssetFinderAsset.Drawing.cs.meta
│ │ │ │ │ ├── AssetFinderAsset.FileInfo.cs
│ │ │ │ │ ├── AssetFinderAsset.FileInfo.cs.meta
│ │ │ │ │ ├── AssetFinderAsset.GuidManager.cs
│ │ │ │ │ ├── AssetFinderAsset.GuidManager.cs.meta
│ │ │ │ │ ├── AssetFinderAsset.PathInfo.cs
│ │ │ │ │ ├── AssetFinderAsset.PathInfo.cs.meta
│ │ │ │ │ ├── AssetFinderAsset.cs
│ │ │ │ │ ├── AssetFinderAsset.cs.meta
│ │ │ │ │ ├── AssetFinderAutoRefreshMode.cs
│ │ │ │ │ ├── AssetFinderAutoRefreshMode.cs.meta
│ │ │ │ │ ├── AssetFinderCache.AssetSearch.cs
│ │ │ │ │ ├── AssetFinderCache.AssetSearch.cs.meta
│ │ │ │ │ ├── AssetFinderCache.AsyncProcessor.cs
│ │ │ │ │ ├── AssetFinderCache.AsyncProcessor.cs.meta
│ │ │ │ │ ├── AssetFinderCache.Constants.cs
│ │ │ │ │ ├── AssetFinderCache.Constants.cs.meta
│ │ │ │ │ ├── AssetFinderCache.Lifecycle.cs
│ │ │ │ │ ├── AssetFinderCache.Lifecycle.cs.meta
│ │ │ │ │ ├── AssetFinderCache.ProjectManager.cs
│ │ │ │ │ ├── AssetFinderCache.ProjectManager.cs.meta
│ │ │ │ │ ├── AssetFinderCache.Scanner.cs
│ │ │ │ │ ├── AssetFinderCache.Scanner.cs.meta
│ │ │ │ │ ├── AssetFinderCache.Search.cs
│ │ │ │ │ ├── AssetFinderCache.Search.cs.meta
│ │ │ │ │ ├── AssetFinderCache.cs
│ │ │ │ │ ├── AssetFinderCache.cs.meta
│ │ │ │ │ ├── AssetFinderGitUtil.cs
│ │ │ │ │ ├── AssetFinderGitUtil.cs.meta
│ │ │ │ │ ├── AssetFinderNavigationHistory.cs
│ │ │ │ │ ├── AssetFinderNavigationHistory.cs.meta
│ │ │ │ │ ├── AssetFinderSelectionManager.cs
│ │ │ │ │ ├── AssetFinderSelectionManager.cs.meta
│ │ │ │ │ ├── AssetFinderSettingExt.cs
│ │ │ │ │ ├── AssetFinderSettingExt.cs.meta
│ │ │ │ │ ├── AssetFinderSmartLock.cs
│ │ │ │ │ ├── AssetFinderSmartLock.cs.meta
│ │ │ │ │ ├── AssetFinderWindowFocus.cs
│ │ │ │ │ └── AssetFinderWindowFocus.cs.meta
│ │ │ │ ├── Core.meta
│ │ │ │ ├── Dev/
│ │ │ │ │ ├── AssetFinderDefine.cs
│ │ │ │ │ ├── AssetFinderDefine.cs.meta
│ │ │ │ │ ├── AssetFinderDev.cs
│ │ │ │ │ ├── AssetFinderDev.cs.meta
│ │ │ │ │ ├── AssetFinderReferenceValidator.cs
│ │ │ │ │ ├── AssetFinderReferenceValidator.cs.meta
│ │ │ │ │ ├── AssetFinderWindowAll.Validator.cs
│ │ │ │ │ └── AssetFinderWindowAll.Validator.cs.meta
│ │ │ │ ├── Dev.meta
│ │ │ │ ├── Drawer/
│ │ │ │ │ ├── AssetFinderAddressableDrawer.cs
│ │ │ │ │ ├── AssetFinderAddressableDrawer.cs.meta
│ │ │ │ │ ├── AssetFinderAssetGroup.cs
│ │ │ │ │ ├── AssetFinderAssetGroup.cs.meta
│ │ │ │ │ ├── AssetFinderAssetGroupDrawer.cs
│ │ │ │ │ ├── AssetFinderAssetGroupDrawer.cs.meta
│ │ │ │ │ ├── AssetFinderAssetOrganizer.cs
│ │ │ │ │ ├── AssetFinderAssetOrganizer.cs.meta
│ │ │ │ │ ├── AssetFinderBookmark.cs
│ │ │ │ │ ├── AssetFinderBookmark.cs.meta
│ │ │ │ │ ├── AssetFinderDeleteEmptyFolder.cs
│ │ │ │ │ ├── AssetFinderDeleteEmptyFolder.cs.meta
│ │ │ │ │ ├── AssetFinderIgnoreDrawer.cs
│ │ │ │ │ ├── AssetFinderIgnoreDrawer.cs.meta
│ │ │ │ │ ├── AssetFinderMissingReference.cs
│ │ │ │ │ ├── AssetFinderMissingReference.cs.meta
│ │ │ │ │ ├── AssetFinderSetting.cs
│ │ │ │ │ ├── AssetFinderSetting.cs.meta
│ │ │ │ │ ├── AssetFinderUsedInBuild.cs
│ │ │ │ │ └── AssetFinderUsedInBuild.cs.meta
│ │ │ │ ├── Drawer.meta
│ │ │ │ ├── Duplicate/
│ │ │ │ │ ├── AssetFinderChunk.cs
│ │ │ │ │ ├── AssetFinderChunk.cs.meta
│ │ │ │ │ ├── AssetFinderDuplicateTree2.cs
│ │ │ │ │ ├── AssetFinderDuplicateTree2.cs.meta
│ │ │ │ │ ├── AssetFinderFileCompare.cs
│ │ │ │ │ ├── AssetFinderFileCompare.cs.meta
│ │ │ │ │ ├── AssetFinderHead.cs
│ │ │ │ │ └── AssetFinderHead.cs.meta
│ │ │ │ ├── Duplicate.meta
│ │ │ │ ├── Extension/
│ │ │ │ │ ├── AssetFinderExtension.cs
│ │ │ │ │ ├── AssetFinderExtension.cs.meta
│ │ │ │ │ ├── AssetFinderHelper.cs
│ │ │ │ │ ├── AssetFinderHelper.cs.meta
│ │ │ │ │ ├── AssetFinderUnity.cs
│ │ │ │ │ ├── AssetFinderUnity.cs.meta
│ │ │ │ │ ├── GUI2.cs
│ │ │ │ │ └── GUI2.cs.meta
│ │ │ │ ├── Extension.meta
│ │ │ │ ├── Extensions/
│ │ │ │ │ ├── AssetFinderWindowExtensions.cs
│ │ │ │ │ ├── AssetFinderWindowExtensions.cs.meta
│ │ │ │ │ ├── CollectionExtensions.cs
│ │ │ │ │ ├── CollectionExtensions.cs.meta
│ │ │ │ │ ├── ColorExtensions.cs
│ │ │ │ │ ├── ColorExtensions.cs.meta
│ │ │ │ │ ├── DictionaryExtensions.cs
│ │ │ │ │ ├── DictionaryExtensions.cs.meta
│ │ │ │ │ ├── GameObjectExtensions.cs
│ │ │ │ │ ├── GameObjectExtensions.cs.meta
│ │ │ │ │ ├── RectExtensions.cs
│ │ │ │ │ ├── RectExtensions.cs.meta
│ │ │ │ │ ├── RectGUIExtensions.cs
│ │ │ │ │ ├── RectGUIExtensions.cs.meta
│ │ │ │ │ ├── SerializedObjectExtensions.cs
│ │ │ │ │ ├── SerializedObjectExtensions.cs.meta
│ │ │ │ │ ├── TabExtensions.cs
│ │ │ │ │ ├── TabExtensions.cs.meta
│ │ │ │ │ ├── UnityObjectExtensions.cs
│ │ │ │ │ └── UnityObjectExtensions.cs.meta
│ │ │ │ ├── Extensions.meta
│ │ │ │ ├── Modules/
│ │ │ │ │ ├── Addressable/
│ │ │ │ │ │ ├── AssetFinderAddressable.ASMStatus.cs
│ │ │ │ │ │ ├── AssetFinderAddressable.ASMStatus.cs.meta
│ │ │ │ │ │ ├── AssetFinderAddressable.AddressInfo.cs
│ │ │ │ │ │ ├── AssetFinderAddressable.AddressInfo.cs.meta
│ │ │ │ │ │ ├── AssetFinderAddressable.ProjectStatus.cs
│ │ │ │ │ │ ├── AssetFinderAddressable.ProjectStatus.cs.meta
│ │ │ │ │ │ ├── AssetFinderAddressable.cs
│ │ │ │ │ │ └── AssetFinderAddressable.cs.meta
│ │ │ │ │ ├── Addressable.meta
│ │ │ │ │ ├── AssetFinderCSV.cs
│ │ │ │ │ ├── AssetFinderCSV.cs.meta
│ │ │ │ │ ├── AssetFinderExport.cs
│ │ │ │ │ ├── AssetFinderExport.cs.meta
│ │ │ │ │ ├── AssetFinderSceneCache.cs
│ │ │ │ │ ├── AssetFinderSceneCache.cs.meta
│ │ │ │ │ ├── AssetFinderSelection.cs
│ │ │ │ │ ├── AssetFinderSelection.cs.meta
│ │ │ │ │ ├── Lightmap/
│ │ │ │ │ │ ├── AssetFinderLightmap.EnlightenRendererInformation.cs
│ │ │ │ │ │ ├── AssetFinderLightmap.EnlightenRendererInformation.cs.meta
│ │ │ │ │ │ ├── AssetFinderLightmap.EnlightenSceneMapping.cs
│ │ │ │ │ │ ├── AssetFinderLightmap.EnlightenSceneMapping.cs.meta
│ │ │ │ │ │ ├── AssetFinderLightmap.EnlightenSystemAtlasInformation.cs
│ │ │ │ │ │ ├── AssetFinderLightmap.EnlightenSystemAtlasInformation.cs.meta
│ │ │ │ │ │ ├── AssetFinderLightmap.EnlightenSystemInformation.cs
│ │ │ │ │ │ ├── AssetFinderLightmap.EnlightenSystemInformation.cs.meta
│ │ │ │ │ │ ├── AssetFinderLightmap.EnlightenTerrainChunksInformation.cs
│ │ │ │ │ │ ├── AssetFinderLightmap.EnlightenTerrainChunksInformation.cs.meta
│ │ │ │ │ │ ├── AssetFinderLightmap.LightBakingOutput.cs
│ │ │ │ │ │ ├── AssetFinderLightmap.LightBakingOutput.cs.meta
│ │ │ │ │ │ ├── AssetFinderLightmap.LightingDataAssetRoot.cs
│ │ │ │ │ │ ├── AssetFinderLightmap.LightingDataAssetRoot.cs.meta
│ │ │ │ │ │ ├── AssetFinderLightmap.LightmapData.cs
│ │ │ │ │ │ ├── AssetFinderLightmap.LightmapData.cs.meta
│ │ │ │ │ │ ├── AssetFinderLightmap.RendererData.cs
│ │ │ │ │ │ ├── AssetFinderLightmap.RendererData.cs.meta
│ │ │ │ │ │ ├── AssetFinderLightmap.SceneObjectIdentifier.cs
│ │ │ │ │ │ ├── AssetFinderLightmap.SceneObjectIdentifier.cs.meta
│ │ │ │ │ │ ├── AssetFinderLightmap.cs
│ │ │ │ │ │ └── AssetFinderLightmap.cs.meta
│ │ │ │ │ └── Lightmap.meta
│ │ │ │ ├── Modules.meta
│ │ │ │ ├── Monitor/
│ │ │ │ │ ├── AssetFinderCacheHelper.cs
│ │ │ │ │ └── AssetFinderCacheHelper.cs.meta
│ │ │ │ ├── Monitor.meta
│ │ │ │ ├── TreeUI/
│ │ │ │ │ ├── AssetFinderRef.cs
│ │ │ │ │ ├── AssetFinderRef.cs.meta
│ │ │ │ │ ├── AssetFinderRefDrawer.Mode.cs
│ │ │ │ │ ├── AssetFinderRefDrawer.Mode.cs.meta
│ │ │ │ │ ├── AssetFinderRefDrawer.Sort.cs
│ │ │ │ │ ├── AssetFinderRefDrawer.Sort.cs.meta
│ │ │ │ │ ├── AssetFinderRefDrawer.cs
│ │ │ │ │ ├── AssetFinderRefDrawer.cs.meta
│ │ │ │ │ ├── AssetFinderSceneRef.cs
│ │ │ │ │ ├── AssetFinderSceneRef.cs.meta
│ │ │ │ │ ├── AssetFinderTreeUI2.cs
│ │ │ │ │ └── AssetFinderTreeUI2.cs.meta
│ │ │ │ ├── TreeUI.meta
│ │ │ │ ├── UI/
│ │ │ │ │ ├── AssetFinderDeleteButton.cs
│ │ │ │ │ ├── AssetFinderDeleteButton.cs.meta
│ │ │ │ │ ├── AssetFinderEnumDrawer.cs
│ │ │ │ │ ├── AssetFinderEnumDrawer.cs.meta
│ │ │ │ │ ├── AssetFinderGUIContent.cs
│ │ │ │ │ ├── AssetFinderGUIContent.cs.meta
│ │ │ │ │ ├── AssetFinderIcon.cs
│ │ │ │ │ ├── AssetFinderIcon.cs.meta
│ │ │ │ │ ├── AssetFinderObjectDrawer.cs
│ │ │ │ │ ├── AssetFinderObjectDrawer.cs.meta
│ │ │ │ │ ├── AssetFinderSearchView.cs
│ │ │ │ │ ├── AssetFinderSearchView.cs.meta
│ │ │ │ │ ├── AssetFinderSplitView.cs
│ │ │ │ │ ├── AssetFinderSplitView.cs.meta
│ │ │ │ │ ├── AssetFinderTabView.cs
│ │ │ │ │ ├── AssetFinderTabView.cs.meta
│ │ │ │ │ ├── AssetFinderToggleList.cs
│ │ │ │ │ ├── AssetFinderToggleList.cs.meta
│ │ │ │ │ ├── Theme/
│ │ │ │ │ │ ├── AssetFinderTheme.Dark.cs
│ │ │ │ │ │ ├── AssetFinderTheme.Dark.cs.meta
│ │ │ │ │ │ ├── AssetFinderTheme.Light.cs
│ │ │ │ │ │ ├── AssetFinderTheme.Light.cs.meta
│ │ │ │ │ │ ├── AssetFinderTheme.cs
│ │ │ │ │ │ └── AssetFinderTheme.cs.meta
│ │ │ │ │ └── Theme.meta
│ │ │ │ ├── UI.meta
│ │ │ │ ├── Window/
│ │ │ │ │ ├── AssetFinderWindowAll.CacheManager.cs
│ │ │ │ │ ├── AssetFinderWindowAll.CacheManager.cs.meta
│ │ │ │ │ ├── AssetFinderWindowAll.Drawing.cs
│ │ │ │ │ ├── AssetFinderWindowAll.Drawing.cs.meta
│ │ │ │ │ ├── AssetFinderWindowAll.GuidManager.cs
│ │ │ │ │ ├── AssetFinderWindowAll.GuidManager.cs.meta
│ │ │ │ │ ├── AssetFinderWindowAll.Initialization.cs
│ │ │ │ │ ├── AssetFinderWindowAll.Initialization.cs.meta
│ │ │ │ │ ├── AssetFinderWindowAll.PanelSettings.cs
│ │ │ │ │ ├── AssetFinderWindowAll.PanelSettings.cs.meta
│ │ │ │ │ ├── AssetFinderWindowAll.SelectionManager.cs
│ │ │ │ │ ├── AssetFinderWindowAll.SelectionManager.cs.meta
│ │ │ │ │ ├── AssetFinderWindowAll.SettingsPanel.cs
│ │ │ │ │ ├── AssetFinderWindowAll.SettingsPanel.cs.meta
│ │ │ │ │ ├── AssetFinderWindowAll.ToolsPanel.cs
│ │ │ │ │ ├── AssetFinderWindowAll.ToolsPanel.cs.meta
│ │ │ │ │ ├── AssetFinderWindowAll.UILayout.cs
│ │ │ │ │ ├── AssetFinderWindowAll.UILayout.cs.meta
│ │ │ │ │ ├── AssetFinderWindowAll.cs
│ │ │ │ │ ├── AssetFinderWindowAll.cs.meta
│ │ │ │ │ ├── AssetFinderWindowBase.cs
│ │ │ │ │ ├── AssetFinderWindowBase.cs.meta
│ │ │ │ │ ├── IRefDraw.cs
│ │ │ │ │ ├── IRefDraw.cs.meta
│ │ │ │ │ ├── IWindow.cs
│ │ │ │ │ └── IWindow.cs.meta
│ │ │ │ └── Window.meta
│ │ │ ├── Script.meta
│ │ │ ├── Virtuesky.Sunflower.AssetFinder.Editor.asmdef
│ │ │ ├── Virtuesky.Sunflower.AssetFinder.Editor.asmdef.meta
│ │ │ ├── v2/
│ │ │ │ ├── AssetFinder.cs
│ │ │ │ ├── AssetFinder.cs.meta
│ │ │ │ ├── Core/
│ │ │ │ │ ├── AssetFinderAssetDB.cs
│ │ │ │ │ ├── AssetFinderAssetDB.cs.meta
│ │ │ │ │ ├── AssetFinderAssetFile.cs
│ │ │ │ │ ├── AssetFinderAssetFile.cs.meta
│ │ │ │ │ ├── AssetFinderCacheAsset.cs
│ │ │ │ │ ├── AssetFinderCacheAsset.cs.meta
│ │ │ │ │ ├── AssetFinderID.cs
│ │ │ │ │ ├── AssetFinderID.cs.meta
│ │ │ │ │ ├── AssetFinderIDRef.cs
│ │ │ │ │ ├── AssetFinderIDRef.cs.meta
│ │ │ │ │ ├── AssetFinderSceneObject.cs
│ │ │ │ │ ├── AssetFinderSceneObject.cs.meta
│ │ │ │ │ ├── SubAssetDetail.cs
│ │ │ │ │ └── SubAssetDetail.cs.meta
│ │ │ │ ├── Core.meta
│ │ │ │ ├── Drawer/
│ │ │ │ │ ├── AssetFinderAssetFileDrawer.cs
│ │ │ │ │ ├── AssetFinderAssetFileDrawer.cs.meta
│ │ │ │ │ ├── AssetFinderCacheAssetEditor.cs
│ │ │ │ │ ├── AssetFinderCacheAssetEditor.cs.meta
│ │ │ │ │ ├── AssetFinderIDDrawer.cs
│ │ │ │ │ └── AssetFinderIDDrawer.cs.meta
│ │ │ │ ├── Drawer.meta
│ │ │ │ ├── GUI/
│ │ │ │ │ ├── AssetFinderAssetGUI.cs
│ │ │ │ │ ├── AssetFinderAssetGUI.cs.meta
│ │ │ │ │ ├── AssetFinderAssetInfo.cs
│ │ │ │ │ └── AssetFinderAssetInfo.cs.meta
│ │ │ │ ├── GUI.meta
│ │ │ │ ├── Parser/
│ │ │ │ │ ├── AssetFinderParser.BinaryAsset.cs
│ │ │ │ │ ├── AssetFinderParser.BinaryAsset.cs.meta
│ │ │ │ │ ├── AssetFinderParser.LightMap.cs
│ │ │ │ │ ├── AssetFinderParser.LightMap.cs.meta
│ │ │ │ │ ├── AssetFinderParser.Model3D.cs
│ │ │ │ │ ├── AssetFinderParser.Model3D.cs.meta
│ │ │ │ │ ├── AssetFinderParser.Shader.cs
│ │ │ │ │ ├── AssetFinderParser.Shader.cs.meta
│ │ │ │ │ ├── AssetFinderParser.ShaderGraph.cs
│ │ │ │ │ ├── AssetFinderParser.ShaderGraph.cs.meta
│ │ │ │ │ ├── AssetFinderParser.Terrain.cs
│ │ │ │ │ ├── AssetFinderParser.Terrain.cs.meta
│ │ │ │ │ ├── AssetFinderParser.UIToolkit.cs
│ │ │ │ │ ├── AssetFinderParser.UIToolkit.cs.meta
│ │ │ │ │ ├── AssetFinderParser.Yaml.cs
│ │ │ │ │ ├── AssetFinderParser.Yaml.cs.meta
│ │ │ │ │ ├── AssetFinderParser.cs
│ │ │ │ │ └── AssetFinderParser.cs.meta
│ │ │ │ ├── Parser.meta
│ │ │ │ ├── UI/
│ │ │ │ │ ├── AssetRefUI.cs
│ │ │ │ │ ├── AssetRefUI.cs.meta
│ │ │ │ │ ├── AssetUI.cs
│ │ │ │ │ ├── AssetUI.cs.meta
│ │ │ │ │ ├── FileUI.cs
│ │ │ │ │ ├── FileUI.cs.meta
│ │ │ │ │ ├── FolderUI.cs
│ │ │ │ │ └── FolderUI.cs.meta
│ │ │ │ ├── UI.meta
│ │ │ │ ├── Unity/
│ │ │ │ │ ├── AssetFinderInitializer.cs
│ │ │ │ │ ├── AssetFinderInitializer.cs.meta
│ │ │ │ │ ├── AssetFinderLightmap.cs
│ │ │ │ │ ├── AssetFinderLightmap.cs.meta
│ │ │ │ │ ├── AssetFinderTerrain.cs
│ │ │ │ │ ├── AssetFinderTerrain.cs.meta
│ │ │ │ │ ├── AssetFinderUSelection.cs
│ │ │ │ │ └── AssetFinderUSelection.cs.meta
│ │ │ │ ├── Unity.meta
│ │ │ │ ├── Utils/
│ │ │ │ │ ├── AssetFinderTimeSlice.cs
│ │ │ │ │ └── AssetFinderTimeSlice.cs.meta
│ │ │ │ └── Utils.meta
│ │ │ └── v2.meta
│ │ └── Editor.meta
│ ├── AssetFinder.meta
│ ├── Audio/
│ │ ├── Editor/
│ │ │ ├── AudioWindowEditor.cs
│ │ │ ├── AudioWindowEditor.cs.meta
│ │ │ ├── EditorAudioPreview.cs
│ │ │ ├── EditorAudioPreview.cs.meta
│ │ │ ├── SoundDataEditor.cs
│ │ │ ├── SoundDataEditor.cs.meta
│ │ │ ├── Virtuesky.Sunflower.Audio.Editor.asmdef
│ │ │ └── Virtuesky.Sunflower.Audio.Editor.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── AudioHelper.cs
│ │ │ ├── AudioHelper.cs.meta
│ │ │ ├── AudioManager.cs
│ │ │ ├── AudioManager.cs.meta
│ │ │ ├── Music_Event/
│ │ │ │ ├── PauseMusicEvent.cs
│ │ │ │ ├── PauseMusicEvent.cs.meta
│ │ │ │ ├── PlayMusicEvent.cs
│ │ │ │ ├── PlayMusicEvent.cs.meta
│ │ │ │ ├── ResumeMusicEvent.cs
│ │ │ │ ├── ResumeMusicEvent.cs.meta
│ │ │ │ ├── StopMusicEvent.cs
│ │ │ │ └── StopMusicEvent.cs.meta
│ │ │ ├── Music_Event.meta
│ │ │ ├── Sfx_Event/
│ │ │ │ ├── FinishSfxEvent.cs
│ │ │ │ ├── FinishSfxEvent.cs.meta
│ │ │ │ ├── PauseSfxEvent.cs
│ │ │ │ ├── PauseSfxEvent.cs.meta
│ │ │ │ ├── PlaySfxEvent.cs
│ │ │ │ ├── PlaySfxEvent.cs.meta
│ │ │ │ ├── ResumeSfxEvent.cs
│ │ │ │ ├── ResumeSfxEvent.cs.meta
│ │ │ │ ├── StopAllSfxEvent.cs
│ │ │ │ ├── StopAllSfxEvent.cs.meta
│ │ │ │ ├── StopSfxEvent.cs
│ │ │ │ └── StopSfxEvent.cs.meta
│ │ │ ├── Sfx_Event.meta
│ │ │ ├── SoundCache.cs
│ │ │ ├── SoundCache.cs.meta
│ │ │ ├── SoundComponent.cs
│ │ │ ├── SoundComponent.cs.meta
│ │ │ ├── SoundData.cs
│ │ │ ├── SoundData.cs.meta
│ │ │ ├── Volume_Variable/
│ │ │ │ ├── MusicVolumeChange.cs
│ │ │ │ ├── MusicVolumeChange.cs.meta
│ │ │ │ ├── SfxVolumeChange.cs
│ │ │ │ └── SfxVolumeChange.cs.meta
│ │ │ ├── Volume_Variable.meta
│ │ │ ├── virtuesky.sunflower.audio.asmdef
│ │ │ └── virtuesky.sunflower.audio.asmdef.meta
│ │ └── Runtime.meta
│ ├── Audio.meta
│ ├── Button/
│ │ ├── Editor/
│ │ │ ├── ButtomCustomEditor.cs
│ │ │ ├── ButtomCustomEditor.cs.meta
│ │ │ ├── Virtuesky.Sunflower.Button.Editor.asmdef
│ │ │ └── Virtuesky.Sunflower.Button.Editor.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── ButtonCustom.cs
│ │ │ ├── ButtonCustom.cs.meta
│ │ │ ├── ButtonTMP.cs
│ │ │ ├── ButtonTMP.cs.meta
│ │ │ ├── ButtonText.cs
│ │ │ ├── ButtonText.cs.meta
│ │ │ ├── ButtonUI.cs
│ │ │ ├── ButtonUI.cs.meta
│ │ │ ├── ButtonUI_TMP.cs
│ │ │ ├── ButtonUI_TMP.cs.meta
│ │ │ ├── ButtonUI_Text.cs
│ │ │ ├── ButtonUI_Text.cs.meta
│ │ │ ├── Virtuesky.Sunflower.Button.asmdef
│ │ │ └── Virtuesky.Sunflower.Button.asmdef.meta
│ │ └── Runtime.meta
│ ├── Button.meta
│ ├── Component/
│ │ ├── AnimancerComponent/
│ │ │ ├── HandleAnimancerComponent.cs
│ │ │ └── HandleAnimancerComponent.cs.meta
│ │ ├── AnimancerComponent.meta
│ │ ├── BounceComponent.cs
│ │ ├── BounceComponent.cs.meta
│ │ ├── Buoyancy2DComponent.cs
│ │ ├── Buoyancy2DComponent.cs.meta
│ │ ├── BuoyancyComponent.cs
│ │ ├── BuoyancyComponent.cs.meta
│ │ ├── EffectAppearComponent.cs
│ │ ├── EffectAppearComponent.cs.meta
│ │ ├── EffectZoomInOutComponent.cs
│ │ ├── EffectZoomInOutComponent.cs.meta
│ │ ├── FollowTargetComponent.cs
│ │ ├── FollowTargetComponent.cs.meta
│ │ ├── MoveComponent.cs
│ │ ├── MoveComponent.cs.meta
│ │ ├── ResizeCameraOrthographicComponent.cs
│ │ ├── ResizeCameraOrthographicComponent.cs.meta
│ │ ├── ResizeMatchCanvasScalerComponent.cs
│ │ ├── ResizeMatchCanvasScalerComponent.cs.meta
│ │ ├── RotateComponent.cs
│ │ ├── RotateComponent.cs.meta
│ │ ├── SafeAreaComponent.cs
│ │ ├── SafeAreaComponent.cs.meta
│ │ ├── SkeletonComponent/
│ │ │ ├── AnimationSkeleton/
│ │ │ │ ├── AnimationSkeleton.cs
│ │ │ │ ├── AnimationSkeleton.cs.meta
│ │ │ │ ├── AnimationSkeletonComponent.cs
│ │ │ │ ├── AnimationSkeletonComponent.cs.meta
│ │ │ │ ├── AnimationSkeletonUIComponent.cs
│ │ │ │ └── AnimationSkeletonUIComponent.cs.meta
│ │ │ ├── AnimationSkeleton.meta
│ │ │ ├── SkinSkeleton/
│ │ │ │ ├── SkinSkeleton.cs
│ │ │ │ ├── SkinSkeleton.cs.meta
│ │ │ │ ├── SkinSkeletonComponent.cs
│ │ │ │ ├── SkinSkeletonComponent.cs.meta
│ │ │ │ ├── SkinSkeletonUIComponent.cs
│ │ │ │ └── SkinSkeletonUIComponent.cs.meta
│ │ │ └── SkinSkeleton.meta
│ │ ├── SkeletonComponent.meta
│ │ ├── TimeRemainingComponent.cs
│ │ ├── TimeRemainingComponent.cs.meta
│ │ ├── virtuesky.sunflower.component.asmdef
│ │ └── virtuesky.sunflower.component.asmdef.meta
│ ├── Component.meta
│ ├── ControlPanel/
│ │ ├── CPAboutDrawer.cs
│ │ ├── CPAboutDrawer.cs.meta
│ │ ├── CPAdjustDrawer.cs
│ │ ├── CPAdjustDrawer.cs.meta
│ │ ├── CPAdvertisingDrawer.cs
│ │ ├── CPAdvertisingDrawer.cs.meta
│ │ ├── CPAppsFlyerDrawer.cs
│ │ ├── CPAppsFlyerDrawer.cs.meta
│ │ ├── CPAssetFinderDrawer.cs
│ │ ├── CPAssetFinderDrawer.cs.meta
│ │ ├── CPAudioDrawer.cs
│ │ ├── CPAudioDrawer.cs.meta
│ │ ├── CPExtensionsDrawer.cs
│ │ ├── CPExtensionsDrawer.cs.meta
│ │ ├── CPFirebaseDrawer.cs
│ │ ├── CPFirebaseDrawer.cs.meta
│ │ ├── CPFolderIconDrawer.cs
│ │ ├── CPFolderIconDrawer.cs.meta
│ │ ├── CPGameServiceDrawer.cs
│ │ ├── CPGameServiceDrawer.cs.meta
│ │ ├── CPHierarchyDrawer.cs
│ │ ├── CPHierarchyDrawer.cs.meta
│ │ ├── CPIapDrawer.cs
│ │ ├── CPIapDrawer.cs.meta
│ │ ├── CPInAppReviewDrawer.cs
│ │ ├── CPInAppReviewDrawer.cs.meta
│ │ ├── CPLevelEditorDrawer.cs
│ │ ├── CPLevelEditorDrawer.cs.meta
│ │ ├── CPLocalizationDrawer.cs
│ │ ├── CPLocalizationDrawer.cs.meta
│ │ ├── CPNotificationChanelDrawer.cs
│ │ ├── CPNotificationChanelDrawer.cs.meta
│ │ ├── CPRegisterPackageDrawer.cs
│ │ ├── CPRegisterPackageDrawer.cs.meta
│ │ ├── CPScriptingDefineSymbolsDrawer.cs
│ │ ├── CPScriptingDefineSymbolsDrawer.cs.meta
│ │ ├── CPSoEventDrawer.cs
│ │ ├── CPSoEventDrawer.cs.meta
│ │ ├── CPSoVariableDrawer.cs
│ │ ├── CPSoVariableDrawer.cs.meta
│ │ ├── CPUtility.cs
│ │ ├── CPUtility.cs.meta
│ │ ├── ConstantControlPanel.cs
│ │ ├── ConstantControlPanel.cs.meta
│ │ ├── ConstantPackage.cs
│ │ ├── ConstantPackage.cs.meta
│ │ ├── ControlPanelWindowEditor.cs
│ │ ├── ControlPanelWindowEditor.cs.meta
│ │ ├── Virtuesky.Sunflower.ControlPanel.Editor.asmdef
│ │ └── Virtuesky.Sunflower.ControlPanel.Editor.asmdef.meta
│ ├── ControlPanel.meta
│ ├── Core/
│ │ ├── Editor/
│ │ │ ├── OptionalPropertyDrawer.cs
│ │ │ ├── OptionalPropertyDrawer.cs.meta
│ │ │ ├── VirtueSky.Sunflower.Core.Editor.asmdef
│ │ │ └── VirtueSky.Sunflower.Core.Editor.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── App.cs
│ │ │ ├── App.cs.meta
│ │ │ ├── BaseMono.cs
│ │ │ ├── BaseMono.cs.meta
│ │ │ ├── BaseSO.cs
│ │ │ ├── BaseSO.cs.meta
│ │ │ ├── CacheComponent.cs
│ │ │ ├── CacheComponent.cs.meta
│ │ │ ├── DelayHandle.cs
│ │ │ ├── DelayHandle.cs.meta
│ │ │ ├── IEntity.cs
│ │ │ ├── IEntity.cs.meta
│ │ │ ├── MonoGlobal.cs
│ │ │ ├── MonoGlobal.cs.meta
│ │ │ ├── Optional.cs
│ │ │ ├── Optional.cs.meta
│ │ │ ├── RuntimeInitialize.cs
│ │ │ ├── RuntimeInitialize.cs.meta
│ │ │ ├── UnityServiceInitialization.cs
│ │ │ ├── UnityServiceInitialization.cs.meta
│ │ │ ├── virtuesky.sunflower.core.asmdef
│ │ │ └── virtuesky.sunflower.core.asmdef.meta
│ │ └── Runtime.meta
│ ├── Core.meta
│ ├── DataStorage/
│ │ ├── Editor/
│ │ │ ├── DataWindowEditor.cs
│ │ │ ├── DataWindowEditor.cs.meta
│ │ │ ├── Virtuesky.Sunflower.DataStorage.Editor.asmdef
│ │ │ └── Virtuesky.Sunflower.DataStorage.Editor.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── GameData.cs
│ │ │ ├── GameData.cs.meta
│ │ │ ├── SerializeAdapter.cs
│ │ │ ├── SerializeAdapter.cs.meta
│ │ │ ├── Virtuesky.Sunflower.DataStorage.asmdef
│ │ │ └── Virtuesky.Sunflower.DataStorage.asmdef.meta
│ │ └── Runtime.meta
│ ├── DataStorage.meta
│ ├── DataType/
│ │ ├── DictionaryCustom.cs
│ │ ├── DictionaryCustom.cs.meta
│ │ ├── ShortDouble.Units.cs
│ │ ├── ShortDouble.Units.cs.meta
│ │ ├── ShortDouble.cs
│ │ ├── ShortDouble.cs.meta
│ │ ├── virtuesky.sunflower.datatype.asmdef
│ │ └── virtuesky.sunflower.datatype.asmdef.meta
│ ├── DataType.meta
│ ├── Events/
│ │ ├── Editor/
│ │ │ ├── BaseEventEditor.cs
│ │ │ ├── BaseEventEditor.cs.meta
│ │ │ ├── EventWindowEditor.cs
│ │ │ ├── EventWindowEditor.cs.meta
│ │ │ ├── virtuesky.sunflower.event.editor.asmdef
│ │ │ └── virtuesky.sunflower.event.editor.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── Base_Event/
│ │ │ │ ├── BaseEvent.cs
│ │ │ │ ├── BaseEvent.cs.meta
│ │ │ │ ├── BaseEventListener.cs
│ │ │ │ ├── BaseEventListener.cs.meta
│ │ │ │ ├── BaseEventResponse.cs
│ │ │ │ ├── BaseEventResponse.cs.meta
│ │ │ │ ├── EventListenerMono.cs
│ │ │ │ └── EventListenerMono.cs.meta
│ │ │ ├── Base_Event.meta
│ │ │ ├── Boolean_Event/
│ │ │ │ ├── BooleanEvent.cs
│ │ │ │ ├── BooleanEvent.cs.meta
│ │ │ │ ├── BooleanEventListener.cs
│ │ │ │ ├── BooleanEventListener.cs.meta
│ │ │ │ ├── BooleanEventResponse.cs
│ │ │ │ ├── BooleanEventResponse.cs.meta
│ │ │ │ ├── Event_Result/
│ │ │ │ │ ├── BoolEventBoolResult.cs
│ │ │ │ │ ├── BoolEventBoolResult.cs.meta
│ │ │ │ │ ├── BoolEventFloatResult.cs
│ │ │ │ │ ├── BoolEventFloatResult.cs.meta
│ │ │ │ │ ├── BoolEventGameObjectResult.cs
│ │ │ │ │ ├── BoolEventGameObjectResult.cs.meta
│ │ │ │ │ ├── BoolEventIntResult.cs
│ │ │ │ │ ├── BoolEventIntResult.cs.meta
│ │ │ │ │ ├── BoolEventObjectResult.cs
│ │ │ │ │ ├── BoolEventObjectResult.cs.meta
│ │ │ │ │ ├── BoolEventStringResult.cs
│ │ │ │ │ ├── BoolEventStringResult.cs.meta
│ │ │ │ │ ├── BoolEventTransformResult.cs
│ │ │ │ │ ├── BoolEventTransformResult.cs.meta
│ │ │ │ │ ├── BoolEventVector3Result.cs
│ │ │ │ │ └── BoolEventVector3Result.cs.meta
│ │ │ │ └── Event_Result.meta
│ │ │ ├── Boolean_Event.meta
│ │ │ ├── Custom/
│ │ │ │ ├── ClickButtonEvent.cs
│ │ │ │ └── ClickButtonEvent.cs.meta
│ │ │ ├── Custom.meta
│ │ │ ├── Dictionary_Event/
│ │ │ │ ├── DictionaryEvent.cs
│ │ │ │ ├── DictionaryEvent.cs.meta
│ │ │ │ ├── DictionaryEventListener.cs
│ │ │ │ ├── DictionaryEventListener.cs.meta
│ │ │ │ ├── DictionaryEventResponse.cs
│ │ │ │ └── DictionaryEventResponse.cs.meta
│ │ │ ├── Dictionary_Event.meta
│ │ │ ├── Event_NoParam/
│ │ │ │ ├── EventListenerNoParam.cs
│ │ │ │ ├── EventListenerNoParam.cs.meta
│ │ │ │ ├── EventNoParam.cs
│ │ │ │ ├── EventNoParam.cs.meta
│ │ │ │ ├── EventResponse.cs
│ │ │ │ ├── EventResponse.cs.meta
│ │ │ │ ├── Event_Result/
│ │ │ │ │ ├── EventNoParamBoolResult.cs
│ │ │ │ │ ├── EventNoParamBoolResult.cs.meta
│ │ │ │ │ ├── EventNoParamFloatResult.cs
│ │ │ │ │ ├── EventNoParamFloatResult.cs.meta
│ │ │ │ │ ├── EventNoParamGameObjectResult.cs
│ │ │ │ │ ├── EventNoParamGameObjectResult.cs.meta
│ │ │ │ │ ├── EventNoParamIntResult.cs
│ │ │ │ │ ├── EventNoParamIntResult.cs.meta
│ │ │ │ │ ├── EventNoParamObjectResult.cs
│ │ │ │ │ ├── EventNoParamObjectResult.cs.meta
│ │ │ │ │ ├── EventNoParamResult.cs
│ │ │ │ │ ├── EventNoParamResult.cs.meta
│ │ │ │ │ ├── EventNoParamStringResult.cs
│ │ │ │ │ ├── EventNoParamStringResult.cs.meta
│ │ │ │ │ ├── EventNoParamTransformResult.cs
│ │ │ │ │ ├── EventNoParamTransformResult.cs.meta
│ │ │ │ │ ├── EventNoParamVector3Result.cs
│ │ │ │ │ └── EventNoParamVector3Result.cs.meta
│ │ │ │ └── Event_Result.meta
│ │ │ ├── Event_NoParam.meta
│ │ │ ├── Float_Event/
│ │ │ │ ├── Event_Result/
│ │ │ │ │ ├── FloatEventBoolResult.cs
│ │ │ │ │ ├── FloatEventBoolResult.cs.meta
│ │ │ │ │ ├── FloatEventFloatResult.cs
│ │ │ │ │ ├── FloatEventFloatResult.cs.meta
│ │ │ │ │ ├── FloatEventGameObjectResult.cs
│ │ │ │ │ ├── FloatEventGameObjectResult.cs.meta
│ │ │ │ │ ├── FloatEventIntResult.cs
│ │ │ │ │ ├── FloatEventIntResult.cs.meta
│ │ │ │ │ ├── FloatEventObjectResult.cs
│ │ │ │ │ ├── FloatEventObjectResult.cs.meta
│ │ │ │ │ ├── FloatEventStringResult.cs
│ │ │ │ │ ├── FloatEventStringResult.cs.meta
│ │ │ │ │ ├── FloatEventTransformResult.cs
│ │ │ │ │ ├── FloatEventTransformResult.cs.meta
│ │ │ │ │ ├── FloatEventVector3Result.cs
│ │ │ │ │ └── FloatEventVector3Result.cs.meta
│ │ │ │ ├── Event_Result.meta
│ │ │ │ ├── FloatEvent.cs
│ │ │ │ ├── FloatEvent.cs.meta
│ │ │ │ ├── FloatEventListener.cs
│ │ │ │ ├── FloatEventListener.cs.meta
│ │ │ │ ├── FloatEventResponse.cs
│ │ │ │ └── FloatEventResponse.cs.meta
│ │ │ ├── Float_Event.meta
│ │ │ ├── GameObject_Event/
│ │ │ │ ├── Event_Result/
│ │ │ │ │ ├── GameObjectEventBoolResult.cs
│ │ │ │ │ ├── GameObjectEventBoolResult.cs.meta
│ │ │ │ │ ├── GameObjectEventFloatResult.cs
│ │ │ │ │ ├── GameObjectEventFloatResult.cs.meta
│ │ │ │ │ ├── GameObjectEventGameObjectResult.cs
│ │ │ │ │ ├── GameObjectEventGameObjectResult.cs.meta
│ │ │ │ │ ├── GameObjectEventIntResult.cs
│ │ │ │ │ ├── GameObjectEventIntResult.cs.meta
│ │ │ │ │ ├── GameObjectEventObjectResult.cs
│ │ │ │ │ ├── GameObjectEventObjectResult.cs.meta
│ │ │ │ │ ├── GameObjectEventStringResult.cs
│ │ │ │ │ ├── GameObjectEventStringResult.cs.meta
│ │ │ │ │ ├── GameObjectEventTransformResult.cs
│ │ │ │ │ ├── GameObjectEventTransformResult.cs.meta
│ │ │ │ │ ├── GameObjectEventVector3Result.cs
│ │ │ │ │ └── GameObjectEventVector3Result.cs.meta
│ │ │ │ ├── Event_Result.meta
│ │ │ │ ├── GameObjectEvent.cs
│ │ │ │ ├── GameObjectEvent.cs.meta
│ │ │ │ ├── GameObjectEventListener.cs
│ │ │ │ ├── GameObjectEventListener.cs.meta
│ │ │ │ ├── GameObjectEventResponse.cs
│ │ │ │ └── GameObjectEventResponse.cs.meta
│ │ │ ├── GameObject_Event.meta
│ │ │ ├── Integer_Event/
│ │ │ │ ├── Event_Result/
│ │ │ │ │ ├── IntEventBoolResult.cs
│ │ │ │ │ ├── IntEventBoolResult.cs.meta
│ │ │ │ │ ├── IntEventFloatResult.cs
│ │ │ │ │ ├── IntEventFloatResult.cs.meta
│ │ │ │ │ ├── IntEventGameObjectResult.cs
│ │ │ │ │ ├── IntEventGameObjectResult.cs.meta
│ │ │ │ │ ├── IntEventIntResult.cs
│ │ │ │ │ ├── IntEventIntResult.cs.meta
│ │ │ │ │ ├── IntEventObjectResult.cs
│ │ │ │ │ ├── IntEventObjectResult.cs.meta
│ │ │ │ │ ├── IntEventStringResult.cs
│ │ │ │ │ ├── IntEventStringResult.cs.meta
│ │ │ │ │ ├── IntEventTransformResult.cs
│ │ │ │ │ ├── IntEventTransformResult.cs.meta
│ │ │ │ │ ├── IntEventVector3Result.cs
│ │ │ │ │ └── IntEventVector3Result.cs.meta
│ │ │ │ ├── Event_Result.meta
│ │ │ │ ├── IntegerEvent.cs
│ │ │ │ ├── IntegerEvent.cs.meta
│ │ │ │ ├── IntegerEventListener.cs
│ │ │ │ ├── IntegerEventListener.cs.meta
│ │ │ │ ├── IntegerEventResponse.cs
│ │ │ │ └── IntegerEventResponse.cs.meta
│ │ │ ├── Integer_Event.meta
│ │ │ ├── Interface_Event/
│ │ │ │ ├── IEvent.cs
│ │ │ │ ├── IEvent.cs.meta
│ │ │ │ ├── IEventListener.cs
│ │ │ │ ├── IEventListener.cs.meta
│ │ │ │ ├── IEventResponse.cs
│ │ │ │ └── IEventResponse.cs.meta
│ │ │ ├── Interface_Event.meta
│ │ │ ├── Object_Event/
│ │ │ │ ├── Event_Result/
│ │ │ │ │ ├── ObjectEventBoolResult.cs
│ │ │ │ │ ├── ObjectEventBoolResult.cs.meta
│ │ │ │ │ ├── ObjectEventFloatResult.cs
│ │ │ │ │ ├── ObjectEventFloatResult.cs.meta
│ │ │ │ │ ├── ObjectEventGameObjectResult.cs
│ │ │ │ │ ├── ObjectEventGameObjectResult.cs.meta
│ │ │ │ │ ├── ObjectEventIntResult.cs
│ │ │ │ │ ├── ObjectEventIntResult.cs.meta
│ │ │ │ │ ├── ObjectEventObjectResult.cs
│ │ │ │ │ ├── ObjectEventObjectResult.cs.meta
│ │ │ │ │ ├── ObjectEventStringResult.cs
│ │ │ │ │ ├── ObjectEventStringResult.cs.meta
│ │ │ │ │ ├── ObjectEventTransformResult.cs
│ │ │ │ │ ├── ObjectEventTransformResult.cs.meta
│ │ │ │ │ ├── ObjectEventVector3Result.cs
│ │ │ │ │ └── ObjectEventVector3Result.cs.meta
│ │ │ │ ├── Event_Result.meta
│ │ │ │ ├── ObjectEvent.cs
│ │ │ │ ├── ObjectEvent.cs.meta
│ │ │ │ ├── ObjectEventListener.cs
│ │ │ │ ├── ObjectEventListener.cs.meta
│ │ │ │ ├── ObjectEventResponse.cs
│ │ │ │ └── ObjectEventResponse.cs.meta
│ │ │ ├── Object_Event.meta
│ │ │ ├── ShortDouble_Event/
│ │ │ │ ├── ShortDoubleEvent.cs
│ │ │ │ ├── ShortDoubleEvent.cs.meta
│ │ │ │ ├── ShortDoubleEventListener.cs
│ │ │ │ ├── ShortDoubleEventListener.cs.meta
│ │ │ │ ├── ShortDoubleEventResponse.cs
│ │ │ │ └── ShortDoubleEventResponse.cs.meta
│ │ │ ├── ShortDouble_Event.meta
│ │ │ ├── String_Event/
│ │ │ │ ├── Event_Result/
│ │ │ │ │ ├── StringEventBoolResult.cs
│ │ │ │ │ ├── StringEventBoolResult.cs.meta
│ │ │ │ │ ├── StringEventFloatResult.cs
│ │ │ │ │ ├── StringEventFloatResult.cs.meta
│ │ │ │ │ ├── StringEventGameObjectResult.cs
│ │ │ │ │ ├── StringEventGameObjectResult.cs.meta
│ │ │ │ │ ├── StringEventIntResult.cs
│ │ │ │ │ ├── StringEventIntResult.cs.meta
│ │ │ │ │ ├── StringEventObjectResult.cs
│ │ │ │ │ ├── StringEventObjectResult.cs.meta
│ │ │ │ │ ├── StringEventStringResult.cs
│ │ │ │ │ ├── StringEventStringResult.cs.meta
│ │ │ │ │ ├── StringEventTransformResult.cs
│ │ │ │ │ ├── StringEventTransformResult.cs.meta
│ │ │ │ │ ├── StringEventVector3Result.cs
│ │ │ │ │ └── StringEventVector3Result.cs.meta
│ │ │ │ ├── Event_Result.meta
│ │ │ │ ├── StringEvent.cs
│ │ │ │ ├── StringEvent.cs.meta
│ │ │ │ ├── StringEventListener.cs
│ │ │ │ ├── StringEventListener.cs.meta
│ │ │ │ ├── StringEventResponse.cs
│ │ │ │ └── StringEventResponse.cs.meta
│ │ │ ├── String_Event.meta
│ │ │ ├── Transform_Event/
│ │ │ │ ├── Event_Result/
│ │ │ │ │ ├── TransformEventBoolResult.cs
│ │ │ │ │ ├── TransformEventBoolResult.cs.meta
│ │ │ │ │ ├── TransformEventFloatResult.cs
│ │ │ │ │ ├── TransformEventFloatResult.cs.meta
│ │ │ │ │ ├── TransformEventGameObjectResult.cs
│ │ │ │ │ ├── TransformEventGameObjectResult.cs.meta
│ │ │ │ │ ├── TransformEventIntResult.cs
│ │ │ │ │ ├── TransformEventIntResult.cs.meta
│ │ │ │ │ ├── TransformEventObjectResult.cs
│ │ │ │ │ ├── TransformEventObjectResult.cs.meta
│ │ │ │ │ ├── TransformEventStringResult.cs
│ │ │ │ │ ├── TransformEventStringResult.cs.meta
│ │ │ │ │ ├── TransformEventTransformResult.cs
│ │ │ │ │ ├── TransformEventTransformResult.cs.meta
│ │ │ │ │ ├── TransformEventVector3Result.cs
│ │ │ │ │ └── TransformEventVector3Result.cs.meta
│ │ │ │ ├── Event_Result.meta
│ │ │ │ ├── TransformEvent.cs
│ │ │ │ ├── TransformEvent.cs.meta
│ │ │ │ ├── TransformEventListener.cs
│ │ │ │ ├── TransformEventListener.cs.meta
│ │ │ │ ├── TransformEventResponse.cs
│ │ │ │ └── TransformEventResponse.cs.meta
│ │ │ ├── Transform_Event.meta
│ │ │ ├── Vector2_Event/
│ │ │ │ ├── Vector2Event.cs
│ │ │ │ ├── Vector2Event.cs.meta
│ │ │ │ ├── Vector2EventListener.cs
│ │ │ │ ├── Vector2EventListener.cs.meta
│ │ │ │ ├── Vector2EventResponse.cs
│ │ │ │ └── Vector2EventResponse.cs.meta
│ │ │ ├── Vector2_Event.meta
│ │ │ ├── Vector3_Event/
│ │ │ │ ├── Event_Result/
│ │ │ │ │ ├── Vector3EventBoolResult.cs
│ │ │ │ │ ├── Vector3EventBoolResult.cs.meta
│ │ │ │ │ ├── Vector3EventFloatResult.cs
│ │ │ │ │ ├── Vector3EventFloatResult.cs.meta
│ │ │ │ │ ├── Vector3EventGameObjectResult.cs
│ │ │ │ │ ├── Vector3EventGameObjectResult.cs.meta
│ │ │ │ │ ├── Vector3EventIntResult.cs
│ │ │ │ │ ├── Vector3EventIntResult.cs.meta
│ │ │ │ │ ├── Vector3EventObjectResult.cs
│ │ │ │ │ ├── Vector3EventObjectResult.cs.meta
│ │ │ │ │ ├── Vector3EventStringResult.cs
│ │ │ │ │ ├── Vector3EventStringResult.cs.meta
│ │ │ │ │ ├── Vector3EventTransformResult.cs
│ │ │ │ │ ├── Vector3EventTransformResult.cs.meta
│ │ │ │ │ ├── Vector3EventVector3Result.cs
│ │ │ │ │ └── Vector3EventVector3Result.cs.meta
│ │ │ │ ├── Event_Result.meta
│ │ │ │ ├── Vector3Event.cs
│ │ │ │ ├── Vector3Event.cs.meta
│ │ │ │ ├── Vector3EventListener.cs
│ │ │ │ ├── Vector3EventListener.cs.meta
│ │ │ │ ├── Vector3EventResponse.cs
│ │ │ │ └── Vector3EventResponse.cs.meta
│ │ │ ├── Vector3_Event.meta
│ │ │ ├── virtuesky.sunflower.event.asmdef
│ │ │ └── virtuesky.sunflower.event.asmdef.meta
│ │ └── Runtime.meta
│ ├── Events.meta
│ ├── FolderIcon/
│ │ ├── Editor/
│ │ │ ├── CustomFolder.cs
│ │ │ ├── CustomFolder.cs.meta
│ │ │ ├── FolderIconSettings.cs
│ │ │ ├── FolderIconSettings.cs.meta
│ │ │ ├── IconDictionaryCreator.cs
│ │ │ ├── IconDictionaryCreator.cs.meta
│ │ │ ├── PackageIcon/
│ │ │ │ ├── icon_folder.unitypackage
│ │ │ │ └── icon_folder.unitypackage.meta
│ │ │ ├── PackageIcon.meta
│ │ │ ├── Virtuesky.Sunflower.FolderIcon.asmdef
│ │ │ └── Virtuesky.Sunflower.FolderIcon.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Icons/
│ │ │ ├── Animations.png.meta
│ │ │ ├── Audio.png.meta
│ │ │ ├── Default/
│ │ │ │ └── Default.png.meta
│ │ │ ├── Default.meta
│ │ │ ├── Editor.png.meta
│ │ │ ├── Fonts.png.meta
│ │ │ ├── Materials.png.meta
│ │ │ ├── Models.png.meta
│ │ │ ├── Plugins.png.meta
│ │ │ ├── Prefabs.png.meta
│ │ │ ├── Presets.png.meta
│ │ │ ├── Resources.png.meta
│ │ │ ├── Scenes.png.meta
│ │ │ ├── Scripts.png.meta
│ │ │ ├── Settings.png.meta
│ │ │ ├── Shaders.png.meta
│ │ │ ├── Sprites.png.meta
│ │ │ └── Textures.png.meta
│ │ └── Icons.meta
│ ├── FolderIcon.meta
│ ├── GameService/
│ │ ├── Runtime/
│ │ │ ├── AppleAuthentication.cs
│ │ │ ├── AppleAuthentication.cs.meta
│ │ │ ├── GooglePlayGamesAuthentication.cs
│ │ │ ├── GooglePlayGamesAuthentication.cs.meta
│ │ │ ├── ServiceAuthentication.cs
│ │ │ ├── ServiceAuthentication.cs.meta
│ │ │ ├── Variable/
│ │ │ │ ├── StatusLoginVariable.cs
│ │ │ │ └── StatusLoginVariable.cs.meta
│ │ │ ├── Variable.meta
│ │ │ ├── Virtuesky.Sunflower.GameService.asmdef
│ │ │ └── Virtuesky.Sunflower.GameService.asmdef.meta
│ │ └── Runtime.meta
│ ├── GameService.meta
│ ├── Hierarchy/
│ │ ├── Attribute/
│ │ │ ├── HierarchyNullable.asmdef
│ │ │ ├── HierarchyNullable.asmdef.meta
│ │ │ ├── HierarchyNullable.cs
│ │ │ └── HierarchyNullable.cs.meta
│ │ ├── Attribute.meta
│ │ ├── Editor/
│ │ │ ├── HierarchyEditor.asmdef
│ │ │ ├── HierarchyEditor.asmdef.meta
│ │ │ ├── Scripts/
│ │ │ │ ├── Component/
│ │ │ │ │ ├── Base/
│ │ │ │ │ │ ├── BaseComponent.cs
│ │ │ │ │ │ └── BaseComponent.cs.meta
│ │ │ │ │ ├── Base.meta
│ │ │ │ │ ├── ChildrenCountComponent.cs
│ │ │ │ │ ├── ChildrenCountComponent.cs.meta
│ │ │ │ │ ├── ColorComponent.cs
│ │ │ │ │ ├── ColorComponent.cs.meta
│ │ │ │ │ ├── ComponentsComponent.cs
│ │ │ │ │ ├── ComponentsComponent.cs.meta
│ │ │ │ │ ├── ErrorComponent.cs
│ │ │ │ │ ├── ErrorComponent.cs.meta
│ │ │ │ │ ├── GameObjectIconComponent.cs
│ │ │ │ │ ├── GameObjectIconComponent.cs.meta
│ │ │ │ │ ├── LayerIconComponent.cs
│ │ │ │ │ ├── LayerIconComponent.cs.meta
│ │ │ │ │ ├── LockComponent.cs
│ │ │ │ │ ├── LockComponent.cs.meta
│ │ │ │ │ ├── MonoBehaviorIconComponent.cs
│ │ │ │ │ ├── MonoBehaviorIconComponent.cs.meta
│ │ │ │ │ ├── PrefabComponent.cs
│ │ │ │ │ ├── PrefabComponent.cs.meta
│ │ │ │ │ ├── RendererComponent.cs
│ │ │ │ │ ├── RendererComponent.cs.meta
│ │ │ │ │ ├── SeparatorComponent.cs
│ │ │ │ │ ├── SeparatorComponent.cs.meta
│ │ │ │ │ ├── StaticComponent.cs
│ │ │ │ │ ├── StaticComponent.cs.meta
│ │ │ │ │ ├── TagIconComponent.cs
│ │ │ │ │ ├── TagIconComponent.cs.meta
│ │ │ │ │ ├── TagLayerComponent.cs
│ │ │ │ │ ├── TagLayerComponent.cs.meta
│ │ │ │ │ ├── TreeMapComponent.cs
│ │ │ │ │ ├── TreeMapComponent.cs.meta
│ │ │ │ │ ├── VerticesAndTrianglesCountComponent.cs
│ │ │ │ │ ├── VerticesAndTrianglesCountComponent.cs.meta
│ │ │ │ │ ├── VisibilityComponent.cs
│ │ │ │ │ └── VisibilityComponent.cs.meta
│ │ │ │ ├── Component.meta
│ │ │ │ ├── Data/
│ │ │ │ │ ├── HierarchyResources.cs
│ │ │ │ │ ├── HierarchyResources.cs.meta
│ │ │ │ │ ├── HierarchySettings.cs
│ │ │ │ │ ├── HierarchySettings.cs.meta
│ │ │ │ │ ├── HierarchySettingsObject.cs
│ │ │ │ │ ├── HierarchySettingsObject.cs.meta
│ │ │ │ │ ├── QSettingsObjectAsset.asset
│ │ │ │ │ └── QSettingsObjectAsset.asset.meta
│ │ │ │ ├── Data.meta
│ │ │ │ ├── Helper/
│ │ │ │ │ ├── HierarchyColorPickerWindow.cs
│ │ │ │ │ ├── HierarchyColorPickerWindow.cs.meta
│ │ │ │ │ ├── HierarchyColorUtils.cs
│ │ │ │ │ ├── HierarchyColorUtils.cs.meta
│ │ │ │ │ ├── HierarchyComponentsOrderList.cs
│ │ │ │ │ ├── HierarchyComponentsOrderList.cs.meta
│ │ │ │ │ ├── HierarchyObjectListInspector.cs
│ │ │ │ │ ├── HierarchyObjectListInspector.cs.meta
│ │ │ │ │ ├── HierarchyObjectListManager.cs
│ │ │ │ │ └── HierarchyObjectListManager.cs.meta
│ │ │ │ ├── Helper.meta
│ │ │ │ ├── QHierarchyInitializer.cs
│ │ │ │ ├── QHierarchyInitializer.cs.meta
│ │ │ │ ├── VHierarchy/
│ │ │ │ │ ├── HierarchySettingsWindow.cs
│ │ │ │ │ ├── HierarchySettingsWindow.cs.meta
│ │ │ │ │ ├── VHierarchy.cs
│ │ │ │ │ └── VHierarchy.cs.meta
│ │ │ │ └── VHierarchy.meta
│ │ │ └── Scripts.meta
│ │ ├── Editor.meta
│ │ ├── FolderHierarchy/
│ │ │ ├── HeaderHierarchy.cs
│ │ │ ├── HeaderHierarchy.cs.meta
│ │ │ ├── HeaderHierarchyIcon.cs
│ │ │ ├── HeaderHierarchyIcon.cs.meta
│ │ │ ├── HierarchyHeader.asmdef
│ │ │ ├── HierarchyHeader.asmdef.meta
│ │ │ ├── InspectorUtility.cs
│ │ │ └── InspectorUtility.cs.meta
│ │ ├── FolderHierarchy.meta
│ │ ├── Icons/
│ │ │ └── HierarchyHighlight.png.meta
│ │ ├── Icons.meta
│ │ ├── Scripts/
│ │ │ ├── HierarchyRuntime.asmdef
│ │ │ ├── HierarchyRuntime.asmdef.meta
│ │ │ ├── ObjectList.cs
│ │ │ └── ObjectList.cs.meta
│ │ └── Scripts.meta
│ ├── Hierarchy.meta
│ ├── Iap/
│ │ ├── Editor/
│ │ │ ├── IapSettingEditor.cs
│ │ │ ├── IapSettingEditor.cs.meta
│ │ │ ├── ObfuscationGenerator.cs
│ │ │ ├── ObfuscationGenerator.cs.meta
│ │ │ ├── TangleFileConsts.cs
│ │ │ ├── TangleFileConsts.cs.meta
│ │ │ ├── TangleObfuscator.cs
│ │ │ ├── TangleObfuscator.cs.meta
│ │ │ ├── Virtusky.Sunflower.Iap.Editor.asmdef
│ │ │ └── Virtusky.Sunflower.Iap.Editor.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── IapDataVariable.cs
│ │ │ ├── IapDataVariable.cs.meta
│ │ │ ├── IapManager.cs
│ │ │ ├── IapManager.cs.meta
│ │ │ ├── IapPurchaseFailed.cs
│ │ │ ├── IapPurchaseFailed.cs.meta
│ │ │ ├── IapPurchaseSuccess.cs
│ │ │ ├── IapPurchaseSuccess.cs.meta
│ │ │ ├── IapSetting.cs
│ │ │ ├── IapSetting.cs.meta
│ │ │ ├── IapStatic.cs
│ │ │ ├── IapStatic.cs.meta
│ │ │ ├── Virtusky.Sunflower.Iap.asmdef
│ │ │ └── Virtusky.Sunflower.Iap.asmdef.meta
│ │ └── Runtime.meta
│ ├── Iap.meta
│ ├── Inspector/
│ │ ├── .editorconfig
│ │ ├── .github/
│ │ │ ├── ISSUE_TEMPLATE/
│ │ │ │ ├── bug_report.md
│ │ │ │ └── config.yml
│ │ │ └── release.yml
│ │ ├── .gitignore
│ │ ├── Editor/
│ │ │ ├── AssemblyInfo.cs
│ │ │ ├── AssemblyInfo.cs.meta
│ │ │ ├── Attributes.cs
│ │ │ ├── Attributes.cs.meta
│ │ │ ├── CustomizeDraw/
│ │ │ │ ├── EditorIconPostProcessor.cs
│ │ │ │ ├── EditorIconPostProcessor.cs.meta
│ │ │ │ ├── EnumAttribue/
│ │ │ │ │ ├── ExtendEnumDrawer.cs
│ │ │ │ │ ├── ExtendEnumDrawer.cs.meta
│ │ │ │ │ ├── SearchableEnumDrawer.cs
│ │ │ │ │ ├── SearchableEnumDrawer.cs.meta
│ │ │ │ │ ├── SearchablePopup.cs
│ │ │ │ │ └── SearchablePopup.cs.meta
│ │ │ │ ├── EnumAttribue.meta
│ │ │ │ ├── HeaderLineDrawer.cs
│ │ │ │ ├── HeaderLineDrawer.cs.meta
│ │ │ │ ├── HelpDrawer.cs
│ │ │ │ ├── HelpDrawer.cs.meta
│ │ │ │ ├── HighlightDrawer.cs
│ │ │ │ ├── HighlightDrawer.cs.meta
│ │ │ │ ├── LayerAttributeDraw.cs
│ │ │ │ ├── LayerAttributeDraw.cs.meta
│ │ │ │ ├── TagAttributeDraw.cs
│ │ │ │ ├── TagAttributeDraw.cs.meta
│ │ │ │ ├── TitleColorAttributeDrawer.cs
│ │ │ │ ├── TitleColorAttributeDrawer.cs.meta
│ │ │ │ ├── UtilityDraw.cs
│ │ │ │ └── UtilityDraw.cs.meta
│ │ │ ├── CustomizeDraw.meta
│ │ │ ├── Editors/
│ │ │ │ ├── TriEditor.cs
│ │ │ │ ├── TriEditor.cs.meta
│ │ │ │ ├── TriEditorCore.cs
│ │ │ │ ├── TriEditorCore.cs.meta
│ │ │ │ ├── TriMonoBehaviourEditor.cs
│ │ │ │ ├── TriMonoBehaviourEditor.cs.meta
│ │ │ │ ├── TriScriptableObjectEditor.cs
│ │ │ │ ├── TriScriptableObjectEditor.cs.meta
│ │ │ │ ├── TriScriptedImporterEditor.cs
│ │ │ │ ├── TriScriptedImporterEditor.cs.meta
│ │ │ │ ├── TriSettingsProvider.cs
│ │ │ │ └── TriSettingsProvider.cs.meta
│ │ │ ├── Editors.meta
│ │ │ ├── Elements/
│ │ │ │ ├── InlineEditorElement.cs
│ │ │ │ ├── InlineEditorElement.cs.meta
│ │ │ │ ├── TriBoxGroupElement.cs
│ │ │ │ ├── TriBoxGroupElement.cs.meta
│ │ │ │ ├── TriBuiltInPropertyElement.cs
│ │ │ │ ├── TriBuiltInPropertyElement.cs.meta
│ │ │ │ ├── TriDropdownElement.cs
│ │ │ │ ├── TriDropdownElement.cs.meta
│ │ │ │ ├── TriFoldoutElement.cs
│ │ │ │ ├── TriFoldoutElement.cs.meta
│ │ │ │ ├── TriHeaderGroupBaseElement.cs
│ │ │ │ ├── TriHeaderGroupBaseElement.cs.meta
│ │ │ │ ├── TriHorizontalGroupElement.cs
│ │ │ │ ├── TriHorizontalGroupElement.cs.meta
│ │ │ │ ├── TriInfoBoxElement.cs
│ │ │ │ ├── TriInfoBoxElement.cs.meta
│ │ │ │ ├── TriInlineGenericElement.cs
│ │ │ │ ├── TriInlineGenericElement.cs.meta
│ │ │ │ ├── TriLabelElement.cs
│ │ │ │ ├── TriLabelElement.cs.meta
│ │ │ │ ├── TriListElement.cs
│ │ │ │ ├── TriListElement.cs.meta
│ │ │ │ ├── TriMultiEditNotSupportedElement.cs
│ │ │ │ ├── TriMultiEditNotSupportedElement.cs.meta
│ │ │ │ ├── TriNoDrawerElement.cs
│ │ │ │ ├── TriNoDrawerElement.cs.meta
│ │ │ │ ├── TriPropertyCollectionBaseElement.cs
│ │ │ │ ├── TriPropertyCollectionBaseElement.cs.meta
│ │ │ │ ├── TriPropertyElement.cs
│ │ │ │ ├── TriPropertyElement.cs.meta
│ │ │ │ ├── TriReferenceElement.cs
│ │ │ │ ├── TriReferenceElement.cs.meta
│ │ │ │ ├── TriTabGroupElement.cs
│ │ │ │ ├── TriTabGroupElement.cs.meta
│ │ │ │ ├── TriUiToolkitPropertyElemenet.cs
│ │ │ │ ├── TriUiToolkitPropertyElemenet.cs.meta
│ │ │ │ ├── TriVerticalGroupElement.cs
│ │ │ │ └── TriVerticalGroupElement.cs.meta
│ │ │ ├── Elements.meta
│ │ │ ├── Resolvers/
│ │ │ │ ├── ActionResolver.cs
│ │ │ │ ├── ActionResolver.cs.meta
│ │ │ │ ├── DropdownValuesResolver.cs
│ │ │ │ ├── DropdownValuesResolver.cs.meta
│ │ │ │ ├── ErrorActionResolver.cs
│ │ │ │ ├── ErrorActionResolver.cs.meta
│ │ │ │ ├── ErrorValueResolver.cs
│ │ │ │ ├── ErrorValueResolver.cs.meta
│ │ │ │ ├── InstanceActionResolver.cs
│ │ │ │ ├── InstanceActionResolver.cs.meta
│ │ │ │ ├── InstanceFieldValueResolver.cs
│ │ │ │ ├── InstanceFieldValueResolver.cs.meta
│ │ │ │ ├── InstanceMethodValueResolver.cs
│ │ │ │ ├── InstanceMethodValueResolver.cs.meta
│ │ │ │ ├── InstancePropertyValueResolver.cs
│ │ │ │ ├── InstancePropertyValueResolver.cs.meta
│ │ │ │ ├── StaticFieldValueResolver.cs
│ │ │ │ ├── StaticFieldValueResolver.cs.meta
│ │ │ │ ├── StaticMethodValueResolver.cs
│ │ │ │ ├── StaticMethodValueResolver.cs.meta
│ │ │ │ ├── StaticPropertyValueResolver.cs
│ │ │ │ ├── StaticPropertyValueResolver.cs.meta
│ │ │ │ ├── ValueResolver.cs
│ │ │ │ └── ValueResolver.cs.meta
│ │ │ ├── Resolvers.meta
│ │ │ ├── Resources/
│ │ │ │ ├── TriInspector_Box_Bg.png.meta
│ │ │ │ ├── TriInspector_Box_Bg_Dark.png.meta
│ │ │ │ ├── TriInspector_Content_Bg.png.meta
│ │ │ │ └── TriInspector_Content_Bg_Dark.png.meta
│ │ │ ├── Resources.meta
│ │ │ ├── TriAttributeDrawer.cs
│ │ │ ├── TriAttributeDrawer.cs.meta
│ │ │ ├── TriCustomDrawer.cs
│ │ │ ├── TriCustomDrawer.cs.meta
│ │ │ ├── TriDrawerOrder.cs
│ │ │ ├── TriDrawerOrder.cs.meta
│ │ │ ├── TriEditorStyles.cs
│ │ │ ├── TriEditorStyles.cs.meta
│ │ │ ├── TriElement.cs
│ │ │ ├── TriElement.cs.meta
│ │ │ ├── TriGroupDrawer.cs
│ │ │ ├── TriGroupDrawer.cs.meta
│ │ │ ├── TriProperty.cs
│ │ │ ├── TriProperty.cs.meta
│ │ │ ├── TriPropertyDefinition.cs
│ │ │ ├── TriPropertyDefinition.cs.meta
│ │ │ ├── TriPropertyDisableProcessor.cs
│ │ │ ├── TriPropertyDisableProcessor.cs.meta
│ │ │ ├── TriPropertyExtension.cs
│ │ │ ├── TriPropertyExtension.cs.meta
│ │ │ ├── TriPropertyHideProcessor.cs
│ │ │ ├── TriPropertyHideProcessor.cs.meta
│ │ │ ├── TriPropertyOverrideContext.cs
│ │ │ ├── TriPropertyOverrideContext.cs.meta
│ │ │ ├── TriPropertyTree.cs
│ │ │ ├── TriPropertyTree.cs.meta
│ │ │ ├── TriPropertyTreeForSerializedObject.cs
│ │ │ ├── TriPropertyTreeForSerializedObject.cs.meta
│ │ │ ├── TriTypeDefinition.cs
│ │ │ ├── TriTypeDefinition.cs.meta
│ │ │ ├── TriTypeProcessor.cs
│ │ │ ├── TriTypeProcessor.cs.meta
│ │ │ ├── TriValidator.cs
│ │ │ ├── TriValidator.cs.meta
│ │ │ ├── TriValue.cs
│ │ │ ├── TriValue.cs.meta
│ │ │ ├── TriValueDrawer.cs
│ │ │ ├── TriValueDrawer.cs.meta
│ │ │ ├── TypeProcessors/
│ │ │ │ ├── TriGroupNextTypeProcessor.cs
│ │ │ │ ├── TriGroupNextTypeProcessor.cs.meta
│ │ │ │ ├── TriRectOffsetTypeProcessor.cs
│ │ │ │ ├── TriRectOffsetTypeProcessor.cs.meta
│ │ │ │ ├── TriRegisterButtonsTypeProcessor.cs
│ │ │ │ ├── TriRegisterButtonsTypeProcessor.cs.meta
│ │ │ │ ├── TriRegisterShownByTriFieldsTypeProcessor.cs
│ │ │ │ ├── TriRegisterShownByTriFieldsTypeProcessor.cs.meta
│ │ │ │ ├── TriRegisterShownByTriPropertiesTypeProcessor.cs
│ │ │ │ ├── TriRegisterShownByTriPropertiesTypeProcessor.cs.meta
│ │ │ │ ├── TriRegisterUnitySerializedFieldsTypeProcessor.cs
│ │ │ │ ├── TriRegisterUnitySerializedFieldsTypeProcessor.cs.meta
│ │ │ │ ├── TriSortPropertiesTypeProcessor.cs
│ │ │ │ └── TriSortPropertiesTypeProcessor.cs.meta
│ │ │ ├── TypeProcessors.meta
│ │ │ ├── Utilities/
│ │ │ │ ├── TriAttributeUtilities.cs
│ │ │ │ ├── TriAttributeUtilities.cs.meta
│ │ │ │ ├── TriDrawersUtilities.cs
│ │ │ │ ├── TriDrawersUtilities.cs.meta
│ │ │ │ ├── TriEditorGUI.cs
│ │ │ │ ├── TriEditorGUI.cs.meta
│ │ │ │ ├── TriEqualityComparer.cs
│ │ │ │ ├── TriEqualityComparer.cs.meta
│ │ │ │ ├── TriGuiHelper.cs
│ │ │ │ ├── TriGuiHelper.cs.meta
│ │ │ │ ├── TriManagedReferenceGui.cs
│ │ │ │ ├── TriManagedReferenceGui.cs.meta
│ │ │ │ ├── TriReflectionUtilities.cs
│ │ │ │ ├── TriReflectionUtilities.cs.meta
│ │ │ │ ├── TriTypeUtilities.cs
│ │ │ │ ├── TriTypeUtilities.cs.meta
│ │ │ │ ├── TriUnityInspectorUtilities.cs
│ │ │ │ ├── TriUnityInspectorUtilities.cs.meta
│ │ │ │ ├── TriUnitySerializationUtilities.cs
│ │ │ │ └── TriUnitySerializationUtilities.cs.meta
│ │ │ ├── Utilities.meta
│ │ │ ├── ValidatorsDrawer.cs
│ │ │ ├── ValidatorsDrawer.cs.meta
│ │ │ ├── VirtueSky.Sunflower.Inspector.Editor.asmdef
│ │ │ └── VirtueSky.Sunflower.Inspector.Editor.asmdef.meta
│ │ ├── Editor.Extras/
│ │ │ ├── Drawers/
│ │ │ │ ├── BuiltinDrawerBase.cs
│ │ │ │ ├── BuiltinDrawerBase.cs.meta
│ │ │ │ ├── BuiltinDrawers.cs
│ │ │ │ ├── BuiltinDrawers.cs.meta
│ │ │ │ ├── ButtonDrawer.cs
│ │ │ │ ├── ButtonDrawer.cs.meta
│ │ │ │ ├── CustomBuiltInDrawer.cs
│ │ │ │ ├── CustomBuiltInDrawer.cs.meta
│ │ │ │ ├── DisplayAsStringDrawer.cs
│ │ │ │ ├── DisplayAsStringDrawer.cs.meta
│ │ │ │ ├── DropdownDrawer.cs
│ │ │ │ ├── DropdownDrawer.cs.meta
│ │ │ │ ├── EnumToggleButtonsDrawer.cs
│ │ │ │ ├── EnumToggleButtonsDrawer.cs.meta
│ │ │ │ ├── GUIColorDrawer.cs
│ │ │ │ ├── GUIColorDrawer.cs.meta
│ │ │ │ ├── IndentDrawer.cs
│ │ │ │ ├── IndentDrawer.cs.meta
│ │ │ │ ├── InlineEditorDrawer.cs
│ │ │ │ ├── InlineEditorDrawer.cs.meta
│ │ │ │ ├── LabelWidthDrawer.cs
│ │ │ │ ├── LabelWidthDrawer.cs.meta
│ │ │ │ ├── ObjectReferenceDrawer.cs
│ │ │ │ ├── ObjectReferenceDrawer.cs.meta
│ │ │ │ ├── OnValueChangedDrawer.cs
│ │ │ │ ├── OnValueChangedDrawer.cs.meta
│ │ │ │ ├── PreviewMeshDrawer.cs
│ │ │ │ ├── PreviewMeshDrawer.cs.meta
│ │ │ │ ├── PropertySpaceDrawer.cs
│ │ │ │ ├── PropertySpaceDrawer.cs.meta
│ │ │ │ ├── SceneDrawer.cs
│ │ │ │ ├── SceneDrawer.cs.meta
│ │ │ │ ├── ShowDrawerChainDrawer.cs
│ │ │ │ ├── ShowDrawerChainDrawer.cs.meta
│ │ │ │ ├── TableListDrawer.cs
│ │ │ │ ├── TableListDrawer.cs.meta
│ │ │ │ ├── TitleDrawer.cs
│ │ │ │ ├── TitleDrawer.cs.meta
│ │ │ │ ├── UnitDrawer.cs
│ │ │ │ └── UnitDrawer.cs.meta
│ │ │ ├── Drawers.meta
│ │ │ ├── GroupDrawers/
│ │ │ │ ├── TriBoxGroupDrawer.cs
│ │ │ │ ├── TriBoxGroupDrawer.cs.meta
│ │ │ │ ├── TriFoldoutGroupDrawer.cs
│ │ │ │ ├── TriFoldoutGroupDrawer.cs.meta
│ │ │ │ ├── TriHorizontalGroupDrawer.cs
│ │ │ │ ├── TriHorizontalGroupDrawer.cs.meta
│ │ │ │ ├── TriTabGroupDrawer.cs
│ │ │ │ ├── TriTabGroupDrawer.cs.meta
│ │ │ │ ├── TriToggleGroupDrawer.cs
│ │ │ │ ├── TriToggleGroupDrawer.cs.meta
│ │ │ │ ├── TriVerticalGroupDrawer.cs
│ │ │ │ └── TriVerticalGroupDrawer.cs.meta
│ │ │ ├── GroupDrawers.meta
│ │ │ ├── Processors/
│ │ │ │ ├── DisableIfProcessor.cs
│ │ │ │ ├── DisableIfProcessor.cs.meta
│ │ │ │ ├── DisableInEditModeProcessor.cs
│ │ │ │ ├── DisableInEditModeProcessor.cs.meta
│ │ │ │ ├── DisableInPlayModeProcessor.cs
│ │ │ │ ├── DisableInPlayModeProcessor.cs.meta
│ │ │ │ ├── HIdeInEditModeProcessor.cs
│ │ │ │ ├── HIdeInEditModeProcessor.cs.meta
│ │ │ │ ├── HideIfProcessor.cs
│ │ │ │ ├── HideIfProcessor.cs.meta
│ │ │ │ ├── HideInPlayModeProcessor.cs
│ │ │ │ └── HideInPlayModeProcessor.cs.meta
│ │ │ ├── Processors.meta
│ │ │ ├── Validators/
│ │ │ │ ├── AssetsOnlyValidator.cs
│ │ │ │ ├── AssetsOnlyValidator.cs.meta
│ │ │ │ ├── DropdownValidator.cs
│ │ │ │ ├── DropdownValidator.cs.meta
│ │ │ │ ├── InfoBoxValidator.cs
│ │ │ │ ├── InfoBoxValidator.cs.meta
│ │ │ │ ├── MissingReferenceValidator.cs
│ │ │ │ ├── MissingReferenceValidator.cs.meta
│ │ │ │ ├── RequiredValidator.cs
│ │ │ │ ├── RequiredValidator.cs.meta
│ │ │ │ ├── SceneObjectsOnlyValidator.cs
│ │ │ │ ├── SceneObjectsOnlyValidator.cs.meta
│ │ │ │ ├── SceneValidator.cs
│ │ │ │ ├── SceneValidator.cs.meta
│ │ │ │ ├── TypeMismatchValidator.cs
│ │ │ │ ├── TypeMismatchValidator.cs.meta
│ │ │ │ ├── ValidateInputValidator.cs
│ │ │ │ └── ValidateInputValidator.cs.meta
│ │ │ ├── Validators.meta
│ │ │ ├── VirtueSky.Sunflower.Inspector.Editor.Extras.asmdef
│ │ │ └── VirtueSky.Sunflower.Inspector.Editor.Extras.asmdef.meta
│ │ ├── Editor.Extras.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── Attributes/
│ │ │ │ ├── Buttons/
│ │ │ │ │ ├── ButtonAttribute.cs
│ │ │ │ │ ├── ButtonAttribute.cs.meta
│ │ │ │ │ ├── EnumToggleButtonsAttribute.cs
│ │ │ │ │ └── EnumToggleButtonsAttribute.cs.meta
│ │ │ │ ├── Buttons.meta
│ │ │ │ ├── Collections/
│ │ │ │ │ ├── ListDrawerSettings.cs
│ │ │ │ │ ├── ListDrawerSettings.cs.meta
│ │ │ │ │ ├── TableListAttribute.cs
│ │ │ │ │ └── TableListAttribute.cs.meta
│ │ │ │ ├── Collections.meta
│ │ │ │ ├── Conditionals/
│ │ │ │ │ ├── DisableIfAttribute.cs
│ │ │ │ │ ├── DisableIfAttribute.cs.meta
│ │ │ │ │ ├── DisableInEditModeAttribute.cs
│ │ │ │ │ ├── DisableInEditModeAttribute.cs.meta
│ │ │ │ │ ├── DisableInPlayModeAttribute.cs
│ │ │ │ │ ├── DisableInPlayModeAttribute.cs.meta
│ │ │ │ │ ├── EnableIfAttribute.cs
│ │ │ │ │ ├── EnableIfAttribute.cs.meta
│ │ │ │ │ ├── EnableInEditModeAttribute.cs
│ │ │ │ │ ├── EnableInEditModeAttribute.cs.meta
│ │ │ │ │ ├── EnableInPlayModeAttribute.cs
│ │ │ │ │ ├── EnableInPlayModeAttribute.cs.meta
│ │ │ │ │ ├── HideIfAttribute.cs
│ │ │ │ │ ├── HideIfAttribute.cs.meta
│ │ │ │ │ ├── HideInEditModeAttribute.cs
│ │ │ │ │ ├── HideInEditModeAttribute.cs.meta
│ │ │ │ │ ├── HideInPlayModeAttribute.cs
│ │ │ │ │ ├── HideInPlayModeAttribute.cs.meta
│ │ │ │ │ ├── ShowIfAttribute.cs
│ │ │ │ │ ├── ShowIfAttribute.cs.meta
│ │ │ │ │ ├── ShowInEditModeAttribute.cs
│ │ │ │ │ ├── ShowInEditModeAttribute.cs.meta
│ │ │ │ │ ├── ShowInPlayModeAttribute.cs
│ │ │ │ │ └── ShowInPlayModeAttribute.cs.meta
│ │ │ │ ├── Conditionals.meta
│ │ │ │ ├── Debug/
│ │ │ │ │ ├── ShowDrawerChainAttribute.cs
│ │ │ │ │ └── ShowDrawerChainAttribute.cs.meta
│ │ │ │ ├── Debug.meta
│ │ │ │ ├── Decorators/
│ │ │ │ │ ├── DisplayAsStringAttribute.cs
│ │ │ │ │ ├── DisplayAsStringAttribute.cs.meta
│ │ │ │ │ ├── DropdownAttribute.cs
│ │ │ │ │ ├── DropdownAttribute.cs.meta
│ │ │ │ │ ├── InlineEditorAttribute.cs
│ │ │ │ │ ├── InlineEditorAttribute.cs.meta
│ │ │ │ │ ├── PreviewMeshAttribute.cs
│ │ │ │ │ ├── PreviewMeshAttribute.cs.meta
│ │ │ │ │ ├── SceneAttribute.cs
│ │ │ │ │ ├── SceneAttribute.cs.meta
│ │ │ │ │ ├── UnitAttribute.cs
│ │ │ │ │ └── UnitAttribute.cs.meta
│ │ │ │ ├── Decorators.meta
│ │ │ │ ├── Groups/
│ │ │ │ │ ├── DeclareBoxGroupAttribute.cs
│ │ │ │ │ ├── DeclareBoxGroupAttribute.cs.meta
│ │ │ │ │ ├── DeclareFoldoutGroupAttribute.cs
│ │ │ │ │ ├── DeclareFoldoutGroupAttribute.cs.meta
│ │ │ │ │ ├── DeclareGroupBaseAttribute.cs
│ │ │ │ │ ├── DeclareGroupBaseAttribute.cs.meta
│ │ │ │ │ ├── DeclareHorizontalGroupAttribute.cs
│ │ │ │ │ ├── DeclareHorizontalGroupAttribute.cs.meta
│ │ │ │ │ ├── DeclareTabGroupAttribute.cs
│ │ │ │ │ ├── DeclareTabGroupAttribute.cs.meta
│ │ │ │ │ ├── DeclareToggleGroupAttribute.cs
│ │ │ │ │ ├── DeclareToggleGroupAttribute.cs.meta
│ │ │ │ │ ├── DeclareVerticalGroupAttribute.cs
│ │ │ │ │ ├── DeclareVerticalGroupAttribute.cs.meta
│ │ │ │ │ ├── GroupAttribute.cs
│ │ │ │ │ ├── GroupAttribute.cs.meta
│ │ │ │ │ ├── GroupNextAttribute.cs
│ │ │ │ │ ├── GroupNextAttribute.cs.meta
│ │ │ │ │ ├── TabAttribute.cs
│ │ │ │ │ └── TabAttribute.cs.meta
│ │ │ │ ├── Groups.meta
│ │ │ │ ├── Misc/
│ │ │ │ │ ├── HideMonoScriptAttribute.cs
│ │ │ │ │ ├── HideMonoScriptAttribute.cs.meta
│ │ │ │ │ ├── HideReferencePickerAttribute.cs
│ │ │ │ │ ├── HideReferencePickerAttribute.cs.meta
│ │ │ │ │ ├── OnValueChangedAttribute.cs
│ │ │ │ │ ├── OnValueChangedAttribute.cs.meta
│ │ │ │ │ ├── PropertyOrderAttribute.cs
│ │ │ │ │ ├── PropertyOrderAttribute.cs.meta
│ │ │ │ │ ├── ReadOnlyAttribute.cs
│ │ │ │ │ ├── ReadOnlyAttribute.cs.meta
│ │ │ │ │ ├── ShowInInspector.cs
│ │ │ │ │ └── ShowInInspector.cs.meta
│ │ │ │ ├── Misc.meta
│ │ │ │ ├── Others/
│ │ │ │ │ ├── DrawWithTriInspectorAttribute.cs
│ │ │ │ │ ├── DrawWithTriInspectorAttribute.cs.meta
│ │ │ │ │ ├── DrawWithUnityAttribute.cs
│ │ │ │ │ └── DrawWithUnityAttribute.cs.meta
│ │ │ │ ├── Others.meta
│ │ │ │ ├── Styling/
│ │ │ │ │ ├── GUIColorAttribute.cs
│ │ │ │ │ ├── GUIColorAttribute.cs.meta
│ │ │ │ │ ├── HideLabelAttribute.cs
│ │ │ │ │ ├── HideLabelAttribute.cs.meta
│ │ │ │ │ ├── IndentAttribute.cs
│ │ │ │ │ ├── IndentAttribute.cs.meta
│ │ │ │ │ ├── InlinePropertyAttribute.cs
│ │ │ │ │ ├── InlinePropertyAttribute.cs.meta
│ │ │ │ │ ├── LabelTextAttribute.cs
│ │ │ │ │ ├── LabelTextAttribute.cs.meta
│ │ │ │ │ ├── LabelWidthAttribute.cs
│ │ │ │ │ ├── LabelWidthAttribute.cs.meta
│ │ │ │ │ ├── PropertySpaceAttribute.cs
│ │ │ │ │ ├── PropertySpaceAttribute.cs.meta
│ │ │ │ │ ├── PropertyTooltipAttribute.cs
│ │ │ │ │ ├── PropertyTooltipAttribute.cs.meta
│ │ │ │ │ ├── TitleAttribute.cs
│ │ │ │ │ └── TitleAttribute.cs.meta
│ │ │ │ ├── Styling.meta
│ │ │ │ ├── Validators/
│ │ │ │ │ ├── AssetsOnlyAttribute.cs
│ │ │ │ │ ├── AssetsOnlyAttribute.cs.meta
│ │ │ │ │ ├── InfoBoxAttribute.cs
│ │ │ │ │ ├── InfoBoxAttribute.cs.meta
│ │ │ │ │ ├── RequiredAttribute.cs
│ │ │ │ │ ├── RequiredAttribute.cs.meta
│ │ │ │ │ ├── SceneObjectsOnlyAttribute.cs
│ │ │ │ │ ├── SceneObjectsOnlyAttribute.cs.meta
│ │ │ │ │ ├── ValidateInputAttribute.cs
│ │ │ │ │ └── ValidateInputAttribute.cs.meta
│ │ │ │ └── Validators.meta
│ │ │ ├── Attributes.meta
│ │ │ ├── ButtonSizes.cs
│ │ │ ├── ButtonSizes.cs.meta
│ │ │ ├── CustomizeAttribute/
│ │ │ │ ├── Attribute/
│ │ │ │ │ ├── EditorIconAttribute.cs
│ │ │ │ │ ├── EditorIconAttribute.cs.meta
│ │ │ │ │ ├── EnumAttribue/
│ │ │ │ │ │ ├── ExtendEnumAttribute.cs
│ │ │ │ │ │ ├── ExtendEnumAttribute.cs.meta
│ │ │ │ │ │ ├── SearchableEnumAttribute.cs
│ │ │ │ │ │ └── SearchableEnumAttribute.cs.meta
│ │ │ │ │ ├── EnumAttribue.meta
│ │ │ │ │ ├── HeaderLineAttribute.cs
│ │ │ │ │ ├── HeaderLineAttribute.cs.meta
│ │ │ │ │ ├── HelpBoxAttribute.cs
│ │ │ │ │ ├── HelpBoxAttribute.cs.meta
│ │ │ │ │ ├── HighlightAttribute.cs
│ │ │ │ │ ├── HighlightAttribute.cs.meta
│ │ │ │ │ ├── LayerAttribute.cs
│ │ │ │ │ ├── LayerAttribute.cs.meta
│ │ │ │ │ ├── TagAttribute.cs
│ │ │ │ │ ├── TagAttribute.cs.meta
│ │ │ │ │ ├── TitleColorAttribute.cs
│ │ │ │ │ └── TitleColorAttribute.cs.meta
│ │ │ │ ├── Attribute.meta
│ │ │ │ ├── GUIDAttribute.cs
│ │ │ │ ├── GUIDAttribute.cs.meta
│ │ │ │ ├── NamedIdAttribute.cs
│ │ │ │ └── NamedIdAttribute.cs.meta
│ │ │ ├── CustomizeAttribute.meta
│ │ │ ├── InlineEditorModes.cs
│ │ │ ├── InlineEditorModes.cs.meta
│ │ │ ├── PreviewMeshRotationMethod.cs
│ │ │ ├── PreviewMeshRotationMethod.cs.meta
│ │ │ ├── TriDropdownList.cs
│ │ │ ├── TriDropdownList.cs.meta
│ │ │ ├── TriValidationResult.cs
│ │ │ ├── TriValidationResult.cs.meta
│ │ │ ├── VirtueSky.Sunflower.Inspector.asmdef
│ │ │ └── VirtueSky.Sunflower.Inspector.asmdef.meta
│ │ ├── Runtime.meta
│ │ ├── Unity.InternalAPIEditorBridge.012/
│ │ │ ├── AdvancedDropdownProxy.cs
│ │ │ ├── AdvancedDropdownProxy.cs.meta
│ │ │ ├── AssemblyInfo.cs
│ │ │ ├── AssemblyInfo.cs.meta
│ │ │ ├── EditorGUIUtilityProxy.cs
│ │ │ ├── EditorGUIUtilityProxy.cs.meta
│ │ │ ├── EditorProxy.cs
│ │ │ ├── EditorProxy.cs.meta
│ │ │ ├── GUIClipProxy.cs
│ │ │ ├── GUIClipProxy.cs.meta
│ │ │ ├── InternalEditorUtilityProxy.cs
│ │ │ ├── InternalEditorUtilityProxy.cs.meta
│ │ │ ├── ReorderableListProxy.cs
│ │ │ ├── ReorderableListProxy.cs.meta
│ │ │ ├── ScriptAttributeUtilityProxy.cs
│ │ │ ├── ScriptAttributeUtilityProxy.cs.meta
│ │ │ ├── Unity.InternalAPIEditorBridge.013.asmdef
│ │ │ └── Unity.InternalAPIEditorBridge.013.asmdef.meta
│ │ ├── Unity.InternalAPIEditorBridge.012.meta
│ │ ├── Version.txt
│ │ └── Version.txt.meta
│ ├── Inspector.meta
│ ├── LevelEditor/
│ │ ├── LevelEditor.cs
│ │ ├── LevelEditor.cs.meta
│ │ ├── LevelSystemEditorSetting.cs
│ │ ├── LevelSystemEditorSetting.cs.meta
│ │ ├── PickObject.cs
│ │ ├── PickObject.cs.meta
│ │ ├── PreviewGenerator.cs
│ │ ├── PreviewGenerator.cs.meta
│ │ ├── Probe.cs
│ │ ├── Probe.cs.meta
│ │ ├── virtuesky.sunflower.leveleditor.asmdef
│ │ └── virtuesky.sunflower.leveleditor.asmdef.meta
│ ├── LevelEditor.meta
│ ├── Linq/
│ │ ├── Aggregate.cs
│ │ ├── Aggregate.cs.meta
│ │ ├── AnyAll.cs
│ │ ├── AnyAll.cs.meta
│ │ ├── Average.cs
│ │ ├── Average.cs.meta
│ │ ├── Chunk.cs
│ │ ├── Chunk.cs.meta
│ │ ├── Contains.cs
│ │ ├── Contains.cs.meta
│ │ ├── Count.cs
│ │ ├── Count.cs.meta
│ │ ├── Distinct.cs
│ │ ├── Distinct.cs.meta
│ │ ├── First.cs
│ │ ├── First.cs.meta
│ │ ├── Flatten.cs
│ │ ├── Flatten.cs.meta
│ │ ├── Last.cs
│ │ ├── Last.cs.meta
│ │ ├── Max.cs
│ │ ├── Max.cs.meta
│ │ ├── Min.cs
│ │ ├── Min.cs.meta
│ │ ├── OrderBy.cs
│ │ ├── OrderBy.cs.meta
│ │ ├── Range.cs
│ │ ├── Range.cs.meta
│ │ ├── Repeat.cs
│ │ ├── Repeat.cs.meta
│ │ ├── Reverse.cs
│ │ ├── Reverse.cs.meta
│ │ ├── Select.cs
│ │ ├── Select.cs.meta
│ │ ├── SelectMany.cs
│ │ ├── SelectMany.cs.meta
│ │ ├── SelectWhere.cs
│ │ ├── SelectWhere.cs.meta
│ │ ├── SequenceEqual.cs
│ │ ├── SequenceEqual.cs.meta
│ │ ├── Single.cs
│ │ ├── Single.cs.meta
│ │ ├── Skip.cs
│ │ ├── Skip.cs.meta
│ │ ├── Sum.cs
│ │ ├── Sum.cs.meta
│ │ ├── Take.cs
│ │ ├── Take.cs.meta
│ │ ├── Utils/
│ │ │ ├── ComparerMagic.cs
│ │ │ ├── ComparerMagic.cs.meta
│ │ │ ├── CustomPartition.cs
│ │ │ ├── CustomPartition.cs.meta
│ │ │ ├── GenericOperators.cs
│ │ │ ├── GenericOperators.cs.meta
│ │ │ ├── SliceHelper.cs
│ │ │ └── SliceHelper.cs.meta
│ │ ├── Utils.meta
│ │ ├── VirtueSky.Sunflower.Linq.asmdef
│ │ ├── VirtueSky.Sunflower.Linq.asmdef.meta
│ │ ├── Where.cs
│ │ ├── Where.cs.meta
│ │ ├── WhereAggregate.cs
│ │ ├── WhereAggregate.cs.meta
│ │ ├── WhereSelect.cs
│ │ ├── WhereSelect.cs.meta
│ │ ├── WhereSum.cs
│ │ ├── WhereSum.cs.meta
│ │ ├── Zip.cs
│ │ └── Zip.cs.meta
│ ├── Linq.meta
│ ├── Localization/
│ │ ├── Editor/
│ │ │ ├── AssetTreeViewItem.cs
│ │ │ ├── AssetTreeViewItem.cs.meta
│ │ │ ├── CsvSerialization.cs
│ │ │ ├── CsvSerialization.cs.meta
│ │ │ ├── EditorMenu.cs
│ │ │ ├── EditorMenu.cs.meta
│ │ │ ├── LanguagePropertyDrawer.cs
│ │ │ ├── LanguagePropertyDrawer.cs.meta
│ │ │ ├── LocaleEditorUtil.cs
│ │ │ ├── LocaleEditorUtil.cs.meta
│ │ │ ├── LocaleSettingsEditor.cs
│ │ │ ├── LocaleSettingsEditor.cs.meta
│ │ │ ├── LocaleTreeView.cs
│ │ │ ├── LocaleTreeView.cs.meta
│ │ │ ├── LocaleTreeViewItem.cs
│ │ │ ├── LocaleTreeViewItem.cs.meta
│ │ │ ├── PostBuildProcessor.cs
│ │ │ ├── PostBuildProcessor.cs.meta
│ │ │ ├── ScriptableLocaleEditor.cs
│ │ │ ├── ScriptableLocaleEditor.cs.meta
│ │ │ ├── VirtueSky.Sunflower.Localization.Editor.asmdef
│ │ │ └── VirtueSky.Sunflower.Localization.Editor.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── Implement/
│ │ │ │ ├── Behaviour/
│ │ │ │ │ ├── LocaleAudioClipComponent.cs
│ │ │ │ │ ├── LocaleAudioClipComponent.cs.meta
│ │ │ │ │ ├── LocaleComponent.cs
│ │ │ │ │ ├── LocaleComponent.cs.meta
│ │ │ │ │ ├── LocaleComponentGeneric.cs
│ │ │ │ │ ├── LocaleComponentGeneric.cs.meta
│ │ │ │ │ ├── LocaleComponentGenericBase.cs
│ │ │ │ │ ├── LocaleComponentGenericBase.cs.meta
│ │ │ │ │ ├── LocaleFontComponent.cs
│ │ │ │ │ ├── LocaleFontComponent.cs.meta
│ │ │ │ │ ├── LocaleMaterialComponent.cs
│ │ │ │ │ ├── LocaleMaterialComponent.cs.meta
│ │ │ │ │ ├── LocalePrefabComponent.cs
│ │ │ │ │ ├── LocalePrefabComponent.cs.meta
│ │ │ │ │ ├── LocaleRendererComponent.cs
│ │ │ │ │ ├── LocaleRendererComponent.cs.meta
│ │ │ │ │ ├── LocaleSharedMaterialComponent.cs
│ │ │ │ │ ├── LocaleSharedMaterialComponent.cs.meta
│ │ │ │ │ ├── LocaleSpriteComponent.cs
│ │ │ │ │ ├── LocaleSpriteComponent.cs.meta
│ │ │ │ │ ├── LocaleTMPFontComponent.cs
│ │ │ │ │ ├── LocaleTMPFontComponent.cs.meta
│ │ │ │ │ ├── LocaleTextAssetComponent.cs
│ │ │ │ │ ├── LocaleTextAssetComponent.cs.meta
│ │ │ │ │ ├── LocaleTextComponent.cs
│ │ │ │ │ ├── LocaleTextComponent.cs.meta
│ │ │ │ │ ├── LocaleTextCompositeComponent.cs
│ │ │ │ │ ├── LocaleTextCompositeComponent.cs.meta
│ │ │ │ │ ├── LocaleTextureComponent.cs
│ │ │ │ │ ├── LocaleTextureComponent.cs.meta
│ │ │ │ │ ├── LocaleTextureMaterialComponent.cs
│ │ │ │ │ ├── LocaleTextureMaterialComponent.cs.meta
│ │ │ │ │ ├── LocaleVideoClipComponent.cs
│ │ │ │ │ └── LocaleVideoClipComponent.cs.meta
│ │ │ │ ├── Behaviour.meta
│ │ │ │ ├── Event/
│ │ │ │ │ ├── ScriptableEventLocaleText.cs
│ │ │ │ │ └── ScriptableEventLocaleText.cs.meta
│ │ │ │ ├── Event.meta
│ │ │ │ ├── Variable/
│ │ │ │ │ ├── LocaleAudioClip.cs
│ │ │ │ │ ├── LocaleAudioClip.cs.meta
│ │ │ │ │ ├── LocaleFont.cs
│ │ │ │ │ ├── LocaleFont.cs.meta
│ │ │ │ │ ├── LocaleMaterial.cs
│ │ │ │ │ ├── LocaleMaterial.cs.meta
│ │ │ │ │ ├── LocalePrefab.cs
│ │ │ │ │ ├── LocalePrefab.cs.meta
│ │ │ │ │ ├── LocaleSprite.cs
│ │ │ │ │ ├── LocaleSprite.cs.meta
│ │ │ │ │ ├── LocaleTMPFont.cs
│ │ │ │ │ ├── LocaleTMPFont.cs.meta
│ │ │ │ │ ├── LocaleText.cs
│ │ │ │ │ ├── LocaleText.cs.meta
│ │ │ │ │ ├── LocaleTextAsset.cs
│ │ │ │ │ ├── LocaleTextAsset.cs.meta
│ │ │ │ │ ├── LocaleTexture.cs
│ │ │ │ │ ├── LocaleTexture.cs.meta
│ │ │ │ │ ├── LocaleVariable.cs
│ │ │ │ │ ├── LocaleVariable.cs.meta
│ │ │ │ │ ├── LocaleVideoClip.cs
│ │ │ │ │ ├── LocaleVideoClip.cs.meta
│ │ │ │ │ ├── ScriptableLocaleBase.cs
│ │ │ │ │ └── ScriptableLocaleBase.cs.meta
│ │ │ │ └── Variable.meta
│ │ │ ├── Implement.meta
│ │ │ ├── Language.cs
│ │ │ ├── Language.cs.meta
│ │ │ ├── Locale.cs
│ │ │ ├── Locale.cs.meta
│ │ │ ├── LocaleChangedEventArgs.cs
│ │ │ ├── LocaleChangedEventArgs.cs.meta
│ │ │ ├── LocaleItem.cs
│ │ │ ├── LocaleItem.cs.meta
│ │ │ ├── LocaleItemBase.cs
│ │ │ ├── LocaleItemBase.cs.meta
│ │ │ ├── LocaleSettings.cs
│ │ │ ├── LocaleSettings.cs.meta
│ │ │ ├── Translate/
│ │ │ │ ├── GoogleTranslateRequest.cs
│ │ │ │ ├── GoogleTranslateRequest.cs.meta
│ │ │ │ ├── GoogleTranslateResponse.cs
│ │ │ │ ├── GoogleTranslateResponse.cs.meta
│ │ │ │ ├── GoogleTranslator.cs
│ │ │ │ └── GoogleTranslator.cs.meta
│ │ │ ├── Translate.meta
│ │ │ ├── VirtueSky.Sunflower.Localization.asmdef
│ │ │ └── VirtueSky.Sunflower.Localization.asmdef.meta
│ │ └── Runtime.meta
│ ├── Localization.meta
│ ├── Misc/
│ │ ├── Common.Animancer.cs
│ │ ├── Common.Animancer.cs.meta
│ │ ├── Common.Collections.cs
│ │ ├── Common.Collections.cs.meta
│ │ ├── Common.Colors.cs
│ │ ├── Common.Colors.cs.meta
│ │ ├── Common.Math.cs
│ │ ├── Common.Math.cs.meta
│ │ ├── Common.Physics.cs
│ │ ├── Common.Physics.cs.meta
│ │ ├── Common.SkeletonAnimation.cs
│ │ ├── Common.SkeletonAnimation.cs.meta
│ │ ├── Common.SkeletonGraphic.cs
│ │ ├── Common.SkeletonGraphic.cs.meta
│ │ ├── Common.Tag.cs
│ │ ├── Common.Tag.cs.meta
│ │ ├── Common.Text.cs
│ │ ├── Common.Text.cs.meta
│ │ ├── Common.Transform.cs
│ │ ├── Common.Transform.cs.meta
│ │ ├── Common.cs
│ │ ├── Common.cs.meta
│ │ ├── virtuesky.sunflower.misc.asmdef
│ │ └── virtuesky.sunflower.misc.asmdef.meta
│ ├── Misc.meta
│ ├── Notifications/
│ │ ├── Editor/
│ │ │ ├── NotificationWindowEditor.cs
│ │ │ ├── NotificationWindowEditor.cs.meta
│ │ │ ├── Virtuesky.Sunflower.Notifications.Editor.asmdef
│ │ │ └── Virtuesky.Sunflower.Notifications.Editor.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── NotificationAndroid.cs
│ │ │ ├── NotificationAndroid.cs.meta
│ │ │ ├── NotificationConsole.cs
│ │ │ ├── NotificationConsole.cs.meta
│ │ │ ├── NotificationIOS.cs
│ │ │ ├── NotificationIOS.cs.meta
│ │ │ ├── NotificationPrepare.cs
│ │ │ ├── NotificationPrepare.cs.meta
│ │ │ ├── NotificationVariable.cs
│ │ │ ├── NotificationVariable.cs.meta
│ │ │ ├── virtuesky.sunflower.notifications.asmdef
│ │ │ └── virtuesky.sunflower.notifications.asmdef.meta
│ │ └── Runtime.meta
│ ├── Notifications.meta
│ ├── ObjectPooling/
│ │ ├── Pool.cs
│ │ ├── Pool.cs.meta
│ │ ├── PoolData.cs
│ │ ├── PoolData.cs.meta
│ │ ├── PoolHandle.cs
│ │ ├── PoolHandle.cs.meta
│ │ ├── PooledObjectId.cs
│ │ ├── PooledObjectId.cs.meta
│ │ ├── virtuesky.sunflower.objectpooling.asmdef
│ │ └── virtuesky.sunflower.objectpooling.asmdef.meta
│ ├── ObjectPooling.meta
│ ├── Rating/
│ │ ├── Editor/
│ │ │ ├── RatingWindowEditor.cs
│ │ │ ├── RatingWindowEditor.cs.meta
│ │ │ ├── Virtuesky.Sunflower.Rating.Editor.asmdef
│ │ │ └── Virtuesky.Sunflower.Rating.Editor.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── InAppReview.cs
│ │ │ ├── InAppReview.cs.meta
│ │ │ ├── virtuesky.sunflower.rating.asmdef
│ │ │ └── virtuesky.sunflower.rating.asmdef.meta
│ │ └── Runtime.meta
│ ├── Rating.meta
│ ├── RemoteConfig/
│ │ ├── FirebaseRemoteConfigData.cs
│ │ ├── FirebaseRemoteConfigData.cs.meta
│ │ ├── FirebaseRemoteConfigManager.cs
│ │ ├── FirebaseRemoteConfigManager.cs.meta
│ │ ├── Virtuesky.Sunflower.RemoteConfigs.asmdef
│ │ └── Virtuesky.Sunflower.RemoteConfigs.asmdef.meta
│ ├── RemoteConfig.meta
│ ├── SimpleJSON/
│ │ ├── SimpleJSON.cs
│ │ ├── SimpleJSON.cs.meta
│ │ ├── SimpleJSONBinary.cs
│ │ ├── SimpleJSONBinary.cs.meta
│ │ ├── SimpleJSONDotNetTypes.cs
│ │ ├── SimpleJSONDotNetTypes.cs.meta
│ │ ├── SimpleJSONUnity.cs
│ │ ├── SimpleJSONUnity.cs.meta
│ │ ├── Virtuesky.Sunflower.SimpleJson.asmdef
│ │ └── Virtuesky.Sunflower.SimpleJson.asmdef.meta
│ ├── SimpleJSON.meta
│ ├── TouchInput/
│ │ ├── Editor/
│ │ │ ├── TouchInputManagerEditor.cs
│ │ │ ├── TouchInputManagerEditor.cs.meta
│ │ │ ├── Virtuesky.Sunflower.TouchInput.Editor.asmdef
│ │ │ └── Virtuesky.Sunflower.TouchInput.Editor.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── InputTouchEvent/
│ │ │ │ ├── InputEventTouchBegin.cs
│ │ │ │ ├── InputEventTouchBegin.cs.meta
│ │ │ │ ├── InputEventTouchCancel.cs
│ │ │ │ ├── InputEventTouchCancel.cs.meta
│ │ │ │ ├── InputEventTouchEnd.cs
│ │ │ │ ├── InputEventTouchEnd.cs.meta
│ │ │ │ ├── InputEventTouchMove.cs
│ │ │ │ ├── InputEventTouchMove.cs.meta
│ │ │ │ ├── InputEventTouchStationary.cs
│ │ │ │ ├── InputEventTouchStationary.cs.meta
│ │ │ │ ├── InputPreventTouchVariable.cs
│ │ │ │ └── InputPreventTouchVariable.cs.meta
│ │ │ ├── InputTouchEvent.meta
│ │ │ ├── TouchInputManager.cs
│ │ │ ├── TouchInputManager.cs.meta
│ │ │ ├── Virtuesky.Sunflower.TouchInput.asmdef
│ │ │ └── Virtuesky.Sunflower.TouchInput.asmdef.meta
│ │ └── Runtime.meta
│ ├── TouchInput.meta
│ ├── Tracking/
│ │ ├── Editor/
│ │ │ ├── TrackingWindowEditor.cs
│ │ │ ├── TrackingWindowEditor.cs.meta
│ │ │ ├── Virtuesky.Sunflower.TrackingEditor.asmdef
│ │ │ └── Virtuesky.Sunflower.TrackingEditor.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── AdjustTracking/
│ │ │ │ ├── AdjustConfig.cs
│ │ │ │ ├── AdjustConfig.cs.meta
│ │ │ │ ├── AdjustTrackingRevenue.cs
│ │ │ │ ├── AdjustTrackingRevenue.cs.meta
│ │ │ │ ├── TrackingAdjust.cs
│ │ │ │ └── TrackingAdjust.cs.meta
│ │ │ ├── AdjustTracking.meta
│ │ │ ├── AppTracking.cs
│ │ │ ├── AppTracking.cs.meta
│ │ │ ├── AppsFlyerTracking/
│ │ │ │ ├── AppsFlyerConfig.cs
│ │ │ │ ├── AppsFlyerConfig.cs.meta
│ │ │ │ ├── AppsFlyerObject.cs
│ │ │ │ ├── AppsFlyerObject.cs.meta
│ │ │ │ ├── AppsFlyerTrackingRevenue.cs
│ │ │ │ ├── AppsFlyerTrackingRevenue.cs.meta
│ │ │ │ ├── TrackingAppsFlyer.cs
│ │ │ │ ├── TrackingAppsFlyer.cs.meta
│ │ │ │ ├── TrackingAppsFlyerFiveParam.cs
│ │ │ │ ├── TrackingAppsFlyerFiveParam.cs.meta
│ │ │ │ ├── TrackingAppsFlyerFourParam.cs
│ │ │ │ ├── TrackingAppsFlyerFourParam.cs.meta
│ │ │ │ ├── TrackingAppsFlyerHasParam.cs
│ │ │ │ ├── TrackingAppsFlyerHasParam.cs.meta
│ │ │ │ ├── TrackingAppsFlyerNoParam.cs
│ │ │ │ ├── TrackingAppsFlyerNoParam.cs.meta
│ │ │ │ ├── TrackingAppsFlyerOneParam.cs
│ │ │ │ ├── TrackingAppsFlyerOneParam.cs.meta
│ │ │ │ ├── TrackingAppsFlyerThreeParam.cs
│ │ │ │ ├── TrackingAppsFlyerThreeParam.cs.meta
│ │ │ │ ├── TrackingAppsFlyerTwoParam.cs
│ │ │ │ └── TrackingAppsFlyerTwoParam.cs.meta
│ │ │ ├── AppsFlyerTracking.meta
│ │ │ ├── FirebaseAnalyticTracking/
│ │ │ │ ├── FirebaseAnalyticTrackingRevenue.cs
│ │ │ │ ├── FirebaseAnalyticTrackingRevenue.cs.meta
│ │ │ │ ├── TrackingFirebase.cs
│ │ │ │ ├── TrackingFirebase.cs.meta
│ │ │ │ ├── TrackingFirebaseFiveParam.cs
│ │ │ │ ├── TrackingFirebaseFiveParam.cs.meta
│ │ │ │ ├── TrackingFirebaseFourParam.cs
│ │ │ │ ├── TrackingFirebaseFourParam.cs.meta
│ │ │ │ ├── TrackingFirebaseNoParam.cs
│ │ │ │ ├── TrackingFirebaseNoParam.cs.meta
│ │ │ │ ├── TrackingFirebaseOneParam.cs
│ │ │ │ ├── TrackingFirebaseOneParam.cs.meta
│ │ │ │ ├── TrackingFirebaseSixParam.cs
│ │ │ │ ├── TrackingFirebaseSixParam.cs.meta
│ │ │ │ ├── TrackingFirebaseThreeParam.cs
│ │ │ │ ├── TrackingFirebaseThreeParam.cs.meta
│ │ │ │ ├── TrackingFirebaseTwoParam.cs
│ │ │ │ └── TrackingFirebaseTwoParam.cs.meta
│ │ │ ├── FirebaseAnalyticTracking.meta
│ │ │ ├── Virtuesky.Sunflower.Tracking.asmdef
│ │ │ └── Virtuesky.Sunflower.Tracking.asmdef.meta
│ │ └── Runtime.meta
│ ├── Tracking.meta
│ ├── Utils/
│ │ ├── Editor/
│ │ │ ├── ConstantDefineSymbols.cs
│ │ │ ├── ConstantDefineSymbols.cs.meta
│ │ │ ├── CreateAsset.cs
│ │ │ ├── CreateAsset.cs.meta
│ │ │ ├── EditorCoroutine.cs
│ │ │ ├── EditorCoroutine.cs.meta
│ │ │ ├── EditorGUIUtils.cs
│ │ │ ├── EditorGUIUtils.cs.meta
│ │ │ ├── EditorGeneric.cs
│ │ │ ├── EditorGeneric.cs.meta
│ │ │ ├── EditorResources.cs
│ │ │ ├── EditorResources.cs.meta
│ │ │ ├── EditorScriptDefineSymbols.cs
│ │ │ ├── EditorScriptDefineSymbols.cs.meta
│ │ │ ├── ExSearchWindow.cs
│ │ │ ├── ExSearchWindow.cs.meta
│ │ │ ├── FileExtension.cs
│ │ │ ├── FileExtension.cs.meta
│ │ │ ├── GameViewUtils.cs
│ │ │ ├── GameViewUtils.cs.meta
│ │ │ ├── Icons/
│ │ │ │ ├── box_bg_dark.psd
│ │ │ │ ├── box_bg_dark.psd.meta
│ │ │ │ ├── box_content_dark.psd
│ │ │ │ ├── box_content_dark.psd.meta
│ │ │ │ ├── even_bg.png.meta
│ │ │ │ ├── even_bg_dark.png.meta
│ │ │ │ ├── even_bg_select.png.meta
│ │ │ │ ├── icon_about.png.meta
│ │ │ │ ├── icon_adjust.png.meta
│ │ │ │ ├── icon_ads.png.meta
│ │ │ │ ├── icon_appsflyer.png.meta
│ │ │ │ ├── icon_audio.png.meta
│ │ │ │ ├── icon_authentication.png.meta
│ │ │ │ ├── icon_button.png.meta
│ │ │ │ ├── icon_controller.png.meta
│ │ │ │ ├── icon_csharp.png.meta
│ │ │ │ ├── icon_extension.png.meta
│ │ │ │ ├── icon_firebase.png.meta
│ │ │ │ ├── icon_folder.png.meta
│ │ │ │ ├── icon_game_service.png.meta
│ │ │ │ ├── icon_gamemanager.png.meta
│ │ │ │ ├── icon_generator.png.meta
│ │ │ │ ├── icon_hierarchy.png.meta
│ │ │ │ ├── icon_iap.png.meta
│ │ │ │ ├── icon_in_app_review.png.meta
│ │ │ │ ├── icon_leaderboard.png.meta
│ │ │ │ ├── icon_locale.png.meta
│ │ │ │ ├── icon_manager.png.meta
│ │ │ │ ├── icon_package.png.meta
│ │ │ │ ├── icon_scriptable.png.meta
│ │ │ │ ├── icon_service.png.meta
│ │ │ │ ├── icon_sound.png.meta
│ │ │ │ ├── icon_sound_mixer.png.meta
│ │ │ │ ├── icon_unity.png.meta
│ │ │ │ ├── script_noti.png.meta
│ │ │ │ ├── scriptable_adjust.png.meta
│ │ │ │ ├── scriptable_adjust2.png.meta
│ │ │ │ ├── scriptable_af.png.meta
│ │ │ │ ├── scriptable_audio.png.meta
│ │ │ │ ├── scriptable_audioclip.png.meta
│ │ │ │ ├── scriptable_event.png.meta
│ │ │ │ ├── scriptable_event_listener.png.meta
│ │ │ │ ├── scriptable_factory.png.meta
│ │ │ │ ├── scriptable_firebase.png.meta
│ │ │ │ ├── scriptable_iap.png.meta
│ │ │ │ ├── scriptable_notification.png.meta
│ │ │ │ ├── scriptable_particle_system.png.meta
│ │ │ │ ├── scriptable_pool.png.meta
│ │ │ │ ├── scriptable_variable.png.meta
│ │ │ │ ├── scriptable_yellow_audioclip.png.meta
│ │ │ │ ├── scriptable_yellow_font.png.meta
│ │ │ │ ├── scriptable_yellow_fontasset.png.meta
│ │ │ │ ├── scriptable_yellow_gameobject.png.meta
│ │ │ │ ├── scriptable_yellow_material.png.meta
│ │ │ │ ├── scriptable_yellow_sprite.png.meta
│ │ │ │ ├── scriptable_yellow_text.png.meta
│ │ │ │ ├── scriptable_yellow_textasset.png.meta
│ │ │ │ ├── scriptable_yellow_texture.png.meta
│ │ │ │ ├── scriptable_yellow_videoclip.png.meta
│ │ │ │ └── virtuesky_removebg.png.meta
│ │ │ ├── Icons.meta
│ │ │ ├── RegistryManager.cs
│ │ │ ├── RegistryManager.cs.meta
│ │ │ ├── TemplateAssembly/
│ │ │ │ ├── PurchasingGeneratedAsmdef.txt
│ │ │ │ ├── PurchasingGeneratedAsmdef.txt.meta
│ │ │ │ ├── PurchasingGeneratedAsmdefMeta.txt
│ │ │ │ └── PurchasingGeneratedAsmdefMeta.txt.meta
│ │ │ ├── TemplateAssembly.meta
│ │ │ ├── TextureUtils.cs
│ │ │ ├── TextureUtils.cs.meta
│ │ │ ├── Uniform.cs
│ │ │ ├── Uniform.cs.meta
│ │ │ ├── UnityPackage/
│ │ │ │ ├── Note_Package.txt
│ │ │ │ ├── Note_Package.txt.meta
│ │ │ │ ├── google-play-game.unitypackage
│ │ │ │ ├── google-play-game.unitypackage.meta
│ │ │ │ ├── max-sdk.unitypackage
│ │ │ │ └── max-sdk.unitypackage.meta
│ │ │ ├── UnityPackage.meta
│ │ │ ├── Virtuesky.Sunflower.UtilsEdtitor.asmdef
│ │ │ └── Virtuesky.Sunflower.UtilsEdtitor.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── ColorExtensions.cs
│ │ │ ├── ColorExtensions.cs.meta
│ │ │ ├── ExtensionUtils.cs
│ │ │ ├── ExtensionUtils.cs.meta
│ │ │ ├── InputUtils.cs
│ │ │ ├── InputUtils.cs.meta
│ │ │ ├── InterfaceUtils.cs
│ │ │ ├── InterfaceUtils.cs.meta
│ │ │ ├── ReflectionUtils.cs
│ │ │ ├── ReflectionUtils.cs.meta
│ │ │ ├── ScriptableSettings.cs
│ │ │ ├── ScriptableSettings.cs.meta
│ │ │ ├── SimpleMath.cs
│ │ │ ├── SimpleMath.cs.meta
│ │ │ ├── StoreUtils.cs
│ │ │ ├── StoreUtils.cs.meta
│ │ │ ├── TimeUtils.cs
│ │ │ ├── TimeUtils.cs.meta
│ │ │ ├── TweenStatic.cs
│ │ │ ├── TweenStatic.cs.meta
│ │ │ ├── Virtuesky.Sunflower.Utils.asmdef
│ │ │ └── Virtuesky.Sunflower.Utils.asmdef.meta
│ │ └── Runtime.meta
│ ├── Utils.meta
│ ├── Variables/
│ │ ├── Editor/
│ │ │ ├── VariableGenerateGuid.cs
│ │ │ ├── VariableGenerateGuid.cs.meta
│ │ │ ├── VariableWindowEditor.cs
│ │ │ ├── VariableWindowEditor.cs.meta
│ │ │ ├── virtuesky.sunflower.variable.Editor.asmdef
│ │ │ └── virtuesky.sunflower.variable.Editor.asmdef.meta
│ │ ├── Editor.meta
│ │ ├── Runtime/
│ │ │ ├── Base_Variable/
│ │ │ │ ├── BaseReference.cs
│ │ │ │ ├── BaseReference.cs.meta
│ │ │ │ ├── BaseVariable.cs
│ │ │ │ ├── BaseVariable.cs.meta
│ │ │ │ ├── BaseVariableListener.cs
│ │ │ │ ├── BaseVariableListener.cs.meta
│ │ │ │ ├── IGuidVariable.cs
│ │ │ │ └── IGuidVariable.cs.meta
│ │ │ ├── Base_Variable.meta
│ │ │ ├── Boolean_Variable/
│ │ │ │ ├── BooleanReference.cs
│ │ │ │ ├── BooleanReference.cs.meta
│ │ │ │ ├── BooleanVariable.cs
│ │ │ │ ├── BooleanVariable.cs.meta
│ │ │ │ ├── BooleanVariableListener.cs
│ │ │ │ └── BooleanVariableListener.cs.meta
│ │ │ ├── Boolean_Variable.meta
│ │ │ ├── Float_Variable/
│ │ │ │ ├── FloatReference.cs
│ │ │ │ ├── FloatReference.cs.meta
│ │ │ │ ├── FloatVariable.cs
│ │ │ │ ├── FloatVariable.cs.meta
│ │ │ │ ├── FloatVariableListener.cs
│ │ │ │ └── FloatVariableListener.cs.meta
│ │ │ ├── Float_Variable.meta
│ │ │ ├── Integer_Variable/
│ │ │ │ ├── IntegerReference.cs
│ │ │ │ ├── IntegerReference.cs.meta
│ │ │ │ ├── IntegerVariable.cs
│ │ │ │ ├── IntegerVariable.cs.meta
│ │ │ │ ├── IntegerVariableListener.cs
│ │ │ │ └── IntegerVariableListener.cs.meta
│ │ │ ├── Integer_Variable.meta
│ │ │ ├── Interface_Variable/
│ │ │ │ ├── IReference.cs
│ │ │ │ ├── IReference.cs.meta
│ │ │ │ ├── IVariable.cs
│ │ │ │ └── IVariable.cs.meta
│ │ │ ├── Interface_Variable.meta
│ │ │ ├── Object_Variable/
│ │ │ │ ├── ObjectReference.cs
│ │ │ │ ├── ObjectReference.cs.meta
│ │ │ │ ├── ObjectVariable.cs
│ │ │ │ ├── ObjectVariable.cs.meta
│ │ │ │ ├── ObjectVariableListener.cs
│ │ │ │ └── ObjectVariableListener.cs.meta
│ │ │ ├── Object_Variable.meta
│ │ │ ├── Rect_Variable/
│ │ │ │ ├── RectVariable.cs
│ │ │ │ └── RectVariable.cs.meta
│ │ │ ├── Rect_Variable.meta
│ │ │ ├── ShortDouble_Variable/
│ │ │ │ ├── ShortDoubleReference.cs
│ │ │ │ ├── ShortDoubleReference.cs.meta
│ │ │ │ ├── ShortDoubleVariable.cs
│ │ │ │ ├── ShortDoubleVariable.cs.meta
│ │ │ │ ├── ShortDoubleVariableListener.cs
│ │ │ │ └── ShortDoubleVariableListener.cs.meta
│ │ │ ├── ShortDouble_Variable.meta
│ │ │ ├── String_Variable/
│ │ │ │ ├── StringVariable.cs
│ │ │ │ ├── StringVariable.cs.meta
│ │ │ │ ├── StringVariableListener.cs
│ │ │ │ └── StringVariableListener.cs.meta
│ │ │ ├── String_Variable.meta
│ │ │ ├── Trasform_Variable/
│ │ │ │ ├── TransformReference.cs
│ │ │ │ ├── TransformReference.cs.meta
│ │ │ │ ├── TransformVariable.cs
│ │ │ │ └── TransformVariable.cs.meta
│ │ │ ├── Trasform_Variable.meta
│ │ │ ├── Vector2_Variable/
│ │ │ │ ├── Vector2Reference.cs
│ │ │ │ ├── Vector2Reference.cs.meta
│ │ │ │ ├── Vector2Variable.cs
│ │ │ │ ├── Vector2Variable.cs.meta
│ │ │ │ ├── Vector2VariableListener.cs
│ │ │ │ └── Vector2VariableListener.cs.meta
│ │ │ ├── Vector2_Variable.meta
│ │ │ ├── Vector3_Variable/
│ │ │ │ ├── Vector3Reference.cs
│ │ │ │ ├── Vector3Reference.cs.meta
│ │ │ │ ├── Vector3Variable.cs
│ │ │ │ ├── Vector3Variable.cs.meta
│ │ │ │ ├── Vector3VariableListener.cs
│ │ │ │ └── Vector3VariableListener.cs.meta
│ │ │ ├── Vector3_Variable.meta
│ │ │ ├── virtuesky.sunflower.variable.asmdef
│ │ │ └── virtuesky.sunflower.variable.asmdef.meta
│ │ └── Runtime.meta
│ ├── Variables.meta
│ ├── Vibration/
│ │ ├── Plugins/
│ │ │ ├── iOS/
│ │ │ │ ├── Vibration/
│ │ │ │ │ ├── HapticFeedback.mm
│ │ │ │ │ ├── HapticFeedback.mm.meta
│ │ │ │ │ ├── Vibration.h
│ │ │ │ │ ├── Vibration.h.meta
│ │ │ │ │ ├── Vibration.mm
│ │ │ │ │ └── Vibration.mm.meta
│ │ │ │ └── Vibration.meta
│ │ │ └── iOS.meta
│ │ ├── Plugins.meta
│ │ ├── README.md
│ │ ├── README.md.meta
│ │ ├── Vibration.cs
│ │ ├── Vibration.cs.meta
│ │ ├── virtuesky.sunflower.vibration.asmdef
│ │ └── virtuesky.sunflower.vibration.asmdef.meta
│ └── Vibration.meta
├── VirtueSky.meta
├── package.json
└── package.json.meta
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/notify-discord-on-release.yml
================================================
name: Notify Discord on Release or Tag
on:
release:
types: [published]
push:
tags:
- '*'
jobs:
notify-discord:
runs-on: ubuntu-latest
steps:
- name: Send release/tag info to Discord
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
EVENT_NAME: ${{ github.event_name }}
RELEASE_TITLE: ${{ github.event.release.name }}
RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
RELEASE_NOTE: ${{ github.event.release.body }}
RELEASE_AUTHOR: ${{ github.event.release.author.login }}
PUSH_TAG_NAME: ${{ github.ref_name }}
REPO: ${{ github.repository }}
ACTOR: ${{ github.actor }}
run: |
if [ "$EVENT_NAME" = "release" ]; then
TITLE="${RELEASE_TITLE:-No release title}"
TAG="${RELEASE_TAG_NAME:-Unknown tag}"
NOTE="${RELEASE_NOTE}"
AUTHOR="${RELEASE_AUTHOR:-$ACTOR}"
EVENT_LABEL="GitHub Release"
else
TITLE="Tag created"
TAG="${PUSH_TAG_NAME:-Unknown tag}"
NOTE="A new tag was pushed without creating a GitHub Release."
AUTHOR="$ACTOR"
EVENT_LABEL="Git Tag"
fi
if [ -z "$NOTE" ]; then
NOTE="(empty)"
fi
if [ ${#NOTE} -gt 1000 ]; then
NOTE="${NOTE:0:1000}..."
fi
jq -n \
--arg event "$EVENT_LABEL" \
--arg title "$TITLE" \
--arg tag "$TAG" \
--arg note "$NOTE" \
--arg repo "$REPO" \
--arg actor "$ACTOR" \
--arg author "$AUTHOR" \
'{
embeds: [
{
title: $event,
fields: [
{ "name": "Repository", "value": $repo, "inline": false },
{ "name": "Triggered By", "value": $actor, "inline": false },
{ "name": "Author", "value": $author, "inline": false },
{ "name": "Title", "value": $title, "inline": false },
{ "name": "Tag Name", "value": $tag, "inline": false },
{ "name": "Note", "value": $note, "inline": false }
]
}
]
}' > payload.json
cat payload.json
curl --fail-with-body \
-H "Content-Type: application/json" \
-X POST \
-d @payload.json \
"$DISCORD_WEBHOOK_URL"
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2024 VirtueSky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: LICENSE.meta
================================================
fileFormatVersion: 2
guid: 602f2b07fec879a41853f704b2b884a5
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: README.md
================================================
# Core scriptable object architecture for building Unity games (Android & iOS)
### Unity 2022.3 LTS
## How To Install
### 1: Download the repository and drop it into folder `Assets`
### 2: Add the line below to `Packages/manifest.json`
- for version `3.5.4`
```json
{
"scopedRegistries": [
{
"name": "npm",
"url": "https://registry.npmjs.org/",
"scopes": [
"com.kyrylokuzyk"
]
}
],
"dependencies": {
"com.virtuesky.sunflower":"https://github.com/VirtueSky/sunflower.git#3.5.4"
}
}
```
- depencies:
```json
"com.unity.nuget.newtonsoft-json": "3.2.1",
"com.unity.serialization": "3.1.1",
"com.unity.collections": "2.1.4",
"com.unity.textmeshpro": "3.0.8",
"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask#2.5.10",
"com.kyrylokuzyk.primetween": "1.3.8",
```
## Includes modules
```bash
├── Core (Update only called once in Monobehaviour, Delay...)
├── Advertising (Support for Max, Admob and IronSource)
├── In App Purchase (IAP)
├── Asset Finder
├── Audio
├── Button
├── Data
├── Scriptable Event
├── Scriptable Variable
├── Firebase Remote Config
├── Tracking (Firebase Analytics, Adjust, AppsFlyer)
├── Tri-Inspector
├── Level Editor
├── Mobile Notification
├── Object Pooling
├── Localization
├── FolderIcons
├── Hierarchy
├── In app review
├── SimpleJSON
├── Tracking Revenue (by Firebase analytic, Adjust or Appsflyer)
├── Vibration (Vibration native support for android & ios)
├── Game Service (Sign in with apple id / google play games service)
├── Misc (Extension support Transform, SafeArea, Play Animancer, Skeleton,...)
├── Touch Input
├── Component
```
#### Note:
- [See Document](https://github.com/VirtueSky/sunflower/wiki)
- [Project implementation](https://github.com/VirtueSky/TheBeginning)
- [Core has similar modules but does not use scriptable architecture](https://github.com/wolf-package/unity-common)
[Join Discord to receive update notifications](https://discord.gg/bpcUyHRZ)
================================================
FILE: README.md.meta
================================================
fileFormatVersion: 2
guid: 7b4ab53c22cea684aa8898738b406520
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Editor/AdSettingEditor.cs
================================================
#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine;
using VirtueSky.Inspector;
using VirtueSky.UtilsEditor;
namespace VirtueSky.Ads
{
[CustomEditor(typeof(AdSetting), true)]
public class AdSettingEditor : Editor
{
private AdSetting _adSetting;
private SerializedProperty _useApplovin;
private SerializedProperty _useAdmob;
private SerializedProperty _useLevelPlay;
private SerializedProperty _enableTrackAdRevenue;
private SerializedProperty _adCheckingInterval;
private SerializedProperty _adLoadingInterval;
private SerializedProperty _sdkKey;
private SerializedProperty _maxBannerVariable;
private SerializedProperty _maxInterVariable;
private SerializedProperty _maxRewardVariable;
private SerializedProperty _maxAppOpenVariable;
private SerializedProperty _admobBannerVariable;
private SerializedProperty _admobInterVariable;
private SerializedProperty _admobRewardVariable;
private SerializedProperty _admobRewardInterVariable;
private SerializedProperty _admobAppOpenVariable;
private SerializedProperty _admobNativeOverlayVariable;
private SerializedProperty _autoTrackingAdImpressionAdmob;
private SerializedProperty _admobEnableTestMode;
private SerializedProperty _enableGDPR;
private SerializedProperty _enableGDPRTestMode;
private SerializedProperty _admobDevicesTest;
private SerializedProperty _androidAppKey;
private SerializedProperty _iOSAppKey;
private SerializedProperty _useTestAppKey;
private SerializedProperty _levelPlayBannerVariable;
private SerializedProperty _levelPlayInterVariable;
private SerializedProperty _levelPlayRewardVariable;
const string pathMax = "/Ads/Applovin";
const string pathAdmob = "/Ads/Admob";
const string pathIronSource = "/Ads/LevelPlay";
void Initialize()
{
_adSetting = target as AdSetting;
_useApplovin = serializedObject.FindProperty("useAppLovin");
_useAdmob = serializedObject.FindProperty("useAdmob");
_useLevelPlay = serializedObject.FindProperty("useLevelPlay");
_enableTrackAdRevenue = serializedObject.FindProperty("enableTrackAdRevenue");
_adCheckingInterval = serializedObject.FindProperty("adCheckingInterval");
_adLoadingInterval = serializedObject.FindProperty("adLoadingInterval");
_sdkKey = serializedObject.FindProperty("sdkKey");
_maxBannerVariable = serializedObject.FindProperty("maxBannerVariable");
_maxInterVariable = serializedObject.FindProperty("maxInterVariable");
_maxRewardVariable = serializedObject.FindProperty("maxRewardVariable");
_maxAppOpenVariable = serializedObject.FindProperty("maxAppOpenVariable");
_admobBannerVariable = serializedObject.FindProperty("admobBannerVariable");
_admobInterVariable = serializedObject.FindProperty("admobInterVariable");
_admobRewardVariable = serializedObject.FindProperty("admobRewardVariable");
_admobRewardInterVariable = serializedObject.FindProperty("admobRewardInterVariable");
_admobAppOpenVariable = serializedObject.FindProperty("admobAppOpenVariable");
_admobNativeOverlayVariable = serializedObject.FindProperty("admobNativeOverlayVariable");
_autoTrackingAdImpressionAdmob = serializedObject.FindProperty("autoTrackingAdImpressionAdmob");
_admobEnableTestMode = serializedObject.FindProperty("admobEnableTestMode");
_admobDevicesTest = serializedObject.FindProperty("admobDevicesTest");
_enableGDPR = serializedObject.FindProperty("enableGDPR");
_enableGDPRTestMode = serializedObject.FindProperty("enableGDPRTestMode");
_androidAppKey = serializedObject.FindProperty("androidAppKey");
_iOSAppKey = serializedObject.FindProperty("iOSAppKey");
_useTestAppKey = serializedObject.FindProperty("useTestAppKey");
_levelPlayBannerVariable = serializedObject.FindProperty("levelPlayBannerVariable");
_levelPlayInterVariable = serializedObject.FindProperty("levelPlayInterVariable");
_levelPlayRewardVariable = serializedObject.FindProperty("levelPlayRewardVariable");
}
void Draw()
{
serializedObject.Update();
Initialize();
// EditorGUILayout.LabelField("ADS SETTING", EditorStyles.boldLabel);
//GuiLine(2);
GUILayout.Space(10);
EditorGUILayout.PropertyField(_adCheckingInterval);
EditorGUILayout.PropertyField(_adLoadingInterval);
EditorGUILayout.PropertyField(_useApplovin);
EditorGUILayout.PropertyField(_useAdmob);
EditorGUILayout.PropertyField(_useLevelPlay);
EditorGUILayout.PropertyField(_enableTrackAdRevenue);
EditorGUILayout.PropertyField(_enableGDPR);
if (_enableGDPR.boolValue)
{
EditorGUILayout.PropertyField(_enableGDPRTestMode);
}
GUILayout.Space(10);
if (_useApplovin.boolValue) SetupMax();
if (_useAdmob.boolValue) SetupAdmob();
if (_useLevelPlay.boolValue) SetupIronSource();
EditorUtility.SetDirty(target);
serializedObject.ApplyModifiedProperties();
}
public override void OnInspectorGUI()
{
Draw();
}
void SetupMax()
{
EditorGUILayout.LabelField("Applovin - Max", StyleLabel());
GuiLine(1);
GUILayout.Space(10);
EditorGUILayout.PropertyField(_sdkKey);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(_maxBannerVariable);
if (_maxBannerVariable.objectReferenceValue == null)
{
GUILayout.Space(2);
if (GUILayout.Button("Create", GUILayout.Width(55)))
{
_maxBannerVariable.objectReferenceValue =
CreateAsset.CreateAndGetScriptableAsset(pathMax);
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(_maxInterVariable);
if (_maxInterVariable.objectReferenceValue == null)
{
GUILayout.Space(2);
if (GUILayout.Button("Create", GUILayout.Width(55)))
{
_maxInterVariable.objectReferenceValue =
CreateAsset.CreateAndGetScriptableAsset(pathMax);
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(_maxRewardVariable);
if (_maxRewardVariable.objectReferenceValue == null)
{
GUILayout.Space(2);
if (GUILayout.Button("Create", GUILayout.Width(55)))
{
_maxRewardVariable.objectReferenceValue =
CreateAsset.CreateAndGetScriptableAsset(pathMax);
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(_maxAppOpenVariable);
if (_maxAppOpenVariable.objectReferenceValue == null)
{
GUILayout.Space(2);
if (GUILayout.Button("Create", GUILayout.Width(55)))
{
_maxAppOpenVariable.objectReferenceValue =
CreateAsset.CreateAndGetScriptableAsset(pathMax);
}
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(10);
}
void SetupAdmob()
{
EditorGUILayout.LabelField("Google - Admob", StyleLabel());
GuiLine(1);
GUILayout.Space(10);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(_admobBannerVariable);
if (_admobBannerVariable.objectReferenceValue == null)
{
GUILayout.Space(2);
if (GUILayout.Button("Create", GUILayout.Width(55)))
{
_admobBannerVariable.objectReferenceValue =
CreateAsset.CreateAndGetScriptableAsset(pathAdmob);
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(_admobInterVariable);
if (_admobInterVariable.objectReferenceValue == null)
{
GUILayout.Space(2);
if (GUILayout.Button("Create", GUILayout.Width(55)))
{
_admobInterVariable.objectReferenceValue =
CreateAsset.CreateAndGetScriptableAsset(pathAdmob);
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(_admobRewardVariable);
if (_admobRewardVariable.objectReferenceValue == null)
{
GUILayout.Space(2);
if (GUILayout.Button("Create", GUILayout.Width(55)))
{
_admobRewardVariable.objectReferenceValue =
CreateAsset.CreateAndGetScriptableAsset(pathAdmob);
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(_admobRewardInterVariable);
if (_admobRewardInterVariable.objectReferenceValue == null)
{
GUILayout.Space(2);
if (GUILayout.Button("Create", GUILayout.Width(55)))
{
_admobRewardInterVariable.objectReferenceValue =
CreateAsset
.CreateAndGetScriptableAsset(pathAdmob);
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(_admobAppOpenVariable);
if (_admobAppOpenVariable.objectReferenceValue == null)
{
GUILayout.Space(2);
if (GUILayout.Button("Create", GUILayout.Width(55)))
{
_admobAppOpenVariable.objectReferenceValue =
CreateAsset.CreateAndGetScriptableAsset(pathAdmob);
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(_admobNativeOverlayVariable);
if (_admobNativeOverlayVariable.objectReferenceValue == null)
{
GUILayout.Space(2);
if (GUILayout.Button("Create", GUILayout.Width(55)))
{
_admobNativeOverlayVariable.objectReferenceValue =
CreateAsset.CreateAndGetScriptableAsset(pathAdmob);
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.PropertyField(_autoTrackingAdImpressionAdmob);
EditorGUILayout.PropertyField(_admobEnableTestMode);
EditorGUILayout.PropertyField(_admobDevicesTest);
GUI.enabled = false;
EditorGUILayout.TextField("App Id Test", "ca-app-pub-3940256099942544~3347511713");
GUI.enabled = true;
GUILayout.Space(10);
if (GUILayout.Button("Open GoogleAdmobSetting", GUILayout.Height(20)))
{
EditorApplication.ExecuteMenuItem("Assets/Google Mobile Ads/Settings...");
}
GUILayout.Space(10);
}
void SetupIronSource()
{
EditorGUILayout.LabelField("Unity - LevelPlay", StyleLabel());
GuiLine(1);
GUILayout.Space(10);
EditorGUILayout.PropertyField(_androidAppKey);
EditorGUILayout.PropertyField(_iOSAppKey);
EditorGUILayout.PropertyField(_useTestAppKey);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(_levelPlayBannerVariable);
if (_levelPlayBannerVariable.objectReferenceValue == null)
{
GUILayout.Space(2);
if (GUILayout.Button("Create", GUILayout.Width(55)))
{
_levelPlayBannerVariable.objectReferenceValue =
CreateAsset.CreateAndGetScriptableAsset(pathIronSource);
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(_levelPlayInterVariable);
if (_levelPlayInterVariable.objectReferenceValue == null)
{
GUILayout.Space(2);
if (GUILayout.Button("Create", GUILayout.Width(55)))
{
_levelPlayInterVariable.objectReferenceValue =
CreateAsset.CreateAndGetScriptableAsset(pathIronSource);
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PropertyField(_levelPlayRewardVariable);
if (_levelPlayRewardVariable.objectReferenceValue == null)
{
GUILayout.Space(2);
if (GUILayout.Button("Create", GUILayout.Width(55)))
{
_levelPlayRewardVariable.objectReferenceValue =
CreateAsset.CreateAndGetScriptableAsset(pathIronSource);
}
}
EditorGUILayout.EndHorizontal();
GUILayout.Space(10);
}
GUIStyle StyleLabel()
{
var style = new GUIStyle();
style.fontSize = 14;
style.normal.textColor = Color.white;
return style;
}
void GuiLine(int i_height = 1)
{
Rect rect = EditorGUILayout.GetControlRect(false, i_height);
rect.height = i_height;
EditorGUI.DrawRect(rect, new Color32(0, 0, 0, 255));
}
}
}
#endif
================================================
FILE: VirtueSky/Advertising/Editor/AdSettingEditor.cs.meta
================================================
fileFormatVersion: 2
guid: 6439b65cc7b34375ace31c6d3f331f3e
timeCreated: 1700021422
================================================
FILE: VirtueSky/Advertising/Editor/AdsWindowEditor.cs
================================================
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using VirtueSky.Inspector;
using VirtueSky.UtilsEditor;
namespace VirtueSky.Ads
{
public class AdsWindowEditor : EditorWindow
{
// private Vector2 _scrollPosition;
// private Editor _editor;
//
// private AdSetting _adSetting;
//
// private bool isSetupTheme = false;
//
// [MenuItem("Sunflower/Ads/AdSetting &4", false)]
// public static void OpenAdSettingsWindows()
// {
// var adSetting =
// CreateAsset.CreateAndGetScriptableAsset("/Ads");
// AdsWindowEditor adWindow = GetWindow("Ads Settings");
// adWindow._adSetting = adSetting;
// if (adWindow == null)
// {
// Debug.LogError("Couldn't open the ads settings window!");
// return;
// }
//
// adWindow.minSize = new Vector2(275, 0);
// adWindow.Show();
// EditorGUIUtility.PingObject(adSetting);
// }
//
// private void OnGUI()
// {
// EditorGUI.DrawRect(new Rect(0, 0, position.width, position.height),
// GameDataEditor.ColorBackgroundRectWindowSunflower.ToColor());
// GUI.contentColor = GameDataEditor.ColorTextContentWindowSunflower.ToColor();
// GUI.backgroundColor = GameDataEditor.ColorContentWindowSunflower.ToColor();
// if (_editor == null) _editor = UnityEditor.Editor.CreateEditor(_adSetting);
//
// if (_editor == null)
// {
// EditorGUILayout.HelpBox("Couldn't create the settings resources editor.",
// MessageType.Error);
// return;
// }
//
// // _editor.DrawHeader();
// _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
// EditorGUILayout.BeginVertical(new GUIStyle { padding = new RectOffset(6, 3, 3, 3) });
// _editor.OnInspectorGUI();
//
// GUILayout.Space(10);
// Handles.color = Color.black;
// Handles.DrawAAPolyLine(3, new Vector3(0, GUILayoutUtility.GetLastRect().y + 10),
// new Vector3(position.width, GUILayoutUtility.GetLastRect().y + 10));
// GUILayout.Space(10);
//
//
// EditorGUILayout.EndVertical();
// EditorGUILayout.EndScrollView();
// }
//
// #region Applovin
//
// private const string pathMax = "/Ads/Applovin";
//
// [MenuItem("Sunflower/Ads/Applovin/Max Client")]
// public static void CreateMaxClient()
// {
// CreateAsset.CreateScriptableAssets(pathMax, "max_ad_client");
// }
//
// [MenuItem("Sunflower/Ads/Applovin/Max Banner")]
// public static void CreateMaxBanner()
// {
// CreateAsset.CreateScriptableAssets(pathMax, "max_banner_variable");
// }
//
// [MenuItem("Sunflower/Ads/Applovin/Max Inter")]
// public static void CreateMaxInter()
// {
// CreateAsset.CreateScriptableAssets(pathMax, "max_inter_variable");
// }
//
// [MenuItem("Sunflower/Ads/Applovin/Max Reward")]
// public static void CreateMaxReward()
// {
// CreateAsset.CreateScriptableAssets(pathMax, "max_reward_variable");
// }
//
// [MenuItem("Sunflower/Ads/Applovin/Max App Open")]
// public static void CreateMaxAppOpen()
// {
// CreateAsset.CreateScriptableAssets(pathMax,
// "max_app_open_variable");
// }
//
// [MenuItem("Sunflower/Ads/Applovin/Max Reward Inter")]
// public static void CreateMaxRewardInter()
// {
// CreateAsset.CreateScriptableAssets(pathMax,
// "max_reward_inter_variable");
// }
//
// #endregion
//
// #region Admob
//
// private const string pathAdmob = "/Ads/Admob";
//
// [MenuItem("Sunflower/Ads/Admob/Admob Client")]
// public static void CreateAdmobClient()
// {
// CreateAsset.CreateScriptableAssets(pathAdmob, "admob_ad_client");
// }
//
// [MenuItem("Sunflower/Ads/Admob/Admob Banner")]
// public static void CreateAdmobBanner()
// {
// CreateAsset.CreateScriptableAssets(pathAdmob,
// "admob_banner_variable");
// }
//
// [MenuItem("Sunflower/Ads/Admob/Admob Inter")]
// public static void CreateAdmobInter()
// {
// CreateAsset.CreateScriptableAssets(pathAdmob,
// "admob_inter_variable");
// }
//
// [MenuItem("Sunflower/Ads/Admob/Admob Reward")]
// public static void CreateAdmobReward()
// {
// CreateAsset.CreateScriptableAssets(pathAdmob,
// "admob_reward_variable");
// }
//
// [MenuItem("Sunflower/Ads/Admob/Admob App Open")]
// public static void CreateAdmobAppOpen()
// {
// CreateAsset.CreateScriptableAssets(pathAdmob,
// "admob_app_open_variable");
// }
//
// [MenuItem("Sunflower/Ads/Admob/Admob Reward Inter")]
// public static void CreateAdmobRewardInter()
// {
// CreateAsset.CreateScriptableAssets(pathAdmob,
// "admob_reward_inter_variable");
// }
//
// #endregion
}
}
#endif
================================================
FILE: VirtueSky/Advertising/Editor/AdsWindowEditor.cs.meta
================================================
fileFormatVersion: 2
guid: 6fd65ccaee60423bbab98afe628d2016
timeCreated: 1695461341
================================================
FILE: VirtueSky/Advertising/Editor/Virtuesky.Sunflower.Advertising.Editor.asmdef
================================================
{
"name": "Virtuesky.Sunflower.Advertising.Editor",
"rootNamespace": "",
"references": [
"GUID:abd57f653a468a04c8d4e281527ff293",
"GUID:c904f6d969e991d459a0843b71c22ec5",
"GUID:324caed91501a9c47a04ebfd87b68794",
"GUID:0b6289df6f84a6f4b982ff72d23e0273"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
================================================
FILE: VirtueSky/Advertising/Editor/Virtuesky.Sunflower.Advertising.Editor.asmdef.meta
================================================
fileFormatVersion: 2
guid: ea1af3c654880af4ca6bb44b012e055e
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Editor.meta
================================================
fileFormatVersion: 2
guid: c0fa69d79e042904290c060082316cf1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmobAdClient.cs
================================================
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
using GoogleMobileAds.Api;
#endif
using VirtueSky.Core;
using VirtueSky.Tracking;
namespace VirtueSky.Ads
{
public sealed class AdmobAdClient : AdClient
{
public AdmobAdClient(AdSetting _adSetting)
{
adSetting = _adSetting;
}
public override void Initialize()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
#if UNITY_IOS
// On Android, Unity is paused when displaying interstitial or rewarded video.
// This setting makes iOS behave consistently with Android.
MobileAds.SetiOSAppPauseOnBackground(true);
#endif
// When true all events raised by GoogleMobileAds will be raised
// on the Unity main thread. The default value is false.
// https://developers.google.com/admob/unity/quick-start#raise_ad_events_on_the_unity_main_thread
MobileAds.RaiseAdEventsOnUnityMainThread = true;
MobileAds.Initialize(initStatus =>
{
App.RunOnMainThread(() =>
{
if (!adSetting.AdmobEnableTestMode) return;
var configuration = new RequestConfiguration
{ TestDeviceIds = adSetting.AdmobDevicesTest };
MobileAds.SetRequestConfiguration(configuration);
});
});
FirebaseAnalyticTrackingRevenue.autoTrackAdImpressionAdmob = adSetting.AutoTrackingAdImpressionAdmob;
adSetting.AdmobBannerVariable.Init();
adSetting.AdmobInterVariable.Init();
adSetting.AdmobRewardVariable.Init();
adSetting.AdmobRewardInterVariable.Init();
adSetting.AdmobAppOpenVariable.Init();
adSetting.AdmobNativeOverlayVariable.Init();
RegisterAppStateChange();
LoadInterstitial();
LoadRewarded();
LoadRewardedInterstitial();
LoadAppOpen();
LoadBanner();
LoadNativeOverlay();
#endif
}
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
void RegisterAppStateChange()
{
GoogleMobileAds.Api.AppStateEventNotifier.AppStateChanged += OnAppStateChanged;
}
void OnAppStateChanged(GoogleMobileAds.Common.AppState state)
{
if (state == GoogleMobileAds.Common.AppState.Foreground && adSetting.AdmobAppOpenVariable.autoShow)
{
if (adSetting.UseAdmob) ShowAppOpen();
}
}
#endif
public override void LoadBanner()
{
if (adSetting.AdmobBannerVariable == null) return;
adSetting.AdmobBannerVariable.Load();
}
public override void LoadInterstitial()
{
if (adSetting.AdmobInterVariable == null) return;
if (!adSetting.AdmobInterVariable.IsReady()) adSetting.AdmobInterVariable.Load();
}
public override void LoadRewarded()
{
if (adSetting.AdmobRewardVariable == null) return;
if (!adSetting.AdmobRewardVariable.IsReady()) adSetting.AdmobRewardVariable.Load();
}
public override void LoadRewardedInterstitial()
{
if (adSetting.AdmobRewardInterVariable == null) return;
if (!adSetting.AdmobRewardInterVariable.IsReady()) adSetting.AdmobRewardInterVariable.Load();
}
public override void LoadAppOpen()
{
if (adSetting.AdmobAppOpenVariable == null) return;
if (!adSetting.AdmobAppOpenVariable.IsReady()) adSetting.AdmobAppOpenVariable.Load();
}
public override void ShowAppOpen()
{
if (statusAppOpenFirstIgnore) adSetting.AdmobAppOpenVariable.Show();
statusAppOpenFirstIgnore = true;
}
public override void LoadNativeOverlay()
{
if (adSetting.AdmobNativeOverlayVariable == null) return;
if (!adSetting.AdmobNativeOverlayVariable.IsReady()) adSetting.AdmobNativeOverlayVariable.Load();
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmobAdClient.cs.meta
================================================
fileFormatVersion: 2
guid: 9dfd25949deb4dd5b2c3d85ef6e79929
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmobAdUnitVariable.cs
================================================
using System;
using UnityEngine;
namespace VirtueSky.Ads
{
public class AdmobAdUnitVariable : AdUnitVariable
{
[SerializeField] protected string androidId;
[SerializeField] protected string iOSId;
[NonSerialized] private string idRuntime = string.Empty;
public override bool IsShowing { get; internal set; }
public override string Id
{
get
{
if (idRuntime == String.Empty)
{
#if UNITY_ANDROID
return androidId;
#elif UNITY_IOS
return iOSId;
#else
return string.Empty;
#endif
}
return idRuntime;
}
}
public override AdUnitVariable Show(string placement = null)
{
ResetChainCallback();
if (!Application.isMobilePlatform || string.IsNullOrEmpty(Id) || AdStatic.IsRemoveAd ||
!IsReady()) return this;
ShowImpl(placement);
return this;
}
public void SetIdRuntime(string unitId)
{
idRuntime = unitId;
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmobAdUnitVariable.cs.meta
================================================
fileFormatVersion: 2
guid: 87d7f6324d01404eb012e525818f93de
timeCreated: 1728634495
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmodUnitVariable/AdmobAppOpenVariable.cs
================================================
using System;
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
using GoogleMobileAds.Api;
using VirtueSky.Tracking;
#endif
using UnityEngine;
using VirtueSky.Inspector;
using VirtueSky.Misc;
namespace VirtueSky.Ads
{
[Serializable]
[EditorIcon("icon_scriptable")]
public class AdmobAppOpenVariable : AdmobAdUnitVariable
{
[Tooltip("Automatically show AppOpenAd when app status is changed")]
public bool autoShow = false;
[Tooltip("Time between closing the previous full-screen ad and starting to show the app open ad - in seconds")]
public float timeBetweenFullScreenAd = 2f;
public bool useTestId;
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
private AppOpenAd _appOpenAd;
#endif
private DateTime _expireTime;
public override void Init()
{
if (useTestId)
{
GetUnitTest();
}
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (AdStatic.IsRemoveAd || string.IsNullOrEmpty(Id)) return;
paidedCallback += AppTracking.TrackRevenue;
#endif
}
public override void Load()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (AdStatic.IsRemoveAd || string.IsNullOrEmpty(Id)) return;
Destroy();
AppOpenAd.Load(Id, new AdRequest(), OnAdLoadCallback);
#endif
}
public override bool IsReady()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
return _appOpenAd != null && _appOpenAd.CanShowAd() && DateTime.Now < _expireTime &&
(DateTime.Now - AdStatic.AdClosingTime).TotalSeconds > timeBetweenFullScreenAd;
#else
return false;
#endif
}
protected override void ShowImpl(string placement = null)
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
_appOpenAd.Show();
#endif
}
public override void Destroy()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (_appOpenAd == null) return;
_appOpenAd.Destroy();
_appOpenAd = null;
#endif
}
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
private void OnAdLoadCallback(AppOpenAd ad, LoadAdError error)
{
// if error is not null, the load request failed.
if (error != null || ad == null)
{
OnAdFailedToLoad(error);
return;
}
_appOpenAd = ad;
_appOpenAd.OnAdPaid += OnAdPaided;
_appOpenAd.OnAdFullScreenContentClosed += OnAdClosed;
_appOpenAd.OnAdFullScreenContentFailed += OnAdFailedToShow;
_appOpenAd.OnAdFullScreenContentOpened += OnAdOpening;
_appOpenAd.OnAdClicked += OnAdClicked;
OnAdLoaded();
// App open ads can be preloaded for up to 4 hours.
_expireTime = DateTime.Now + TimeSpan.FromHours(4);
}
private void OnAdClicked()
{
Common.CallActionAndClean(ref clickedCallback);
OnClickedAdEvent?.Invoke();
}
private void OnAdOpening()
{
AdStatic.waitAppOpenDisplayedAction?.Invoke();
AdStatic.IsShowingAd = true;
IsShowing = true;
Common.CallActionAndClean(ref displayedCallback);
OnDisplayedAdEvent?.Invoke();
}
private void OnAdFailedToShow(AdError obj)
{
Common.CallActionAndClean(ref failedToDisplayCallback);
OnFailedToDisplayAdEvent?.Invoke(obj.GetMessage());
}
private void OnAdClosed()
{
AdStatic.waitAppOpenClosedAction?.Invoke();
AdStatic.IsShowingAd = false;
IsShowing = false;
Common.CallActionAndClean(ref closedCallback);
OnClosedAdEvent?.Invoke();
Destroy();
}
private void OnAdPaided(AdValue value)
{
paidedCallback?.Invoke(value.Value / 1000000f,
"Admob",
Id,
"AppOpenAd", AdMediation.Admob.ToString());
}
private void OnAdLoaded()
{
Common.CallActionAndClean(ref loadedCallback);
OnLoadAdEvent?.Invoke();
}
private void OnAdFailedToLoad(LoadAdError error)
{
Common.CallActionAndClean(ref failedToLoadCallback);
OnFailedToLoadAdEvent?.Invoke(error.GetMessage());
}
#endif
[ContextMenu("Get Id test")]
void GetUnitTest()
{
#if UNITY_ANDROID
androidId = "ca-app-pub-3940256099942544/9257395921";
#elif UNITY_IOS
iOSId = "ca-app-pub-3940256099942544/5575463023";
#endif
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmodUnitVariable/AdmobAppOpenVariable.cs.meta
================================================
fileFormatVersion: 2
guid: bcb8d284edcd4713bbddf482061bd612
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmodUnitVariable/AdmobBannerVariable.cs
================================================
using System;
using System.Collections;
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
using GoogleMobileAds.Api;
using VirtueSky.Tracking;
#endif
using UnityEngine;
using VirtueSky.Core;
using VirtueSky.Inspector;
using VirtueSky.Misc;
namespace VirtueSky.Ads
{
[Serializable]
[EditorIcon("icon_scriptable")]
public class AdmobBannerVariable : AdmobAdUnitVariable
{
public AdsSize size = AdsSize.Adaptive;
public AdsPosition position = AdsPosition.Bottom;
public bool useCollapsible;
public bool useTestId;
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
private BannerView _bannerView;
#endif
private readonly WaitForSeconds _waitBannerReload = new WaitForSeconds(5f);
private IEnumerator _reload;
private bool _isBannerShowing;
private bool _previousBannerShowStatus;
public override void Init()
{
if (useTestId)
{
GetUnitTest();
}
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (AdStatic.IsRemoveAd || string.IsNullOrEmpty(Id)) return;
paidedCallback += AppTracking.TrackRevenue;
#endif
}
public override void Load()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (AdStatic.IsRemoveAd || string.IsNullOrEmpty(Id)) return;
Destroy();
_bannerView = new BannerView(Id, ConvertSize(), ConvertPosition());
_bannerView.OnAdFullScreenContentClosed += OnAdClosed;
_bannerView.OnBannerAdLoadFailed += OnAdFailedToLoad;
_bannerView.OnBannerAdLoaded += OnAdLoaded;
_bannerView.OnAdFullScreenContentOpened += OnAdOpening;
_bannerView.OnAdPaid += OnAdPaided;
_bannerView.OnAdClicked += OnAdClicked;
var adRequest = new AdRequest();
if (useCollapsible)
{
adRequest.Extras.Add("collapsible", ConvertPlacementCollapsible());
}
_bannerView.LoadAd(adRequest);
#endif
}
public bool IsCollapsible()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (_bannerView == null) return false;
return _bannerView.IsCollapsible();
#else
return false;
#endif
}
void OnWaitAppOpenClosed()
{
if (_previousBannerShowStatus)
{
_previousBannerShowStatus = false;
Show();
}
}
void OnWaitAppOpenDisplayed()
{
_previousBannerShowStatus = _isBannerShowing;
if (_isBannerShowing) HideBanner();
}
public override bool IsReady()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
return _bannerView != null;
#else
return false;
#endif
}
protected override void ShowImpl(string placement = null)
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
_isBannerShowing = true;
IsShowing = true;
AdStatic.waitAppOpenClosedAction = OnWaitAppOpenClosed;
AdStatic.waitAppOpenDisplayedAction = OnWaitAppOpenDisplayed;
if (_bannerView == null)
{
Load();
}
_bannerView.Show();
#endif
}
public override void Destroy()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (_bannerView == null) return;
_isBannerShowing = false;
IsShowing = false;
AdStatic.waitAppOpenClosedAction = null;
AdStatic.waitAppOpenDisplayedAction = null;
_bannerView.Destroy();
_bannerView = null;
#endif
}
public override void HideBanner()
{
base.HideBanner();
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
_isBannerShowing = false;
IsShowing = false;
if (_bannerView != null) _bannerView.Hide();
#endif
}
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
public AdSize ConvertSize()
{
switch (size)
{
case AdsSize.Adaptive:
return AdSize.GetCurrentOrientationAnchoredAdaptiveBannerAdSizeWithWidth(
AdSize.FullWidth);
case AdsSize.MediumRectangle: return AdSize.MediumRectangle;
case AdsSize.Leaderboard: return AdSize.Leaderboard;
case AdsSize.IABBanner: return AdSize.IABBanner;
//case BannerSize.SmartBanner: return AdSize.SmartBanner;
default: return AdSize.Banner;
}
}
public AdPosition ConvertPosition()
{
switch (position)
{
case AdsPosition.Top: return AdPosition.Top;
case AdsPosition.Bottom: return AdPosition.Bottom;
case AdsPosition.TopLeft: return AdPosition.TopLeft;
case AdsPosition.TopRight: return AdPosition.TopRight;
case AdsPosition.BottomLeft: return AdPosition.BottomLeft;
case AdsPosition.BottomRight: return AdPosition.BottomRight;
default: return AdPosition.Bottom;
}
}
public string ConvertPlacementCollapsible()
{
if (position == AdsPosition.Top)
{
return "top";
}
else if (position == AdsPosition.Bottom)
{
return "bottom";
}
return "bottom";
}
private void OnAdPaided(AdValue value)
{
paidedCallback?.Invoke(value.Value / 1000000f,
"Admob",
Id,
"BannerAd", AdMediation.Admob.ToString());
}
private void OnAdClicked()
{
Common.CallActionAndClean(ref clickedCallback);
OnClickedAdEvent?.Invoke();
}
private void OnAdOpening()
{
Common.CallActionAndClean(ref displayedCallback);
OnDisplayedAdEvent?.Invoke();
}
private void OnAdLoaded()
{
Common.CallActionAndClean(ref loadedCallback);
OnLoadAdEvent?.Invoke();
}
private void OnAdFailedToLoad(LoadAdError error)
{
Common.CallActionAndClean(ref failedToLoadCallback);
OnFailedToLoadAdEvent?.Invoke(error.GetMessage());
if (_reload != null) App.StopCoroutine(_reload);
_reload = DelayBannerReload();
App.StartCoroutine(_reload);
}
private void OnAdClosed()
{
Common.CallActionAndClean(ref closedCallback);
OnClosedAdEvent?.Invoke();
}
private IEnumerator DelayBannerReload()
{
yield return _waitBannerReload;
Load();
}
#endif
[ContextMenu("Get Id test")]
void GetUnitTest()
{
#if UNITY_ANDROID
androidId = "ca-app-pub-3940256099942544/6300978111";
#elif UNITY_IOS
iOSId = "ca-app-pub-3940256099942544/2934735716";
#endif
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmodUnitVariable/AdmobBannerVariable.cs.meta
================================================
fileFormatVersion: 2
guid: 87ac8fce1cfb4b09805621d419310150
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmodUnitVariable/AdmobInterVariable.cs
================================================
using System;
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
using GoogleMobileAds.Api;
using VirtueSky.Tracking;
#endif
using UnityEngine;
using VirtueSky.Inspector;
using VirtueSky.Misc;
namespace VirtueSky.Ads
{
[Serializable]
[EditorIcon("icon_scriptable")]
public class AdmobInterVariable : AdmobAdUnitVariable
{
public bool useTestId;
[NonSerialized] internal Action completedCallback;
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
private InterstitialAd _interstitialAd;
#endif
public override void Init()
{
if (useTestId)
{
GetUnitTest();
}
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (AdStatic.IsRemoveAd || string.IsNullOrEmpty(Id)) return;
paidedCallback += AppTracking.TrackRevenue;
#endif
}
public override void Load()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (AdStatic.IsRemoveAd || string.IsNullOrEmpty(Id)) return;
Destroy();
InterstitialAd.Load(Id, new AdRequest(), AdLoadCallback);
#endif
}
public override bool IsReady()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
return _interstitialAd != null && _interstitialAd.CanShowAd();
#else
return false;
#endif
}
protected override void ShowImpl(string placement = null)
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
_interstitialAd.Show();
#endif
}
protected override void ResetChainCallback()
{
base.ResetChainCallback();
completedCallback = null;
}
public override void Destroy()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (_interstitialAd == null) return;
_interstitialAd.Destroy();
_interstitialAd = null;
#endif
}
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
private void AdLoadCallback(InterstitialAd ad, LoadAdError error)
{
// if error is not null, the load request failed.
if (error != null || ad == null)
{
OnAdFailedToLoad(error);
return;
}
_interstitialAd = ad;
_interstitialAd.OnAdPaid += OnAdPaided;
_interstitialAd.OnAdFullScreenContentClosed += OnAdClosed;
_interstitialAd.OnAdFullScreenContentFailed += OnAdFailedToShow;
_interstitialAd.OnAdFullScreenContentOpened += OnAdOpening;
_interstitialAd.OnAdClicked += OnAdClicked;
OnAdLoaded();
}
private void OnAdClicked()
{
Common.CallActionAndClean(ref clickedCallback);
OnClickedAdEvent?.Invoke();
}
private void OnAdOpening()
{
AdStatic.IsShowingAd = true;
IsShowing = true;
Common.CallActionAndClean(ref displayedCallback);
OnDisplayedAdEvent?.Invoke();
}
private void OnAdFailedToShow(AdError error)
{
Common.CallActionAndClean(ref failedToDisplayCallback);
OnFailedToDisplayAdEvent?.Invoke(error.GetMessage());
}
private void OnAdClosed()
{
AdStatic.IsShowingAd = false;
IsShowing = false;
Common.CallActionAndClean(ref completedCallback);
OnClosedAdEvent?.Invoke();
Destroy();
}
private void OnAdPaided(AdValue value)
{
paidedCallback?.Invoke(value.Value / 1000000f,
"Admob",
Id,
"InterstitialAd", AdMediation.Admob.ToString());
}
private void OnAdLoaded()
{
Common.CallActionAndClean(ref loadedCallback);
OnLoadAdEvent?.Invoke();
}
private void OnAdFailedToLoad(LoadAdError error)
{
Common.CallActionAndClean(ref failedToLoadCallback);
OnFailedToLoadAdEvent?.Invoke(error.GetMessage());
}
#endif
[ContextMenu("Get Id test")]
void GetUnitTest()
{
#if UNITY_ANDROID
androidId = "ca-app-pub-3940256099942544/1033173712";
#elif UNITY_IOS
iOSId = "ca-app-pub-3940256099942544/4411468910";
#endif
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmodUnitVariable/AdmobInterVariable.cs.meta
================================================
fileFormatVersion: 2
guid: b04367785ed74ad986055150f5961286
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmodUnitVariable/AdmobNativeOverlayVariable.cs
================================================
using System;
using UnityEngine;
using VirtueSky.Inspector;
using VirtueSky.Utils;
using System.Collections;
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
using GoogleMobileAds.Api;
#endif
using VirtueSky.Core;
using VirtueSky.Misc;
using VirtueSky.Tracking;
namespace VirtueSky.Ads
{
[Serializable]
[EditorIcon("icon_scriptable")]
public class AdmobNativeOverlayVariable : AdmobAdUnitVariable
{
public enum NativeTemplate
{
Small,
Medium
}
[SerializeField] private bool useTestId;
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
[HeaderLine("NativeAd Options", false)] [SerializeField]
private AdChoicesPlacement adChoicesPlacement;
[SerializeField] private MediaAspectRatio mediaAspectRatio;
[SerializeField] private VideoOptions videoOptions;
[HeaderLine("NativeAd Style", false)] public NativeTemplate nativeTemplate;
public Color mainBackgroundColor = Color.white;
public AdsSize adsSize = AdsSize.MediumRectangle;
public AdsPosition adsPosition = AdsPosition.Bottom;
// public NativeTemplateFontStyle nativeTemplateFontStyle;
private NativeOverlayAd _nativeOverlayAd;
#endif
private readonly WaitForSeconds _waitReload = new WaitForSeconds(5f);
private IEnumerator _reload;
///
/// Init ads and register callback tracking
///
public override void Init()
{
if (useTestId)
{
GetUnitTest();
}
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (AdStatic.IsRemoveAd || string.IsNullOrEmpty(Id)) return;
paidedCallback += AppTracking.TrackRevenue;
#endif
}
///
/// Load ads
///
public override void Load()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (AdStatic.IsRemoveAd || string.IsNullOrEmpty(Id)) return;
if (_nativeOverlayAd != null)
{
Destroy();
}
var adRequest = new AdRequest();
var option = new NativeAdOptions
{
AdChoicesPlacement = adChoicesPlacement,
MediaAspectRatio = mediaAspectRatio,
VideoOptions = videoOptions
};
NativeOverlayAd.Load(Id, adRequest, option, AdLoadCallback);
#endif
}
public override bool IsReady()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
return _nativeOverlayAd != null;
#else
return false;
#endif
}
protected override void ShowImpl(string placement = null)
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (_nativeOverlayAd != null)
_nativeOverlayAd.Show();
#endif
}
///
/// destroy native overlay ads
///
public override void Destroy()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (_nativeOverlayAd != null)
{
_nativeOverlayAd.Destroy();
_nativeOverlayAd = null;
}
#endif
}
///
/// Hide native overlay ads
///
public void Hide()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (_nativeOverlayAd != null) _nativeOverlayAd.Hide();
#endif
}
///
/// Render native overlay ads default
///
public void RenderAd()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (_nativeOverlayAd == null) return;
_nativeOverlayAd.RenderTemplate(Style(), ConvertSize(), ConvertPosition(adsPosition));
#endif
}
///
/// Render native ads according to uiElement, use canvas overlay
///
/// RectTransform of uiElement, used to determine position for native overlay ads
public void RenderAd(RectTransform uiElement)
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (_nativeOverlayAd == null) return;
(int admobX, int admobY) = ConvertUiElementPosToNativeAdsPos(uiElement);
_nativeOverlayAd.RenderTemplate(Style(), admobX, admobY);
#endif
}
///
/// Render native ads according to uiElement, use canvas overlay
///
/// RectTransform of uiElement, used to determine position for native overlay ads
/// Custom width for native overlay ads
/// Custom height for native overlay ads
public void RenderAd(RectTransform uiElement, int width, int height)
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (_nativeOverlayAd == null) return;
(int admobX, int admobY) = ConvertUiElementPosToNativeAdsPos(uiElement);
_nativeOverlayAd.RenderTemplate(Style(), new AdSize(width, height), admobX, admobY);
#endif
}
///
/// Render native ads according to uiElement, use canvas screen-space camera
/// Can use position and size of uiElement for native overlay ads
///
/// RectTransform of uiElement, used to determine position for native overlay ads
/// Camera render uiElement
public void RenderAd(RectTransform uiElement, Camera camera, bool useSizeUiElement = true)
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (_nativeOverlayAd == null) return;
(int admobX, int admobY) = ConvertUiElementPosToNativeAdsPos(uiElement, camera);
if (useSizeUiElement)
{
_nativeOverlayAd?.RenderTemplate(Style(),
new AdSize((int)uiElement.rect.width, (int)uiElement.rect.height), admobX, admobY);
}
else
{
_nativeOverlayAd?.RenderTemplate(Style(), admobX, admobY);
}
#endif
}
///
/// Render native ads according to uiElement, use canvas screen-space camera
/// Can use position of uiElement and custom size for native overlay ads
///
/// RectTransform of uiElement, used to determine position for native overlay ads
/// Camera render uiElement
/// Custom width for native overlay ads
/// Custom height for native overlay ads
public void RenderAd(RectTransform uiElement, Camera camera, int width, int height)
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (_nativeOverlayAd == null) return;
(int admobX, int admobY) = ConvertUiElementPosToNativeAdsPos(uiElement, camera, width, height);
_nativeOverlayAd?.RenderTemplate(Style(), new AdSize(width, height), admobX, admobY);
#endif
}
(int, int) ConvertUiElementPosToNativeAdsPos(RectTransform uiElement, Camera camera, int width, int height)
{
var worldPosition = uiElement.TransformPoint(uiElement.position);
Vector2 screenPosition = camera.WorldToScreenPoint(worldPosition);
float dpi = Screen.dpi / 160f;
var admobX = (int)((screenPosition.x - width / 2) / dpi);
var admobY = (int)(((Screen.height - (int)screenPosition.y) - height / 2) / dpi);
return (admobX, admobY);
}
(int, int) ConvertUiElementPosToNativeAdsPos(RectTransform uiElement, Camera camera)
{
var worldPosition = uiElement.TransformPoint(uiElement.position);
Vector2 screenPosition = camera.WorldToScreenPoint(worldPosition);
float dpi = Screen.dpi / 160f;
var admobX = (int)((screenPosition.x - (int)uiElement.rect.width / 2) / dpi);
var admobY = (int)(((Screen.height - (int)screenPosition.y) - (int)uiElement.rect.height / 2) / dpi);
return (admobX, admobY);
}
(int, int) ConvertUiElementPosToNativeAdsPos(RectTransform uiElement)
{
var screenPosition = uiElement.ToWorldPosition();
float dpi = Screen.dpi / 160f;
var admobX = (int)(screenPosition.x / dpi);
var admobY = (int)((Screen.height - (int)screenPosition.y) / dpi);
return (admobX, admobY);
}
public void SetPosition(AdsPosition adsPosition)
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
_nativeOverlayAd.SetTemplatePosition(ConvertPosition(adsPosition));
#endif
}
public void SetPosition(int x, int y)
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
_nativeOverlayAd.SetTemplatePosition(x, y);
#endif
}
public void SetPosition(RectTransform uiElement)
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
(int x, int y) = ConvertUiElementPosToNativeAdsPos(uiElement);
_nativeOverlayAd.SetTemplatePosition(x, y);
#endif
}
public void SetPosition(RectTransform uiElement, Camera camera)
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
(int x, int y) = ConvertUiElementPosToNativeAdsPos(uiElement, camera);
_nativeOverlayAd.SetTemplatePosition(x, y);
#endif
}
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
public NativeTemplateStyle Style()
{
return new NativeTemplateStyle
{
TemplateId = nativeTemplate.ToString().ToLower(),
MainBackgroundColor = mainBackgroundColor,
// CallToActionText = new NativeTemplateTextStyle
// {
// BackgroundColor = Color.green,
// TextColor = Color.white,
// FontSize = 9,
// Style = nativeTemplateFontStyle
// }
};
}
AdPosition ConvertPosition(AdsPosition _adsPosition)
{
return _adsPosition switch
{
AdsPosition.Top => AdPosition.Top,
AdsPosition.Bottom => AdPosition.Bottom,
AdsPosition.TopLeft => AdPosition.TopLeft,
AdsPosition.TopRight => AdPosition.TopRight,
AdsPosition.BottomLeft => AdPosition.BottomLeft,
AdsPosition.BottomRight => AdPosition.BottomRight,
_ => AdPosition.Center
};
}
AdSize ConvertSize()
{
return adsSize switch
{
AdsSize.Banner => AdSize.Banner,
AdsSize.MediumRectangle => AdSize.MediumRectangle,
AdsSize.IABBanner => AdSize.IABBanner,
AdsSize.Leaderboard => AdSize.Leaderboard,
_ => AdSize.MediumRectangle,
};
}
private void AdLoadCallback(NativeOverlayAd ad, LoadAdError error)
{
if (error != null || ad == null)
{
OnAdFailedToLoad(error);
return;
}
_nativeOverlayAd = ad;
_nativeOverlayAd.OnAdPaid += OnAdPaided;
_nativeOverlayAd.OnAdClicked += OnAdClicked;
_nativeOverlayAd.OnAdFullScreenContentOpened += OnAdOpening;
_nativeOverlayAd.OnAdFullScreenContentClosed += OnAdClosed;
OnAdLoaded();
}
private void OnAdLoaded()
{
Common.CallActionAndClean(ref loadedCallback);
OnLoadAdEvent?.Invoke();
}
private void OnAdClosed()
{
IsShowing = false;
Common.CallActionAndClean(ref closedCallback);
OnClosedAdEvent?.Invoke();
}
private void OnAdOpening()
{
IsShowing = true;
Common.CallActionAndClean(ref displayedCallback);
OnDisplayedAdEvent?.Invoke();
}
private void OnAdClicked()
{
Common.CallActionAndClean(ref clickedCallback);
OnClickedAdEvent?.Invoke();
}
private void OnAdPaided(AdValue value)
{
paidedCallback?.Invoke(value.Value / 1000000f,
"Admob",
Id,
"NativeOverlayAd", AdMediation.Admob.ToString());
}
private void OnAdFailedToLoad(LoadAdError error)
{
Common.CallActionAndClean(ref failedToLoadCallback);
OnFailedToLoadAdEvent?.Invoke(error.GetMessage());
if (_reload != null) App.StopCoroutine(_reload);
_reload = DelayReload();
App.StartCoroutine(_reload);
}
private IEnumerator DelayReload()
{
yield return _waitReload;
Load();
}
#endif
[ContextMenu("Get Id test")]
void GetUnitTest()
{
#if UNITY_ANDROID
androidId = "ca-app-pub-3940256099942544/2247696110";
#elif UNITY_IOS
iOSId = "ca-app-pub-3940256099942544/3986624511";
#endif
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmodUnitVariable/AdmobNativeOverlayVariable.cs.meta
================================================
fileFormatVersion: 2
guid: 8b15194a160445d694586481bfdc91cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmodUnitVariable/AdmobRewardInterVariable.cs
================================================
using System;
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
using GoogleMobileAds.Api;
#endif
using UnityEngine;
using VirtueSky.Inspector;
using VirtueSky.Misc;
using VirtueSky.Tracking;
namespace VirtueSky.Ads
{
[Serializable]
[EditorIcon("icon_scriptable")]
public class AdmobRewardInterVariable : AdmobAdUnitVariable
{
public bool useTestId;
[NonSerialized] internal Action completedCallback;
[NonSerialized] internal Action skippedCallback;
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
private RewardedInterstitialAd _rewardedInterstitialAd;
#endif
public override void Init()
{
if (useTestId)
{
GetUnitTest();
}
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (string.IsNullOrEmpty(Id)) return;
paidedCallback += AppTracking.TrackRevenue;
#endif
}
public bool IsEarnRewarded { get; private set; }
public override void Load()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (string.IsNullOrEmpty(Id)) return;
Destroy();
RewardedInterstitialAd.Load(Id, new AdRequest(), OnAdLoadCallback);
#endif
}
public override bool IsReady()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
return _rewardedInterstitialAd != null && _rewardedInterstitialAd.CanShowAd();
#else
return false;
#endif
}
protected override void ShowImpl(string placement = null)
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
_rewardedInterstitialAd.Show(UserEarnedRewardCallback);
#endif
}
protected override void ResetChainCallback()
{
base.ResetChainCallback();
completedCallback = null;
skippedCallback = null;
}
public override AdUnitVariable Show(string placement = null)
{
ResetChainCallback();
if (!UnityEngine.Application.isMobilePlatform || string.IsNullOrEmpty(Id) || !IsReady())
return this;
ShowImpl(placement);
return this;
}
public override void Destroy()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (_rewardedInterstitialAd == null) return;
_rewardedInterstitialAd.Destroy();
_rewardedInterstitialAd = null;
IsEarnRewarded = false;
#endif
}
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
private void OnAdLoadCallback(RewardedInterstitialAd ad, LoadAdError error)
{
// if error is not null, the load request failed.
if (error != null || ad == null)
{
OnAdFailedToLoad(error);
return;
}
_rewardedInterstitialAd = ad;
_rewardedInterstitialAd.OnAdFullScreenContentClosed += OnAdClosed;
_rewardedInterstitialAd.OnAdFullScreenContentOpened += OnAdOpening;
_rewardedInterstitialAd.OnAdFullScreenContentFailed += OnAdFailedToShow;
_rewardedInterstitialAd.OnAdPaid += OnAdPaided;
_rewardedInterstitialAd.OnAdClicked += OnAdClicked;
OnAdLoaded();
}
private void OnAdClicked()
{
Common.CallActionAndClean(ref clickedCallback);
OnClickedAdEvent?.Invoke();
}
private void OnAdFailedToLoad(LoadAdError error)
{
Common.CallActionAndClean(ref failedToLoadCallback);
OnFailedToLoadAdEvent?.Invoke(error.GetMessage());
}
private void OnAdLoaded()
{
Common.CallActionAndClean(ref loadedCallback);
OnLoadAdEvent?.Invoke();
}
private void OnAdPaided(AdValue value)
{
paidedCallback?.Invoke(value.Value / 1000000f,
"Admob",
Id,
"RewardedInterstitialAd", AdMediation.Admob.ToString());
}
private void OnAdFailedToShow(AdError error)
{
Common.CallActionAndClean(ref failedToDisplayCallback);
OnFailedToDisplayAdEvent?.Invoke(error.GetMessage());
}
private void OnAdOpening()
{
AdStatic.IsShowingAd = true;
IsShowing = true;
Common.CallActionAndClean(ref displayedCallback);
OnDisplayedAdEvent?.Invoke();
}
private void OnAdClosed()
{
AdStatic.IsShowingAd = false;
IsShowing = false;
Common.CallActionAndClean(ref closedCallback);
OnClosedAdEvent?.Invoke();
if (IsEarnRewarded)
{
Common.CallActionAndClean(ref completedCallback);
_rewardedInterstitialAd.Destroy();
return;
}
Common.CallActionAndClean(ref skippedCallback);
_rewardedInterstitialAd.Destroy();
}
private void UserEarnedRewardCallback(Reward reward)
{
IsEarnRewarded = true;
}
#endif
[ContextMenu("Get Id test")]
void GetUnitTest()
{
#if UNITY_ANDROID
androidId = "ca-app-pub-3940256099942544/5354046379";
#elif UNITY_IOS
iOSId = "ca-app-pub-3940256099942544/6978759866";
#endif
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmodUnitVariable/AdmobRewardInterVariable.cs.meta
================================================
fileFormatVersion: 2
guid: ddd2127be5434974aa775c9d809bc25f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmodUnitVariable/AdmobRewardVariable.cs
================================================
using System;
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
using GoogleMobileAds.Api;
#endif
using UnityEngine;
using VirtueSky.Inspector;
using VirtueSky.Misc;
using VirtueSky.Tracking;
namespace VirtueSky.Ads
{
[Serializable]
[EditorIcon("icon_scriptable")]
public class AdmobRewardVariable : AdmobAdUnitVariable
{
public bool useTestId;
[NonSerialized] internal Action completedCallback;
[NonSerialized] internal Action skippedCallback;
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
private RewardedAd _rewardedAd;
#endif
public override void Init()
{
if (useTestId)
{
GetUnitTest();
}
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (string.IsNullOrEmpty(Id)) return;
paidedCallback += AppTracking.TrackRevenue;
#endif
}
public bool IsEarnRewarded { get; private set; }
public override void Load()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (string.IsNullOrEmpty(Id)) return;
Destroy();
RewardedAd.Load(Id, new AdRequest(), AdLoadCallback);
#endif
}
public override bool IsReady()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
return _rewardedAd != null && _rewardedAd.CanShowAd();
#else
return false;
#endif
}
protected override void ShowImpl(string placement = null)
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
_rewardedAd.Show(UserRewardEarnedCallback);
#endif
}
public override AdUnitVariable Show(string placement = null)
{
ResetChainCallback();
if (!UnityEngine.Application.isMobilePlatform || string.IsNullOrEmpty(Id) || !IsReady())
return this;
ShowImpl(placement);
return this;
}
protected override void ResetChainCallback()
{
base.ResetChainCallback();
completedCallback = null;
skippedCallback = null;
}
public override void Destroy()
{
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
if (_rewardedAd == null) return;
_rewardedAd.Destroy();
_rewardedAd = null;
IsEarnRewarded = false;
#endif
}
#if VIRTUESKY_ADS && VIRTUESKY_ADMOB
private void AdLoadCallback(RewardedAd ad, LoadAdError error)
{
// if error is not null, the load request failed.
if (error != null || ad == null)
{
OnAdFailedToLoad(error);
return;
}
_rewardedAd = ad;
_rewardedAd.OnAdFullScreenContentClosed += OnAdClosed;
_rewardedAd.OnAdFullScreenContentFailed += OnAdFailedToShow;
_rewardedAd.OnAdFullScreenContentOpened += OnAdOpening;
_rewardedAd.OnAdPaid += OnAdPaided;
_rewardedAd.OnAdClicked += OnAdClicked;
OnAdLoaded();
}
private void OnAdClicked()
{
Common.CallActionAndClean(ref clickedCallback);
OnClickedAdEvent?.Invoke();
}
private void OnAdPaided(AdValue value)
{
paidedCallback?.Invoke(value.Value / 1000000f,
"Admob",
Id,
"RewardedAd", AdMediation.Admob.ToString());
}
private void OnAdOpening()
{
AdStatic.IsShowingAd = true;
IsShowing = true;
Common.CallActionAndClean(ref displayedCallback);
OnDisplayedAdEvent?.Invoke();
}
private void OnAdFailedToShow(AdError obj)
{
Common.CallActionAndClean(ref failedToDisplayCallback);
OnFailedToDisplayAdEvent?.Invoke(obj.GetMessage());
}
private void OnAdClosed()
{
AdStatic.IsShowingAd = false;
IsShowing = false;
Common.CallActionAndClean(ref closedCallback);
OnClosedAdEvent?.Invoke();
if (IsEarnRewarded)
{
Common.CallActionAndClean(ref completedCallback);
Destroy();
return;
}
Common.CallActionAndClean(ref skippedCallback);
Destroy();
}
private void OnAdLoaded()
{
Common.CallActionAndClean(ref loadedCallback);
OnLoadAdEvent?.Invoke();
}
private void OnAdFailedToLoad(LoadAdError error)
{
Common.CallActionAndClean(ref failedToLoadCallback);
OnFailedToLoadAdEvent?.Invoke(error.GetMessage());
}
private void UserRewardEarnedCallback(Reward reward)
{
IsEarnRewarded = true;
}
#endif
[ContextMenu("Get Id test")]
void GetUnitTest()
{
#if UNITY_ANDROID
androidId = "ca-app-pub-3940256099942544/5224354917";
#elif UNITY_IOS
iOSId = "ca-app-pub-3940256099942544/1712485313";
#endif
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmodUnitVariable/AdmobRewardVariable.cs.meta
================================================
fileFormatVersion: 2
guid: 0199f7cf7b5f44cfa63f780b9ff50857
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/Admob/AdmodUnitVariable.meta
================================================
fileFormatVersion: 2
guid: 5a8d9ff9a56c4478887d3a9190da2563
timeCreated: 1695462336
================================================
FILE: VirtueSky/Advertising/Runtime/Admob.meta
================================================
fileFormatVersion: 2
guid: 488c040c267923f4e91f3c963c4e9536
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/General/AdClient.cs
================================================
using VirtueSky.Tracking;
namespace VirtueSky.Ads
{
public abstract class AdClient
{
protected AdSetting adSetting;
protected bool statusAppOpenFirstIgnore;
// public AdClient(AdSetting _adSetting)
// {
// adSetting = _adSetting;
// }
public void SetupAdSetting(AdSetting _adSetting)
{
this.adSetting = _adSetting;
}
public abstract void Initialize();
public abstract void LoadBanner();
public abstract void LoadInterstitial();
public abstract void LoadRewarded();
public abstract void LoadRewardedInterstitial();
public abstract void LoadAppOpen();
public abstract void ShowAppOpen();
//Currently, native overlay ads is only available for admob.
public abstract void LoadNativeOverlay();
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/General/AdClient.cs.meta
================================================
fileFormatVersion: 2
guid: 7af305bf9c13403badeef5393ca8283a
timeCreated: 1695566887
================================================
FILE: VirtueSky/Advertising/Runtime/General/AdInfo.cs
================================================
namespace VirtueSky.Ads
{
public class AdsInfo
{
public string AdUnitId { get; private set; }
public string AdFormat { get; private set; }
public string Placement { get; private set; }
public string AdNetwork { get; private set; }
public double Revenue { get; private set; }
public string AdMediation { get; private set; }
public string AuctionId { get; private set; }
#if VIRTUESKY_APPLOVIN
public AdsInfo(MaxSdkBase.AdInfo info)
{
AdUnitId = info.AdUnitIdentifier;
AdFormat = info.AdFormat;
Placement = info.Placement;
AdNetwork = info.NetworkName;
Revenue = info.Revenue;
AdMediation = Ads.AdMediation.AppLovin.ToString();
AuctionId = "";
}
#endif
#if VIRTUESKY_LEVELPLAY
public AdsInfo(Unity.Services.LevelPlay.LevelPlayAdInfo info)
{
AdUnitId = info.AdUnitId;
AdFormat = info.AdFormat;
Placement = info.PlacementName;
AdNetwork = info.AdNetwork;
Revenue = (double)info.Revenue;
AdMediation = Ads.AdMediation.LevelPlay.ToString();
AuctionId = info.AuctionId;
}
#endif
public AdsInfo(string adUnitId, string adFormat, string placement, string adNetwork, double revenue, string adMediation, string auctionId)
{
AdUnitId = adUnitId;
AdFormat = adFormat;
Placement = placement;
AdNetwork = adNetwork;
Revenue = revenue;
AdMediation = adMediation;
AuctionId = auctionId;
}
}
public class AdsError
{
public int ErrorCode { get; private set; }
public string ErrorMessage { get; private set; }
#if VIRTUESKY_APPLOVIN
public AdsError(MaxSdkBase.ErrorInfo info)
{
ErrorCode = (int)info.Code;
ErrorMessage = info.Message;
}
#endif
#if VIRTUESKY_LEVELPLAY
public AdsError(Unity.Services.LevelPlay.LevelPlayAdError adError)
{
ErrorCode = adError.ErrorCode;
ErrorMessage = adError.ErrorMessage;
}
#endif
#if VIRTUESKY_ADMOB
public AdsError(GoogleMobileAds.Api.AdError adError)
{
ErrorCode = adError.GetCode();
ErrorMessage = adError.GetMessage();
}
#endif
public AdsError(int errorCode, string errorMessage)
{
ErrorCode = errorCode;
ErrorMessage = errorMessage;
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/General/AdInfo.cs.meta
================================================
fileFormatVersion: 2
guid: 12c07896615a460a871b51f30257302f
timeCreated: 1776757990
================================================
FILE: VirtueSky/Advertising/Runtime/General/AdSetting.cs
================================================
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
using VirtueSky.Inspector;
#if UNITY_EDITOR
using VirtueSky.UtilsEditor;
#endif
namespace VirtueSky.Ads
{
[EditorIcon("icon_scriptable")]
public class AdSetting : ScriptableObject
{
[Range(5, 100), SerializeField] private float adCheckingInterval = 8f;
[Range(5, 100), SerializeField] private float adLoadingInterval = 15f;
[SerializeField] private bool useAppLovin = true;
[SerializeField] private bool useAdmob;
[SerializeField] private bool useLevelPlay;
[SerializeField] private bool enableTrackAdRevenue = true;
[Tooltip("Install google-mobile-ads sdk to use GDPR"), SerializeField]
private bool enableGDPR;
[SerializeField] private bool enableGDPRTestMode;
public float AdCheckingInterval => adCheckingInterval;
public float AdLoadingInterval => adLoadingInterval;
public bool UseAppLovin => useAppLovin;
public bool UseAdmob => useAdmob;
public bool UseLevelPlay => useLevelPlay;
public bool EnableTrackAdRevenue => enableTrackAdRevenue;
public bool EnableGDPR => enableGDPR;
public bool EnableGDPRTestMode => enableGDPRTestMode;
#region AppLovin
[TextArea, SerializeField] private string sdkKey;
[SerializeField] private MaxBannerVariable maxBannerVariable;
[SerializeField] private MaxInterVariable maxInterVariable;
[SerializeField] private MaxRewardVariable maxRewardVariable;
[SerializeField] private MaxAppOpenVariable maxAppOpenVariable;
public string SdkKey => sdkKey;
public MaxBannerVariable MaxBannerVariable => maxBannerVariable;
public MaxInterVariable MaxInterVariable => maxInterVariable;
public MaxRewardVariable MaxRewardVariable => maxRewardVariable;
public MaxAppOpenVariable MaxAppOpenVariable => maxAppOpenVariable;
#endregion
#region Admob
// [HeaderLine("Admob")]
[SerializeField] private AdmobBannerVariable admobBannerVariable;
[SerializeField] private AdmobInterVariable admobInterVariable;
[SerializeField] private AdmobRewardVariable admobRewardVariable;
[SerializeField] private AdmobRewardInterVariable admobRewardInterVariable;
[SerializeField] private AdmobAppOpenVariable admobAppOpenVariable;
[SerializeField] private AdmobNativeOverlayVariable admobNativeOverlayVariable;
[Tooltip(
"If you enable and connect admob with firebase, ad_impression will be automatically tracked. If you disable and disconnect admob with firebase, ad_impression will be tracked manually."),
SerializeField]
private bool autoTrackingAdImpressionAdmob = true;
[SerializeField] private bool admobEnableTestMode;
[SerializeField] private List admobDevicesTest;
public AdmobBannerVariable AdmobBannerVariable => admobBannerVariable;
public AdmobInterVariable AdmobInterVariable => admobInterVariable;
public AdmobRewardVariable AdmobRewardVariable => admobRewardVariable;
public AdmobRewardInterVariable AdmobRewardInterVariable => admobRewardInterVariable;
public AdmobAppOpenVariable AdmobAppOpenVariable => admobAppOpenVariable;
public AdmobNativeOverlayVariable AdmobNativeOverlayVariable => admobNativeOverlayVariable;
public bool AdmobEnableTestMode => admobEnableTestMode;
public bool AutoTrackingAdImpressionAdmob => autoTrackingAdImpressionAdmob;
public List AdmobDevicesTest => admobDevicesTest;
#endregion
#region LevelPlay
[SerializeField] private string androidAppKey;
[SerializeField] private string iOSAppKey;
[SerializeField] private bool useTestAppKey;
[SerializeField] private LevelPlayBannerVariable levelPlayBannerVariable;
[SerializeField] private LevelPlayInterVariable levelPlayInterVariable;
[SerializeField] private LevelPlayRewardVariable levelPlayRewardVariable;
public string AndroidAppKey
{
get => androidAppKey;
set => androidAppKey = value;
}
public string IosAppKey
{
get => iOSAppKey;
set => iOSAppKey = value;
}
public string AppKey
{
get
{
#if UNITY_ANDROID
return androidAppKey;
#elif UNITY_IOS
return iOSAppKey;
#else
return string.Empty;
#endif
}
set
{
#if UNITY_ANDROID
androidAppKey = value;
#elif UNITY_IOS
iOSAppKey = value;
#endif
}
}
public bool UseTestAppKey => useTestAppKey;
public LevelPlayBannerVariable LevelPlayBannerVariable => levelPlayBannerVariable;
public LevelPlayInterVariable LevelPlayInterVariable => levelPlayInterVariable;
public LevelPlayRewardVariable LevelPlayRewardVariable => levelPlayRewardVariable;
#endregion
}
public enum AdMediation
{
AppLovin,
Admob,
LevelPlay
}
public enum AdsPosition
{
Top = 1,
Bottom = 0,
TopLeft = 2,
TopRight = 3,
BottomLeft = 4,
BottomRight = 5,
Center = 6,
}
public enum AdsSize
{
Banner = 0, // 320x50
Adaptive = 5, // full width
MediumRectangle = 1, // 300x250
IABBanner = 2, // 468x60
Leaderboard = 3, // 728x90
// SmartBanner = 4,
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/General/AdSetting.cs.meta
================================================
fileFormatVersion: 2
guid: 4faf330974234e808669c796dc78cdee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/General/AdStatic.cs
================================================
using System;
using UnityEngine;
using VirtueSky.DataStorage;
namespace VirtueSky.Ads
{
public static class AdStatic
{
public static bool IsRemoveAd
{
get => GameData.Get($"{Application.identifier}_removeads", false);
set => GameData.Set($"{Application.identifier}_removeads", value);
}
public static DateTime AdClosingTime { get; internal set; }
private static bool isShowingAd;
public static bool IsShowingAd
{
get => isShowingAd;
internal set
{
isShowingAd = value;
if (!value)
{
AdClosingTime = DateTime.Now;
}
}
}
internal static Action waitAppOpenDisplayedAction;
internal static Action waitAppOpenClosedAction;
public static AdUnitVariable OnDisplayed(this AdUnitVariable unit, Action onDisplayed)
{
unit.displayedCallback = onDisplayed;
return unit;
}
public static AdUnitVariable OnClosed(this AdUnitVariable unit, Action onClosed)
{
unit.closedCallback = onClosed;
return unit;
}
public static AdUnitVariable OnLoaded(this AdUnitVariable unit, Action onLoaded)
{
unit.loadedCallback = onLoaded;
return unit;
}
public static AdUnitVariable OnFailedToLoad(this AdUnitVariable unit, Action onFailedToLoad)
{
unit.failedToLoadCallback = onFailedToLoad;
return unit;
}
public static AdUnitVariable OnFailedToDisplay(this AdUnitVariable unit, Action onFailedToDisplay)
{
unit.failedToDisplayCallback = onFailedToDisplay;
return unit;
}
public static AdUnitVariable OnClicked(this AdUnitVariable unit, Action onClicked)
{
unit.clickedCallback = onClicked;
return unit;
}
public static AdUnitVariable OnCompleted(this AdUnitVariable unit, Action onCompleted)
{
if (!Application.isMobilePlatform)
{
onCompleted?.Invoke();
}
switch (unit)
{
case AdmobInterVariable admobInter:
admobInter.completedCallback = onCompleted;
return unit;
case AdmobRewardVariable admobReward:
admobReward.completedCallback = onCompleted;
return unit;
case AdmobRewardInterVariable admobRewardInter:
admobRewardInter.completedCallback = onCompleted;
return unit;
case MaxInterVariable maxInter:
maxInter.completedCallback = onCompleted;
return unit;
case MaxRewardVariable maxReward:
maxReward.completedCallback = onCompleted;
return unit;
case LevelPlayInterVariable ironSourceInterVariable:
ironSourceInterVariable.completedCallback = onCompleted;
return unit;
case LevelPlayRewardVariable ironSourceRewardVariable:
ironSourceRewardVariable.completedCallback = onCompleted;
return unit;
}
return unit;
}
public static AdUnitVariable OnSkipped(this AdUnitVariable unit, Action onSkipped)
{
switch (unit)
{
case AdmobRewardVariable admobReward:
admobReward.skippedCallback = onSkipped;
return unit;
case AdmobRewardInterVariable admobRewardInter:
admobRewardInter.skippedCallback = onSkipped;
return unit;
case MaxRewardVariable maxReward:
maxReward.skippedCallback = onSkipped;
return unit;
case LevelPlayRewardVariable ironSourceRewardVariable:
ironSourceRewardVariable.skippedCallback = onSkipped;
return unit;
}
return unit;
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/General/AdStatic.cs.meta
================================================
fileFormatVersion: 2
guid: 4132cc5be9fa4959a8bafd62a51bb7ca
timeCreated: 1695460884
================================================
FILE: VirtueSky/Advertising/Runtime/General/AdUnitVariable.cs
================================================
using System;
using UnityEngine;
namespace VirtueSky.Ads
{
public abstract class AdUnitVariable : ScriptableObject, IAdUnit
{
[NonSerialized] internal Action loadedCallback;
[NonSerialized] internal Action failedToLoadCallback;
[NonSerialized] internal Action displayedCallback;
[NonSerialized] internal Action failedToDisplayCallback;
[NonSerialized] internal Action closedCallback;
[NonSerialized] internal Action clickedCallback;
[NonSerialized] public Action paidedCallback;
public Action OnLoadAdEvent;
public Action OnFailedToLoadAdEvent;
public Action OnDisplayedAdEvent;
public Action OnFailedToDisplayAdEvent;
public Action OnClosedAdEvent;
public Action OnClickedAdEvent;
public abstract bool IsShowing { get; internal set; }
public virtual string Id
{
get => "";
}
protected virtual void ShowImpl(string placement = null)
{
}
protected virtual void ResetChainCallback()
{
loadedCallback = null;
failedToDisplayCallback = null;
failedToLoadCallback = null;
closedCallback = null;
}
public virtual void HideBanner()
{
}
public abstract AdUnitVariable Show(string placement = null);
public virtual void Init()
{
}
public virtual void Load()
{
}
public virtual bool IsReady()
{
return false;
}
public virtual void Destroy()
{
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/General/AdUnitVariable.cs.meta
================================================
fileFormatVersion: 2
guid: 0a0b123368074cd1a359dea0f1ef9233
timeCreated: 1695460627
================================================
FILE: VirtueSky/Advertising/Runtime/General/Advertising.cs
================================================
using System;
using System.Collections;
using System.Collections.Generic;
#if VIRTUESKY_ADMOB
using GoogleMobileAds.Api;
using GoogleMobileAds.Ump.Api;
#endif
#if UNITY_IOS
using Unity.Advertisement.IosSupport;
#endif
using UnityEngine;
using UnityEngine.Serialization;
#if UNITY_EDITOR
using VirtueSky.UtilsEditor;
#endif
using VirtueSky.Events;
using VirtueSky.Inspector;
using VirtueSky.Misc;
using VirtueSky.Tracking;
using VirtueSky.Utils;
namespace VirtueSky.Ads
{
[EditorIcon("icon_controller"), HideMonoScript]
public class Advertising : MonoBehaviour
{
[Space] [SerializeField] private bool dontDestroyOnLoad = false;
[Tooltip("Require"), SerializeField] private AdSetting adSetting;
[Tooltip("Allows nulls"), SerializeField]
private BooleanEvent changePreventDisplayAppOpenEvent;
#if VIRTUESKY_ADMOB
[Space] [HeaderLine("Admob GDPR")] [Tooltip("Allows nulls"), SerializeField]
private EventNoParam callShowAgainGDPREvent;
#endif
private IEnumerator autoLoadAdCoroutine;
private float _lastTimeLoadInterstitialAdTimestamp = DEFAULT_TIMESTAMP;
private float _lastTimeLoadRewardedTimestamp = DEFAULT_TIMESTAMP;
private float _lastTimeLoadRewardedInterstitialTimestamp = DEFAULT_TIMESTAMP;
private float _lastTimeLoadAppOpenTimestamp = DEFAULT_TIMESTAMP;
private const float DEFAULT_TIMESTAMP = -1000;
// private AdClient currentAdClient;
private AdClient maxAdClient;
private AdClient admobAdClient;
private AdClient ironSourceAdClient;
private void Awake()
{
if (dontDestroyOnLoad)
{
DontDestroyOnLoad(this.gameObject);
}
}
private void OnEnable()
{
#if VIRTUESKY_ADMOB
if (callShowAgainGDPREvent != null)
{
callShowAgainGDPREvent.AddListener(ShowPrivacyOptionsForm);
}
#endif
}
private void OnDisable()
{
#if VIRTUESKY_ADMOB
if (callShowAgainGDPREvent != null)
{
callShowAgainGDPREvent.RemoveListener(ShowPrivacyOptionsForm);
}
#endif
}
private void Start()
{
if (changePreventDisplayAppOpenEvent != null)
changePreventDisplayAppOpenEvent.AddListener(OnChangePreventDisplayOpenAd);
if (adSetting.EnableGDPR)
{
#if VIRTUESKY_ADMOB
#if UNITY_IOS
if (ATTrackingStatusBinding.GetAuthorizationTrackingStatus() ==
ATTrackingStatusBinding.AuthorizationTrackingStatus.AUTHORIZED)
{
InitGdpr();
}
else
{
InitAdClient();
}
#else
InitGdpr();
#endif
#endif
}
else
{
InitAdClient();
}
}
void InitAdClient()
{
AppTracking.Init(adSetting.EnableTrackAdRevenue);
if (adSetting.UseAppLovin)
{
maxAdClient = new MaxAdClient(adSetting);
maxAdClient.Initialize();
Debug.Log($"Use: {maxAdClient}".SetColor(CustomColor.Cyan));
}
if (adSetting.UseAdmob)
{
admobAdClient = new AdmobAdClient(adSetting);
admobAdClient.Initialize();
Debug.Log($"Use: {admobAdClient}".SetColor(CustomColor.Cyan));
}
if (adSetting.UseLevelPlay)
{
ironSourceAdClient = new LevelPlayAdClient(adSetting);
ironSourceAdClient.Initialize();
Debug.Log($"Use: {ironSourceAdClient}".SetColor(CustomColor.Cyan));
}
InitAutoLoadAds();
}
private void InitAutoLoadAds()
{
if (autoLoadAdCoroutine != null) StopCoroutine(autoLoadAdCoroutine);
autoLoadAdCoroutine = IeAutoLoadAll();
StartCoroutine(autoLoadAdCoroutine);
}
private void OnChangePreventDisplayOpenAd(bool state)
{
AdStatic.IsShowingAd = state;
}
IEnumerator IeAutoLoadAll(float delay = 0)
{
if (delay > 0) yield return new WaitForSeconds(delay);
while (true)
{
AutoLoadInterAds();
AutoLoadRewardAds();
AutoLoadRewardInterAds();
AutoLoadAppOpenAds();
yield return new WaitForSeconds(adSetting.AdCheckingInterval);
}
}
#region Func Load Ads
void AutoLoadInterAds()
{
if (Time.realtimeSinceStartup - _lastTimeLoadInterstitialAdTimestamp <
adSetting.AdLoadingInterval) return;
if (adSetting.UseAppLovin) maxAdClient.LoadInterstitial();
if (adSetting.UseAdmob) admobAdClient.LoadInterstitial();
if (adSetting.UseLevelPlay) ironSourceAdClient.LoadInterstitial();
_lastTimeLoadInterstitialAdTimestamp = Time.realtimeSinceStartup;
}
void AutoLoadRewardAds()
{
if (Time.realtimeSinceStartup - _lastTimeLoadRewardedTimestamp <
adSetting.AdLoadingInterval) return;
if (adSetting.UseAppLovin) maxAdClient.LoadRewarded();
if (adSetting.UseAdmob) admobAdClient.LoadRewarded();
if (adSetting.UseLevelPlay) ironSourceAdClient.LoadRewarded();
_lastTimeLoadRewardedTimestamp = Time.realtimeSinceStartup;
}
void AutoLoadRewardInterAds()
{
if (Time.realtimeSinceStartup - _lastTimeLoadRewardedInterstitialTimestamp <
adSetting.AdLoadingInterval) return;
if (adSetting.UseAppLovin) maxAdClient.LoadRewardedInterstitial();
if (adSetting.UseAdmob) admobAdClient.LoadRewardedInterstitial();
if (adSetting.UseLevelPlay) ironSourceAdClient.LoadRewardedInterstitial();
_lastTimeLoadRewardedInterstitialTimestamp = Time.realtimeSinceStartup;
}
void AutoLoadAppOpenAds()
{
if (Time.realtimeSinceStartup - _lastTimeLoadAppOpenTimestamp <
adSetting.AdLoadingInterval) return;
if (adSetting.UseAppLovin) maxAdClient.LoadAppOpen();
if (adSetting.UseAdmob) admobAdClient.LoadAppOpen();
if (adSetting.UseLevelPlay) ironSourceAdClient.LoadAppOpen();
_lastTimeLoadAppOpenTimestamp = Time.realtimeSinceStartup;
}
#endregion
#region Admob GDPR
#if VIRTUESKY_ADMOB
private void InitGdpr()
{
#if UNITY_EDITOR
InitAdClient();
#else
string deviceID = SystemInfo.deviceUniqueIdentifier;
string deviceIDUpperCase = deviceID.ToUpper();
Debug.Log("TestDeviceHashedId = " + deviceIDUpperCase);
ConsentRequestParameters request = new ConsentRequestParameters { TagForUnderAgeOfConsent = false };
if (adSetting.EnableGDPRTestMode)
{
List listDeviceIdTestMode = new List();
listDeviceIdTestMode.Add(deviceIDUpperCase);
request.ConsentDebugSettings = new ConsentDebugSettings
{
DebugGeography = DebugGeography.EEA,
TestDeviceHashedIds = listDeviceIdTestMode
};
}
ConsentInformation.Update(request, OnConsentInfoUpdated);
#endif
}
private void OnConsentInfoUpdated(FormError consentError)
{
if (consentError != null)
{
Debug.Log("error consentError = " + consentError);
return;
}
ConsentForm.LoadAndShowConsentFormIfRequired((FormError formError) =>
{
if (formError != null)
{
Debug.Log("error consentError = " + consentError);
return;
}
Debug.Log("ConsentStatus = " + ConsentInformation.ConsentStatus.ToString());
Debug.Log("CanRequestAds = " + ConsentInformation.CanRequestAds());
if (ConsentInformation.CanRequestAds())
{
MobileAds.RaiseAdEventsOnUnityMainThread = true;
InitAdClient();
}
}
);
}
public void LoadAndShowConsentForm()
{
Debug.Log("LoadAndShowConsentForm Start!");
ConsentForm.Load((consentForm, loadError) =>
{
if (loadError != null)
{
Debug.Log("error loadError = " + loadError);
return;
}
consentForm.Show(showError =>
{
if (showError != null)
{
Debug.Log("error showError = " + showError);
return;
}
});
});
}
private void ShowPrivacyOptionsForm()
{
Debug.Log("Showing privacy options form.");
ConsentForm.ShowPrivacyOptionsForm((FormError showError) =>
{
if (showError != null)
{
Debug.LogError("Error showing privacy options form with error: " + showError.Message);
}
});
}
#endif
#endregion
#if UNITY_EDITOR
private void Reset()
{
adSetting = CreateAsset.CreateAndGetScriptableAsset("/Ads/Setting");
}
#endif
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/General/Advertising.cs.meta
================================================
fileFormatVersion: 2
guid: e380b752cb6c40aaace317083692044f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: 1c63e3b6583d6b54a8de6efcd2a86725, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/General/IAdUnit.cs
================================================
namespace VirtueSky.Ads
{
public interface IAdUnit
{
public void Init();
public void Load();
public bool IsReady();
public void Destroy();
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/General/IAdUnit.cs.meta
================================================
fileFormatVersion: 2
guid: 5114c7ef942a40ff9e7dbf458d6ed339
timeCreated: 1728631990
================================================
FILE: VirtueSky/Advertising/Runtime/General.meta
================================================
fileFormatVersion: 2
guid: f3d6f74e978547a43947efa49ed48e87
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/LevelPlay/LevelPlayAdClient.cs
================================================
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
using Unity.Services.LevelPlay;
#endif
using VirtueSky.Core;
using VirtueSky.Tracking;
namespace VirtueSky.Ads
{
public sealed class LevelPlayAdClient : AdClient
{
public LevelPlayAdClient(AdSetting _adSetting)
{
adSetting = _adSetting;
}
public bool SdkInitializationCompleted { get; private set; }
public override void Initialize()
{
SdkInitializationCompleted = false;
if (adSetting.UseTestAppKey)
{
adSetting.AndroidAppKey = "85460dcd";
adSetting.IosAppKey = "8545d445";
}
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
App.AddPauseCallback(OnAppStateChange);
LevelPlay.OnInitSuccess += SdkInitializationCompletedEvent;
LevelPlay.OnImpressionDataReady += ImpressionDataReadyEvent;
adSetting.LevelPlayBannerVariable.Init();
adSetting.LevelPlayInterVariable.Init();
adSetting.LevelPlayRewardVariable.Init();
LevelPlay.ValidateIntegration();
LevelPlay.Init(adSetting.AppKey);
#endif
}
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
private void ImpressionDataReadyEvent(LevelPlayImpressionData impressionData)
{
if (impressionData.Revenue != null)
{
AppTracking.TrackRevenue((double)impressionData.Revenue, impressionData.AdNetwork,
impressionData.MediationAdUnitId,
impressionData.AdFormat, AdMediation.LevelPlay.ToString());
}
}
private void OnAppStateChange(bool pauseStatus)
{
LevelPlay.SetPauseGame(pauseStatus);
}
void SdkInitializationCompletedEvent(LevelPlayConfiguration config)
{
SdkInitializationCompleted = true;
LoadInterstitial();
LoadRewarded();
LoadBanner();
}
#endif
public override void LoadBanner()
{
if (adSetting.LevelPlayBannerVariable == null) return;
adSetting.LevelPlayBannerVariable.Load();
}
public override void LoadInterstitial()
{
if (adSetting.LevelPlayInterVariable == null) return;
if (!adSetting.LevelPlayInterVariable.IsReady()) adSetting.LevelPlayInterVariable.Load();
}
public override void LoadRewarded()
{
if (adSetting.LevelPlayRewardVariable == null) return;
if (!adSetting.LevelPlayRewardVariable.IsReady()) adSetting.LevelPlayRewardVariable.Load();
}
public override void LoadRewardedInterstitial()
{
}
public override void LoadAppOpen()
{
}
public override void ShowAppOpen()
{
}
public override void LoadNativeOverlay()
{
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/LevelPlay/LevelPlayAdClient.cs.meta
================================================
fileFormatVersion: 2
guid: 163589e8de4d4fcaaac2df3fbf96f414
timeCreated: 1719850536
================================================
FILE: VirtueSky/Advertising/Runtime/LevelPlay/LevelPlayAdUnitVariable.cs
================================================
using System;
using UnityEngine;
namespace VirtueSky.Ads
{
public class LevelPlayAdUnitVariable : AdUnitVariable
{
[SerializeField] protected string androidId;
[SerializeField] protected string iOSId;
[NonSerialized] private string idRuntime = string.Empty;
public override bool IsShowing { get; internal set; }
public override string Id
{
get
{
if (idRuntime == String.Empty)
{
#if UNITY_ANDROID
return androidId;
#elif UNITY_IOS
return iOSId;
#else
return string.Empty;
#endif
}
return idRuntime;
}
}
public override AdUnitVariable Show(string placement = null)
{
ResetChainCallback();
if (!Application.isMobilePlatform || AdStatic.IsRemoveAd || !IsReady()) return this;
ShowImpl(placement);
return this;
}
public void SetIdRuntime(string unitId)
{
idRuntime = unitId;
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/LevelPlay/LevelPlayAdUnitVariable.cs.meta
================================================
fileFormatVersion: 2
guid: 591b3c010489423bba0d8576ef5fbe49
timeCreated: 1728631885
================================================
FILE: VirtueSky/Advertising/Runtime/LevelPlay/LevelPlayUnitVariable/LevelPlayBannerVariable.cs
================================================
using System;
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
using Unity.Services.LevelPlay;
using VirtueSky.Core;
#endif
using System.Collections;
using UnityEngine;
using VirtueSky.Inspector;
using VirtueSky.Misc;
namespace VirtueSky.Ads
{
[Serializable]
[EditorIcon("icon_scriptable")]
public class LevelPlayBannerVariable : LevelPlayAdUnitVariable
{
public AdsSize size;
public AdsPosition position;
public bool isShowOnLoad;
private bool _isBannerDestroyed = true;
private bool _isBannerShowing;
private bool _previousBannerShowStatus;
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
private LevelPlayBannerAd bannerAd;
private readonly WaitForSeconds _waitBannerReload = new WaitForSeconds(5f);
private IEnumerator _reload;
#endif
public override void Init()
{
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
if (AdStatic.IsRemoveAd) return;
_isBannerDestroyed = true;
#endif
}
public override void Load()
{
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
if (AdStatic.IsRemoveAd) return;
Destroy();
LevelPlayBannerAd.Config.Builder builder = new LevelPlayBannerAd.Config.Builder();
builder.SetPosition(ConvertBannerPosition());
builder.SetSize(ConvertBannerSize());
builder.SetDisplayOnLoad(isShowOnLoad);
var config = builder.Build();
bannerAd = new LevelPlayBannerAd(Id, config);
bannerAd.OnAdLoaded += BannerOnAdLoadedEvent;
bannerAd.OnAdLoadFailed += BannerOnAdLoadFailedEvent;
bannerAd.OnAdClicked += BannerOnAdClickedEvent;
bannerAd.OnAdDisplayed += BannerOnAdDisplayedEvent;
bannerAd.OnAdDisplayFailed += BannerOnAdDisplayFailedEvent;
bannerAd.OnAdLeftApplication += BannerOnAdLeftApplicationEvent;
bannerAd.LoadAd();
_isBannerDestroyed = false;
#endif
}
void OnWaitAppOpenClosed()
{
if (_previousBannerShowStatus)
{
_previousBannerShowStatus = false;
Show();
}
}
void OnWaitAppOpenDisplayed()
{
_previousBannerShowStatus = _isBannerShowing;
if (_isBannerShowing) HideBanner();
}
public override bool IsReady()
{
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
return bannerAd != null;
#else
return false;
#endif
}
protected override void ShowImpl(string placement = null)
{
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
_isBannerShowing = true;
IsShowing = true;
AdStatic.waitAppOpenClosedAction = OnWaitAppOpenClosed;
AdStatic.waitAppOpenDisplayedAction = OnWaitAppOpenDisplayed;
Load();
bannerAd.ShowAd();
#endif
}
public override void Destroy()
{
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
_isBannerShowing = false;
_isBannerDestroyed = true;
IsShowing = false;
AdStatic.waitAppOpenClosedAction = null;
AdStatic.waitAppOpenDisplayedAction = null;
bannerAd.DestroyAd();
#endif
}
public override void HideBanner()
{
base.HideBanner();
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
_isBannerShowing = false;
IsShowing = false;
bannerAd.HideAd();
#endif
}
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
private LevelPlayAdSize ConvertBannerSize()
{
switch (size)
{
case AdsSize.Banner: return LevelPlayAdSize.BANNER;
case AdsSize.Adaptive: return LevelPlayAdSize.LARGE;
case AdsSize.MediumRectangle: return LevelPlayAdSize.MEDIUM_RECTANGLE;
case AdsSize.Leaderboard: return LevelPlayAdSize.LEADERBOARD;
default: return LevelPlayAdSize.BANNER;
}
}
private LevelPlayBannerPosition ConvertBannerPosition()
{
switch (position)
{
case AdsPosition.Bottom: return LevelPlayBannerPosition.BottomCenter;
case AdsPosition.Top: return LevelPlayBannerPosition.TopCenter;
default: return LevelPlayBannerPosition.BottomCenter;
}
}
#region Fun Callback
void BannerOnAdLoadedEvent(LevelPlayAdInfo adInfo)
{
Common.CallActionAndClean(ref loadedCallback);
OnLoadAdEvent?.Invoke();
}
void BannerOnAdLoadFailedEvent(LevelPlayAdError ironSourceError)
{
Common.CallActionAndClean(ref failedToLoadCallback);
OnFailedToLoadAdEvent?.Invoke(ironSourceError.ErrorMessage);
if (_reload != null) App.StopCoroutine(_reload);
_reload = DelayBannerReload();
App.StartCoroutine(_reload);
}
private IEnumerator DelayBannerReload()
{
yield return _waitBannerReload;
Load();
}
void BannerOnAdClickedEvent(LevelPlayAdInfo adInfo)
{
Common.CallActionAndClean(ref clickedCallback);
OnClickedAdEvent?.Invoke();
}
void BannerOnAdDisplayedEvent(LevelPlayAdInfo adInfo)
{
IsShowing = true;
Common.CallActionAndClean(ref displayedCallback);
OnDisplayedAdEvent?.Invoke();
}
void BannerOnAdDisplayFailedEvent(LevelPlayAdInfo adInfo, LevelPlayAdError adError)
{
Common.CallActionAndClean(ref failedToDisplayCallback);
OnFailedToDisplayAdEvent?.Invoke(adError.ErrorMessage);
}
void BannerOnAdLeftApplicationEvent(LevelPlayAdInfo adInfo)
{
}
#endregion
#endif
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/LevelPlay/LevelPlayUnitVariable/LevelPlayBannerVariable.cs.meta
================================================
fileFormatVersion: 2
guid: f5151f19197e497aa56422344920b819
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/LevelPlay/LevelPlayUnitVariable/LevelPlayInterVariable.cs
================================================
using System;
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
using Unity.Services.LevelPlay;
#endif
using UnityEngine;
using VirtueSky.Inspector;
using VirtueSky.Misc;
namespace VirtueSky.Ads
{
[Serializable]
[EditorIcon("icon_scriptable")]
public class LevelPlayInterVariable : LevelPlayAdUnitVariable
{
[NonSerialized] internal Action completedCallback;
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
private LevelPlayInterstitialAd interstitialAd;
#endif
public override void Init()
{
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
if (AdStatic.IsRemoveAd) return;
#endif
}
public override void Load()
{
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
if (AdStatic.IsRemoveAd) return;
var configBuilder = new LevelPlayInterstitialAd.Config.Builder();
var config = configBuilder.Build();
interstitialAd = new LevelPlayInterstitialAd(Id, config);
interstitialAd.OnAdLoaded += InterstitialOnAdLoadedEvent;
interstitialAd.OnAdLoadFailed += InterstitialOnAdLoadFailed;
interstitialAd.OnAdDisplayed += InterstitialOnAdDisplayEvent;
interstitialAd.OnAdClicked += InterstitialOnAdClickedEvent;
interstitialAd.OnAdDisplayFailed += InterstitialOnAdDisplayFailedEvent;
interstitialAd.OnAdClosed += InterstitialOnAdClosedEvent;
interstitialAd.LoadAd();
#endif
}
public override bool IsReady()
{
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
return interstitialAd.IsAdReady();
#else
return false;
#endif
}
protected override void ShowImpl(string placement = null)
{
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
interstitialAd.ShowAd(placement);
#endif
}
public override void Destroy()
{
}
protected override void ResetChainCallback()
{
base.ResetChainCallback();
completedCallback = null;
}
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
#region Fun Callback
void InterstitialOnAdLoadedEvent(LevelPlayAdInfo adInfo)
{
Common.CallActionAndClean(ref loadedCallback);
OnLoadAdEvent?.Invoke();
}
void InterstitialOnAdLoadFailed(LevelPlayAdError ironSourceError)
{
Common.CallActionAndClean(ref failedToLoadCallback);
OnFailedToLoadAdEvent?.Invoke(ironSourceError.ErrorMessage);
}
void InterstitialOnAdDisplayEvent(LevelPlayAdInfo adInfo)
{
AdStatic.IsShowingAd = true;
IsShowing = true;
Common.CallActionAndClean(ref displayedCallback);
OnDisplayedAdEvent?.Invoke();
}
void InterstitialOnAdClickedEvent(LevelPlayAdInfo adInfo)
{
Common.CallActionAndClean(ref clickedCallback);
OnClickedAdEvent?.Invoke();
}
void InterstitialOnAdDisplayFailedEvent(LevelPlayAdInfo adInfo, LevelPlayAdError adError)
{
Common.CallActionAndClean(ref failedToDisplayCallback);
OnFailedToDisplayAdEvent?.Invoke(adError.ErrorMessage);
}
void InterstitialOnAdClosedEvent(LevelPlayAdInfo adInfo)
{
AdStatic.IsShowingAd = false;
IsShowing = false;
Common.CallActionAndClean(ref completedCallback);
OnClosedAdEvent?.Invoke();
Load();
}
#endregion
#endif
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/LevelPlay/LevelPlayUnitVariable/LevelPlayInterVariable.cs.meta
================================================
fileFormatVersion: 2
guid: d1c4f30e37f14d78abad68a6b2d0a847
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/LevelPlay/LevelPlayUnitVariable/LevelPlayRewardVariable.cs
================================================
using System;
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
using Unity.Services.LevelPlay;
#endif
using VirtueSky.Inspector;
using VirtueSky.Misc;
namespace VirtueSky.Ads
{
[Serializable]
[EditorIcon("icon_scriptable")]
public class LevelPlayRewardVariable : LevelPlayAdUnitVariable
{
[NonSerialized] internal Action completedCallback;
[NonSerialized] internal Action skippedCallback;
public bool IsEarnRewarded { get; private set; }
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
LevelPlayRewardedAd rewardedAd;
#endif
public override void Init()
{
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
if (AdStatic.IsRemoveAd) return;
#endif
}
public override void Load()
{
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
if (AdStatic.IsRemoveAd) return;
var configBuilder = new LevelPlayRewardedAd.Config.Builder();
var config = configBuilder.Build();
rewardedAd = new LevelPlayRewardedAd(Id, config);
rewardedAd.OnAdLoaded += OnAdLoaded;
rewardedAd.OnAdDisplayed += RewardedVideoOnAdDisplayedEvent;
rewardedAd.OnAdClosed += RewardedVideoOnAdClosedEvent;
rewardedAd.OnAdDisplayFailed += RewardedVideoOnAdDisplayFailedEvent;
rewardedAd.OnAdRewarded += RewardedVideoOnAdRewardedEvent;
rewardedAd.OnAdClicked += RewardedVideoOnAdClickedEvent;
rewardedAd.OnAdLoadFailed += RewardedVideoOnAdLoadFailedEvent;
rewardedAd.LoadAd();
#endif
}
public override bool IsReady()
{
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
return rewardedAd != null && rewardedAd.IsAdReady();
#else
return false;
#endif
}
protected override void ShowImpl(string placement = null)
{
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
rewardedAd.ShowAd(placement);
#endif
}
public override AdUnitVariable Show(string placement = null)
{
ResetChainCallback();
if (!UnityEngine.Application.isMobilePlatform || !IsReady()) return this;
ShowImpl(placement);
return this;
}
public override void Destroy()
{
}
protected override void ResetChainCallback()
{
base.ResetChainCallback();
completedCallback = null;
skippedCallback = null;
}
#if VIRTUESKY_ADS && VIRTUESKY_LEVELPLAY
#region Fun Callback
void OnAdLoaded(LevelPlayAdInfo adInfo)
{
Common.CallActionAndClean(ref loadedCallback);
OnLoadAdEvent?.Invoke();
}
private void RewardedVideoOnAdLoadFailedEvent(LevelPlayAdError ironSourceError)
{
Common.CallActionAndClean(ref failedToLoadCallback);
OnFailedToLoadAdEvent?.Invoke(ironSourceError.ToString());
}
void RewardedVideoOnAdDisplayedEvent(LevelPlayAdInfo adInfo)
{
AdStatic.IsShowingAd = true;
IsShowing = true;
Common.CallActionAndClean(ref displayedCallback);
OnDisplayedAdEvent?.Invoke();
}
void RewardedVideoOnAdClosedEvent(LevelPlayAdInfo adInfo)
{
AdStatic.IsShowingAd = false;
IsShowing = false;
Common.CallActionAndClean(ref closedCallback);
OnClosedAdEvent?.Invoke();
if (!IsReady()) rewardedAd.LoadAd();
if (IsEarnRewarded)
{
Common.CallActionAndClean(ref completedCallback);
IsEarnRewarded = false;
return;
}
Common.CallActionAndClean(ref skippedCallback);
}
void RewardedVideoOnAdDisplayFailedEvent(LevelPlayAdInfo adInfo, LevelPlayAdError ironSourceError)
{
Common.CallActionAndClean(ref failedToDisplayCallback);
OnFailedToDisplayAdEvent?.Invoke(ironSourceError.ToString());
}
void RewardedVideoOnAdRewardedEvent(LevelPlayAdInfo info, LevelPlayReward reward)
{
IsEarnRewarded = true;
}
void RewardedVideoOnAdClickedEvent(LevelPlayAdInfo adInfo)
{
Common.CallActionAndClean(ref clickedCallback);
OnClickedAdEvent?.Invoke();
}
#endregion
#endif
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/LevelPlay/LevelPlayUnitVariable/LevelPlayRewardVariable.cs.meta
================================================
fileFormatVersion: 2
guid: 6cb21eb6778e4c0ea10445060b08a271
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/LevelPlay/LevelPlayUnitVariable.meta
================================================
fileFormatVersion: 2
guid: 7d50cc93b33f4995a04161afc0514175
timeCreated: 1719850500
================================================
FILE: VirtueSky/Advertising/Runtime/LevelPlay.meta
================================================
fileFormatVersion: 2
guid: 1170ec045aef4f39ba9d642d37973423
timeCreated: 1719850368
================================================
FILE: VirtueSky/Advertising/Runtime/Max/MaxAdClient.cs
================================================
using VirtueSky.Core;
namespace VirtueSky.Ads
{
public sealed class MaxAdClient : AdClient
{
public MaxAdClient(AdSetting _adSetting)
{
adSetting = _adSetting;
}
public override void Initialize()
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
MaxSdk.SetSdkKey(adSetting.SdkKey);
MaxSdk.InitializeSdk();
adSetting.MaxBannerVariable.Init();
adSetting.MaxInterVariable.Init();
adSetting.MaxRewardVariable.Init();
adSetting.MaxAppOpenVariable.Init();
App.AddPauseCallback(OnAppStateChange);
LoadInterstitial();
LoadRewarded();
LoadRewardedInterstitial();
LoadAppOpen();
LoadBanner();
#endif
}
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
private void OnAppStateChange(bool pauseStatus)
{
if (!pauseStatus && adSetting.MaxAppOpenVariable.autoShow)
{
if (adSetting.UseAppLovin) ShowAppOpen();
}
}
#endif
public override void LoadBanner()
{
if (adSetting.MaxBannerVariable == null) return;
adSetting.MaxBannerVariable.Load();
}
public override void LoadInterstitial()
{
if (adSetting.MaxInterVariable == null) return;
if (!adSetting.MaxInterVariable.IsReady()) adSetting.MaxInterVariable.Load();
}
public override void LoadRewarded()
{
if (adSetting.MaxRewardVariable == null) return;
if (!adSetting.MaxRewardVariable.IsReady()) adSetting.MaxRewardVariable.Load();
}
public override void LoadRewardedInterstitial()
{
}
public override void LoadAppOpen()
{
if (adSetting.MaxAppOpenVariable == null) return;
if (!adSetting.MaxAppOpenVariable.IsReady()) adSetting.MaxAppOpenVariable.Load();
}
public override void ShowAppOpen()
{
if (statusAppOpenFirstIgnore) adSetting.MaxAppOpenVariable.Show();
statusAppOpenFirstIgnore = true;
}
public override void LoadNativeOverlay()
{
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/Max/MaxAdClient.cs.meta
================================================
fileFormatVersion: 2
guid: ccaea541beb11497691c82e48d3e2bf7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/Max/MaxAdUnitVariable.cs
================================================
using System;
using UnityEngine;
namespace VirtueSky.Ads
{
public class MaxAdUnitVariable : AdUnitVariable
{
[SerializeField] protected string androidId;
[SerializeField] protected string iOSId;
[NonSerialized] private string idRuntime = string.Empty;
public override bool IsShowing { get; internal set; }
public override string Id
{
get
{
if (idRuntime == String.Empty)
{
#if UNITY_ANDROID
return androidId;
#elif UNITY_IOS
return iOSId;
#else
return string.Empty;
#endif
}
return idRuntime;
}
}
public override AdUnitVariable Show(string placement = null)
{
ResetChainCallback();
if (!Application.isMobilePlatform || string.IsNullOrEmpty(Id) || AdStatic.IsRemoveAd ||
!IsReady()) return this;
ShowImpl(placement);
return this;
}
public void SetIdRuntime(string unitId)
{
idRuntime = unitId;
}
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/Max/MaxAdUnitVariable.cs.meta
================================================
fileFormatVersion: 2
guid: 8d49a7d460324b64bf0e810452eb8db6
timeCreated: 1728632190
================================================
FILE: VirtueSky/Advertising/Runtime/Max/MaxUnitVariable/MaxAppOpenVariable.cs
================================================
using System;
using UnityEngine;
using VirtueSky.Inspector;
using VirtueSky.Misc;
using VirtueSky.Tracking;
namespace VirtueSky.Ads
{
[Serializable]
[EditorIcon("icon_scriptable")]
public class MaxAppOpenVariable : MaxAdUnitVariable
{
[Tooltip("Automatically show AppOpenAd when app status is changed")]
public bool autoShow = false;
[Tooltip("Time between closing the previous full-screen ad and starting to show the app open ad - in seconds")]
public float timeBetweenFullScreenAd = 2f;
public override void Init()
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
if (AdStatic.IsRemoveAd || string.IsNullOrEmpty(Id)) return;
paidedCallback += AppTracking.TrackRevenue;
MaxSdkCallbacks.AppOpen.OnAdDisplayedEvent += OnAdDisplayed;
MaxSdkCallbacks.AppOpen.OnAdHiddenEvent += OnAdHidden;
MaxSdkCallbacks.AppOpen.OnAdLoadedEvent += OnAdLoaded;
MaxSdkCallbacks.AppOpen.OnAdDisplayFailedEvent += OnAdDisplayFailed;
MaxSdkCallbacks.AppOpen.OnAdLoadFailedEvent += OnAdLoadFailed;
MaxSdkCallbacks.AppOpen.OnAdRevenuePaidEvent += OnAdRevenuePaid;
MaxSdkCallbacks.AppOpen.OnAdClickedEvent += OnAdClicked;
#endif
}
public override void Load()
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
if (AdStatic.IsRemoveAd || string.IsNullOrEmpty(Id)) return;
MaxSdk.LoadAppOpenAd(Id);
#endif
}
public override bool IsReady()
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
return !string.IsNullOrEmpty(Id) && MaxSdk.IsAppOpenAdReady(Id) &&
(DateTime.Now - AdStatic.AdClosingTime).TotalSeconds > timeBetweenFullScreenAd;
#else
return false;
#endif
}
protected override void ShowImpl(string placement = null)
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
MaxSdk.ShowAppOpenAd(Id, placement: placement);
#endif
}
public override void Destroy()
{
}
#region Func Callback
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
private void OnAdLoaded(string unit, MaxSdkBase.AdInfo info)
{
Common.CallActionAndClean(ref loadedCallback);
OnLoadAdEvent?.Invoke();
}
private void OnAdRevenuePaid(string unit, MaxSdkBase.AdInfo info)
{
paidedCallback?.Invoke(info.Revenue,
info.NetworkName,
unit,
info.AdFormat, AdMediation.AppLovin.ToString());
}
private void OnAdLoadFailed(string unit, MaxSdkBase.ErrorInfo info)
{
Common.CallActionAndClean(ref failedToLoadCallback);
OnFailedToLoadAdEvent?.Invoke(info.Message);
}
private void OnAdDisplayFailed(string unit, MaxSdkBase.ErrorInfo errorInfo,
MaxSdkBase.AdInfo info)
{
Common.CallActionAndClean(ref failedToDisplayCallback);
OnFailedToDisplayAdEvent?.Invoke(errorInfo.Message);
}
private void OnAdHidden(string unit, MaxSdkBase.AdInfo info)
{
AdStatic.waitAppOpenClosedAction?.Invoke();
AdStatic.IsShowingAd = false;
IsShowing = false;
Common.CallActionAndClean(ref closedCallback);
OnClosedAdEvent?.Invoke();
if (!string.IsNullOrEmpty(Id)) MaxSdk.LoadAppOpenAd(Id);
}
private void OnAdDisplayed(string unit, MaxSdkBase.AdInfo info)
{
AdStatic.waitAppOpenDisplayedAction?.Invoke();
AdStatic.IsShowingAd = true;
IsShowing = true;
Common.CallActionAndClean(ref displayedCallback);
OnDisplayedAdEvent?.Invoke();
}
private void OnAdClicked(string unit, MaxSdkBase.AdInfo info)
{
Common.CallActionAndClean(ref clickedCallback);
OnClickedAdEvent?.Invoke();
}
#endif
#endregion
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/Max/MaxUnitVariable/MaxAppOpenVariable.cs.meta
================================================
fileFormatVersion: 2
guid: 1e29fec6165f4c3baceac878abc88638
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/Max/MaxUnitVariable/MaxBannerVariable.cs
================================================
using System;
using UnityEngine;
using VirtueSky.Inspector;
using VirtueSky.Misc;
using VirtueSky.Tracking;
namespace VirtueSky.Ads
{
[Serializable]
[EditorIcon("icon_scriptable")]
public class MaxBannerVariable : MaxAdUnitVariable
{
public AdsSize size;
public AdsPosition position;
private bool _isBannerDestroyed = true;
private bool _isBannerShowing;
private bool _previousBannerShowStatus;
public override void Init()
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
if (AdStatic.IsRemoveAd || string.IsNullOrEmpty(Id)) return;
paidedCallback += AppTracking.TrackRevenue;
MaxSdkCallbacks.Banner.OnAdLoadedEvent += OnAdLoaded;
MaxSdkCallbacks.Banner.OnAdExpandedEvent += OnAdExpanded;
MaxSdkCallbacks.Banner.OnAdLoadFailedEvent += OnAdLoadFailed;
MaxSdkCallbacks.Banner.OnAdCollapsedEvent += OnAdCollapsed;
MaxSdkCallbacks.Banner.OnAdRevenuePaidEvent += OnAdRevenuePaid;
MaxSdkCallbacks.Banner.OnAdClickedEvent += OnAdClicked;
if (size != AdsSize.Adaptive)
{
MaxSdk.SetBannerExtraParameter(Id, "adaptive_banner", "false");
}
#endif
}
public override void Load()
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
if (AdStatic.IsRemoveAd || string.IsNullOrEmpty(Id)) return;
if (_isBannerDestroyed)
{
MaxSdk.CreateBanner(Id, ConvertPosition());
_isBannerDestroyed = false;
}
#endif
}
void OnWaitAppOpenClosed()
{
if (_previousBannerShowStatus)
{
_previousBannerShowStatus = false;
Show();
}
}
void OnWaitAppOpenDisplayed()
{
_previousBannerShowStatus = _isBannerShowing;
if (_isBannerShowing) HideBanner();
}
public override bool IsReady()
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
return !string.IsNullOrEmpty(Id);
#else
return false;
#endif
}
protected override void ShowImpl(string placement = null)
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
_isBannerShowing = true;
IsShowing = true;
AdStatic.waitAppOpenClosedAction = OnWaitAppOpenClosed;
AdStatic.waitAppOpenDisplayedAction = OnWaitAppOpenDisplayed;
Load();
MaxSdk.ShowBanner(Id);
#endif
}
public override void Destroy()
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
if (string.IsNullOrEmpty(Id)) return;
_isBannerShowing = false;
_isBannerDestroyed = true;
IsShowing = false;
AdStatic.waitAppOpenClosedAction = null;
AdStatic.waitAppOpenDisplayedAction = null;
MaxSdk.DestroyBanner(Id);
#endif
}
public override void HideBanner()
{
base.HideBanner();
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
_isBannerShowing = false;
IsShowing = false;
if (string.IsNullOrEmpty(Id)) return;
MaxSdk.HideBanner(Id);
#endif
}
#region Fun Callback
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
public MaxSdkBase.BannerPosition ConvertPosition()
{
switch (position)
{
case AdsPosition.Top: return MaxSdkBase.BannerPosition.TopCenter;
case AdsPosition.Bottom: return MaxSdkBase.BannerPosition.BottomCenter;
case AdsPosition.TopLeft: return MaxSdkBase.BannerPosition.TopLeft;
case AdsPosition.TopRight: return MaxSdkBase.BannerPosition.TopRight;
case AdsPosition.BottomLeft: return MaxSdkBase.BannerPosition.BottomLeft;
case AdsPosition.BottomRight: return MaxSdkBase.BannerPosition.BottomRight;
default:
return MaxSdkBase.BannerPosition.BottomCenter;
}
}
private void OnAdRevenuePaid(string unit, MaxSdkBase.AdInfo info)
{
paidedCallback?.Invoke(info.Revenue,
info.NetworkName,
unit,
info.AdFormat, AdMediation.AppLovin.ToString());
}
private void OnAdLoaded(string unit, MaxSdkBase.AdInfo info)
{
Common.CallActionAndClean(ref loadedCallback);
OnLoadAdEvent?.Invoke();
}
private void OnAdExpanded(string unit, MaxSdkBase.AdInfo info)
{
Common.CallActionAndClean(ref displayedCallback);
OnDisplayedAdEvent?.Invoke();
}
private void OnAdLoadFailed(string unit, MaxSdkBase.ErrorInfo info)
{
Common.CallActionAndClean(ref failedToLoadCallback);
OnFailedToLoadAdEvent?.Invoke(info.Message);
Destroy();
}
private void OnAdCollapsed(string unit, MaxSdkBase.AdInfo info)
{
Common.CallActionAndClean(ref closedCallback);
OnClosedAdEvent?.Invoke();
}
private void OnAdClicked(string arg1, MaxSdkBase.AdInfo arg2)
{
Common.CallActionAndClean(ref clickedCallback);
OnClickedAdEvent?.Invoke();
}
#endif
#endregion
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/Max/MaxUnitVariable/MaxBannerVariable.cs.meta
================================================
fileFormatVersion: 2
guid: 2d54005e18d14434a4feed9988bfd43c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/Max/MaxUnitVariable/MaxInterVariable.cs
================================================
using System;
using UnityEngine;
using VirtueSky.Ads;
using VirtueSky.Inspector;
using VirtueSky.Misc;
using VirtueSky.Tracking;
namespace VirtueSky.Ads
{
[Serializable]
[EditorIcon("icon_scriptable")]
public class MaxInterVariable : MaxAdUnitVariable
{
[NonSerialized] internal Action completedCallback;
public override void Init()
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
if (AdStatic.IsRemoveAd || string.IsNullOrEmpty(Id)) return;
paidedCallback += AppTracking.TrackRevenue;
MaxSdkCallbacks.Interstitial.OnAdLoadedEvent += OnAdLoaded;
MaxSdkCallbacks.Interstitial.OnAdLoadFailedEvent += OnAdLoadFailed;
MaxSdkCallbacks.Interstitial.OnAdRevenuePaidEvent += OnAdRevenuePaid;
MaxSdkCallbacks.Interstitial.OnAdDisplayedEvent += OnAdDisplayed;
MaxSdkCallbacks.Interstitial.OnAdHiddenEvent += OnAdHidden;
MaxSdkCallbacks.Interstitial.OnAdDisplayFailedEvent += OnAdDisplayFailed;
MaxSdkCallbacks.Interstitial.OnAdClickedEvent += OnAdClicked;
#endif
}
public override void Load()
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
if (AdStatic.IsRemoveAd || string.IsNullOrEmpty(Id)) return;
MaxSdk.LoadInterstitial(Id);
#endif
}
public override bool IsReady()
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
return !string.IsNullOrEmpty(Id) && MaxSdk.IsInterstitialReady(Id);
#else
return false;
#endif
}
protected override void ShowImpl(string placement = null)
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
MaxSdk.ShowInterstitial(Id, placement: placement);
#endif
}
public override void Destroy()
{
}
protected override void ResetChainCallback()
{
base.ResetChainCallback();
completedCallback = null;
}
#region Func Callback
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
private void OnAdDisplayFailed(string unit, MaxSdkBase.ErrorInfo error,
MaxSdkBase.AdInfo info)
{
Common.CallActionAndClean(ref failedToDisplayCallback);
OnFailedToDisplayAdEvent?.Invoke(error.Message);
}
private void OnAdHidden(string unit, MaxSdkBase.AdInfo info)
{
AdStatic.IsShowingAd = false;
IsShowing = false;
Common.CallActionAndClean(ref completedCallback);
OnClosedAdEvent?.Invoke();
if (!string.IsNullOrEmpty(Id)) MaxSdk.LoadInterstitial(Id);
}
private void OnAdDisplayed(string unit, MaxSdkBase.AdInfo info)
{
AdStatic.IsShowingAd = true;
IsShowing = true;
Common.CallActionAndClean(ref displayedCallback);
OnDisplayedAdEvent?.Invoke();
}
private void OnAdRevenuePaid(string unit, MaxSdkBase.AdInfo info)
{
paidedCallback?.Invoke(info.Revenue,
info.NetworkName,
unit,
info.AdFormat, AdMediation.AppLovin.ToString());
}
private void OnAdLoadFailed(string unit, MaxSdkBase.ErrorInfo info)
{
Common.CallActionAndClean(ref failedToLoadCallback);
OnFailedToLoadAdEvent?.Invoke(info.Message);
}
private void OnAdLoaded(string unit, MaxSdkBase.AdInfo info)
{
Common.CallActionAndClean(ref loadedCallback);
OnLoadAdEvent?.Invoke();
}
private void OnAdClicked(string unit, MaxSdkBase.AdInfo info)
{
Common.CallActionAndClean(ref clickedCallback);
OnClickedAdEvent?.Invoke();
}
#endif
#endregion
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/Max/MaxUnitVariable/MaxInterVariable.cs.meta
================================================
fileFormatVersion: 2
guid: 7f2517077a9341d38d7486727d83a132
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/Max/MaxUnitVariable/MaxRewardVariable.cs
================================================
using System;
using UnityEngine;
using VirtueSky.Inspector;
using VirtueSky.Misc;
using VirtueSky.Tracking;
namespace VirtueSky.Ads
{
[Serializable]
[EditorIcon("icon_scriptable")]
public class MaxRewardVariable : MaxAdUnitVariable
{
[NonSerialized] internal Action completedCallback;
[NonSerialized] internal Action skippedCallback;
public bool IsEarnRewarded { get; private set; }
public override void Init()
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
if (string.IsNullOrEmpty(Id)) return;
paidedCallback += AppTracking.TrackRevenue;
MaxSdkCallbacks.Rewarded.OnAdDisplayedEvent += OnAdDisplayed;
MaxSdkCallbacks.Rewarded.OnAdHiddenEvent += OnAdHidden;
MaxSdkCallbacks.Rewarded.OnAdLoadedEvent += OnAdLoaded;
MaxSdkCallbacks.Rewarded.OnAdDisplayFailedEvent += OnAdDisplayFailed;
MaxSdkCallbacks.Rewarded.OnAdLoadFailedEvent += OnAdLoadFailed;
MaxSdkCallbacks.Rewarded.OnAdRevenuePaidEvent += OnAdRevenuePaid;
MaxSdkCallbacks.Rewarded.OnAdReceivedRewardEvent += OnAdReceivedReward;
MaxSdkCallbacks.Rewarded.OnAdClickedEvent += OnAdClicked;
#endif
}
public override void Load()
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
if (string.IsNullOrEmpty(Id)) return;
MaxSdk.LoadRewardedAd(Id);
#endif
}
public override bool IsReady()
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
return !string.IsNullOrEmpty(Id) && MaxSdk.IsRewardedAdReady(Id);
#else
return false;
#endif
}
protected override void ShowImpl(string placement = null)
{
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
MaxSdk.ShowRewardedAd(Id, placement: placement);
#endif
}
public override AdUnitVariable Show(string placement = null)
{
ResetChainCallback();
if (!UnityEngine.Application.isMobilePlatform || !IsReady()) return this;
ShowImpl(placement);
return this;
}
public override void Destroy()
{
}
protected override void ResetChainCallback()
{
base.ResetChainCallback();
completedCallback = null;
skippedCallback = null;
}
#region Func Callback
#if VIRTUESKY_ADS && VIRTUESKY_APPLOVIN
private void OnAdReceivedReward(string unit, MaxSdkBase.Reward reward,
MaxSdkBase.AdInfo info)
{
IsEarnRewarded = true;
}
private void OnAdRevenuePaid(string unit, MaxSdkBase.AdInfo info)
{
paidedCallback?.Invoke(info.Revenue,
info.NetworkName,
unit,
info.AdFormat, AdMediation.AppLovin.ToString());
}
private void OnAdLoadFailed(string unit, MaxSdkBase.ErrorInfo info)
{
Common.CallActionAndClean(ref failedToLoadCallback);
OnFailedToLoadAdEvent?.Invoke(info.Message);
}
private void OnAdDisplayFailed(string unit, MaxSdkBase.ErrorInfo errorInfo,
MaxSdkBase.AdInfo info)
{
Common.CallActionAndClean(ref failedToDisplayCallback);
OnFailedToDisplayAdEvent?.Invoke(errorInfo.Message);
}
private void OnAdLoaded(string unit, MaxSdkBase.AdInfo info)
{
Common.CallActionAndClean(ref loadedCallback);
OnLoadAdEvent?.Invoke();
}
private void OnAdHidden(string unit, MaxSdkBase.AdInfo info)
{
AdStatic.IsShowingAd = false;
IsShowing = false;
Common.CallActionAndClean(ref closedCallback);
OnClosedAdEvent?.Invoke();
if (!IsReady()) MaxSdk.LoadRewardedAd(Id);
if (IsEarnRewarded)
{
Common.CallActionAndClean(ref completedCallback);
IsEarnRewarded = false;
return;
}
Common.CallActionAndClean(ref skippedCallback);
}
private void OnAdDisplayed(string unit, MaxSdkBase.AdInfo info)
{
AdStatic.IsShowingAd = true;
IsShowing = true;
Common.CallActionAndClean(ref displayedCallback);
OnDisplayedAdEvent?.Invoke();
}
private void OnAdClicked(string unit, MaxSdkBase.AdInfo info)
{
Common.CallActionAndClean(ref clickedCallback);
OnClickedAdEvent?.Invoke();
}
#endif
#endregion
}
}
================================================
FILE: VirtueSky/Advertising/Runtime/Max/MaxUnitVariable/MaxRewardVariable.cs.meta
================================================
fileFormatVersion: 2
guid: 20d8d1652da34664a50db28dbc997814
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: ed5ec0bb4ec55df47af0ecc2bd7be5f9, type: 3}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/Max/MaxUnitVariable.meta
================================================
fileFormatVersion: 2
guid: 74b6a60977c643ee956805295399665c
timeCreated: 1695462087
================================================
FILE: VirtueSky/Advertising/Runtime/Max.meta
================================================
fileFormatVersion: 2
guid: f30fd55eb75a48b4691e1a17d8ad50c2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime/Virtuesky.Sunflower.Advertising.asmdef
================================================
{
"name": "Virtuesky.Sunflower.Advertising",
"rootNamespace": "",
"references": [
"GUID:fca7ec166e04dc948b624a983315e2c9",
"GUID:acb3cac55c622ec459c8caadf707623a",
"GUID:32dbaa332e571bf429b7de517f75f074",
"GUID:bd40169efe8642149b1d2b72ba4903ce",
"GUID:c904f6d969e991d459a0843b71c22ec5",
"GUID:324caed91501a9c47a04ebfd87b68794",
"GUID:a4cfc1a18fa3a469b96d885db522f42e",
"GUID:35d694408290717499b3838802212c7f",
"GUID:5e9107a8f2499184ea26564811dda246",
"GUID:760a4c7888534400e882b82c5b3fba06",
"GUID:00c479e63b1c74419820a39073267645",
"GUID:c282fd4f3fc2c7540914e85842a013c7"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
================================================
FILE: VirtueSky/Advertising/Runtime/Virtuesky.Sunflower.Advertising.asmdef.meta
================================================
fileFormatVersion: 2
guid: abd57f653a468a04c8d4e281527ff293
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising/Runtime.meta
================================================
fileFormatVersion: 2
guid: f71c7d92eeff8c34492800ba35756ee8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/Advertising.meta
================================================
fileFormatVersion: 2
guid: 4a6fce59e6956244e8e4045dcfa6387c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/AssetFinder.cs
================================================
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace VirtueSky.AssetFinder.Editor
{
public enum Dependency
{
All,
Direct,
Indirect
}
public enum DepthFilter
{
All,
Equal,
NotEqual,
Less,
LessEqual,
Greater,
GreaterEqual
}
public enum Sorting
{
None,
Type,
Path,
Size
}
[Serializable]
public class AssetFinderInfo
{
public string guid;
public string assetPath;
public string fileName;
public string extension;
public System.Type assetType;
public int usageCount;
public int usedByCount;
public bool isInBuild;
public long fileSize;
public bool isFolder;
public bool isBuiltin;
public AssetFinderInfo(string guid)
{
this.guid = guid;
RefreshInfo();
}
internal void RefreshInfo()
{
if (string.IsNullOrEmpty(guid)) return;
assetPath = AssetDatabase.GUIDToAssetPath(guid);
if (string.IsNullOrEmpty(assetPath))
{
fileName = "Missing";
extension = "";
assetType = null;
fileSize = 0;
isFolder = false;
isBuiltin = false;
return;
}
fileName = Path.GetFileName(assetPath);
extension = Path.GetExtension(assetPath);
// Check if it's a folder
isFolder = AssetDatabase.IsValidFolder(assetPath);
// Check if it's builtin using the proper constant
isBuiltin = AssetFinderAsset.BUILT_IN_ASSETS.Contains(guid);
if (!isFolder)
{
UnityObject obj = AssetDatabase.LoadAssetAtPath(assetPath);
assetType = obj?.GetType();
// Get file size
try
{
if (File.Exists(assetPath))
{
var fileInfo = new FileInfo(assetPath);
fileSize = fileInfo.Length;
}
}
catch
{
fileSize = 0;
}
}
else
{
assetType = typeof(DefaultAsset);
fileSize = 0;
}
// Get usage counts from AssetFinderAsset if available
if (AssetFinderCache.isReady)
{
AssetFinderAsset asset = AssetFinderCache.Api.Get(guid);
if (asset != null)
{
usageCount = asset.UseGUIDsCount;
usedByCount = asset.UsedByMap.Count;
}
}
}
internal void UpdateBuildStatus(HashSet buildGuids)
{
isInBuild = buildGuids != null && buildGuids.Contains(guid);
}
}
public static class AssetFinder
{
public static bool IsReady => AssetFinderCache.isReady;
public static void ScanProject()
{
AssetFinderCache.DeleteCache();
AssetFinderCache.CreateCache();
}
public static void Refresh()
{
if (!AssetFinderCache.hasCache)
{
AssetFinderLOG.LogWarning("AssetFinder cache not found. Use AssetFinder.ScanProject() first.");
return;
}
AssetFinderCache.Api.Check4Changes(true);
}
public static List GetUses(string[] guids, Dependency dep = Dependency.All, int depth = 0, DepthFilter filter = DepthFilter.All, Sorting sort = Sorting.None)
{
if (!IsReady)
{
AssetFinderLOG.LogWarning("AssetFinder cache not ready. Use AssetFinder.ScanProject() first.");
return new List();
}
if (guids == null || guids.Length == 0) return new List();
Dictionary refs = AssetFinderRef.FindUsage(guids);
return ProcessResults(refs, dep, depth, filter, sort);
}
public static List GetUsedBy(string[] guids, Dependency dep = Dependency.All, int depth = 0, DepthFilter filter = DepthFilter.All, Sorting sort = Sorting.None)
{
if (!IsReady)
{
AssetFinderLOG.LogWarning("AssetFinder cache not ready. Use AssetFinder.ScanProject() first.");
return new List();
}
if (guids == null || guids.Length == 0) return new List();
Dictionary refs = AssetFinderRef.FindUsedBy(guids);
return ProcessResults(refs, dep, depth, filter, sort);
}
public static List GetUnused(Sorting sort = Sorting.None)
{
if (!IsReady)
{
AssetFinderLOG.LogWarning("AssetFinder cache not ready. Use AssetFinder.ScanProject() first.");
return new List();
}
List unusedAssets = AssetFinderCache.Api.ScanUnused(true);
return ProcessAssetResults(unusedAssets, sort);
}
public static List GetInBuild(Sorting sort = Sorting.None)
{
if (!IsReady)
{
AssetFinderLOG.LogWarning("AssetFinder cache not ready. Use AssetFinder.ScanProject() first.");
return new List();
}
var usedInBuild = new AssetFinderUsedInBuild(null, () => ConvertSorting(sort), () => AssetFinderRefDrawer.Mode.Type);
usedInBuild.RefreshView();
if (usedInBuild.refs == null) return new List();
var buildGuids = new HashSet(usedInBuild.refs.Keys);
var assets = usedInBuild.refs.Values.Select(r => r.asset).ToList();
var results = ProcessAssetResults(assets, sort);
// Set isInBuild flag for all returned assets
foreach (var assetInfo in results)
{
assetInfo.UpdateBuildStatus(buildGuids);
}
return results;
}
public static Dictionary GetUsesCount(string[] guids)
{
var result = new Dictionary();
if (!IsReady)
{
AssetFinderLOG.LogWarning("AssetFinder cache not ready. Use AssetFinder.ScanProject() first.");
return result;
}
if (guids == null || guids.Length == 0) return result;
foreach (string guid in guids)
{
if (string.IsNullOrEmpty(guid)) continue;
AssetFinderAsset asset = AssetFinderCache.Api.Get(guid);
result[guid] = asset?.UseGUIDsCount ?? 0;
}
return result;
}
public static Dictionary GetUsedByCount(string[] guids)
{
var result = new Dictionary();
if (!IsReady)
{
AssetFinderLOG.LogWarning("AssetFinder cache not ready. Use AssetFinder.ScanProject() first.");
return result;
}
if (guids == null || guids.Length == 0) return result;
foreach (string guid in guids)
{
if (string.IsNullOrEmpty(guid)) continue;
AssetFinderAsset asset = AssetFinderCache.Api.Get(guid);
result[guid] = asset?.UsedByMap.Count ?? 0;
}
return result;
}
public static bool IsUses(string[] guids)
{
if (!IsReady || guids == null || guids.Length == 0) return false;
foreach (string guid in guids)
{
if (string.IsNullOrEmpty(guid)) continue;
AssetFinderAsset asset = AssetFinderCache.Api.Get(guid);
if (asset != null && asset.UseGUIDsCount > 0) return true;
}
return false;
}
public static bool IsUsedBy(string[] guids)
{
if (!IsReady || guids == null || guids.Length == 0) return false;
foreach (string guid in guids)
{
if (string.IsNullOrEmpty(guid)) continue;
AssetFinderAsset asset = AssetFinderCache.Api.Get(guid);
if (asset != null && asset.UsedByMap.Count > 0) return true;
}
return false;
}
public static bool IsInBuild(string[] guids)
{
if (!IsReady)
{
AssetFinderLOG.LogWarning("AssetFinder cache not ready. Use AssetFinder.ScanProject() first.");
return false;
}
if (guids == null || guids.Length == 0) return false;
var usedInBuild = new AssetFinderUsedInBuild(null, () => AssetFinderRefDrawer.Sort.Type, () => AssetFinderRefDrawer.Mode.Type);
usedInBuild.RefreshView();
if (usedInBuild.refs == null) return false;
var buildGuids = new HashSet(usedInBuild.refs.Keys);
foreach (string guid in guids)
{
if (buildGuids.Contains(guid)) return true;
}
return false;
}
private static List ProcessAssetResults(List assets, Sorting sorting)
{
if (assets == null) return new List();
var results = new List();
foreach (AssetFinderAsset asset in assets)
{
if (asset == null) continue;
var assetInfo = new AssetFinderInfo(asset.guid);
results.Add(assetInfo);
}
return ApplySorting(results, sorting);
}
private static List ProcessResults(Dictionary refs, Dependency dependency, int depth, DepthFilter filter, Sorting sorting)
{
if (refs == null) return new List();
var results = new List();
var processedGuids = new HashSet();
foreach (KeyValuePair kvp in refs)
{
AssetFinderRef refItem = kvp.Value;
// Apply dependency filter
if (dependency == Dependency.Direct && refItem.depth > 1) continue;
if (dependency == Dependency.Indirect && refItem.depth <= 1) continue;
// Apply depth filter with mathematically correct comparisons
if (!MatchesDepthFilter(refItem.depth, depth, filter)) continue;
if (processedGuids.Contains(refItem.asset.guid)) continue;
processedGuids.Add(refItem.asset.guid);
var assetInfo = new AssetFinderInfo(refItem.asset.guid);
results.Add(assetInfo);
}
return ApplySorting(results, sorting);
}
private static bool MatchesDepthFilter(int itemDepth, int targetDepth, DepthFilter filter)
{
switch (filter)
{
case DepthFilter.All:
return true;
case DepthFilter.Equal:
return itemDepth == targetDepth;
case DepthFilter.NotEqual:
return itemDepth != targetDepth;
case DepthFilter.Less:
return itemDepth < targetDepth;
case DepthFilter.LessEqual:
return itemDepth <= targetDepth;
case DepthFilter.Greater:
return itemDepth > targetDepth;
case DepthFilter.GreaterEqual:
return itemDepth >= targetDepth;
default:
return true;
}
}
private static List ApplySorting(List assetInfos, Sorting sorting)
{
switch (sorting)
{
case Sorting.Type:
return assetInfos.OrderBy(info => info.assetType?.Name ?? "")
.ThenBy(info => info.assetPath)
.ToList();
case Sorting.Path:
return assetInfos.OrderBy(info => info.assetPath)
.ThenBy(info => info.assetType?.Name ?? "")
.ToList();
case Sorting.Size:
return assetInfos.OrderByDescending(info => info.fileSize)
.ThenBy(info => info.assetPath)
.ToList();
default:
return assetInfos;
}
}
private static AssetFinderRefDrawer.Sort ConvertSorting(Sorting sorting)
{
switch (sorting)
{
case Sorting.Type: return AssetFinderRefDrawer.Sort.Type;
case Sorting.Path: return AssetFinderRefDrawer.Sort.Path;
case Sorting.Size: return AssetFinderRefDrawer.Sort.Size;
default: return AssetFinderRefDrawer.Sort.Type;
}
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/AssetFinder.cs.meta
================================================
fileFormatVersion: 2
guid: 336a181c1abdb455f8388370e58f0496
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/AssetFinderAssetType.cs
================================================
// deleted
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/AssetFinderAssetType.cs.meta
================================================
fileFormatVersion: 2
guid: b077597d32a3b4e43bd7bfc007c3e6dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/AssetFinderCacheEditor.cs
================================================
// deleted
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/AssetFinderCacheEditor.cs.meta
================================================
fileFormatVersion: 2
guid: 18708e9bf7db0f04fa917a0db924382c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/AssetFinderDuplicate.cs
================================================
// deleted
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/AssetFinderDuplicate.cs.meta
================================================
fileFormatVersion: 2
guid: 57aae5701da3c644c937c59e2da6da85
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/AssetFinderRef.cs
================================================
// deleted
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/AssetFinderRef.cs.meta
================================================
fileFormatVersion: 2
guid: 7ef09719313b4f943a49eb3a2b0f934d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/AssetFinderWindowExtension.cs
================================================
namespace VirtueSky.AssetFinder.Editor {
public class AssetFinderWindowExtension {
public static void ShowWindow() {
AssetFinderWindowAll.ShowWindow();
}
public static void DeleteCache() {
AssetFinderCache.DeleteCache();
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/AssetFinderWindowExtension.cs.meta
================================================
fileFormatVersion: 2
guid: 10b0c9ac533b455baa0c863c779748bd
timeCreated: 1764820294
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.AssetState.cs
================================================
namespace VirtueSky.AssetFinder.Editor
{
partial class AssetFinderAsset
{
public enum AssetState
{
NEW,
CACHE,
MISSING
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.AssetState.cs.meta
================================================
fileFormatVersion: 2
guid: cd01dcf20ca0ec442af562e1bb400560
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.AssetType.cs
================================================
namespace VirtueSky.AssetFinder.Editor
{
partial class AssetFinderAsset
{
public enum AssetType
{
UNKNOWN,
FOLDER,
SCRIPT,
SCENE,
DLL,
REFERENCABLE,
BINARY_ASSET,
MODEL,
TERRAIN,
LIGHTING_DATA,
NON_READABLE,
BUILT_IN
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.AssetType.cs.meta
================================================
fileFormatVersion: 2
guid: 0d8fbadc1183faf4fa0008a3c8ef122e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.Classes.cs
================================================
using System;
using System.Collections.Generic;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderAsset
{
[Serializable]
internal class Classes
{
public string guid;
public List ids;
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.Classes.cs.meta
================================================
fileFormatVersion: 2
guid: 5474ab83390e2904b92f5b45bd2fe0dc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.Constants.cs
================================================
using System;
using System.Collections.Generic;
using UnityEngine;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderAsset
{
// ------------------------------ CONSTANTS ---------------------------
public const int MIN_FILE_SIZE_2LOG = 1024 * 1024; // 1 MB
internal static bool shouldWriteImportLog;
private static readonly HashSet SCRIPT_EXTENSIONS = new HashSet
{
".cs", ".js", ".boo", ".h", ".java", ".cpp", ".m", ".mm", ".cginclude", ".shadersubgraph"
};
private static readonly HashSet REFERENCABLE_EXTENSIONS = new HashSet
{
".anim", ".controller", ".mat", ".unity", ".guiskin", ".prefab",
".overridecontroller", ".mask", ".rendertexture", ".cubemap", ".flare", ".playable",
".mat", ".physicsmaterial", ".fontsettings", ".asset", ".prefs", ".spriteatlas",
".terrainlayer", ".asmdef", ".preset", ".spriteLib", ".shader", ".hlsl", ".cginc", ".glsl"
};
private static readonly HashSet REFERENCABLE_JSON = new HashSet
{
".shadergraph", ".shadersubgraph"
};
private static readonly HashSet UI_TOOLKIT = new HashSet
{
".uss", ".uxml", ".tss"
};
private static readonly HashSet REFERENCABLE_META = new HashSet
{
".texture2darray",
".png", ".jpg", ".jpeg", ".tga", ".tif", ".tiff", ".psd", ".bmp", ".exr", ".gif",
};
///
/// Extensions that rarely contain references to other assets and can be
/// ignored when deciding if an asset is "critical".
///
private static readonly HashSet NON_REFERENCE_EXTENSIONS =
new HashSet(StringComparer.OrdinalIgnoreCase)
{
".wav", ".mp3", ".ogg", ".aif", ".aiff",
".txt", ".json", ".xml", ".csv",
".html", ".htm", ".yaml", ".md"
};
internal static readonly HashSet BUILT_IN_ASSETS = new HashSet
{
"0000000000000000f000000000000000",
"0000000000000000e000000000000000",
"0000000000000000d000000000000000"
};
private static readonly Dictionary HashClasses = new Dictionary();
internal static Dictionary cacheImage = new Dictionary();
public static float ignoreTS;
private static string _logPath;
private static int binaryLoaded;
private static DateTime scanStartTime;
private static string logPath
{
get
{
if (!string.IsNullOrEmpty(_logPath)) return _logPath;
_logPath = System.IO.Path.Combine(Application.dataPath, "../fr2-import.log");
return _logPath;
}
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.Constants.cs.meta
================================================
fileFormatVersion: 2
guid: 9e3eb5ee34a8a3a41b281a3f9d1652e8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.ContentLoader.cs
================================================
using System;
using System.IO;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderAsset
{
// ----------------------------- CONTENT LOADING ---------------------------------------
internal void LoadContent()
{
if (!fileContentDirty) return;
m_cachefileWriteTS = m_fileWriteTS;
m_forceIncludeInBuild = false;
if (IsMissing || type == AssetType.NON_READABLE) return;
if (type == AssetType.DLL)
{
AssetFinderLOG.LogWarning("Parsing DLL not yet supportted ");
return;
}
AssetFinderLOG.Log("LoadContent ... infoHash=" + fileInfoHash + ": assetPath=" + m_assetPath);
var startTime = DateTime.Now;
if (shouldWriteImportLog && (fileSize >= MIN_FILE_SIZE_2LOG))
{
var logMessage = $"{startTime:yyyy-MM-dd HH:mm:ss} - {assetPath}, Size: {fileSize} bytes";
File.AppendAllText(logPath, logMessage + Environment.NewLine);
}
if (!ExistOnDisk())
{
state = AssetState.MISSING;
return;
}
ClearUseGUIDs();
if (IsFolder)
{
LoadFolder();
} else if (IsReferencable)
{
LoadYAML2();
} else if (IsBinaryAsset)
{
LoadBinaryAsset();
}
if (shouldWriteImportLog && (fileSize >= MIN_FILE_SIZE_2LOG))
{
DateTime endTime = DateTime.Now;
double duration = (endTime - startTime).TotalMilliseconds;
var logMessage = $", Duration: {duration} ms";
File.AppendAllText(logPath, logMessage + Environment.NewLine);
}
}
internal void LoadContentFast()
{
if (!fileContentDirty) return;
m_cachefileWriteTS = m_fileWriteTS;
m_forceIncludeInBuild = false;
if (IsMissing) return;
if (type == AssetType.SCRIPT || type == AssetType.DLL || type == AssetType.FOLDER) return;
// if (assetPath.StartsWith("Packages/")) return;
DateTime startTime = DateTime.Now;
if (shouldWriteImportLog && (fileSize >= MIN_FILE_SIZE_2LOG))
{
var logMessage = $"{startTime:yyyy-MM-dd HH:mm:ss} - {assetPath}, Size: {fileSize} bytes";
File.AppendAllText(logPath, logMessage);
}
ClearUseGUIDs();
if (fileSize > 5 * 1024 * 1024 || type == AssetType.NON_READABLE || IsBinaryAsset)
{
string[] dependencies = AssetDatabase.GetDependencies(assetPath, false);
foreach (string dependency in dependencies)
{
string guid = AssetDatabase.AssetPathToGUID(dependency);
if (!string.IsNullOrEmpty(guid) && (guid != this.guid))
{
AddUseGUID(guid);
}
}
} else if (IsReferencable)
{
LoadYAML2();
}
// CRITICAL FIX: Validate with AssetDatabase to catch missing references
using (AssetFinderDev.NoLog)
{
ValidateWithAssetDatabase();
}
if (shouldWriteImportLog && (fileSize >= MIN_FILE_SIZE_2LOG))
{
DateTime endTime = DateTime.Now;
double duration = (endTime - startTime).TotalMilliseconds;
var logMessage = $", Duration: {duration} ms";
File.AppendAllText(logPath, logMessage + Environment.NewLine);
}
}
internal void ValidateWithAssetDatabase()
{
if (IsMissing || IsFolder || type == AssetType.SCRIPT) return;
try
{
// Get Unity's known dependencies
string[] unityDeps = AssetDatabase.GetDependencies(assetPath, false);
if (unityDeps == null) return;
var unityGuids = new HashSet();
foreach (string depPath in unityDeps)
{
if (depPath == assetPath) continue; // Skip self-reference
string depGuid = AssetDatabase.AssetPathToGUID(depPath);
if (!string.IsNullOrEmpty(depGuid))
unityGuids.Add(depGuid);
}
// Compare with FR2's parsed results
var fr2Guids = new HashSet(UseGUIDs.Keys);
var missingInFR2 = new List();
// Find guids in Unity but not in FR2
foreach (string unityGuid in unityGuids)
{
if (!fr2Guids.Contains(unityGuid))
missingInFR2.Add(unityGuid);
}
if (missingInFR2.Count > 0 && extension != ".shadergraph")
{
AssetFinderLOG.LogWarning($"AssetDatabase has {missingInFR2.Count} more dependencies than FR2 for '{assetPath}'. " +
"This may indicate unsaved changes or new asset types not handled by FR2 parser.");
// Add missing references to FR2's cache
foreach (string missingGuid in missingInFR2)
{
AddUseGUID(missingGuid);
AssetFinderLOG.Log($"Add missingGUID: {missingGuid} --> {AssetDatabase.GUIDToAssetPath(missingGuid)}");
}
}
}
catch
{
AssetFinderLOG.LogWarning($"Failed to validate dependencies for {assetPath}");
}
}
internal void LoadYAML2()
{
if (!m_pathLoaded) LoadPathInfo();
if (!File.Exists(m_assetPath))
{
state = AssetState.MISSING;
return;
}
if (m_assetPath == "ProjectSettings/EditorBuildSettings.asset")
{
EditorBuildSettingsScene[] listScenes = EditorBuildSettings.scenes;
foreach (EditorBuildSettingsScene scene in listScenes)
{
if (!scene.enabled) continue;
string path = scene.path;
string guid = AssetDatabase.AssetPathToGUID(path);
AddUseGUID(guid, 0);
// AssetFinderLOG.Log("AddScene: " + path);
}
}
if (string.IsNullOrEmpty(extension))
{
AssetFinderLOG.LogWarning($"Something wrong? <{m_extension}>");
}
if (extension == ".spriteatlas") // check for force include in build
{
var atlasAsset = AssetDatabase.LoadAssetAtPath(m_assetPath);
if (atlasAsset != null)
{
var so = new SerializedObject(atlasAsset);
SerializedProperty prop = so.FindProperty("m_EditorData.bindAsDefault");
m_forceIncludeInBuild = prop.boolValue;
}
}
AssetFinderParser.ReadContent(m_assetPath, AddUseGUID);
}
internal void LoadFolder()
{
if (!Directory.Exists(m_assetPath))
{
state = AssetState.MISSING;
return;
}
// do not analyse folders outside project
if (!m_assetPath.StartsWith("Assets/")) return;
try
{
string[] files = Directory.GetFiles(m_assetPath);
string[] dirs = Directory.GetDirectories(m_assetPath);
foreach (string f in files)
{
if (f.EndsWith(".meta", StringComparison.Ordinal)) continue;
string fguid = AssetDatabase.AssetPathToGUID(f);
if (string.IsNullOrEmpty(fguid)) continue;
AddUseGUID(fguid);
}
foreach (string d in dirs)
{
string fguid = AssetDatabase.AssetPathToGUID(d);
if (string.IsNullOrEmpty(fguid)) continue;
AddUseGUID(fguid);
}
}
catch (Exception e)
{
AssetFinderLOG.LogWarning("LoadFolder() error :: " + e + "\n" + assetPath);
}
finally
{
state = AssetState.MISSING;
}
}
internal void LoadBinaryAsset()
{
ClearUseGUIDs();
UnityObject assetData = AssetDatabase.LoadAssetAtPath(m_assetPath, typeof(UnityObject));
if (assetData is GameObject go)
{
type = AssetType.MODEL;
LoadGameObject(go);
binaryLoaded += 10;
} else if (assetData is TerrainData terrainData)
{
type = AssetType.TERRAIN;
LoadTerrainData(terrainData);
binaryLoaded += 20;
} else if (assetData is LightingDataAsset lightAsset)
{
type = AssetType.LIGHTING_DATA;
LoadLightingData(lightAsset);
binaryLoaded += 20;
} else
{
LoadSerialized(assetData);
binaryLoaded++;
}
AssetFinderLOG.Log("LoadBinaryAsset :: " + assetData + ":" + type);
if (binaryLoaded <= 30) return;
binaryLoaded = 0;
AssetFinderUnity.UnloadUnusedAssets();
}
internal void LoadGameObject(GameObject go)
{
Component[] compList = go.GetComponentsInChildren();
for (var i = 0; i < compList.Length; i++)
{
LoadSerialized(compList[i]);
}
}
internal void LoadSerialized(UnityObject target)
{
SerializedProperty[] props = AssetFinderUnity.xGetSerializedProperties(target, true);
for (var i = 0; i < props.Length; i++)
{
if (props[i].propertyType != SerializedPropertyType.ObjectReference) continue;
UnityObject refObj = props[i].objectReferenceValue;
if (refObj == null) continue;
string refGUID = AssetDatabase.AssetPathToGUID(
AssetDatabase.GetAssetPath(refObj)
);
AddUseGUID(refGUID);
}
}
private void AddTextureGUID(SerializedProperty prop)
{
if (prop == null || prop.objectReferenceValue == null) return;
string path = AssetDatabase.GetAssetPath(prop.objectReferenceValue);
if (string.IsNullOrEmpty(path)) return;
AddUseGUID(AssetDatabase.AssetPathToGUID(path));
}
internal void LoadLightingData(LightingDataAsset asset)
{
foreach (Texture texture in AssetFinderLightmap.Read(asset))
{
if (texture == null) continue;
string path = AssetDatabase.GetAssetPath(texture);
string assetGUID = AssetDatabase.AssetPathToGUID(path);
if (!string.IsNullOrEmpty(assetGUID))
{
AddUseGUID(assetGUID);
}
}
}
internal void LoadTerrainData(TerrainData terrain)
{
#if UNITY_2018_3_OR_NEWER
TerrainLayer[] arr0 = terrain.terrainLayers;
for (var i = 0; i < arr0.Length; i++)
{
string aPath = AssetDatabase.GetAssetPath(arr0[i]);
string refGUID = AssetDatabase.AssetPathToGUID(aPath);
AddUseGUID(refGUID);
}
#endif
DetailPrototype[] arr = terrain.detailPrototypes;
for (var i = 0; i < arr.Length; i++)
{
string aPath = AssetDatabase.GetAssetPath(arr[i].prototypeTexture);
string refGUID = AssetDatabase.AssetPathToGUID(aPath);
AddUseGUID(refGUID);
}
TreePrototype[] arr2 = terrain.treePrototypes;
for (var i = 0; i < arr2.Length; i++)
{
string aPath = AssetDatabase.GetAssetPath(arr2[i].prefab);
string refGUID = AssetDatabase.AssetPathToGUID(aPath);
AddUseGUID(refGUID);
}
AssetFinderTerrain.TerrainTextureData[] arr3 = AssetFinderTerrain.GetTerrainTextureDatas(terrain);
for (var i = 0; i < arr3.Length; i++)
{
AssetFinderTerrain.TerrainTextureData texs = arr3[i];
for (var k = 0; k < texs.textures.Length; k++)
{
Texture2D tex = texs.textures[k];
if (tex == null) continue;
string aPath = AssetDatabase.GetAssetPath(tex);
if (string.IsNullOrEmpty(aPath)) continue;
string refGUID = AssetDatabase.AssetPathToGUID(aPath);
if (string.IsNullOrEmpty(refGUID)) continue;
AddUseGUID(refGUID);
}
}
}
internal static void ClearLog()
{
if (shouldWriteImportLog)
{
File.WriteAllText(logPath, string.Empty);
} else
{
if (File.Exists(logPath)) File.Delete(logPath);
}
scanStartTime = DateTime.Now;
}
internal static void WriteTotalScanTime()
{
if (!shouldWriteImportLog) return;
double totalScanTime = (DateTime.Now - scanStartTime).TotalSeconds;
File.AppendAllText(logPath, $"\nTotal scan time: {totalScanTime} seconds\n");
}
private void ClearUseGUIDs()
{
// AssetFinderLOG.Log("ClearUseGUIDs: " + assetPath);
UseGUIDs.Clear();
UseGUIDsList.Clear();
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.ContentLoader.cs.meta
================================================
fileFormatVersion: 2
guid: 8b4f01c105762d742adedeca5785b5ca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.Drawing.cs
================================================
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderAsset
{
// ----------------------------- UI DRAWING & USER ACTIONS ---------------------------------------
internal class AssetFinderAssetDrawConfig
{
public bool highlight;
public bool drawPath = true;
public bool showFileSize = true;
public bool showABName = false;
public bool showAtlasName = false;
public bool showUsageIcon = true;
public IWindow window = null;
public bool drawExtension = true;
public Action onShowDetails = null;
public AssetFinderAssetDrawConfig(
bool highlight,
bool drawPath = true,
bool showFileSize = true,
bool showABName = false,
bool showAtlasName = false,
bool showUsageIcon = true,
IWindow window = null,
bool drawExtension = true,
Action onShowDetails = null)
{
this.highlight = highlight;
this.drawPath = drawPath;
this.showFileSize = showFileSize;
this.showABName = showABName;
this.showAtlasName = showAtlasName;
this.showUsageIcon = showUsageIcon;
this.window = window;
this.drawExtension = drawExtension;
this.onShowDetails = onShowDetails;
}
}
internal float Draw(
Rect r,
AssetFinderAssetDrawConfig cfg
)
{
Rect rowRect = new Rect(r.x, r.y, r.width, AssetFinderTheme.Current.TreeItemHeight);
bool isHover = rowRect.Contains(Event.current.mousePosition);
bool singleLine = r.height <= 18f;
float rw = r.width;
bool selected = AssetFinderBookmark.Contains(guid);
r.height = AssetFinderTheme.Current.TreeItemHeight;
bool hasMouse = (Event.current.type == EventType.MouseUp) && r.Contains(Event.current.mousePosition);
if (hasMouse && (Event.current.button == 1))
{
var menu = new GenericMenu();
if (m_extension == ".prefab") menu.AddItem(AssetFinderGUIContent.FromString("Edit in Scene"), false, EditPrefab);
menu.AddItem(AssetFinderGUIContent.FromString("Open"), false, Open);
menu.AddItem(AssetFinderGUIContent.FromString("Ping"), false, Ping);
#if UNITY_2022_3_OR_NEWER
menu.AddItem(AssetFinderGUIContent.FromString("Properties..."), false, OpenProperties);
#endif
menu.AddItem(AssetFinderGUIContent.FromString(guid), false, CopyGUID);
//menu.AddItem(AssetFinderGUIContent.FromString("Select in Project Panel"), false, Select);
menu.AddSeparator(string.Empty);
menu.AddItem(AssetFinderGUIContent.FromString("Copy path"), false, CopyAssetPath);
menu.AddItem(AssetFinderGUIContent.FromString("Copy full path"), false, CopyAssetPathFull);
menu.ShowAsContext();
Event.current.Use();
}
if (IsMissing)
{
if (!singleLine) r.y += 16f;
if (Event.current.type != EventType.Repaint) return 0;
GUI.Label(r, AssetFinderGUIContent.FromString(guid), EditorStyles.whiteBoldLabel);
return 0;
}
Rect iconRect = GUI2.LeftRect(16f, ref r);
GUI2.LeftRect(2f, ref r);
if (Event.current.type == EventType.Repaint)
{
Texture icon = AssetDatabase.GetCachedIcon(m_assetPath);
if (icon != null) GUI.DrawTexture(iconRect, icon, ScaleMode.ScaleToFit);
}
if ((Event.current.type == EventType.MouseDown) && (Event.current.button == 0))
{
Rect pingRect = iconRect; //AssetFinderSetting.PingRow ? new Rect(0, r.y, r.x + r.width, r.height) :
if (pingRect.Contains(Event.current.mousePosition))
{
if (Event.current.control || Event.current.command)
{
if (selected)
{
RemoveFromSelection();
} else
{
AddToSelection();
}
if (cfg.window != null) cfg.window.Repaint();
} else if (Event.current.clickCount == 2)
{
Open();
Event.current.Use();
} else
{
Ping();
}
}
}
if (isHover)
{
if (cfg.onShowDetails != null)
{
r.xMax -= 10f;
var (detailRect, flex) = r.ExtractRight(22f);
if (GUI.Button(detailRect, new GUIContent("...", "Show Details"), EditorStyles.miniButton))
{
cfg.onShowDetails?.Invoke();
}
r = flex;
}
#if UNITY_2022_3_OR_NEWER
var (propRect, flex1) = r.ExtractRight(22f);
if (GUI.Button(propRect, new GUIContent("P", "Open Properties"), EditorStyles.miniButton))
{
OpenProperties();
}
r = flex1;
#endif
}
if (Event.current.type != EventType.Repaint) return 0;
if ((UsedByMap != null) && (UsedByMap.Count > 0))
{
GUIContent str = AssetFinderGUIContent.FromInt(UsedByMap.Count);
Rect countRect = iconRect;
countRect.x -= 16f;
countRect.xMin = -10f;
GUI.Label(countRect, str, GUI2.miniLabelAlignRight);
}
float pathW = cfg.drawPath && !string.IsNullOrEmpty(assetFolder)
? EditorStyles.miniLabel.CalcSize(AssetFinderGUIContent.FromString(assetFolder)).x
: 8f;
float nameW = cfg.drawPath
? EditorStyles.boldLabel.CalcSize(AssetFinderGUIContent.FromString(assetName)).x
: EditorStyles.label.CalcSize(AssetFinderGUIContent.FromString(assetName)).x;
float extW = string.IsNullOrEmpty(extension) ? 0f : EditorStyles.miniLabel.CalcSize(AssetFinderGUIContent.FromString(extension)).x;
Color cc = GUI.skin.settings.selectionColor;
if (singleLine)
{
Rect lbRect = GUI2.LeftRect(pathW + nameW + extW, ref r);
if (selected)
{
Color c1 = GUI.color;
GUI.color = cc;
GUI.DrawTexture(lbRect, EditorGUIUtility.whiteTexture);
GUI.color = c1;
}
if (cfg.drawPath)
{
if (!string.IsNullOrEmpty(assetFolder))
{
Color c2 = GUI.color;
GUI.color = new Color(c2.r, c2.g, c2.b, c2.a * 0.5f);
GUI.Label(GUI2.LeftRect(pathW, ref lbRect), AssetFinderGUIContent.FromString(assetFolder), EditorStyles.miniLabel);
GUI.color = c2;
}
GUI.Label(lbRect, AssetFinderGUIContent.FromString(assetName), EditorStyles.boldLabel);
} else
{
GUI.Label(lbRect, AssetFinderGUIContent.FromString(assetName), EditorStyles.label);
}
lbRect.xMin += nameW - 2f;
lbRect.y += 1f;
if (!string.IsNullOrEmpty(extension) && cfg.drawExtension)
{
Color c3 = GUI.color;
GUI.color = new Color(c3.r, c3.g, c3.b, c3.a * 0.7f);
GUI.Label(lbRect, AssetFinderGUIContent.FromString(extension), EditorStyles.miniLabel);
GUI.color = c3;
}
} else
{
if (cfg.drawPath) GUI.Label(new Rect(r.x, r.y + 16f, r.width, r.height), AssetFinderGUIContent.FromString(m_assetFolder), EditorStyles.miniLabel);
Rect lbRect = GUI2.LeftRect(nameW, ref r);
if (selected) GUI2.Rect(lbRect, cc);
GUI.Label(lbRect, AssetFinderGUIContent.FromString(assetName), EditorStyles.boldLabel);
}
Rect rr = GUI2.RightRect(10f, ref r);
if (cfg.highlight)
{
rr.xMin += 2f;
rr.width = 1f;
GUI2.Rect(rr, GUI2.darkGreen);
}
Color c = GUI.color;
GUI.color = new Color(c.r, c.g, c.b, c.a * 0.5f);
// (Properties button drawn earlier to receive click events)
if (cfg.showFileSize)
{
Rect fsRect = GUI2.RightRect(40f, ref r);
if (fileSizeText == null) fileSizeText = AssetFinderGUIContent.FromString(AssetFinderHelper.GetfileSizeString(fileSize));
GUI.Label(fsRect, fileSizeText, GUI2.miniLabelAlignRight);
}
if (!string.IsNullOrEmpty(m_addressable))
{
Rect adRect = GUI2.RightRect(100f, ref r);
GUI.Label(adRect, AssetFinderGUIContent.FromString(m_addressable), GUI2.miniLabelAlignRight);
}
if (cfg.showUsageIcon && (HashUsedByClassesIds != null))
{
foreach (int item in HashUsedByClassesIds)
{
if (!AssetFinderUnity.HashClassesNormal.ContainsKey(item)) continue;
string name = AssetFinderUnity.HashClassesNormal[item];
if (!HashClasses.TryGetValue(item, out Type t))
{
t = AssetFinderUnity.GetType(name);
HashClasses.Add(item, t);
}
bool isExisted = cacheImage.TryGetValue(name, out GUIContent content);
if (content == null)
{
content = t == null ? GUIContent.none : AssetFinderGUIContent.FromType(t, name);
}
if (!isExisted)
{
cacheImage.Add(name, content);
} else
{
cacheImage[name] = content;
}
if (content != null)
{
try
{
GUI.Label(GUI2.RightRect(15f, ref r), content, GUI2.miniLabelAlignRight);
}
catch (Exception e)
{
AssetFinderLOG.LogWarning(e);
}
}
}
}
if (cfg.showAtlasName)
{
GUI2.RightRect(10f, ref r);
Rect abRect = GUI2.RightRect(120f, ref r);
if (!string.IsNullOrEmpty(m_atlas)) GUI.Label(abRect, AssetFinderGUIContent.FromString(m_atlas), GUI2.miniLabelAlignRight);
}
if (cfg.showABName)
{
GUI2.RightRect(10f, ref r);
Rect abRect = GUI2.RightRect(100f, ref r);
if (!string.IsNullOrEmpty(m_assetbundle)) GUI.Label(abRect, AssetFinderGUIContent.FromString(m_assetbundle), GUI2.miniLabelAlignRight);
}
if (true)
{
GUI2.RightRect(10f, ref r);
Rect abRect = GUI2.RightRect(100f, ref r);
if (!string.IsNullOrEmpty(m_addressable)) GUI.Label(abRect, AssetFinderGUIContent.FromString(m_addressable), GUI2.miniLabelAlignRight);
}
GUI.color = c;
if (Event.current.type == EventType.Repaint) return rw < pathW + nameW ? 32f : 18f;
return r.height;
}
internal GenericMenu AddArray(
GenericMenu menu, System.Collections.Generic.List list, string prefix, string title,
string emptyTitle, bool showAsset, int max = 10)
{
menu.AddItem(AssetFinderGUIContent.FromString(emptyTitle), true, null);
return menu;
}
internal void CopyGUID()
{
EditorGUIUtility.systemCopyBuffer = guid;
Debug.Log(guid);
}
internal void CopyName()
{
EditorGUIUtility.systemCopyBuffer = m_assetName;
Debug.Log(m_assetName);
}
internal void CopyAssetPath()
{
EditorGUIUtility.systemCopyBuffer = m_assetPath;
Debug.Log(m_assetPath);
}
internal void CopyAssetPathFull()
{
string fullName = new FileInfo(m_assetPath).FullName;
EditorGUIUtility.systemCopyBuffer = fullName;
Debug.Log(fullName);
}
internal void RemoveFromSelection()
{
if (AssetFinderBookmark.Contains(guid)) AssetFinderBookmark.Remove(guid);
}
internal void AddToSelection()
{
if (!AssetFinderBookmark.Contains(guid)) AssetFinderBookmark.Add(guid);
}
internal void Ping()
{
if (EditorWindow.focusedWindow is AssetFinderWindowAll fr2Window)
{
fr2Window.smartLock.SetPingLockState(AssetFinderSmartLock.PingLockState.Asset);
}
EditorApplication.delayCall += () =>
{
var asset = AssetDatabase.LoadAssetAtPath(m_assetPath, typeof(UnityObject));
if (asset != null)
{
EditorGUIUtility.PingObject(asset);
}
};
// Only use event if it exists (not null when called from context menu)
if (Event.current != null)
{
Event.current.Use();
}
}
internal void Open()
{
AssetDatabase.OpenAsset(
AssetDatabase.LoadAssetAtPath(m_assetPath, typeof(UnityObject))
);
}
internal void OpenProperties()
{
#if UNITY_2022_3_OR_NEWER
var obj = AssetDatabase.LoadAssetAtPath(m_assetPath, typeof(UnityObject));
if (obj != null)
{
EditorUtility.OpenPropertyEditor(obj);
}
#endif
}
internal void EditPrefab()
{
UnityObject prefab = AssetDatabase.LoadAssetAtPath(m_assetPath, typeof(UnityObject));
UnityObject.Instantiate(prefab);
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.Drawing.cs.meta
================================================
fileFormatVersion: 2
guid: 92421bd9e4cef8a4e969c1da1c6f6769
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.FileInfo.cs
================================================
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderAsset
{
// ----------------------- FILE INFO ------------------------
public bool fileInfoDirty => type == AssetType.UNKNOWN || m_fileInfoReadTS <= m_assetChangeTS;
public bool fileContentDirty => (m_fileWriteTS != m_cachefileWriteTS) && !isBuiltIn;
public bool isDirty => (fileInfoDirty || fileContentDirty) && !isBuiltIn;
public bool isBuiltIn => type == AssetType.BUILT_IN;
public bool hasBeenScanned => m_cachefileWriteTS > 0 || isBuiltIn;
internal string fileInfoHash => LoadFileInfo().m_fileInfoHash;
internal long fileSize => LoadFileInfo().m_fileSize;
public string AtlasName => LoadFileInfo().m_atlas;
public string AssetBundleName => LoadFileInfo().m_assetbundle;
public string AddressableName => LoadFileInfo().m_addressable;
private bool ExistOnDisk()
{
if (isBuiltIn) return true;
if (IsMissing) return false; // asset not exist - no need to check FileSystem!
if (type == AssetType.FOLDER || type == AssetType.UNKNOWN)
{
if (Directory.Exists(m_assetPath))
{
if (type == AssetType.UNKNOWN) type = AssetType.FOLDER;
return true;
}
if (type == AssetType.FOLDER) return false;
}
// must be file here
if (!File.Exists(m_assetPath)) return false;
if (type == AssetType.UNKNOWN) GuessAssetType();
return true;
}
internal AssetFinderAsset LoadFileInfo()
{
if (!fileInfoDirty) return this;
if (string.IsNullOrEmpty(m_assetPath)) LoadPathInfo(); // always reload Path Info
m_fileInfoReadTS = AssetFinderUnity.Epoch(DateTime.Now);
if (isBuiltIn) return this;
if (IsMissing)
{
return this;
}
if (!ExistOnDisk())
{
state = AssetState.MISSING;
return this;
}
if (type == AssetType.FOLDER) return this; // nothing to read
Type assetType = AssetDatabase.GetMainAssetTypeAtPath(m_assetPath);
if (assetType == typeof(AssetFinderCache)) return this;
var info = new FileInfo(m_assetPath);
m_fileSize = info.Length;
m_fileInfoHash = info.Length + info.Extension;
m_addressable = AssetFinderUnity.GetAddressable(guid);
m_assetbundle = AssetDatabase.GetImplicitAssetBundleName(m_assetPath);
if (assetType == typeof(Texture2D))
{
AssetImporter importer = AssetImporter.GetAtPath(m_assetPath);
if (importer is TextureImporter tImporter)
{
#pragma warning disable CS0618
if (tImporter.qualifiesForSpritePacking) m_atlas = tImporter.spritePackingTag;
#pragma warning restore CS0618
}
}
// check if file content changed
var metaInfo = new FileInfo(m_assetPath + ".meta");
int assetTime = AssetFinderUnity.Epoch(info.LastWriteTime);
int metaTime = AssetFinderUnity.Epoch(metaInfo.LastWriteTime);
// update fileChangeTimeStamp
m_fileWriteTS = Mathf.Max(metaTime, assetTime);
return this;
}
internal void GuessAssetType()
{
var ext = extension.ToLowerInvariant();
if (SCRIPT_EXTENSIONS.Contains(ext))
{
type = AssetType.SCRIPT;
} else if (REFERENCABLE_EXTENSIONS.Contains(ext))
{
bool isUnity = ext == ".unity";
type = isUnity ? AssetType.SCENE : AssetType.REFERENCABLE;
if (ext == ".asset" || isUnity || ext == ".spriteatlas")
{
var buffer = new byte[5];
FileStream stream = null;
try
{
stream = File.OpenRead(m_assetPath);
stream.Read(buffer, 0, 5);
stream.Close();
}
#if AssetFinderDEBUG
catch (Exception e)
{
AssetFinderLOG.LogWarning("Guess Asset Type error :: " + e + "\n" + m_assetPath);
#else
catch
{
#endif
if (stream != null) stream.Close();
state = AssetState.MISSING;
return;
} finally
{
if (stream != null) stream.Close();
}
var str = string.Empty;
foreach (byte t in buffer)
{
str += (char)t;
}
if (str != "%YAML") type = AssetType.BINARY_ASSET;
}
} else if (REFERENCABLE_JSON.Contains(ext) || UI_TOOLKIT.Contains(ext))
{
type = AssetType.REFERENCABLE;
} else if (REFERENCABLE_META.Contains(ext))
{
type = AssetType.REFERENCABLE;
} else if (ext == ".fbx")
{
type = AssetType.MODEL;
} else if (ext == ".dll")
{
type = AssetType.DLL;
} else
{
type = AssetType.NON_READABLE;
}
}
internal void MarkAsDirty(bool isMoved = true, bool force = false)
{
if (isMoved)
{
string newPath = AssetDatabase.GUIDToAssetPath(guid);
if (newPath != m_assetPath)
{
m_pathLoaded = false;
m_assetPath = newPath;
}
}
state = AssetState.CACHE;
m_assetChangeTS = AssetFinderUnity.Epoch(DateTime.Now); // re-read FileInfo
if (force) m_cachefileWriteTS = 0;
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.FileInfo.cs.meta
================================================
fileFormatVersion: 2
guid: def6a6c199d5b5c44b9920f983b64d92
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.GuidManager.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderAsset
{
// ----------------------------- GUID MANAGEMENT ---------------------------------------
public Dictionary> UseGUIDs
{
get
{
if (_UseGUIDs != null) return _UseGUIDs;
_UseGUIDs = new Dictionary>(UseGUIDsList.Count);
for (var i = 0; i < UseGUIDsList.Count; i++)
{
string guid = UseGUIDsList[i].guid;
if (_UseGUIDs.ContainsKey(guid))
{
for (var j = 0; j < UseGUIDsList[i].ids.Count; j++)
{
long val = UseGUIDsList[i].ids[j];
if (_UseGUIDs[guid].Contains(val)) continue;
_UseGUIDs[guid].Add(UseGUIDsList[i].ids[j]);
}
} else
{
_UseGUIDs.Add(guid, new HashSet(UseGUIDsList[i].ids));
}
}
return _UseGUIDs;
}
}
internal void AddUseGUID(string fguid, long fFileId = -1)
{
AddUseGUID(fguid, fFileId, true);
}
internal void AddUseGUID(string fguid, long fFileId, bool checkExist)
{
// if (checkExist && UseGUIDs.ContainsKey(fguid)) return;
if (!IsValidGUID(fguid)) return;
if (!UseGUIDs.ContainsKey(fguid))
{
UseGUIDsList.Add(new Classes
{
guid = fguid,
ids = new List()
});
UseGUIDs.Add(fguid, new HashSet());
}
if (fFileId == -1) return;
if (UseGUIDs[fguid].Contains(fFileId)) return;
UseGUIDs[fguid].Add(fFileId);
Classes i = UseGUIDsList.FirstOrDefault(x => x.guid == fguid);
if (i != null) i.ids.Add(fFileId);
}
public void AddUsedBy(string guid, AssetFinderAsset asset)
{
if (UsedByMap.ContainsKey(guid)) return;
if (guid == this.guid)
{
return;
}
UsedByMap.Add(guid, asset);
if (HashUsedByClassesIds == null) HashUsedByClassesIds = new HashSet();
if (asset.UseGUIDs.TryGetValue(this.guid, out HashSet output))
{
foreach (int item in output)
{
HashUsedByClassesIds.Add(item);
}
}
}
public int UsageCount()
{
return UsedByMap.Count;
}
public int UseGUIDsCount
{
get
{
// Return 0 for ignored assets and package assets because we don't scan their content
if (IsExcluded || inPackages)
{
return 0;
}
return UseGUIDs.Count;
}
}
public string DebugUseGUID()
{
return $"{guid} : {assetPath}\n{string.Join("\n", UseGUIDsList.Select(item => item.guid).ToArray())}";
}
internal static bool IsValidGUID(string guid)
{
return AssetDatabase.GUIDToAssetPath(guid) != AssetFinderCache.CachePath; // just skip AssetFinderCache asset
}
internal static List FindUsageGUIDs(AssetFinderAsset asset, bool includeScriptSymbols)
{
var result = new HashSet();
if (asset == null)
{
AssetFinderLOG.LogWarning("Asset invalid : " + asset.m_assetName);
return result.ToList();
}
foreach (KeyValuePair> item in asset.UseGUIDs)
{
result.Add(item.Key);
}
return result.ToList();
}
internal static List FindUsedByGUIDs(AssetFinderAsset asset)
{
return asset.UsedByMap.Keys.ToList();
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.GuidManager.cs.meta
================================================
fileFormatVersion: 2
guid: 9bb02cbb695900d4b91c00fbee372c7d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.PathInfo.cs
================================================
using System;
using System.Globalization;
using UnityEditor;
using UnityEngine;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderAsset
{
// ----------------------- PATH INFO ------------------------
[NonSerialized] private string m_assetFolder;
[NonSerialized] private string m_assetName;
[NonSerialized] private string m_assetPath;
[NonSerialized] private string m_extension;
[NonSerialized] private bool m_inEditor;
[NonSerialized] private bool m_inPackage;
[NonSerialized] private bool m_inPlugins;
[NonSerialized] private bool m_inResources;
[NonSerialized] private bool m_inStreamingAsset;
[NonSerialized] private bool m_pathLoaded;
public string assetName => LoadPathInfo().m_assetName;
public string assetPath
{
get
{
if (!string.IsNullOrEmpty(m_assetPath)) return m_assetPath;
m_assetPath = AssetDatabase.GUIDToAssetPath(guid);
if (string.IsNullOrEmpty(m_assetPath)) state = AssetState.MISSING;
return m_assetPath;
}
}
public string parentFolderPath => LoadPathInfo().m_assetFolder;
public string assetFolder => LoadPathInfo().m_assetFolder;
public string extension => LoadPathInfo().m_extension;
public bool inEditor => LoadPathInfo().m_inEditor;
public bool inPlugins => LoadPathInfo().m_inPlugins;
public bool inPackages => LoadPathInfo().m_inPackage;
public bool inResources => LoadPathInfo().m_inResources;
public bool inStreamingAsset => LoadPathInfo().m_inStreamingAsset;
internal bool IsExcluded
{
get
{
if (excludeTS >= ignoreTS) return _isExcluded;
excludeTS = ignoreTS;
_isExcluded = false;
var h = AssetFinderSetting.IgnoreAsset;
foreach (string item in h)
{
if (!m_assetPath.StartsWith(item, false, CultureInfo.InvariantCulture)) continue;
_isExcluded = true;
return true;
}
return false;
}
}
public AssetFinderAsset LoadPathInfo()
{
if (m_pathLoaded) return this;
m_pathLoaded = true;
m_assetPath = AssetDatabase.GUIDToAssetPath(guid);
if (string.IsNullOrEmpty(assetPath))
{
state = AssetState.MISSING;
return this;
}
// #if AssetFinderDEBUG
// AssetFinderLOG.Log("LoadPathInfo ... " + fileInfoHash + ":" + AssetDatabase.GUIDToAssetPath(guid));
// #endif
AssetFinderUnity.SplitPath(m_assetPath, out m_assetName, out m_extension, out m_assetFolder);
if (m_assetFolder.StartsWith("Assets/"))
{
m_assetFolder = m_assetFolder.Substring(7);
} else if (!AssetFinderUnity.StringStartsWith(m_assetPath,"Project Settings/", "Library/")) m_assetFolder = "built-in/";
m_inEditor = m_assetPath.Contains("/Editor/") || m_assetPath.Contains("/Editor Default Resources/");
m_inResources = m_assetPath.Contains("/Resources/");
m_inStreamingAsset = m_assetPath.Contains("/StreamingAssets/");
m_inPlugins = m_assetPath.Contains("/Plugins/");
m_inPackage = m_assetPath.StartsWith("Packages/");
return this;
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.PathInfo.cs.meta
================================================
fileFormatVersion: 2
guid: aeaa48e4ac2b35e43951fa13a606b81c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.cs
================================================
#if AssetFinderADDRESSABLE
using UnityEditor.AddressableAssets;
using UnityEngine.AddressableAssets;
#endif
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.Serialization;
using UnityObject = UnityEngine.Object;
namespace VirtueSky.AssetFinder.Editor
{
[Serializable]
internal partial class AssetFinderAsset
{
// Constants moved to AssetFinderAsset.Constants.cs
// ----------------------------- DRAW ---------------------------------------
[SerializeField] public string guid;
// Need to read FileInfo: soft-cache (always re-read when needed)
[FormerlySerializedAs("type2")] [SerializeField] public AssetType type;
[SerializeField] private string m_fileInfoHash;
[SerializeField] private string m_assetbundle;
[SerializeField] private string m_addressable;
[SerializeField] private string m_atlas;
[SerializeField] private long m_fileSize;
[SerializeField] private int m_assetChangeTS; // Realtime when asset changed (trigger by import asset operation)
[SerializeField] private int m_fileInfoReadTS; // Realtime when asset being read
[SerializeField] private int m_fileWriteTS; // file's lastModification (file content + meta)
[SerializeField] private int m_cachefileWriteTS; // file's lastModification at the time the content being read
[SerializeField] private bool m_forceIncludeInBuild;
[SerializeField] internal int refreshStamp; // use to check if asset has been deleted (refreshStamp not updated)
[SerializeField] internal List UseGUIDsList = new List();
private bool _isExcluded;
private Dictionary> _UseGUIDs;
private float excludeTS;
// ----------------------------- DRAW ---------------------------------------
[NonSerialized] private GUIContent fileSizeText;
internal HashSet HashUsedByClassesIds = new HashSet();
// Path info moved to AssetFinderAsset.PathInfo.cs
// Do not cache
[NonSerialized] internal AssetState state;
internal Dictionary UsedByMap = new Dictionary();
public AssetFinderAsset(string guid)
{
this.guid = guid;
type = BUILT_IN_ASSETS.Contains(guid) ? AssetType.BUILT_IN : AssetType.UNKNOWN;
}
public bool forcedIncludedInBuild => m_forceIncludeInBuild;
// ----------------------- TYPE INFO ------------------------
internal bool IsFolder => type == AssetType.FOLDER;
internal bool IsScript => type == AssetType.SCRIPT;
internal bool IsMissing => (state == AssetState.MISSING) && !isBuiltIn;
internal bool IsReferencable => type == AssetType.REFERENCABLE ||
type == AssetType.SCENE;
internal bool IsBinaryAsset => type == AssetType.BINARY_ASSET ||
type == AssetType.MODEL ||
type == AssetType.TERRAIN ||
type == AssetType.LIGHTING_DATA;
// ------------------------------- GETTERS -----------------------------
internal bool IsCriticalAsset()
{
// Packages assets are always non-critical
if (string.IsNullOrEmpty(assetPath)) // && assetPath.StartsWith("Packages/")
{
return false;
}
if (AssetDatabase.IsValidFolder(assetPath))
{
return false;
}
if (type == AssetType.UNKNOWN) GuessAssetType();
// Fast checks on asset type
switch (type)
{
case AssetType.REFERENCABLE:
case AssetType.SCENE:
case AssetType.BINARY_ASSET:
case AssetType.MODEL:
case AssetType.TERRAIN:
case AssetType.LIGHTING_DATA:
return true;
case AssetType.FOLDER:
case AssetType.SCRIPT:
case AssetType.DLL:
case AssetType.NON_READABLE:
{
// if (assetPath.Contains(".png")) AssetFinderLOG.LogWarning($"Wrong assetType? {type}");
return false;
}
}
if (string.IsNullOrEmpty(extension))
{
// if (assetPath.Contains(".png")) AssetFinderLOG.LogWarning($"Wrong extensions? {extension}");
return false;
}
var result = !NON_REFERENCE_EXTENSIONS.Contains(extension);
// if (assetPath.Contains(".png")) AssetFinderLOG.LogWarning($"Result = {result}");
return result;
}
// GUID management methods moved to AssetFinderAsset.GuidManager.cs
public override string ToString()
{
return $"AssetFinderAsset[{m_assetName}]";
}
// AddUseGUID methods moved to AssetFinderAsset.GuidManager.cs
// ----------------------------- STATIC ---------------------------------------
internal static int SortByExtension(AssetFinderAsset a1, AssetFinderAsset a2)
{
if (a1 == null) return -1;
if (a2 == null) return 1;
int result = string.Compare(a1.m_extension, a2.m_extension, StringComparison.Ordinal);
return result == 0 ? string.Compare(a1.m_assetName, a2.m_assetName, StringComparison.Ordinal) : result;
}
internal static List FindUsage(AssetFinderAsset asset)
{
if (asset == null) return null;
List refs = AssetFinderCache.Api.FindAssets(asset.UseGUIDs.Keys.ToArray(), true);
return refs;
}
internal static List FindUsedBy(AssetFinderAsset asset)
{
return asset.UsedByMap.Values.ToList();
}
// ----------------------------- REPLACE GUIDS ---------------------------------------
internal bool ReplaceReference(string fromGUID, string toGUID, TerrainData terrain = null)
{
if (IsMissing) return false;
if (IsReferencable)
{
if (!File.Exists(m_assetPath))
{
state = AssetState.MISSING;
return false;
}
try
{
string text = File.ReadAllText(m_assetPath).Replace("\r", "\n");
File.WriteAllText(m_assetPath, text.Replace(fromGUID, toGUID));
return true;
} catch (Exception e)
{
state = AssetState.MISSING;
AssetFinderLOG.LogWarning("Replace Reference error :: " + e + "\n" + m_assetPath);
}
return false;
}
if (type == AssetType.TERRAIN)
{
var fromObj = AssetFinderUnity.LoadAssetWithGUID(fromGUID);
var toObj = AssetFinderUnity.LoadAssetWithGUID(toGUID);
var found = 0;
if (fromObj is Texture2D tex)
{
DetailPrototype[] arr = terrain.detailPrototypes;
for (var i = 0; i < arr.Length; i++)
{
if (arr[i].prototypeTexture != tex) continue;
found++;
arr[i].prototypeTexture = (Texture2D)toObj;
}
terrain.detailPrototypes = arr;
AssetFinderTerrain.ReplaceTerrainTextureDatas(terrain, tex, (Texture2D)toObj);
}
if (fromObj is GameObject go)
{
TreePrototype[] arr2 = terrain.treePrototypes;
for (var i = 0; i < arr2.Length; i++)
{
if (arr2[i].prefab != go) continue;
found++;
arr2[i].prefab = (GameObject)toObj;
}
terrain.treePrototypes = arr2;
}
return found > 0;
}
AssetFinderLOG.LogWarning("Something wrong, should never be here - Ignored <" + m_assetPath + "> : not a readable type, can not replace ! " + type);
return false;
}
internal string ReplaceFileIdIfNeeded(string line, long toFileId)
{
const string FileID = "fileID: ";
int index = line.IndexOf(FileID, StringComparison.Ordinal);
if (index < 0 || toFileId <= 0) return line;
int startIndex = index + FileID.Length;
int endIndex = line.IndexOf(',', startIndex);
if (endIndex > startIndex)
{
string fromFileId = line.Substring(startIndex, endIndex - startIndex);
if (long.TryParse(fromFileId, out long fileType) &&
fileType.ToString().StartsWith(toFileId.ToString().Substring(0, 3)))
{
AssetFinderLOG.Log($"ReplaceReference: fromFileId {fromFileId} to File Id {toFileId}");
return line.Replace(fromFileId, toFileId.ToString());
}
AssetFinderLOG.LogWarning($"[Skip] Difference file type: {fromFileId} -> {toFileId}");
} else
{
AssetFinderLOG.LogWarning("Cannot parse fileID in the line.");
}
return line;
}
internal bool ReplaceReference(string fromGUID, string toGUID, long toFileId, TerrainData terrain = null)
{
if (IsMissing)
{
return false;
}
if (IsReferencable)
{
if (!File.Exists(m_assetPath))
{
state = AssetState.MISSING;
return false;
}
try
{
var sb = new StringBuilder();
string text = File.ReadAllText(assetPath);
var currentIndex = 0;
while (currentIndex < text.Length)
{
int lineEndIndex = text.IndexOfAny(new[] { '\r', '\n' }, currentIndex);
if (lineEndIndex == -1)
{
lineEndIndex = text.Length;
}
string line = text.Substring(currentIndex, lineEndIndex - currentIndex);
// Check if the line contains the GUID and possibly the fileID
if (line.Contains(fromGUID))
{
line = ReplaceFileIdIfNeeded(line, toFileId);
line = line.Replace(fromGUID, toGUID);
}
sb.Append(line);
// Skip through any EOL characters
while (lineEndIndex < text.Length)
{
char c = text[lineEndIndex];
if (c == '\r' || c == '\n')
{
sb.Append(c);
lineEndIndex++;
}
break;
}
currentIndex = lineEndIndex;
}
File.WriteAllText(assetPath, sb.ToString());
//AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.Default);
return true;
} catch (Exception e)
{
state = AssetState.MISSING;
AssetFinderLOG.LogWarning("Replace Reference error :: " + e + "\n" + m_assetPath);
}
return false;
}
if (type == AssetType.TERRAIN)
{
var fromObj = AssetFinderUnity.LoadAssetWithGUID(fromGUID);
var toObj = AssetFinderUnity.LoadAssetWithGUID(toGUID);
var found = 0;
// var terrain = AssetDatabase.LoadAssetAtPath(assetPath, typeof(Object)) as TerrainData;
if (fromObj is Texture2D)
{
DetailPrototype[] arr = terrain.detailPrototypes;
for (var i = 0; i < arr.Length; i++)
{
if (arr[i].prototypeTexture == (Texture2D)fromObj)
{
found++;
arr[i].prototypeTexture = (Texture2D)toObj;
}
}
terrain.detailPrototypes = arr;
AssetFinderTerrain.ReplaceTerrainTextureDatas(terrain, (Texture2D)fromObj, (Texture2D)toObj);
}
if (fromObj is GameObject go)
{
TreePrototype[] arr2 = terrain.treePrototypes;
for (var i = 0; i < arr2.Length; i++)
{
if (arr2[i].prefab != go) continue;
found++;
arr2[i].prefab = (GameObject)toObj;
}
terrain.treePrototypes = arr2;
}
// EditorUtility.SetDirty(terrain);
// AssetDatabase.SaveAssets();
// AssetFinderUnity.UnloadUnusedAssets();
return found > 0;
}
AssetFinderLOG.LogWarning("Something wrong, should never be here - Ignored <" + m_assetPath +
"> : not a readable type, can not replace ! " + type);
return false;
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAsset.cs.meta
================================================
fileFormatVersion: 2
guid: c543b7730e7086043a9e90ab6713e909
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAutoRefreshMode.cs
================================================
namespace VirtueSky.AssetFinder.Editor
{
public enum AssetFinderAutoRefreshMode
{
On, // Auto refresh enabled
Off, // Auto refresh disabled by user
AutoOff // Auto refresh automatically disabled due to frequent changes
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderAutoRefreshMode.cs.meta
================================================
fileFormatVersion: 2
guid: fcfe60c37601b3e419b544b3cf969b50
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.AssetSearch.cs
================================================
// this file has been deleted
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.AssetSearch.cs.meta
================================================
fileFormatVersion: 2
guid: 8e7f2fbc78d4980488c970632c6aaab2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.AsyncProcessor.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderCache
{
internal static void DelayCheck4Changes()
{
EditorApplication.update -= Check;
EditorApplication.update += Check;
}
private static void Check()
{
if (EditorApplication.isCompiling || EditorApplication.isUpdating || AssetFinderSettingExt.disable)
{
delayCounter = 100;
return;
}
if (Api == null) return;
if (delayCounter-- > 0) return;
EditorApplication.update -= Check;
Api.IncrementalRefresh();
}
internal void Check4Changes(bool force)
{
if (EditorApplication.isCompiling || EditorApplication.isUpdating || AssetFinderSettingExt.disable)
{
DelayCheck4Changes();
return;
}
ready = false;
ReadFromProject(force);
// AssetFinderLOG.Log($"After ReadFromProject :: WorkCount: {workCount}, AssetMap: {AssetMap.Count}, AssetList: {AssetList.Count}");
Check4Work();
}
internal void RefreshUsedByOnlyFromCache()
{
if (EditorApplication.isCompiling || EditorApplication.isUpdating || AssetFinderSettingExt.disable) return;
ready = false;
ReadFromCache();
workCount = 0;
if (queueLoadContent != null) queueLoadContent.Clear();
Check4Usage();
}
internal void IncrementalRefresh()
{
if (EditorApplication.isCompiling || EditorApplication.isUpdating || AssetFinderSettingExt.disable)
{
DelayCheck4Changes();
return;
}
ready = false;
workCount = 0;
if (queueLoadContent != null) queueLoadContent.Clear();
if (AssetMap == null)
{
Debug.LogWarning("Why should the AssetMap == null? The FR2 cache might be incompatible?");
return;
}
// CRITICAL FIX: First check for new assets that were added to the project
var paths = AssetDatabase.GetAllAssetPaths();
cacheStamp++;
// Check for new assets
foreach (string p in paths)
{
bool isValid = AssetFinderUnity.StringStartsWith(p, "Assets/", "Packages/", "Library/", "ProjectSettings/");
if (!isValid) continue;
string guid = AssetDatabase.AssetPathToGUID(p);
if (!AssetFinderAsset.IsValidGUID(guid)) continue;
if (!AssetMap.TryGetValue(guid, out AssetFinderAsset asset))
{
// New asset detected - add it
AddAsset(guid, false); // Don't force, let auto refresh logic decide
}
else
{
// Mark existing asset so it won't be deleted
asset.refreshStamp = cacheStamp;
}
}
// Remove deleted assets
for (int i = AssetList.Count - 1; i >= 0; i--)
{
if (AssetList[i].refreshStamp != cacheStamp) RemoveAsset(AssetList[i]);
}
// Only process dirty assets and assets that have never been scanned
foreach (var asset in AssetList) // only scan in AssetList
{
// Skip non-critical assets
if (!asset.IsCriticalAsset())
{
if (asset.isDirty)
{
AssetFinderLOG.Log($"[INVALID] non-critical asset is dirty???\n" +
$" asset: {asset.assetPath}: isCritical = {asset.IsCriticalAsset()} | isDirty = {asset.isDirty} | assetType: {asset.type}");
}
continue;
}
// Skip ignored assets - they shouldn't have their content read
if (asset.IsExcluded)
{
AssetFinderLOG.Log($"Skipping ignored asset: {asset.assetPath}");
continue;
}
// Only process if asset is dirty or has never been scanned
if (asset.isDirty || !asset.hasBeenScanned)
{
workCount++;
queueLoadContent.Add(asset);
}
}
// Clear the HasChanged flag since we're now processing the changes
HasChanged = false;
AssetFinderLOG.Log($"Incremental refresh: Processing {workCount} dirty/unscanned assets");
Check4Work();
}
internal void Check4Usage()
{
currentState = ProcessingState.BuildingUsedBy;
// CRITICAL FIX: Clear UsedByMap for ALL assets in AssetMap, not just AssetList
// This ensures that non-critical assets (like PNGs) get their stale references cleared
foreach (var kvp in AssetMap)
{
var item = kvp.Value;
if (item.IsMissing) continue;
AssetFinderUnity.Clear(ref item.UsedByMap);
}
foreach (var item in AssetList)
{
if (item.IsMissing) continue;
AsyncUsedBy(item);
}
workCount = 0;
ready = true;
currentState = ProcessingState.Idle;
HasChanged = false; // Clear dirty state when processing is complete
onReady?.Invoke();
}
internal void Check4Work()
{
if (workCount == 0)
{
Check4Usage();
return;
}
ready = false;
currentState = ProcessingState.ReadingContent;
EditorApplication.update -= AsyncProcess;
EditorApplication.update += AsyncProcess;
AssetFinderAsset.ClearLog();
}
internal void AsyncProcess()
{
if (this == null) return;
if (AssetFinderSettingExt.disable) return;
if (EditorApplication.isCompiling || EditorApplication.isUpdating) return;
if (frameSkipped++ < 10 - 2 * priority) return;
frameSkipped = 0;
float t = Time.realtimeSinceStartup;
// AssetFinderLOG.Log("AsyncProcess: time=" + Mathf.Round(t) + " : progress = " + progress*workCount + "/" + workCount + " : isReady =" + isReady + " ::: queueLoadCount = " + queueLoadContent.Count);
if (!AsyncWork(queueLoadContent, AsyncLoadContent, t)) return;
AssetFinderAsset.WriteTotalScanTime();
EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
EditorApplication.update -= AsyncProcess;
if (HasPendingChanges())
{
AssetFinderLOG.Log("FR2: Detected changes during processing, restarting incremental refresh");
IncrementalRefresh();
return;
}
Check4Usage();
}
private bool HasPendingChanges()
{
return AssetMap.Any(kvp => kvp.Value.isDirty && !queueLoadContent.Contains(kvp.Value));
}
internal bool AsyncWork(List arr, Action action, float t)
{
const float FRAME_DURATION = 1f / 60f; // Cache as const to avoid division
float endTime = t + FRAME_DURATION; // Calculate end time once
int c = arr.Count;
while (c-- > 0)
{
T last = arr[c];
arr.RemoveAt(c);
action(c, last);
// Check time less frequently to reduce overhead
if (Time.realtimeSinceStartup >= endTime) return false;
}
if (GC_CountDown-- <= 0) // GC every 5 frames
{
GC.Collect(2, GCCollectionMode.Forced, true, true);
GC_CountDown = 5;
}
return c <= 0;
}
internal void AsyncLoadContent(int idx, AssetFinderAsset asset)
{
// Update the current asset name
currentAssetName = asset.assetPath;
if (asset.fileInfoDirty) asset.LoadFileInfo();
if (asset.fileContentDirty) asset.LoadContentFast();
}
internal void AsyncUsedBy(AssetFinderAsset asset)
{
if (AssetMap == null) Check4Changes(false);
if (asset.IsFolder) return;
// AssetFinderLOG.Log("Async UsedBy: " + asset.assetPath);
foreach (KeyValuePair> item in asset.UseGUIDs)
{
if (!AssetMap.TryGetValue(item.Key, out AssetFinderAsset tAsset)) continue;
if (tAsset == null || tAsset.UsedByMap == null) continue;
if (!tAsset.UsedByMap.ContainsKey(asset.guid)) tAsset.AddUsedBy(asset.guid, asset);
}
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.AsyncProcessor.cs.meta
================================================
fileFormatVersion: 2
guid: 6b401fe2e5dd2e740bfc479d22492019
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.Constants.cs
================================================
using System.Collections.Generic;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderCache
{
internal const string DEFAULT_CACHE_PATH = "Assets/_Sunflower/Editor/FinderCache/AssetFinderCache.asset";
internal const string CACHE_VERSION = "2.6.4";
internal static int cacheStamp;
internal static System.Action onReady;
internal static bool _triedToLoadCache;
internal static AssetFinderCache _cache;
internal static string _cacheGUID;
internal static bool _cacheJustCreated;
internal static string _cachePath;
public static readonly int priority = 5;
private static readonly HashSet SPECIAL_USE_ASSETS = new HashSet
{
"Assets/link.xml", // this file used to control build/link process do not remove
"Assets/csc.rsp",
"Assets/mcs.rsp",
"Assets/GoogleService-Info.plist",
"Assets/google-services.json"
};
private static readonly HashSet SPECIAL_EXTENSIONS = new HashSet
{
".asmdef",
".cginc",
".cs",
".dll",
".mdb",
".pdb",
".rsp",
".md",
".winmd",
".xml",
".XML",
".tsv",
".csv",
".json",
".pdf",
".txt",
".giparams",
".wlt",
".preset",
".exr",
".aar",
".srcaar",
".pom",
".bin",
".html",
".chm",
".data",
".jsp",
".unitypackage"
};
[System.NonSerialized] internal static int delayCounter;
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.Constants.cs.meta
================================================
fileFormatVersion: 2
guid: 6c55ca682f1f6844b8cd730021311b53
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.Lifecycle.cs
================================================
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
using System.Linq;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderCache
{
public static bool CheckSameVersion()
{
if (_cache == null) return false;
return _cache._curCacheVersion == CACHE_VERSION;
}
public void MarkChanged()
{
HasChanged = true;
}
private static void FoundCache()
{
_cachePath = AssetDatabase.GetAssetPath(_cache);
_cache.ReadFromCache();
_cacheGUID = AssetDatabase.AssetPathToGUID(_cachePath);
if (AssetFinderSettingExt.isAutoRefreshEnabled || _cacheJustCreated)
{
if (_cacheJustCreated) _cache.Check4Changes(true);
else _cache.RefreshUsedByOnlyFromCache();
}
else
{
_cache.RefreshUsedByOnlyFromCache();
}
// Reset flag after use
_cacheJustCreated = false;
}
private static bool RestoreCacheFromPath(string path, bool validateOnly, bool forceLoad)
{
if (string.IsNullOrEmpty(path)) return false;
if (!File.Exists(path)) return false;
_cache = AssetFinderUnity.LoadAssetAtPath(path);
if (_cache == null) return false;
if (validateOnly && !forceLoad) return true;
FoundCache();
return true;
}
private static void TryLoadCache()
{
_triedToLoadCache = true;
// Simple type-based search scoped to Assets/ only
var cacheAssets = AssetDatabase.FindAssets("t:AssetFinderCache", new[] { "Assets" });
if (cacheAssets.Length > 0)
foreach (var guid in cacheAssets)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
if (!string.IsNullOrEmpty(path) && RestoreCacheFromPath(path, true, true))
return;
}
}
internal static void DeleteCache()
{
if (_cache == null) return;
try
{
_cache.AssetList.Clear();
_cache.AssetMap.Clear();
_cache.queueLoadContent.Clear();
_cache = null;
if (!string.IsNullOrEmpty(_cachePath)) AssetDatabase.DeleteAsset(_cachePath);
}
catch
{
// ignored
}
AssetDatabase.SaveAssets();
using (AssetFinderDev.NoLog)
{
AssetDatabase.Refresh();
}
}
internal static void CreateCache()
{
_cache = CreateInstance();
_cache._curCacheVersion = CACHE_VERSION;
var path = Application.dataPath + DEFAULT_CACHE_PATH
.Substring(0, DEFAULT_CACHE_PATH.LastIndexOf('/') + 1).Replace("Assets", string.Empty);
if (!Directory.Exists(path)) Directory.CreateDirectory(path);
AssetDatabase.CreateAsset(_cache, DEFAULT_CACHE_PATH);
EditorUtility.SetDirty(_cache);
// Set force refresh flag for initial/recreated cache
_cacheJustCreated = true;
FoundCache();
// Delay the scan by one frame so UI can update first
EditorApplication.delayCall -= DelayCheck4Changes;
EditorApplication.delayCall += DelayCheck4Changes;
}
private void OnEnable()
{
if (_cache == null) _cache = this;
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.Lifecycle.cs.meta
================================================
fileFormatVersion: 2
guid: f5c9fe5f4b20f53498fda1e06bf8ef0a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.ProjectManager.cs
================================================
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderCache
{
internal void ReadFromCache()
{
if (AssetFinderSettingExt.disable)
{
AssetFinderLOG.LogWarning("Something wrong??? FR2 is disabled!");
}
if (AssetList == null) AssetList = new List();
AssetFinderUnity.Clear(ref queueLoadContent);
AssetFinderUnity.Clear(ref AssetMap);
// Create a new filtered list for critical assets only
var filteredAssetList = new List();
for (var i = 0; i < AssetList.Count; i++)
{
AssetFinderAsset item = AssetList[i];
item.state = AssetFinderAsset.AssetState.CACHE;
string path = AssetDatabase.GUIDToAssetPath(item.guid);
if (string.IsNullOrEmpty(path))
{
item.type = AssetFinderAsset.AssetType.UNKNOWN; // to make sure if GUIDs being reused for a different kind of asset
item.state = AssetFinderAsset.AssetState.MISSING;
AssetMap.Add(item.guid, item);
// Only keep critical assets in AssetList
if (item.IsCriticalAsset())
{
filteredAssetList.Add(item);
}
continue;
}
if (AssetMap.ContainsKey(item.guid))
{
AssetFinderLOG.LogWarning("Something wrong, cache found twice <" + item.guid + ">");
continue;
}
AssetMap.Add(item.guid, item);
// Only keep critical assets in AssetList
if (item.IsCriticalAsset())
{
filteredAssetList.Add(item);
}
}
// Replace AssetList with filtered list containing only critical assets
AssetList = filteredAssetList;
}
internal void ClearCacheCompletely()
{
// AssetFinderLOG.Log("=== ClearCacheCompletely START ===");
// AssetFinderLOG.Log($"Before Clear - AssetList: {AssetList?.Count ?? 0}, AssetMap: {AssetMap?.Count ?? 0}, queueLoadContent: {queueLoadContent?.Count ?? 0}");
// Clear all cache data structures
if (AssetList != null) AssetList.Clear();
else AssetList = new List();
if (AssetMap != null) AssetMap.Clear();
else AssetMap = new Dictionary();
if (queueLoadContent != null) queueLoadContent.Clear();
else queueLoadContent = new List();
// Reset state
ready = false;
workCount = 0;
cacheStamp = 0;
HasChanged = false;
currentState = ProcessingState.Idle;
System.GC.Collect();
}
internal void ReadFromProject(bool force)
{
if (AssetMap == null || AssetMap.Count == 0) ReadFromCache();
foreach (string b in AssetFinderAsset.BUILT_IN_ASSETS)
{
if (AssetMap.ContainsKey(b)) continue;
var asset = new AssetFinderAsset(b);
AssetMap.Add(b, asset);
// Only add built-in assets to AssetList if they are critical
if (asset.IsCriticalAsset())
{
AssetList.Add(asset);
}
}
string[] paths = AssetDatabase.GetAllAssetPaths();
cacheStamp++;
workCount = 0;
if (queueLoadContent != null) queueLoadContent.Clear();
// Check for new assets
int validPaths = 0;
int newAssets = 0;
int existingAssets = 0;
foreach (string p in paths)
{
bool isValid = AssetFinderUnity.StringStartsWith(p, "Assets/", "Packages/", "Library/", "ProjectSettings/");
if (!isValid)
{
continue; // Skip invalid paths silently to avoid log spam
}
validPaths++;
string guid = AssetDatabase.AssetPathToGUID(p);
if (!AssetFinderAsset.IsValidGUID(guid))
{
continue;
}
if (!AssetMap.TryGetValue(guid, out AssetFinderAsset asset))
{
newAssets++;
AddAsset(guid, force);
} else
{
existingAssets++;
asset.refreshStamp = cacheStamp; // mark this asset so it won't be deleted
if (!asset.IsCriticalAsset()) continue; // not something we can handle
if (!asset.isDirty && !force) continue;
if (force) asset.MarkAsDirty(true, true);
if (!asset.IsExcluded && (force || _cacheJustCreated || AssetFinderSettingExt.isAutoRefreshEnabled))
{
workCount++;
queueLoadContent.Add(asset);
}
}
}
// Check for deleted assets
for (int i = AssetList.Count - 1; i >= 0; i--)
{
if (AssetList[i].refreshStamp != cacheStamp) RemoveAsset(AssetList[i]);
}
}
internal void RefreshAsset(string guid, bool force)
{
if (!AssetMap.TryGetValue(guid, out AssetFinderAsset asset)) return;
RefreshAsset(asset, force);
}
internal void RefreshSelection()
{
string[] list = AssetFinderUnity.Selection_AssetGUIDs;
for (var i = 0; i < list.Length; i++)
{
RefreshAsset(list[i], true);
}
Check4Work();
}
internal void RefreshAsset(AssetFinderAsset asset, bool force)
{
asset.MarkAsDirty(true, force);
// If we're currently processing and this asset isn't already in the queue, add it
if (currentState != ProcessingState.Idle && !queueLoadContent.Contains(asset))
{
workCount++;
queueLoadContent.Add(asset);
}
DelayCheck4Changes();
}
internal void AddAsset(string guid, bool force = false)
{
if (AssetMap.ContainsKey(guid))
{
AssetFinderLOG.LogWarning("guid already exist <" + guid + ">");
return;
}
var asset = new AssetFinderAsset(guid);
asset.LoadPathInfo();
asset.refreshStamp = cacheStamp;
AssetMap.Add(guid, asset);
// Do not load content for AssetFinderCache asset
if (guid == CacheGUID) return;
if (!asset.IsCriticalAsset()) return;
// Critical assets (even if ignored) should be added to AssetList
AssetList.Add(asset);
// CRITICAL FIX: Always queue new assets for content loading when force=true
bool shouldQueue = !asset.IsExcluded && (force || _cacheJustCreated || AssetFinderSettingExt.isAutoRefreshEnabled || currentState != ProcessingState.Idle);
// AssetFinderLOG.Log($"AddAsset: {asset.assetPath} - shouldQueue: {shouldQueue} (IsExcluded: {asset.IsExcluded}, force: {force}, _cacheJustCreated: {_cacheJustCreated}, autoRefresh: {AssetFinderSettingExt.isAutoRefreshEnabled}, currentState: {currentState})");
if (shouldQueue)
{
workCount++;
queueLoadContent.Add(asset);
// AssetFinderLOG.Log($"QUEUED new asset for content loading: {asset.assetPath}");
}
else
{
// When content loading is skipped, mark as ready but dirty for future scans
asset.MarkAsDirty(true, false);
// AssetFinderLOG.Log($"SKIPPED new asset: {asset.assetPath} - marked as dirty for future scan");
}
}
internal void RemoveAsset(string guid)
{
if (!AssetMap.ContainsKey(guid)) return;
RemoveAsset(AssetMap[guid]);
}
internal void RemoveAsset(AssetFinderAsset asset)
{
AssetList.Remove(asset);
// Deleted Asset : still in the map but not in the AssetList
asset.state = AssetFinderAsset.AssetState.MISSING;
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.ProjectManager.cs.meta
================================================
fileFormatVersion: 2
guid: 462f88bf52231f446ad48db4501584fa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.Scanner.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderCache
{
internal List> ScanSimilar(Action IgnoreWhenScan, Action IgnoreFolderWhenScan)
{
if (AssetMap == null) Check4Changes(true);
var dict = new Dictionary>();
foreach (KeyValuePair item in AssetMap)
{
if (item.Value == null) continue;
if (item.Value.IsMissing || item.Value.IsFolder) continue;
if (item.Value.inPlugins) continue;
if (item.Value.inEditor) continue;
if (item.Value.IsExcluded) continue;
if (!item.Value.assetPath.StartsWith("Assets/")) continue;
if (AssetFinderSetting.IsTypeExcluded(AssetFinderAssetGroupDrawer.GetIndex(item.Value.extension)))
{
if (IgnoreWhenScan != null) IgnoreWhenScan();
continue;
}
string hash = item.Value.fileInfoHash;
if (string.IsNullOrEmpty(hash))
{
AssetFinderLOG.LogWarning("Hash can not be null! ");
continue;
}
if (!dict.TryGetValue(hash, out List list))
{
list = new List();
dict.Add(hash, list);
}
list.Add(item.Value);
}
return dict.Values
.Where(item => item.Count > 1)
.OrderByDescending(item => item[0].fileSize)
.Select(item => item.Select(asset => asset.assetPath).ToList())
.ToList();
}
internal List ScanUnused(bool recursive = true)
{
if (AssetMap == null) Check4Changes(false);
// Get Addressable assets
HashSet addressable = AssetFinderAddressable.isOk ? AssetFinderAddressable.GetAddresses()
.SelectMany(item => item.Value.assetGUIDs.Union(item.Value.childGUIDs))
.ToHashSet() : new HashSet();
var result = new List();
var unusedAssets = new HashSet();
// First pass: find directly unused assets (level 1)
foreach (KeyValuePair item in AssetMap)
{
AssetFinderAsset v = item.Value;
if (v.IsMissing || v.inEditor || v.IsScript || v.inResources || v.inPlugins || v.inStreamingAsset || v.IsFolder) continue;
if (!v.assetPath.StartsWith("Assets/")) continue; // ignore built-in / packages assets
if (v.forcedIncludedInBuild) continue; // ignore assets that are forced to be included in build
if (v.assetName == "LICENSE") continue; // ignore license files
// --- Ignore assets in ignored folders or exact ignored paths ---
bool isIgnored = AssetFinderSetting.IgnoreAsset.Any(ignore =>
v.assetPath.Equals(ignore, StringComparison.OrdinalIgnoreCase) ||
(v.assetPath.StartsWith(ignore + "/", StringComparison.OrdinalIgnoreCase))
);
if (isIgnored) continue;
// --- Ignore assets with unknown or no extension ---
string ext = System.IO.Path.GetExtension(v.assetPath);
Type assetType = UnityEditor.AssetDatabase.GetMainAssetTypeAtPath(v.assetPath);
if (string.IsNullOrEmpty(ext) || assetType == typeof(DefaultAsset))
{
continue;
}
if (SPECIAL_USE_ASSETS.Contains(v.assetPath)) continue; // ignore assets with special use (can not remove)
if (SPECIAL_EXTENSIONS.Contains(v.extension)) continue;
if (v.type == AssetFinderAsset.AssetType.DLL) continue;
if (v.type == AssetFinderAsset.AssetType.SCRIPT) continue;
if (v.type == AssetFinderAsset.AssetType.UNKNOWN) continue;
if (addressable.Contains(v.guid)) continue;
// special handler for .spriteatlas
if (v.extension == ".spriteatlas")
{
var isInUsed = false;
List allSprites = v.UseGUIDs.Keys.ToList();
foreach (string spriteGUID in allSprites)
{
AssetFinderAsset asset = Api.Get(spriteGUID);
if (asset.UsedByMap.Count <= 1) continue; // only use by this atlas
isInUsed = true;
break; // this one is used by other assets
}
if (isInUsed) continue;
}
if (v.IsExcluded)
{
// Debug.Log($"Excluded: {v.assetPath}");
continue;
}
if (!string.IsNullOrEmpty(v.AtlasName)) continue;
if (!string.IsNullOrEmpty(v.AssetBundleName)) continue;
if (!string.IsNullOrEmpty(v.AddressableName)) continue;
if (v.UsedByMap.Count == 0) //&& !AssetFinderAsset.IGNORE_UNUSED_GUIDS.Contains(v.guid)
{
result.Add(v);
unusedAssets.Add(v.guid);
}
}
// If not recursive, return the level 1 results
if (!recursive)
{
result.Sort((item1, item2) => item1.extension == item2.extension
? string.Compare(item1.assetPath, item2.assetPath, StringComparison.Ordinal)
: string.Compare(item1.extension, item2.extension, StringComparison.Ordinal));
return result;
}
// Recursive scan for higher level unused assets
bool foundNewUnused = true;
while (foundNewUnused)
{
foundNewUnused = false;
var newUnusedAssets = new HashSet();
foreach (KeyValuePair item in AssetMap)
{
AssetFinderAsset v = item.Value;
// Skip if already in result or doesn't meet basic criteria
if (unusedAssets.Contains(v.guid)) continue;
if (v.IsMissing || v.inEditor || v.IsScript || v.inResources || v.inPlugins || v.inStreamingAsset || v.IsFolder) continue;
if (!v.assetPath.StartsWith("Assets/")) continue;
if (v.forcedIncludedInBuild) continue;
if (v.assetName == "LICENSE") continue;
// --- Ignore assets in ignored folders or exact ignored paths ---
bool isIgnored = AssetFinderSetting.IgnoreAsset.Any(ignore =>
v.assetPath.Equals(ignore, StringComparison.OrdinalIgnoreCase) ||
(v.assetPath.StartsWith(ignore + "/", StringComparison.OrdinalIgnoreCase))
);
if (isIgnored) continue;
// --- Ignore assets with unknown or no extension ---
string ext = System.IO.Path.GetExtension(v.assetPath);
Type assetType = UnityEditor.AssetDatabase.GetMainAssetTypeAtPath(v.assetPath);
if (string.IsNullOrEmpty(ext) || assetType == typeof(DefaultAsset))
{
continue;
}
if (SPECIAL_USE_ASSETS.Contains(v.assetPath)) continue;
if (SPECIAL_EXTENSIONS.Contains(v.extension)) continue;
if (v.type == AssetFinderAsset.AssetType.DLL) continue;
if (v.type == AssetFinderAsset.AssetType.SCRIPT) continue;
if (v.type == AssetFinderAsset.AssetType.UNKNOWN) continue;
if (addressable.Contains(v.guid)) continue;
if (v.IsExcluded) continue;
if (!string.IsNullOrEmpty(v.AtlasName)) continue;
if (!string.IsNullOrEmpty(v.AssetBundleName)) continue;
if (!string.IsNullOrEmpty(v.AddressableName)) continue;
// Check if this asset is only used by already identified unused assets
if (v.UsedByMap.Count > 0)
{
bool onlyUsedByUnusedAssets = true;
foreach (var usedBy in v.UsedByMap)
{
if (!unusedAssets.Contains(usedBy.Key))
{
onlyUsedByUnusedAssets = false;
break;
}
}
if (onlyUsedByUnusedAssets)
{
result.Add(v);
newUnusedAssets.Add(v.guid);
foundNewUnused = true;
}
}
}
// Add newly found unused assets to the master list
unusedAssets.UnionWith(newUnusedAssets);
}
result.Sort((item1, item2) => item1.extension == item2.extension
? string.Compare(item1.assetPath, item2.assetPath, StringComparison.Ordinal)
: string.Compare(item1.extension, item2.extension, StringComparison.Ordinal));
return result;
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.Scanner.cs.meta
================================================
fileFormatVersion: 2
guid: 294f715733c2fbf41b655b25454bf807
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.Search.cs
================================================
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderCache
{
internal static List FindUsage(string[] listGUIDs)
{
if (!isReady) return null;
List refs = Api.FindAssets(listGUIDs, true);
for (var i = 0; i < refs.Count; i++)
{
List tmp = AssetFinderAsset.FindUsage(refs[i]);
for (var j = 0; j < tmp.Count; j++)
{
AssetFinderAsset itm = tmp[j];
if (refs.Contains(itm)) continue;
refs.Add(itm);
}
}
return refs.Select(item => item.guid).ToList();
}
internal AssetFinderAsset Get(string guid, bool autoNew = false)
{
if (autoNew && !AssetMap.ContainsKey(guid)) AddAsset(guid);
return AssetMap.GetValueOrDefault(guid);
}
internal List FindAssetsOfType(AssetFinderAsset.AssetType type)
{
var result = new List();
foreach (KeyValuePair item in AssetMap)
{
if (item.Value.type != type) continue;
result.Add(item.Value);
}
return result;
}
internal AssetFinderAsset FindAsset(string guid, string fileId)
{
if (AssetMap == null) Check4Changes(false);
if (!isReady)
{
AssetFinderLOG.LogWarning("Cache not ready !");
return null;
}
if (string.IsNullOrEmpty(guid)) return null;
//for (var i = 0; i < guids.Length; i++)
{
//string guid = guids[i];
if (!AssetMap.TryGetValue(guid, out AssetFinderAsset asset)) return null;
if (asset.IsMissing) return null;
if (asset.IsFolder) return null;
return asset;
}
}
internal List FindAssets(string[] guids, bool scanFolder)
{
if (AssetMap == null) Check4Changes(false);
var result = new List();
if (!isReady)
{
AssetFinderLOG.LogWarning("Cache not ready !");
return result;
}
var folderList = new List();
if (guids.Length == 0) return result;
for (var i = 0; i < guids.Length; i++)
{
string guid = guids[i];
AssetFinderAsset asset;
if (!AssetMap.TryGetValue(guid, out asset)) continue;
if (asset.IsMissing) continue;
if (asset.IsFolder)
{
if (!folderList.Contains(asset)) folderList.Add(asset);
} else
{
result.Add(asset);
}
}
if (!scanFolder || folderList.Count == 0) return result;
int count = folderList.Count;
for (var i = 0; i < count; i++)
{
AssetFinderAsset item = folderList[i];
// for (var j = 0; j < item.UseGUIDs.Count; j++)
// {
// AssetFinderAsset a;
// if (!AssetMap.TryGetValue(item.UseGUIDs[j], out a)) continue;
foreach (KeyValuePair> useM in item.UseGUIDs)
{
AssetFinderAsset a;
if (!AssetMap.TryGetValue(useM.Key, out a)) continue;
if (a.IsMissing) continue;
if (a.IsFolder)
{
if (!folderList.Contains(a))
{
folderList.Add(a);
count++;
}
} else
{
result.Add(a);
}
}
}
return result;
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.Search.cs.meta
================================================
fileFormatVersion: 2
guid: 96e6f3d54f320f74cbcb5ebb0e8f987e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.cs
================================================
//#define AssetFinderDEBUG
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderCache : ScriptableObject
{
[SerializeField] private bool _autoRefresh;
[SerializeField] private string _curCacheVersion;
[SerializeField] public List AssetList;
[SerializeField] internal AssetFinderSetting setting = new AssetFinderSetting();
// ----------------------------------- INSTANCE -------------------------------------
[SerializeField] public int timeStamp;
[NonSerialized] internal Dictionary AssetMap;
// Track the current asset being processed
[NonSerialized] internal string currentAssetName;
private int frameSkipped;
internal int GC_CountDown = 5;
[NonSerialized] internal List queueLoadContent;
internal bool ready;
[NonSerialized] internal int workCount;
[NonSerialized] internal ProcessingState currentState = ProcessingState.Idle;
internal static string CacheGUID
{
get
{
if (!string.IsNullOrEmpty(_cacheGUID)) return _cacheGUID;
if (_cache != null)
{
_cachePath = AssetDatabase.GetAssetPath(_cache);
_cacheGUID = AssetDatabase.AssetPathToGUID(_cachePath);
return _cacheGUID;
}
return null;
}
}
internal static string CachePath
{
get
{
if (!string.IsNullOrEmpty(_cachePath)) return _cachePath;
if (_cache != null)
{
_cachePath = AssetDatabase.GetAssetPath(_cache);
return _cachePath;
}
return null;
}
}
[SerializeField] private bool _hasChanged;
public bool HasChanged
{
get => _hasChanged;
private set => _hasChanged = value;
}
internal static bool hasChanges => Api != null && Api.workCount > 0;
public static void Reload()
{
DelayCheck4Changes();
}
internal static AssetFinderCache Api
{
get
{
if (_cache != null) return _cache;
if (!_triedToLoadCache) TryLoadCache();
return _cache;
}
}
internal static bool isReady
{
get
{
if (AssetFinderSettingExt.disable) return false;
if (!_triedToLoadCache) TryLoadCache();
return (_cache != null) && _cache.ready;
}
}
internal static bool hasCache
{
get
{
if (!_triedToLoadCache) TryLoadCache();
return _cache != null;
}
}
internal float progress
{
get
{
int n = workCount - queueLoadContent.Count;
return workCount == 0 ? 1 : n / (float)workCount;
}
}
}
internal enum ProcessingState
{
Idle, // Not processing anything
ReadingContent, // Currently reading asset content
BuildingUsedBy // Currently building usedBy relationships
}
internal static class AssetFinderLOG
{
public static void Log(object message)
{
#if AssetFinderDEBUG || AssetFinderDEV
UnityEngine.Debug.Log(message);
#endif
}
public static void Log(object message, UnityEngine.Object context)
{
#if AssetFinderDEBUG || AssetFinderDEV
UnityEngine.Debug.Log(message, context);
#endif
}
public static void LogWarning(object message)
{
#if AssetFinderDEBUG || AssetFinderDEV
UnityEngine.Debug.LogWarning(message);
#endif
}
public static void LogWarning(object message, UnityEngine.Object context)
{
#if AssetFinderDEBUG || AssetFinderDEV
UnityEngine.Debug.LogWarning(message, context);
#endif
}
public static void LogError(object message)
{
#if AssetFinderDEBUG || AssetFinderDEV
UnityEngine.Debug.LogError(message);
#endif
}
public static void LogError(object message, UnityEngine.Object context)
{
#if AssetFinderDEBUG || AssetFinderDEV
UnityEngine.Debug.LogError(message, context);
#endif
}
}
[CustomEditor(typeof(AssetFinderCache))]
internal class AssetFinderCacheEditor : UnityEditor.Editor
{
private static string inspectGUID;
private static int index;
public override void OnInspectorGUI()
{
var c = (AssetFinderCache)target;
GUILayout.Label("Total : " + c.AssetList.Count);
// AssetFinderCache.DrawPriorityGUI();
UnityObject s = Selection.activeObject;
if (s == null) return;
string guid = AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(s));
if (inspectGUID != guid)
{
inspectGUID = guid;
index = c.AssetList.FindIndex(item => item.guid == guid);
}
if (index != -1)
{
if (index >= c.AssetList.Count) index = 0;
serializedObject.Update();
SerializedProperty prop = serializedObject.FindProperty("AssetList").GetArrayElementAtIndex(index);
prop.isExpanded = true;
EditorGUILayout.PropertyField(prop, true);
}
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderCache.cs.meta
================================================
fileFormatVersion: 2
guid: 30361f5a1e2f6bf449882a4a0fb04e00
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderGitUtil.cs
================================================
using System.IO;
using UnityEngine;
namespace VirtueSky.AssetFinder.Editor
{
internal static class AssetFinderGitUtil
{
private static string gitRootPath;
public static bool IsGitProject()
{
if (!string.IsNullOrEmpty(gitRootPath)) return true;
string currentPath = Application.dataPath;
DirectoryInfo dir = new DirectoryInfo(currentPath);
var maxDepth = 10;
while (dir != null && maxDepth > 0) // Prevent infinite loop
{
maxDepth--;
if (Directory.Exists(Path.Combine(dir.FullName, ".git")))
{
gitRootPath = dir.FullName;
return true;
}
dir = dir.Parent;
}
return false;
}
public static bool CheckGitIgnoreContainsFR2Cache()
{
if (string.IsNullOrEmpty(gitRootPath)) IsGitProject();
if (string.IsNullOrEmpty(gitRootPath)) return false;
string gitIgnorePath = Path.Combine(gitRootPath, ".gitignore");
if (!File.Exists(gitIgnorePath)) return false;
string[] lines = File.ReadAllLines(gitIgnorePath);
foreach (string line in lines)
{
string trimmedLine = line.Trim();
if (string.IsNullOrEmpty(trimmedLine) || trimmedLine.StartsWith("#")) continue;
if (trimmedLine == "**/AssetFinderCache.asset*" || trimmedLine == "AssetFinderCache.asset*" || trimmedLine == "*AssetFinderCache.asset*")
{
return true;
}
}
return false;
}
public static void AddFR2CacheToGitIgnore()
{
try
{
string content = File.Exists(".gitignore") ? File.ReadAllText(".gitignore") : "";
// Make sure the file ends with a newline
if (!string.IsNullOrEmpty(content) && !content.EndsWith("\n"))
{
content += "\n";
}
content += "**/AssetFinderCache.asset*\n";
File.WriteAllText(".gitignore", content);
}
catch (System.Exception e)
{
AssetFinderLOG.LogError($"Failed to update .gitignore: {e.Message}");
}
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderGitUtil.cs.meta
================================================
fileFormatVersion: 2
guid: fdb05d77241c6fe4093af96a6787ef3a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderNavigationHistory.cs
================================================
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace VirtueSky.AssetFinder.Editor
{
internal class AssetFinderNavigationHistory
{
private readonly List history = new List();
private int currentIndex = -1;
private const int MAX_HISTORY_SIZE = 20;
private AssetFinderWindowAll window;
private bool isNavigating = false;
public bool CanGoBack => currentIndex > 0 && GetValidHistoryCount() > 1;
public bool CanGoForward => currentIndex < history.Count - 1 && GetValidHistoryCount() > 1;
public void SetWindow(AssetFinderWindowAll windowAll)
{
window = windowAll;
}
public void RecordSelection(UnityObject[] selection)
{
if (selection == null || selection.Length == 0) return;
if (isNavigating) return;
var validSelection = selection.Where(obj => obj != null).ToArray();
if (validSelection.Length == 0) return;
if (currentIndex >= 0 && currentIndex < history.Count)
{
UnityObject[] current = CleanHistoryEntry(history[currentIndex]);
if (AreSelectionsEqual(current, validSelection)) return;
}
if (currentIndex < history.Count - 1)
{
history.RemoveRange(currentIndex + 1, history.Count - currentIndex - 1);
}
history.Add(validSelection.ToArray());
currentIndex = history.Count - 1;
if (history.Count > MAX_HISTORY_SIZE)
{
history.RemoveAt(0);
currentIndex--;
}
}
public bool GoBack()
{
if (!CanGoBack) return false;
CleanInvalidHistoryEntries();
if (currentIndex <= 0) return false;
currentIndex--;
var validSelection = CleanHistoryEntry(history[currentIndex]);
if (validSelection.Length == 0)
{
history.RemoveAt(currentIndex);
if (currentIndex >= history.Count) currentIndex = history.Count - 1;
return GoBack();
}
isNavigating = true;
UpdateFR2SelectionDirectly(validSelection);
isNavigating = false;
return true;
}
public bool GoForward()
{
if (!CanGoForward) return false;
CleanInvalidHistoryEntries();
if (currentIndex >= history.Count - 1) return false;
currentIndex++;
var validSelection = CleanHistoryEntry(history[currentIndex]);
if (validSelection.Length == 0)
{
history.RemoveAt(currentIndex);
currentIndex--;
return GoForward();
}
isNavigating = true;
UpdateFR2SelectionDirectly(validSelection);
isNavigating = false;
return true;
}
private void CleanInvalidHistoryEntries()
{
for (int i = history.Count - 1; i >= 0; i--)
{
var cleanedEntry = CleanHistoryEntry(history[i]);
if (cleanedEntry.Length == 0)
{
history.RemoveAt(i);
if (currentIndex >= i) currentIndex--;
}
else
{
history[i] = cleanedEntry;
}
}
if (currentIndex < 0 && history.Count > 0) currentIndex = 0;
if (currentIndex >= history.Count) currentIndex = history.Count - 1;
}
private UnityObject[] CleanHistoryEntry(UnityObject[] entry)
{
return entry?.Where(obj => obj != null).ToArray() ?? new UnityObject[0];
}
private int GetValidHistoryCount()
{
return history.Count(entry => CleanHistoryEntry(entry).Length > 0);
}
private void UpdateFR2SelectionDirectly(UnityObject[] selection)
{
if (window == null) return;
var validSelection = selection?.Where(obj => obj != null).ToArray() ?? new UnityObject[0];
// For navigation history, we want to directly set both Unity and FR2 selection
// We bypass the smart lock mechanism entirely by setting FR2 selection directly
window.SetFR2Selection(validSelection);
Selection.objects = validSelection;
}
private static bool AreSelectionsEqual(UnityObject[] a, UnityObject[] b)
{
if (a.Length != b.Length) return false;
for (int i = 0; i < a.Length; i++)
{
if (a[i] != b[i]) return false;
}
return true;
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderNavigationHistory.cs.meta
================================================
fileFormatVersion: 2
guid: ac0493b90c0af8f4fb734aa7650864ac
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderSelectionManager.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace VirtueSky.AssetFinder.Editor
{
///
/// Unified Selection Manager for FindReference2
///
/// This system replaces the fragmented selection logic found in multiple classes and provides:
///
/// BENEFITS:
/// - Single source of truth for all selection state
/// - Automatic Unity version compatibility (2019.4+ vs 2021+)
/// - Separation of scene objects vs assets with proper typing
/// - Reduced GC pressure through efficient data structures
/// - Centralized Selection.selectionChanged monitoring
/// - Thread-safe singleton pattern
///
/// ARCHITECTURE:
/// - AssetFinderSelectionManager: Main coordinator and event dispatcher
/// - AssetFinderSceneSelection: Handles GameObject/Component selection with int instanceIds
/// - AssetFinderAssetSelection: Handles asset selection with GUID/FileID pairs
///
/// UNITY VERSION DIFFERENCES HANDLED:
/// - Unity 2018.1+: Uses AssetDatabase.TryGetGUIDAndLocalFileIdentifier
/// - Pre-2018.1: Uses reflection to access m_LocalIdentfierInFile property
///
/// USAGE:
/// - Window classes subscribe to AssetFinderSelectionManager.SelectionChanged
/// - Access current selection via Instance.SceneSelection or Instance.AssetSelection
/// - All selection changes automatically propagate to subscribers
///
/// MIGRATION:
/// - Replaces scattered Selection.objects calls
/// - Eliminates string-based instanceId storage
/// - Removes duplicated Unity version compatibility code
/// - Centralizes selection caching and frame-based optimization
///
[InitializeOnLoad]
internal class AssetFinderSelectionManager
{
public static event System.Action SelectionChanged;
private static AssetFinderSelectionManager _instance;
// Static constructor - called automatically when Unity loads/recompiles
static AssetFinderSelectionManager()
{
// Initialize immediately when class is loaded
Initialize();
}
public static AssetFinderSelectionManager Instance
{
get
{
if (_instance != null) return _instance;
Initialize();
return _instance;
}
}
private AssetFinderSceneSelection sceneSelection;
private AssetFinderAssetSelection assetSelection;
// Cached Unity selection for comparison
private UnityObject[] cachedUnitySelection = Array.Empty();
public AssetFinderSceneSelection SceneSelection => sceneSelection;
public AssetFinderAssetSelection AssetSelection => assetSelection;
public bool IsSelectingSceneObjects => sceneSelection?.Count > 0;
public bool IsSelectingAssets => assetSelection?.Count > 0;
public bool HasSelection => TotalCount > 0;
public int TotalCount => (sceneSelection?.Count ?? 0) + (assetSelection?.Count ?? 0);
private static void Initialize()
{
if (_instance != null)
{
Debug.LogWarning("AssetFinderSelectionManager already initialized - cleaning up first");
Cleanup();
}
_instance = new AssetFinderSelectionManager();
_instance.InitializeInstance();
// Ensure cleanup happens on domain reload
#if UNITY_2019_1_OR_NEWER
UnityEditor.AssemblyReloadEvents.beforeAssemblyReload -= Cleanup;
UnityEditor.AssemblyReloadEvents.beforeAssemblyReload += Cleanup;
#endif
}
private void InitializeInstance()
{
sceneSelection = new AssetFinderSceneSelection();
assetSelection = new AssetFinderAssetSelection();
// Ensure we don't double-subscribe (idempotent)
Selection.selectionChanged -= OnUnitySelectionChanged;
Selection.selectionChanged += OnUnitySelectionChanged;
// Initialize with current Unity selection
RefreshFromUnitySelection();
}
public UnityObject[] GetUnitySelection()
{
return cachedUnitySelection ?? Array.Empty();
}
private void RefreshFromUnitySelection()
{
var currentSelection = Selection.objects ?? Array.Empty();
if (AreSelectionsEqual(cachedUnitySelection, currentSelection)) return;
cachedUnitySelection = currentSelection;
UpdateFR2Selection(currentSelection);
SelectionChanged?.Invoke();
}
private static bool AreSelectionsEqual(UnityObject[] selection1, UnityObject[] selection2)
{
if (selection1 == null && selection2 == null) return true;
if (selection1 == null || selection2 == null) return false;
if (selection1.Length != selection2.Length) return false;
// Order matters for selection comparison
for (int i = 0; i < selection1.Length; i++)
{
if (selection1[i] != selection2[i]) return false;
}
return true;
}
internal void UpdateFR2Selection(UnityObject[] newSelection)
{
sceneSelection.Clear();
assetSelection.Clear();
if (newSelection == null || newSelection.Length == 0) return;
foreach (var obj in newSelection)
{
if (obj == null) continue;
if (AssetDatabase.Contains(obj))
{
assetSelection.AddAsset(obj);
}
else if (obj is GameObject gameObject)
{
sceneSelection.AddGameObject(gameObject);
}
else if (obj is Component component && component.gameObject != null)
{
sceneSelection.AddGameObject(component.gameObject);
}
}
}
private void OnUnitySelectionChanged()
{
// AssetFinderLOG.Log("OnUnitySelectionChanged()");
RefreshFromUnitySelection();
}
public static void Cleanup()
{
if (_instance == null) return;
Selection.selectionChanged -= _instance.OnUnitySelectionChanged;
_instance = null;
}
internal class AssetFinderSceneSelection
{
private readonly HashSet instanceIds = new HashSet();
private readonly Dictionary gameObjects = new Dictionary();
// Cached array - updated only when collection changes
private GameObject[] cachedGameObjectArray = Array.Empty();
private bool arrayDirty = false;
public int Count => instanceIds.Count;
public IReadOnlyCollection InstanceIds => instanceIds;
public IReadOnlyCollection GameObjects => gameObjects.Values;
public void AddGameObject(UnityObject obj)
{
if (obj == null) return;
GameObject go = obj as GameObject ?? (obj as Component)?.gameObject;
if (go == null) return;
int instanceId = go.GetInstanceID();
if (instanceIds.Add(instanceId))
{
gameObjects[instanceId] = go;
arrayDirty = true;
}
}
public void Remove(int instanceId)
{
if (instanceIds.Remove(instanceId))
{
gameObjects.Remove(instanceId);
arrayDirty = true;
}
}
public bool Contains(int instanceId)
{
return instanceIds.Contains(instanceId);
}
public bool Contains(GameObject go)
{
return go != null && instanceIds.Contains(go.GetInstanceID());
}
public void Clear()
{
instanceIds.Clear();
gameObjects.Clear();
arrayDirty = true;
}
public GameObject[] ToArray()
{
if (arrayDirty)
{
cachedGameObjectArray = gameObjects.Values.Where(go => go != null).ToArray();
arrayDirty = false;
}
return cachedGameObjectArray;
}
}
internal class AssetFinderAssetSelection
{
private readonly Dictionary assets = new Dictionary();
// Cached arrays - updated only when collection changes
private string[] cachedGuidsArray = Array.Empty();
private bool guidArrayDirty = false;
public int Count => assets.Count;
public IReadOnlyCollection AssetGuids => assets.Keys;
public IReadOnlyCollection AssetEntries => assets.Values;
public struct AssetEntry
{
public string guid;
public long fileId;
public string assetPath;
public AssetEntry(string guid, long fileId, string assetPath)
{
this.guid = guid;
this.fileId = fileId;
this.assetPath = assetPath;
}
}
public void AddAsset(UnityObject obj)
{
if (obj == null) return;
string assetPath = AssetDatabase.GetAssetPath(obj);
if (string.IsNullOrEmpty(assetPath)) return;
string guid = AssetDatabase.AssetPathToGUID(assetPath);
if (string.IsNullOrEmpty(guid)) return;
long fileId = GetFileId(obj);
if (!assets.ContainsKey(guid))
{
guidArrayDirty = true;
}
assets[guid] = new AssetEntry(guid, fileId, assetPath);
}
private static long GetFileId(UnityObject obj)
{
#if UNITY_2018_1_OR_NEWER
if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out _, out long fileId))
{
return fileId;
}
#else
try
{
var serializedObject = new SerializedObject(obj);
var inspectorModeInfo = typeof(SerializedObject).GetProperty("inspectorMode",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
inspectorModeInfo?.SetValue(serializedObject, InspectorMode.Debug, null);
var localIdProp = serializedObject.FindProperty("m_LocalIdentfierInFile");
if (localIdProp != null)
{
long localId = localIdProp.longValue;
if (localId <= 0) localId = localIdProp.intValue;
return localId;
}
}
catch { }
#endif
return -1;
}
public void Remove(string guid)
{
if (assets.Remove(guid))
{
guidArrayDirty = true;
}
}
public bool Contains(string guid)
{
return assets.ContainsKey(guid);
}
public void Clear()
{
assets.Clear();
guidArrayDirty = true;
}
public string[] GetGuids()
{
if (guidArrayDirty)
{
cachedGuidsArray = assets.Keys.ToArray();
guidArrayDirty = false;
}
return cachedGuidsArray;
}
public AssetEntry? GetAssetEntry(string guid)
{
if (assets.TryGetValue(guid, out var entry)) return entry;
return null;
}
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderSelectionManager.cs.meta
================================================
fileFormatVersion: 2
guid: 21a87406f1e91644592297a6bf1e285d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderSettingExt.cs
================================================
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace VirtueSky.AssetFinder.Editor
{
[Serializable] internal class AssetFinderSettingExt
{
public static AssetFinderAutoRefreshMode autoRefreshMode
{
get => inst._autoRefresh;
set
{
if (inst._autoRefresh == value) return;
inst._autoRefresh = value;
EditorUtility.SetDirty(AssetFinderCache.Api);
EditorApplication.update -= DelaySave;
EditorApplication.update += DelaySave;
}
}
public static bool isAutoRefreshEnabled
{
get
{
if (EditorApplication.isPlayingOrWillChangePlaymode) return false;
return autoRefreshMode == AssetFinderAutoRefreshMode.On;
}
}
public static bool disable
{
get => inst.internalDisabled;
set => inst.internalDisabled = value;
}
public static bool hideToolsWarning
{
get => inst._hideToolsWarning;
set
{
if (inst._hideToolsWarning == value) return;
inst._hideToolsWarning = value;
EditorUtility.SetDirty(AssetFinderCache.Api);
EditorApplication.update -= DelaySave;
EditorApplication.update += DelaySave;
}
}
public static bool isGitProject;
public static bool gitIgnoreAdded
{
get => inst._gitIgnoreAdded;
set
{
if (inst._gitIgnoreAdded == value) return;
inst._gitIgnoreAdded = value;
EditorUtility.SetDirty(AssetFinderCache.Api);
EditorApplication.update -= DelaySave;
EditorApplication.update += DelaySave;
}
}
public static bool hideGitIgnoreWarning
{
get => inst._hideGitIgnoreWarning;
set
{
if (inst._hideGitIgnoreWarning == value) return;
inst._hideGitIgnoreWarning = value;
EditorUtility.SetDirty(AssetFinderCache.Api);
EditorApplication.update -= DelaySave;
EditorApplication.update += DelaySave;
}
}
private const string path = "Library/FR2/fr2.cfg";
private static AssetFinderSettingExt inst;
static AssetFinderSettingExt()
{
inst = new AssetFinderSettingExt();
if (!File.Exists(path)) return;
try
{
string content = File.ReadAllText(path);
JsonUtility.FromJsonOverwrite(content, inst);
}
catch (Exception e)
{
AssetFinderLOG.LogWarning(e);
}
}
static void DelaySave()
{
EditorApplication.update -= DelaySave;
try
{
Directory.CreateDirectory("Library/FR2/");
File.WriteAllText(path, JsonUtility.ToJson(inst));
}
catch (Exception e)
{
AssetFinderLOG.LogWarning(e);
}
}
[SerializeField] private bool _disableInPlayMode = true;
[SerializeField] private bool _disabled;
[SerializeField] private AssetFinderAutoRefreshMode _autoRefresh;
[SerializeField] private bool _hideToolsWarning;
[SerializeField] private bool _isGitProject;
[SerializeField] private bool _gitIgnoreAdded;
[SerializeField] private bool _hideGitIgnoreWarning;
private bool internalDisabled
{
get => _disabled || (_disableInPlayMode && EditorApplication.isPlayingOrWillChangePlaymode);
set
{
ref bool disableRef = ref _disabled;
if (EditorApplication.isPlayingOrWillChangePlaymode) disableRef = ref _disableInPlayMode;
if (disableRef == value) return;
disableRef = value;
// disable at runtime: only disable `disableInPlayMode`
// enable at runtime: enable all
if (!value) _disabled = false;
EditorUtility.SetDirty(AssetFinderCache.Api);
EditorApplication.update -= DelaySave;
EditorApplication.update += DelaySave;
}
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderSettingExt.cs.meta
================================================
fileFormatVersion: 2
guid: 42a3a6faf827daa46aa8f448c8aec943
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderSmartLock.cs
================================================
// #define AssetFinderDEBUG
using System;
using UnityEditor;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace VirtueSky.AssetFinder.Editor
{
internal class AssetFinderSmartLock
{
public enum PingLockState
{
None,
Scene, // Ping/highlight action triggered from scene context
Asset // Ping/highlight action triggered from asset context
}
private PingLockState pingLockState = PingLockState.None;
public void SetPingLockState(PingLockState state)
{
pingLockState = state;
#if AssetFinderDEBUG
if (state != PingLockState.None)
{
AssetFinderLOG.Log($"SmartLock: Set ping lock state to {state}");
}
#endif
}
public bool ConsumePingLockState()
{
bool hadPingLock = pingLockState != PingLockState.None;
if (hadPingLock)
{
// AssetFinderLOG.Log($"SmartLock: Consuming ping lock state {pingLockState}");
pingLockState = PingLockState.None;
}
return hadPingLock;
}
public bool ShouldRefreshWithSmartLogic(EditorWindow window, UnityObject[] panelSelection = null)
{
if (!AssetFinderSelectionManager.Instance.HasSelection) return false;
if (ConsumePingLockState()) return false;
if (panelSelection == null || panelSelection.Length == 0) return true;
return window != EditorWindow.focusedWindow;
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderSmartLock.cs.meta
================================================
fileFormatVersion: 2
guid: b83b815529ea4e343857b73a07eb868b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderWindowFocus.cs
================================================
using System;
using UnityEditor;
using UnityEditor.Compilation;
namespace VirtueSky.AssetFinder.Editor
{
[InitializeOnLoad]
public static class AssetFinderWindowFocus
{
public static event Action FocusedWindowChanged = delegate { };
public static string CurrentWindowType => EditorWindow.focusedWindow?.GetType().Name;
public static string PreviousWindowType { get; private set; }
#if UNITY_6000_0_OR_NEWER
// ---------- Native implementation ----------
static AssetFinderWindowFocus()
{
EditorWindow.windowFocusChanged += Raise;
AssemblyReloadEvents.beforeAssemblyReload += Cleanup;
}
private static void Raise()
{
var current = EditorWindow.focusedWindow;
PreviousWindowType = _lastWindowType;
_lastWindowType = current?.GetType().Name;
if (current != null) FocusedWindowChanged(current);
}
private static void Cleanup() =>
EditorWindow.windowFocusChanged -= Raise;
#elif UNITY_2023_1_OR_NEWER
// ---------- Legacy implementation for Unity 2023-2024 ----------
static AssetFinderWindowFocus()
{
EditorWindow.focusedWindowChanged += Raise;
AssemblyReloadEvents.beforeAssemblyReload += Cleanup;
}
private static void Raise()
{
var current = EditorWindow.focusedWindow;
PreviousWindowType = _lastWindowType;
_lastWindowType = current?.GetType().Name;
if (current != null) FocusedWindowChanged(current);
}
private static void Cleanup() =>
EditorWindow.focusedWindowChanged -= Raise;
#else
// ---------- Fallback: poll each editor-tick ----------
private static EditorWindow _last;
static AssetFinderWindowFocus()
{
EditorApplication.update += Tick;
AssemblyReloadEvents.beforeAssemblyReload += Cleanup;
}
private static void Tick()
{
var current = EditorWindow.focusedWindow;
if (current == _last) return;
PreviousWindowType = _lastWindowType;
_last = current;
_lastWindowType = current?.GetType().Name;
if (current != null) FocusedWindowChanged(current);
}
private static void Cleanup() =>
EditorApplication.update -= Tick;
#endif
private static string _lastWindowType;
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core/AssetFinderWindowFocus.cs.meta
================================================
fileFormatVersion: 2
guid: cdd3a4fc755761c42b4a385842caecbc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Core.meta
================================================
fileFormatVersion: 2
guid: fd43daf252014e319860d77e81a52db8
timeCreated: 1746365393
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Dev/AssetFinderDefine.cs
================================================
namespace VirtueSky.AssetFinder.Editor
{
internal static class AssetFinderDefine
{
internal static bool IsDebugModeEnabled()
{
string cscPath = GetCscFilePath();
return System.IO.File.Exists(cscPath) && HasDefine(System.IO.File.ReadAllText(cscPath), "AssetFinderDEBUG");
}
internal static void ToggleDebugMode(bool enable)
{
string cscPath = GetCscFilePath();
string content = System.IO.File.Exists(cscPath) ? System.IO.File.ReadAllText(cscPath) : "";
content = enable ? AddDefine(content, "AssetFinderDEBUG") : RemoveDefine(content, "AssetFinderDEBUG");
if (!string.IsNullOrWhiteSpace(content))
{
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(cscPath));
System.IO.File.WriteAllText(cscPath, content);
}
else if (System.IO.File.Exists(cscPath))
{
System.IO.File.Delete(cscPath);
}
UnityEditor.AssetDatabase.Refresh();
UnityEngine.Debug.Log($"FR2 Developer Mode {(enable ? "enabled" : "disabled")}. Unity will recompile.");
}
private static string GetCscFilePath()
{
return System.IO.Path.Combine("Assets", "csc.rsp");
}
internal static bool HasDefine(string content, string define)
{
if (string.IsNullOrWhiteSpace(content)) return false;
var defines = ReadDefines(content);
return defines.Contains(define);
}
internal static string AddDefine(string content, string define)
{
var defines = ReadDefines(content);
if (!defines.Contains(define))
{
defines.Add(define);
}
return WriteDefines(defines);
}
internal static string RemoveDefine(string content, string define)
{
var defines = ReadDefines(content);
defines.Remove(define);
return WriteDefines(defines);
}
private static System.Collections.Generic.List ReadDefines(string content)
{
var result = new System.Collections.Generic.List();
if (string.IsNullOrWhiteSpace(content)) return result;
string[] lines = content.Split('\n');
foreach (string line in lines)
{
string trimmedLine = line.Trim();
if (trimmedLine.StartsWith("-define:"))
{
// Extract existing symbols from -define: (same logic as GDK)
string definesString = trimmedLine.Substring(8); // Skip "-define:"
if (!string.IsNullOrEmpty(definesString))
{
result.AddRange(definesString.Split(';'));
}
}
}
return result;
}
private static string WriteDefines(System.Collections.Generic.List defines)
{
// Clean up empty/whitespace defines
var cleanDefines = new System.Collections.Generic.List();
foreach (string define in defines)
{
string trimmed = define?.Trim();
if (!string.IsNullOrEmpty(trimmed))
{
cleanDefines.Add(trimmed);
}
}
// Write exactly like GDK does
return cleanDefines.Count > 0 ? $"-define:{string.Join(";", cleanDefines)}" : string.Empty;
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Dev/AssetFinderDefine.cs.meta
================================================
fileFormatVersion: 2
guid: 6012e303bbe76174e8881b5cb101e950
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Dev/AssetFinderDev.cs
================================================
// #define AssetFinderDEV
using System;
namespace VirtueSky.AssetFinder.Editor
{
public class AssetFinderDev
{
public static IDisposable NoLog => new NoLogScope();
private readonly struct NoLogScope : IDisposable
{
#if AssetFinderDEV
internal NoLogScope(bool _) { }
public void Dispose() { }
#else
private readonly bool _saved;
internal NoLogScope(bool _)
{
_saved = UnityEngine.Debug.unityLogger.logEnabled;
UnityEngine.Debug.unityLogger.logEnabled = false;
}
public void Dispose() => UnityEngine.Debug.unityLogger.logEnabled = _saved;
#endif
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Dev/AssetFinderDev.cs.meta
================================================
fileFormatVersion: 2
guid: 2e217d3aaf8ffd94d97abbbf9b0e9df5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Dev/AssetFinderReferenceValidator.cs
================================================
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace VirtueSky.AssetFinder.Editor
{
#if AssetFinderDEV
///
/// Comprehensive reference validation system comparing FR2 vs Unity's GetDependencies
///
internal class AssetFinderReferenceValidator
{
private struct ValidationResult
{
public int totalAssets;
public int assetsWithDifferences;
public int missingInFR2;
public int extraInFR2;
public List differences;
public Dictionary missingByExtension;
public Dictionary extraByExtension;
public Dictionary assetTypeIssues;
}
private struct AssetDifference
{
public string assetPath;
public string guid;
public string assetType;
public List missingInFR2; // In Unity but not in FR2
public List extraInFR2; // In FR2 but not in Unity
public string summary;
}
private readonly Dictionary unityDependencyCache = new Dictionary();
private readonly System.Text.StringBuilder reportBuilder = new System.Text.StringBuilder();
public void ValidateAllReferences(bool exportToFile = false)
{
var startTime = System.DateTime.Now;
var result = new ValidationResult
{
differences = new List(),
missingByExtension = new Dictionary(),
extraByExtension = new Dictionary(),
assetTypeIssues = new Dictionary()
};
// Clear caches
unityDependencyCache.Clear();
reportBuilder.Clear();
// Get all critical assets from FR2 cache
var criticalAssets = AssetFinderCache.Api.AssetList
.Where(asset => asset != null && !asset.IsMissing && asset.IsCriticalAsset())
.ToList();
result.totalAssets = criticalAssets.Count;
AppendToReport($"=== FR2 REFERENCE VALIDATION REPORT ===");
AppendToReport($"Generated: {System.DateTime.Now:yyyy-MM-dd HH:mm:ss}");
AppendToReport($"Total critical assets to analyze: {result.totalAssets}");
AppendToReport("");
AssetFinderLOG.Log($"[AssetFinderVALIDATION] Analyzing {result.totalAssets} critical assets...");
var processedCount = 0;
var lastProgressReport = 0;
foreach (var asset in criticalAssets)
{
processedCount++;
// Progress reporting (every 10%)
var progressPercent = (processedCount * 100) / result.totalAssets;
if (progressPercent >= lastProgressReport + 10)
{
lastProgressReport = progressPercent;
AssetFinderLOG.Log($"[AssetFinderVALIDATION] Progress: {progressPercent}% ({processedCount}/{result.totalAssets})");
}
var difference = CompareAssetReferences(asset);
if (difference.missingInFR2.Count > 0 || difference.extraInFR2.Count > 0)
{
result.differences.Add(difference);
result.assetsWithDifferences++;
result.missingInFR2 += difference.missingInFR2.Count;
result.extraInFR2 += difference.extraInFR2.Count;
// Track by extension and asset type
string assetExt = System.IO.Path.GetExtension(asset.assetPath).ToLower();
result.assetTypeIssues[assetExt] = result.assetTypeIssues.GetValueOrDefault(assetExt, 0) + 1;
foreach (var missing in difference.missingInFR2)
{
string depPath = ExtractPathFromDependencyString(missing);
string depExt = System.IO.Path.GetExtension(depPath).ToLower();
result.missingByExtension[depExt] = result.missingByExtension.GetValueOrDefault(depExt, 0) + 1;
}
foreach (var extra in difference.extraInFR2)
{
string depPath = ExtractPathFromDependencyString(extra);
string depExt = System.IO.Path.GetExtension(depPath).ToLower();
result.extraByExtension[depExt] = result.extraByExtension.GetValueOrDefault(depExt, 0) + 1;
}
}
}
var duration = System.DateTime.Now - startTime;
LogValidationResults(result, duration, exportToFile);
}
private AssetDifference CompareAssetReferences(AssetFinderAsset asset)
{
var difference = new AssetDifference
{
assetPath = asset.assetPath,
guid = asset.guid,
assetType = asset.type.ToString(),
missingInFR2 = new List(),
extraInFR2 = new List()
};
try
{
// Get Unity's dependencies (with caching)
string[] unityDeps;
if (!unityDependencyCache.TryGetValue(asset.assetPath, out unityDeps))
{
unityDeps = AssetDatabase.GetDependencies(asset.assetPath, false); // Direct dependencies only
unityDependencyCache[asset.assetPath] = unityDeps;
}
var unityGuids = new HashSet();
foreach (string depPath in unityDeps)
{
if (depPath == asset.assetPath) continue; // Skip self-reference
string depGuid = AssetDatabase.AssetPathToGUID(depPath);
if (!string.IsNullOrEmpty(depGuid))
{
unityGuids.Add(depGuid);
}
}
// Get FR2's dependencies
var fr2Guids = new HashSet();
if (asset.UseGUIDs != null)
{
fr2Guids.UnionWith(asset.UseGUIDs.Keys);
}
// Find differences
foreach (string unityGuid in unityGuids)
{
if (!fr2Guids.Contains(unityGuid))
{
string depPath = AssetDatabase.GUIDToAssetPath(unityGuid);
difference.missingInFR2.Add($"{unityGuid} ({depPath})");
}
}
foreach (string fr2Guid in fr2Guids)
{
if (!unityGuids.Contains(fr2Guid))
{
string depPath = AssetDatabase.GUIDToAssetPath(fr2Guid);
difference.extraInFR2.Add($"{fr2Guid} ({depPath})");
}
}
// Create summary
if (difference.missingInFR2.Count > 0 || difference.extraInFR2.Count > 0)
{
difference.summary = $"Missing: {difference.missingInFR2.Count}, Extra: {difference.extraInFR2.Count}";
}
}
catch (System.Exception e)
{
difference.summary = $"Error during comparison: {e.Message}";
}
return difference;
}
private void LogValidationResults(ValidationResult result, System.TimeSpan duration, bool exportToFile)
{
AppendToReport($"Analysis completed in {duration.TotalSeconds:F2} seconds");
AppendToReport($"Assets with differences: {result.assetsWithDifferences}");
AppendToReport($"Total missing references in FR2: {result.missingInFR2}");
AppendToReport($"Total extra references in FR2: {result.extraInFR2}");
AppendToReport("");
if (result.assetsWithDifferences == 0)
{
AppendToReport("🎉 Perfect match! FR2 and Unity have identical reference detection.");
AssetFinderLOG.Log("🎉 Perfect match! FR2 and Unity have identical reference detection.");
}
else
{
float accuracy = ((result.totalAssets - result.assetsWithDifferences) * 100f / result.totalAssets);
AppendToReport($"Accuracy: {accuracy:F1}%");
AssetFinderLOG.Log($"[AssetFinderVALIDATION] Accuracy: {accuracy:F1}%");
GenerateDetailedReport(result);
}
if (exportToFile)
{
string filePath = System.IO.Path.Combine(Application.dataPath, "../AssetFinderValidation_Report.txt");
System.IO.File.WriteAllText(filePath, reportBuilder.ToString());
AssetFinderLOG.Log($"[AssetFinderVALIDATION] Detailed report exported to: {filePath}");
}
else
{
// Console output (limited)
AssetFinderLOG.Log("=== FR2 REFERENCE VALIDATION RESULTS ===");
AssetFinderLOG.Log($"Analysis completed in {duration.TotalSeconds:F2} seconds");
AssetFinderLOG.Log($"Total assets analyzed: {result.totalAssets}");
AssetFinderLOG.Log($"Assets with differences: {result.assetsWithDifferences}");
if (result.assetsWithDifferences > 0)
{
AssetFinderLOG.Log($"Accuracy: {((result.totalAssets - result.assetsWithDifferences) * 100f / result.totalAssets):F1}%");
AssetFinderLOG.Log("Use 'Validate References (Export to File)' for detailed analysis.");
}
}
}
private void GenerateDetailedReport(ValidationResult result)
{
AppendToReport("=== TOP PROBLEMATIC ASSET TYPES ===");
foreach (var kvp in result.assetTypeIssues.OrderByDescending(x => x.Value).Take(10))
{
AppendToReport($"{kvp.Key}: {kvp.Value} assets with differences");
}
AppendToReport("");
AppendToReport("=== MOST COMMONLY MISSED DEPENDENCY TYPES ===");
foreach (var kvp in result.missingByExtension.OrderByDescending(x => x.Value).Take(10))
{
AppendToReport($"{kvp.Key}: {kvp.Value} instances");
}
AppendToReport("");
AppendToReport("=== MOST COMMONLY OVER-DETECTED TYPES ===");
foreach (var kvp in result.extraByExtension.OrderByDescending(x => x.Value).Take(10))
{
AppendToReport($"{kvp.Key}: {kvp.Value} instances");
}
AppendToReport("");
AppendToReport("=== DETAILED DIFFERENCES ===");
var maxDetailsToShow = Mathf.Min(50, result.differences.Count);
if (result.differences.Count > maxDetailsToShow)
{
AppendToReport($"Showing first {maxDetailsToShow} differences (out of {result.differences.Count} total):");
}
for (int i = 0; i < maxDetailsToShow; i++)
{
var diff = result.differences[i];
AppendToReport($"\n[{i + 1}] {diff.assetPath}");
AppendToReport($" Type: {diff.assetType} | GUID: {diff.guid}");
AppendToReport($" Summary: {diff.summary}");
if (diff.missingInFR2.Count > 0)
{
AppendToReport($" Missing in FR2 ({diff.missingInFR2.Count}):");
foreach (var missing in diff.missingInFR2.Take(5))
{
AppendToReport($" - {missing}");
}
if (diff.missingInFR2.Count > 5)
{
AppendToReport($" ... and {diff.missingInFR2.Count - 5} more");
}
}
if (diff.extraInFR2.Count > 0)
{
AppendToReport($" Extra in FR2 ({diff.extraInFR2.Count}):");
foreach (var extra in diff.extraInFR2.Take(5))
{
AppendToReport($" - {extra}");
}
if (diff.extraInFR2.Count > 5)
{
AppendToReport($" ... and {diff.extraInFR2.Count - 5} more");
}
}
}
}
private void AppendToReport(string line)
{
reportBuilder.AppendLine(line);
}
private string ExtractPathFromDependencyString(string depString)
{
// Extract path from format "guid (path)"
int startParen = depString.IndexOf('(');
int endParen = depString.IndexOf(')', startParen);
if (startParen >= 0 && endParen > startParen)
{
return depString.Substring(startParen + 1, endParen - startParen - 1);
}
return depString;
}
private void AnalyzeValidationPatterns(List differences)
{
// This method is now integrated into GenerateDetailedReport
}
}
#endif
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Dev/AssetFinderReferenceValidator.cs.meta
================================================
fileFormatVersion: 2
guid: 95909f9cceec2c94d98b1f9a061b26d3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Dev/AssetFinderWindowAll.Validator.cs
================================================
using UnityEditor;
using UnityEngine;
namespace VirtueSky.AssetFinder.Editor
{
internal partial class AssetFinderWindowAll
{
public override void AddToCustomMenu(GenericMenu menu)
{
#if AssetFinderDEV
menu.AddItem(new GUIContent("Refresh Cache"), false, () => AssetFinderCache.Api.Check4Changes(true));
menu.AddItem(new GUIContent("Validate References vs Unity"), false, ()=>ValidateReferencesVsUnity());
menu.AddItem(new GUIContent("Validate References (Export to File)"), false, () => ValidateReferencesVsUnity(true));
menu.AddItem(new GUIContent("Debug Selected Assets"), false, DebugSelectedAssets);
#endif
}
#if AssetFinderDEV
private void ValidateReferencesVsUnity(bool exportToFile = false)
{
if (!AssetFinderCache.isReady)
{
AssetFinderLOG.LogWarning("[AssetFinderVALIDATION] Cache not ready. Please wait for cache to finish loading.");
return;
}
if (exportToFile)
{
AssetFinderLOG.Log("[AssetFinderVALIDATION] Starting validation with file export...");
}
else
{
AssetFinderLOG.Log("[AssetFinderVALIDATION] Starting comprehensive reference validation against Unity's GetDependencies...");
}
var validator = new AssetFinderReferenceValidator();
validator.ValidateAllReferences(exportToFile);
}
private void DebugSelectedAssets()
{
if (Selection.objects == null || Selection.objects.Length == 0)
{
AssetFinderLOG.LogWarning("[AssetFinderDEBUG] No objects selected for debugging");
return;
}
foreach (var obj in Selection.objects)
{
string path = AssetDatabase.GetAssetPath(obj);
if (string.IsNullOrEmpty(path)) continue;
string guid = AssetDatabase.AssetPathToGUID(path);
Debug.Log($"[AssetFinderDEBUG] === {obj.name} ({guid}) ===");
if (!AssetFinderCache.isReady)
{
AssetFinderLOG.LogWarning("[AssetFinderDEBUG] Cache not ready!");
continue;
}
AssetFinderAsset asset = AssetFinderCache.Api.Get(guid);
if (asset == null)
{
AssetFinderLOG.LogWarning("[AssetFinderDEBUG] Asset not found in cache!");
continue;
}
Debug.Log($"Type: {asset.type} | Critical: {asset.IsCriticalAsset()} | Extension: {asset.extension}");
AssetFinderLOG.Log($"Uses: {asset.UseGUIDs?.Count ?? 0} | UsedBy: {asset.UsedByMap?.Count ?? 0}");
Debug.Log($"InAssetList: {AssetFinderCache.Api.AssetList?.Contains(asset) ?? false} | Dirty: {asset.isDirty}");
}
}
#endif
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Dev/AssetFinderWindowAll.Validator.cs.meta
================================================
fileFormatVersion: 2
guid: dd971677ed211ef41826f4ab2d3bb028
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Dev.meta
================================================
fileFormatVersion: 2
guid: d6d7bf6fb14b44c697ed962d9ca4fac6
timeCreated: 1753192840
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Drawer/AssetFinderAddressableDrawer.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace VirtueSky.AssetFinder.Editor
{
internal class AssetFinderAddressableDrawer : IRefDraw
{
private const string AUTO_DEPEND_TITLE = "(Auto dependency)";
private readonly Dictionary AsmMessage = new Dictionary
{
{ AssetFinderAddressable.ASMStatus.None, "-" },
{ AssetFinderAddressable.ASMStatus.AsmNotFound, "Addressable Package not imported!" },
{ AssetFinderAddressable.ASMStatus.TypeNotFound, "Addressable Classes not found (addressable library code changed?)!" },
{ AssetFinderAddressable.ASMStatus.FieldNotFound, "Addressable Fields not found (addressable library code changed?)!" },
{ AssetFinderAddressable.ASMStatus.AsmOK, "-" }
};
internal readonly AssetFinderRefDrawer drawer;
internal readonly Dictionary map = new Dictionary();
private readonly Dictionary ProjectStatusMessage = new Dictionary
{
{ AssetFinderAddressable.ProjectStatus.None, "-" },
{ AssetFinderAddressable.ProjectStatus.NoSettings, "No Addressables Settings found!\nOpen [Window/Asset Management/Addressables/Groups] to create new Addressables Settings!\n \n" },
{ AssetFinderAddressable.ProjectStatus.NoGroup, "No AssetBundle Group created!" },
{ AssetFinderAddressable.ProjectStatus.Ok, "-" }
};
private bool dirty;
internal List groups;
internal float maxWidth;
internal Dictionary refs;
public AssetFinderAddressableDrawer(IWindow window, Func getSortMode, Func getGroupMode)
{
this.window = window;
drawer = new AssetFinderRefDrawer(new AssetFinderRefDrawer.AssetDrawingConfig
{
window = window,
getSortMode = getSortMode,
getGroupMode = getGroupMode,
showFullPath = false,
showFileSize = true,
showExtension = true,
showUsageType = false,
showAssetBundleName = false,
showAtlasName = false
})
{
messageNoRefs = "No Addressable Asset",
messageEmpty = "No Addressable Asset",
customGetGroup = GetGroup,
customDrawGroupLabel = DrawGroupLabel,
beforeItemDraw = BeforeDrawItem,
afterItemDraw = AfterDrawItem
};
dirty = true;
drawer.SetDirty();
}
public IWindow window { get; set; }
public int ElementCount()
{
return refs?.Count ?? 0;
}
public bool Draw(Rect rect)
{
if (dirty) RefreshView();
if (refs == null) return false;
rect.yMax -= 24f;
bool result = drawer.Draw(rect);
Rect btnRect = rect;
btnRect.xMin = btnRect.xMax - 24f;
btnRect.yMin = btnRect.yMax;
btnRect.height = 24f;
if (GUI.Button(btnRect, AssetFinderIcon.Refresh.image))
{
AssetFinderAddressable.Scan();
RefreshView();
}
return result;
}
public bool DrawLayout()
{
if (dirty) RefreshView();
return drawer.DrawLayout();
}
private string GetGroup(AssetFinderRef rf)
{
return rf.group;
}
private void DrawGroupLabel(Rect r, string label, int childCount)
{
Color c = GUI.contentColor;
if (label == AUTO_DEPEND_TITLE)
{
Color c1 = c;
c1.a = 0.5f;
GUI.contentColor = c1;
}
GUI.Label(r, AssetFinderGUIContent.FromString(label), EditorStyles.boldLabel);
GUI.contentColor = c;
}
private void BeforeDrawItem(Rect r, AssetFinderRef rf)
{
string guid = rf.asset.guid;
if (map.TryGetValue(guid, out AssetFinderAddressable.AddressInfo address)) return;
Color c = GUI.contentColor;
c.a = 0.35f;
GUI.contentColor = c;
}
private void AfterDrawItem(Rect r, AssetFinderRef rf)
{
string guid = rf.asset.guid;
if (!map.TryGetValue(guid, out AssetFinderAddressable.AddressInfo address))
{
Color c2 = GUI.contentColor;
c2.a = 1f;
GUI.contentColor = c2;
return;
}
Color c = GUI.contentColor;
Color c1 = c;
c1.a = 0.5f;
GUI.contentColor = c1;
{
r.xMin = r.xMax - maxWidth;
GUI.Label(r, AssetFinderGUIContent.FromString(address.address), EditorStyles.miniLabel);
}
GUI.contentColor = c;
}
public void SetDirty()
{
dirty = true;
drawer.SetDirty();
}
public void RefreshView()
{
if (refs == null) refs = new Dictionary();
refs.Clear();
Dictionary addresses = AssetFinderAddressable.GetAddresses();
if (AssetFinderAddressable.asmStatus != AssetFinderAddressable.ASMStatus.AsmOK)
{
drawer.messageNoRefs = AsmMessage[AssetFinderAddressable.asmStatus];
} else if (AssetFinderAddressable.projectStatus != AssetFinderAddressable.ProjectStatus.Ok)
{
drawer.messageNoRefs = ProjectStatusMessage[AssetFinderAddressable.projectStatus];
}
drawer.messageEmpty = drawer.messageNoRefs;
if (addresses == null) addresses = new Dictionary();
groups = addresses.Keys.ToList();
map.Clear();
if (addresses.Count > 0)
{
var maxLengthGroup = string.Empty;
foreach (KeyValuePair kvp in addresses)
{
foreach (string guid in kvp.Value.assetGUIDs)
{
if (refs.ContainsKey(guid)) continue;
AssetFinderAsset asset = AssetFinderCache.Api.Get(guid);
refs.Add(guid, new AssetFinderRef(0, 1, asset, null, null)
{
isSceneRef = false,
group = kvp.Value.bundleGroup
});
map.Add(guid, kvp.Value);
if (maxLengthGroup.Length < kvp.Value.address.Length)
{
maxLengthGroup = kvp.Value.address;
}
}
foreach (string guid in kvp.Value.childGUIDs)
{
if (refs.ContainsKey(guid)) continue;
AssetFinderAsset asset = AssetFinderCache.Api.Get(guid);
refs.Add(guid, new AssetFinderRef(0, 1, asset, null, null)
{
isSceneRef = false,
group = kvp.Value.bundleGroup
});
map.Add(guid, kvp.Value);
if (maxLengthGroup.Length < kvp.Value.address.Length)
{
maxLengthGroup = kvp.Value.address;
}
}
}
var miniLabelStyle = EditorStyles.miniLabel ?? new GUIStyle();
maxWidth = miniLabelStyle.CalcSize(
AssetFinderGUIContent.FromString(maxLengthGroup)
).x + 16f;
// Find usage
Dictionary usages = AssetFinderRef.FindUsage(map.Keys.ToArray());
foreach (KeyValuePair kvp in usages)
{
if (refs.ContainsKey(kvp.Key)) continue;
AssetFinderRef v = kvp.Value;
// do not take script
if (v.asset.IsScript) continue;
if (v.asset.IsExcluded) continue;
refs.Add(kvp.Key, kvp.Value);
kvp.Value.depth = 1;
kvp.Value.group = AUTO_DEPEND_TITLE;
}
}
dirty = false;
drawer.SetRefs(refs);
}
internal void RefreshSort()
{
drawer.RefreshSort();
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Drawer/AssetFinderAddressableDrawer.cs.meta
================================================
fileFormatVersion: 2
guid: d1f842a83edda6e42a6c4ea4dc024049
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Drawer/AssetFinderAssetGroup.cs
================================================
using System.Collections.Generic;
namespace VirtueSky.AssetFinder.Editor
{
internal class AssetFinderAssetGroup
{
public string name;
public HashSet extension;
public AssetFinderAssetGroup(string name, params string[] exts)
{
this.name = name;
extension = new HashSet();
for (var i = 0; i < exts.Length; i++)
{
extension.Add(exts[i]);
}
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Drawer/AssetFinderAssetGroup.cs.meta
================================================
fileFormatVersion: 2
guid: bd3abcb0505d46544820add49a5c7377
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Drawer/AssetFinderAssetGroupDrawer.cs
================================================
using UnityEditor;
using UnityEngine;
namespace VirtueSky.AssetFinder.Editor
{
internal static class AssetFinderAssetGroupDrawer
{
// ------------------------------- STATIC -----------------------------
internal static readonly AssetFinderAssetGroup[] FILTERS =
{
new AssetFinderAssetGroup("Scene", ".unity"),
new AssetFinderAssetGroup("Prefab", ".prefab"),
new AssetFinderAssetGroup("Model", ".3df", ".3dm", ".3dmf", ".3dv", ".3dx", ".c5d", ".lwo", ".lws", ".ma", ".mb",
".mesh", ".vrl", ".wrl", ".wrz", ".fbx", ".dae", ".3ds", ".dxf", ".obj", ".skp", ".max", ".blend"),
new AssetFinderAssetGroup("Material", ".mat", ".cubemap", ".physicsmaterial"),
new AssetFinderAssetGroup("Texture", ".ai", ".apng", ".png", ".bmp", ".cdr", ".dib", ".eps", ".exif", ".ico", ".icon",
".j", ".j2c", ".j2k", ".jas", ".jiff", ".jng", ".jp2", ".jpc", ".jpe", ".jpeg", ".jpf", ".jpg", "jpw",
"jpx", "jtf", ".mac", ".omf", ".qif", ".qti", "qtif", ".tex", ".tfw", ".tga", ".tif", ".tiff", ".wmf",
".psd", ".exr", ".rendertexture"),
new AssetFinderAssetGroup("Video", ".asf", ".asx", ".avi", ".dat", ".divx", ".dvx", ".mlv", ".m2l", ".m2t", ".m2ts",
".m2v", ".m4e", ".m4v", "mjp", ".mov", ".movie", ".mp21", ".mp4", ".mpe", ".mpeg", ".mpg", ".mpv2",
".ogm", ".qt", ".rm", ".rmvb", ".wmv", ".xvid", ".flv"),
new AssetFinderAssetGroup("Audio", ".mp3", ".wav", ".ogg", ".aif", ".aiff", ".mod", ".it", ".s3m", ".xm"),
new AssetFinderAssetGroup("Script", ".cs", ".js", ".boo", ".h"),
new AssetFinderAssetGroup("Text", ".txt", ".json", ".xml", ".bytes", ".sql"),
new AssetFinderAssetGroup("Shader", ".shader", ".cginc", ".shadervariants"),
new AssetFinderAssetGroup("Animation", ".anim", ".controller", ".overridecontroller", ".mask"),
new AssetFinderAssetGroup("Font", ".ttf", ".otf", ".dfont", ".ttc"),
new AssetFinderAssetGroup("Unity Asset", ".asset", ".guiskin", ".flare", ".fontsettings", ".prefs", ".playable", ".signal"),
new AssetFinderAssetGroup("Others") //
};
private static AssetFinderIgnoreDrawer _ignore;
private static AssetFinderIgnoreDrawer ignore
{
get
{
if (_ignore == null) _ignore = new AssetFinderIgnoreDrawer();
return _ignore;
}
}
public static int GetIndex(string ext)
{
// Normalize extension to lowercase for case-insensitive comparison
string normalizedExt = ext?.ToLowerInvariant() ?? "";
for (var i = 0; i < FILTERS.Length - 1; i++)
{
if (FILTERS[i].extension.Contains(normalizedExt)) return i;
}
return FILTERS.Length - 1; //Others
}
public static bool DrawSearchFilter()
{
int n = FILTERS.Length;
var nCols = 4;
int nRows = Mathf.CeilToInt(n / (float)nCols);
var result = false;
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);
{
if (GUILayout.Button("All", EditorStyles.toolbarButton) && !AssetFinderSetting.IsIncludeAllType())
{
AssetFinderSetting.IncludeAllType();
result = true;
}
if (GUILayout.Button("None", EditorStyles.toolbarButton) && (AssetFinderSetting.GetExcludeType() != -1))
{
AssetFinderSetting.ExcludeAllType();
result = true;
}
}
EditorGUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
for (var i = 0; i < nCols; i++)
{
GUILayout.BeginVertical();
for (var j = 0; j < nRows; j++)
{
int idx = i * nCols + j;
if (idx >= n) break;
bool s = !AssetFinderSetting.IsTypeExcluded(idx);
bool s1 = GUILayout.Toggle(s, FILTERS[idx].name);
if (s1 != s)
{
result = true;
AssetFinderSetting.ToggleTypeExclude(idx);
}
}
GUILayout.EndVertical();
if ((i + 1) * nCols >= n) break;
}
GUILayout.EndHorizontal();
return result;
}
public static void SetDirtyIgnore()
{
ignore.SetDirty();
}
public static bool DrawIgnoreFolder()
{
var change = false;
ignore.Draw();
return change;
}
}
}
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Drawer/AssetFinderAssetGroupDrawer.cs.meta
================================================
fileFormatVersion: 2
guid: 253072b20ae7c4f4d8119fcba52ca790
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
================================================
FILE: VirtueSky/AssetFinder/Editor/Script/Drawer/AssetFinderAssetOrganizer.cs
================================================
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityObject = UnityEngine.Object;
namespace VirtueSky.AssetFinder.Editor
{
internal class AssetFinderAssetOrganizer : IRefDraw
{
// Processing state and dependencies
private readonly IWindow _window;
private bool _isProcessing;
private float _progress;
private string _currentFolderPath;
private HashSet _foldersToProcess = new HashSet();
private Dictionary> _assetsToMove = new Dictionary>();
private List _plannedMoves = new List();
// Results tracking
private Dictionary _organizedFolders = new Dictionary();
private List _errorAssets = new List();
private string _reportTitle;
// Settings backup
private AssetFinderAutoRefreshMode _originalAutoRefreshMode;
// Asset classification
private static readonly string[] SpecialFolders = {"/Editor/", "/Resources/", "/StreamingAssets/", "/Gizmos/", "/Plugins/", "/Standard Assets/"};
private static readonly string[] ScriptExtensions = { ".cs", ".js", ".boo" };
private HashSet _spriteAtlasFolders = new HashSet();
private Dictionary _spriteAtlasInfo = new Dictionary();
private Dictionary _spineAssetInfo = new Dictionary();
private HashSet _spineAssets = new HashSet();
internal AssetFinderAssetOrganizer(IWindow window, Func getSort, Func getGroup)
{
_window = window;
}
public IWindow window => _window;
public int ElementCount()
{
return _organizedFolders.Count + _errorAssets.Count;
}
public bool Draw(Rect rect)
{
GUI.BeginClip(rect);
GUILayout.BeginArea(new Rect(0, 0, rect.width, rect.height));
bool result = DrawLayout();
GUILayout.EndArea();
GUI.EndClip();
return result;
}
public bool DrawLayout()
{
GUILayout.BeginVertical();
{
if (_isProcessing)
{
DrawProgressBar();
}
else
{
DrawSelectedFolders();
GUILayout.Space(10);
bool hasFoldersSelected = _foldersToProcess.Count > 0;
GUI.enabled = hasFoldersSelected;
if (GUILayout.Button("Organize Selected Folder" + (hasFoldersSelected && _foldersToProcess.Count > 1 ? "s" : ""), AssetFinderTheme.Current.ActionButtonHeight))
{
StartProcessing();
}
GUI.enabled = true;
if (!hasFoldersSelected)
{
EditorGUILayout.HelpBox("Please select one or more folders in the Project panel to organize.", MessageType.Info);
}
if (!string.IsNullOrEmpty(_reportTitle))
{
EditorGUILayout.HelpBox(_reportTitle, MessageType.Info);
}
if (_organizedFolders.Count > 0)
{
GUILayout.Space(10);
EditorGUILayout.LabelField("Organized Assets:", EditorStyles.boldLabel);
GUILayout.BeginVertical(EditorStyles.helpBox);
foreach (var folder in _organizedFolders.OrderBy(f => f.Key))
{
EditorGUILayout.LabelField($"{folder.Key}: {folder.Value} assets", EditorStyles.miniLabel);
}
GUILayout.EndVertical();
}
if (_errorAssets.Count > 0)
{
GUILayout.Space(10);
EditorGUILayout.LabelField("Errors:", EditorStyles.boldLabel);
GUILayout.BeginVertical(EditorStyles.helpBox);
foreach (string error in _errorAssets.Take(10))
{
EditorGUILayout.LabelField(error, EditorStyles.miniLabel);
}
if (_errorAssets.Count > 10)
{
EditorGUILayout.LabelField($"...and {_errorAssets.Count - 10} more", EditorStyles.miniLabel);
}
GUILayout.EndVertical();
}
}
}
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
return true;
}
private void DrawSelectedFolders()
{
_foldersToProcess.Clear();
foreach (UnityObject obj in Selection.objects)
{
string path = AssetDatabase.GetAssetPath(obj);
if (string.IsNullOrEmpty(path)) continue;
if (AssetDatabase.IsValidFolder(path))
{
_foldersToProcess.Add(path);
}
}
CleanupSelectionHierarchy();
if (_foldersToProcess.Count > 0)
{
EditorGUILayout.LabelField("Selected Folders:", EditorStyles.boldLabel);
GUILayout.BeginVertical(EditorStyles.helpBox);
foreach (string folder in _foldersToProcess.OrderBy(f => f))
{
EditorGUILayout.LabelField(folder, EditorStyles.miniLabel);
}
GUILayout.EndVertical();
}
}
private void CleanupSelectionHierarchy()
{
var foldersToRemove = new HashSet();
foreach (string folder in _foldersToProcess)
{
foreach (string otherFolder in _foldersToProcess)
{
if (folder != otherFolder && folder.StartsWith(otherFolder + "/"))
{
foldersToRemove.Add(folder);
}
}
}
foreach (string folderToRemove in foldersToRemove)
{
_foldersToProcess.Remove(folderToRemove);
}
}
private void DrawProgressBar()
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("Organizing assets...", EditorStyles.boldLabel);
Rect rect = AssetFinderTheme.Current.GetProgressBarRect();
EditorGUI.ProgressBar(rect, _progress, _currentFolderPath);
EditorGUILayout.Space();
if (GUILayout.Button("Cancel", AssetFinderTheme.Current.CancelButtonHeight))
{
CancelProcessing();
}
}
private void StartProcessing()
{
DisableAutoRefresh();
_isProcessing = true;
_progress = 0f;
_currentFolderPath = string.Empty;
_organizedFolders.Clear();
_errorAssets.Clear();
_assetsToMove.Clear();
_plannedMoves.Clear();
_reportTitle = null;
_spriteAtlasFolders.Clear();
_spriteAtlasInfo.Clear();
_spineAssetInfo.Clear();
_spineAssets.Clear();
AnalyzeSpineAssets();
AnalyzeSpriteAtlases();
foreach (string folderPath in _foldersToProcess)
{
AnalyzeFolder(folderPath);
}
CleanupEmptyMoveEntries();
var scriptMoves = new Dictionary>();
var nonScriptMoves = new Dictionary>();
foreach (var kvp in _assetsToMove)
{
var scripts = new List();
var nonScripts = new List();
foreach (var assetPath in kvp.Value)
{
string ext = Path.GetExtension(assetPath).ToLowerInvariant();
if (ScriptExtensions.Contains(ext)) scripts.Add(assetPath);
else nonScripts.Add(assetPath);
}
if (nonScripts.Count > 0) nonScriptMoves[kvp.Key] = nonScripts;
if (scripts.Count > 0) scriptMoves[kvp.Key] = scripts;
}
_assetsToMove.Clear();
foreach (var kvp in nonScriptMoves)
{
_assetsToMove[kvp.Key] = kvp.Value;
}
foreach (var kvp in scriptMoves)
{
_assetsToMove[kvp.Key] = kvp.Value;
}
PlanAllMoves();
if (_plannedMoves.Count == 0)
{
_reportTitle = "No assets needed to be organized.";
CleanupAfterProcessing();
return;
}
EditorApplication.update -= ProcessNextBatch;
EditorApplication.update += ProcessNextBatch;
}
private void CancelProcessing()
{
_isProcessing = false;
CleanupAfterProcessing();
}
private void CleanupAfterProcessing()
{
_isProcessing = false;
RestoreAutoRefreshMode();
AssetFinderDeleteEmptyFolder.DeleteAllEmptyFoldersRecursive("Assets");
AssetDatabase.Refresh();
_window.Repaint();
}
private void DisableAutoRefresh()
{
_originalAutoRefreshMode = AssetFinderSettingExt.autoRefreshMode;
AssetFinderSettingExt.autoRefreshMode = AssetFinderAutoRefreshMode.Off;
}
private void RestoreAutoRefreshMode()
{
AssetFinderSettingExt.autoRefreshMode = _originalAutoRefreshMode;
}
private void AnalyzeSpineAssets()
{
string[] spineGUIDs = AssetDatabase.FindAssets("t:SkeletonDataAsset");
foreach (string guid in spineGUIDs)
{
string skeletonPath = AssetDatabase.GUIDToAssetPath(guid);
UnityObject skeletonAsset = AssetDatabase.LoadAssetAtPath(skeletonPath);
if (skeletonAsset != null)
{
string skeletonName = Path.GetFileNameWithoutExtension(skeletonPath);
if (skeletonName.EndsWith("_SkeletonData"))
{
skeletonName = skeletonName.Substring(0, skeletonName.Length - "_SkeletonData".Length);
}
var spineInfo = new SpineAssetInfo { skeletonPath = skeletonPath, spineName = skeletonName };
spineInfo.assets.Add(skeletonPath);
_spineAssets.Add(skeletonPath);
SerializedObject so = new SerializedObject(skeletonAsset);
SerializedProperty iterator = so.GetIterator();
while (iterator.NextVisible(true))
{
if (iterator.propertyType == SerializedPropertyType.ObjectReference && iterator.objectReferenceValue != null)
{
string refPath = AssetDatabase.GetAssetPath(iterator.objectReferenceValue);
if (!string.IsNullOrEmpty(refPath) && refPath != skeletonPath)
{
spineInfo.assets.Add(refPath);
_spineAssets.Add(refPath);
}
}
}
_spineAssetInfo[skeletonPath] = spineInfo;
}
}
}
private void AnalyzeSpriteAtlases()
{
string[] atlasGUIDs = AssetDatabase.FindAssets("t:SpriteAtlas");
foreach (string guid in atlasGUIDs)
{
string atlasPath = AssetDatabase.GUIDToAssetPath(guid);
UnityObject atlasAsset = AssetDatabase.LoadAssetAtPath(atlasPath);
if (atlasAsset != null)
{
var atlasInfo = new SpriteAtlasInfo { atlasPath = atlasPath };
SerializedObject so = new SerializedObject(atlasAsset);
SerializedProperty objectsForPacking = so.FindProperty("m_EditorData.packables");
if (objectsForPacking != null && objectsForPacking.isArray)
{
for (int i = 0; i < objectsForPacking.arraySize; i++)
{
SerializedProperty element = objectsForPacking.GetArrayElementAtIndex(i);
UnityObject obj = element.objectReferenceValue;
if (obj != null)
{
string objPath = AssetDatabase.GetAssetPath(obj);
if (AssetDatabase.IsValidFolder(objPath))
{
_spriteAtlasFolders.Add(objPath);
atlasInfo.folders.Add(objPath);
}
else
{
atlasInfo.sprites.Add(objPath);
}
}
}
}
_spriteAtlasInfo[atlasPath] = atlasInfo;
}
}
}
private void AnalyzeFolder(string folderPath)
{
if (string.IsNullOrEmpty(folderPath)) return;
if (!AssetDatabase.IsValidFolder(folderPath)) return;
string[] assetGUIDs = AssetDatabase.FindAssets("*", new[] { folderPath });
foreach (string guid in assetGUIDs)
{
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
if (AssetDatabase.IsValidFolder(assetPath) || assetPath.EndsWith(".meta"))
continue;
string assetPathLower = assetPath.Replace('\\', '/').ToLowerInvariant();
bool inSpecial = SpecialFolders.Any(sf => assetPathLower.Contains(sf.ToLowerInvariant()));
if (inSpecial) continue;
if (IsSpineAsset(assetPath)) continue;
if (IsInSpriteAtlasFolder(assetPath)) continue;
string extension = Path.GetExtension(assetPath).ToLowerInvariant();
string folderType = DetermineFolderType(assetPath, extension);
string targetFolderPath = $"{folderPath}/{folderType}";
string currentAssetFolder = Path.GetDirectoryName(assetPath);
if (NormalizePath(currentAssetFolder).Equals(NormalizePath(targetFolderPath), StringComparison.OrdinalIgnoreCase))
continue;
string key = folderPath + "|" + folderType;
if (!_assetsToMove.ContainsKey(key)) _assetsToMove[key] = new List();
_assetsToMove[key].Add(assetPath);
}
foreach (var spineInfo in _spineAssetInfo.Values)
{
bool anyAssetInFolder = spineInfo.assets.Any(asset => asset.StartsWith(folderPath + "/") || asset == folderPath);
if (anyAssetInFolder)
{
string targetPath = folderPath + "/Spines/" + spineInfo.spineName;
string key = targetPath + "|SPINE_GROUP|" + spineInfo.spineName;
if (!_assetsToMove.ContainsKey(key)) _assetsToMove[key] = new List();
foreach (string asset in spineInfo.assets)
{
if (asset.StartsWith(folderPath + "/") || asset == folderPath)
{
string currentAssetFolder = Path.GetDirectoryName(asset);
if (!NormalizePath(currentAssetFolder).Equals(NormalizePath(targetPath), StringComparison.OrdinalIgnoreCase))
{
_assetsToMove[key].Add(asset);
}
}
}
}
}
foreach (var atlasInfo in _spriteAtlasInfo.Values)
{
if (atlasInfo.atlasPath.StartsWith(folderPath + "/") || atlasInfo.atlasPath == folderPath)
{
string atlasName = Path.GetFileNameWithoutExtension(atlasInfo.atlasPath);
string targetPath = folderPath + "/SpriteAtlas";
string currentAtlasFolder = Path.GetDirectoryName(atlasInfo.atlasPath);
if (!NormalizePath(currentAtlasFolder).Equals(NormalizePath(targetPath), StringComparison.OrdinalIgnoreCase))
{
string atlasKey = targetPath + "|ATLAS|" + atlasName;
if (!_assetsToMove.ContainsKey(atlasKey)) _assetsToMove[atlasKey] = new List();
_assetsToMove[atlasKey].Add(atlasInfo.atlasPath);
}
if (atlasInfo.folders.Count == 1 && atlasInfo.sprites.Count == 0)
{
string singleFolder = atlasInfo.folders[0];
if (singleFolder.StartsWith(folderPath + "/") || singleFolder == folderPath)
{
string expectedFolderPath = Path.Combine(targetPath, atlasName);
if (!NormalizePath(singleFolder).Equals(NormalizePath(expectedFolderPath), StringComparison.OrdinalIgnoreCase))
{
string folderKey = targetPath + "|ATLAS_FOLDER|" + atlasName;
if (!_assetsToMove.ContainsKey(folderKey)) _assetsToMove[folderKey] = new List();
_assetsToMove[folderKey].Add(singleFolder);
}
}
}
}
}
}
private bool IsSpineAsset(string assetPath)
{
return _spineAssets.Contains(assetPath);
}
private bool IsInSpriteAtlasFolder(string assetPath)
{
return _spriteAtlasFolders.Any(folder => assetPath.StartsWith(folder + "/"));
}
private string DetermineFolderType(string assetPath, string extension)
{
if (extension == ".shadervariants")
{
return "Shaders";
}
if (extension == ".lighting")
{
return "Scenes";
}
if (extension == ".asset")
{
Type assetType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
if (assetType != null)
{
string typeName = assetType.Name;
if (typeName == "LightingDataAsset" || typeName == "LightingSettings")
{
return "Scenes";
}
if (typeName == "TMPro_FontAsset" || typeName.Contains("FontAsset"))
{
return "Fonts";
}
if (typeName == "Mesh")
{
return "Models";
}
if (typeName == "AnimationClip")
{
return "Animations";
}
}
}
if (IsTextureImportedAsSprite(assetPath))
{
return "Sprites";
}
string folderType = GetAssetFolderType(extension);
if (folderType == "Others")
{
Type mainType = AssetDatabase.GetMainAssetTypeAtPath(assetPath);
if (mainType != null)
{
string typeName = mainType.Name;
string specialType = GetAssetFolderTypeByTypeName(typeName);
if (specialType != null) folderType = specialType;
else folderType = typeName;
}
}
return folderType;
}
private bool IsTextureImportedAsSprite(string assetPath)
{
AssetImporter importer = AssetImporter.GetAtPath(assetPath);
if (importer is TextureImporter textureImporter)
{
return textureImporter.textureType == TextureImporterType.Sprite;
}
return false;
}
private string GetAssetFolderTypeByTypeName(string typeName)
{
for (int i = 0; i < AssetFinderAssetGroupDrawer.FILTERS.Length - 1; i++)
{
AssetFinderAssetGroup filter = AssetFinderAssetGroupDrawer.FILTERS[i];
if (!string.IsNullOrEmpty(filter.name) && filter.name.Equals(typeName, StringComparison.OrdinalIgnoreCase))
{
return filter.name + "s";
}
}
return null;
}
private void PlanAllMoves()
{
var planned = new List();
var usedNames = new HashSet(StringComparer.OrdinalIgnoreCase);
foreach (var kvp in _assetsToMove)
{
string[] parts = kvp.Key.Split('|');
string parentFolder = parts[0];
string folderType = parts[1];
if (parts.Length > 2 && (parts[1] == "SPINE_GROUP" || parts[1] == "ATLAS" || parts[1] == "ATLAS_FOLDER"))
{
string newName = parts[2];
string targetFolderPath = parentFolder;
string absTargetFolder = AssetPathToAbs(targetFolderPath);
if (!Directory.Exists(absTargetFolder)) Directory.CreateDirectory(absTargetFolder);
if (parts[1] == "SPINE_GROUP")
{
foreach (string assetPath in kvp.Value)
{
string srcAbs = AssetPathToAbs(assetPath);
string fileName = Path.GetFileName(assetPath);
string dstAbs = Path.Combine(absTargetFolder, fileName);
if (NormalizePath(srcAbs).Equals(NormalizePath(dstAbs), StringComparison.OrdinalIgnoreCase))
continue;
planned.Add(new PlannedMove {
srcAsset = srcAbs,
srcMeta = srcAbs + ".meta",
dstAsset = dstAbs,
dstMeta = dstAbs + ".meta",
isFolder = false
});
}
}
else if (parts[1] == "ATLAS")
{
foreach (string assetPath in kvp.Value)
{
string srcAbs = AssetPathToAbs(assetPath);
string fileName = Path.GetFileName(assetPath);
string dstAbs = Path.Combine(absTargetFolder, fileName);
if (NormalizePath(srcAbs).Equals(NormalizePath(dstAbs), StringComparison.OrdinalIgnoreCase))
continue;
planned.Add(new PlannedMove {
srcAsset = srcAbs,
srcMeta = srcAbs + ".meta",
dstAsset = dstAbs,
dstMeta = dstAbs + ".meta",
isFolder = false
});
}
}
else if (parts[1] == "ATLAS_FOLDER")
{
foreach (string folderPath in kvp.Value)
{
string srcAbs = AssetPathToAbs(folderPath);
string dstAbs = Path.Combine(absTargetFolder, newName);
if (NormalizePath(srcAbs).Equals(NormalizePath(dstAbs), StringComparison.OrdinalIgnoreCase))
continue;
planned.Add(new PlannedMove {
srcAsset = srcAbs,
srcMeta = srcAbs + ".meta",
dstAsset = dstAbs,
dstMeta = dstAbs + ".meta",
isFolder = true
});
}
}
continue;
}
string targetFolderPath2 = $"{parentFolder}/{folderType}";
string absTargetFolder2 = AssetPathToAbs(targetFolderPath2);
if (!Directory.Exists(absTargetFolder2)) Directory.CreateDirectory(absTargetFolder2);
foreach (string assetPath in kvp.Value)
{
string fileName = Path.GetFileName(assetPath);
string baseName = Path.GetFileNameWithoutExtension(fileName);
string ext = Path.GetExtension(fileName);
string srcAbs = AssetPathToAbs(assetPath);
string srcMeta = srcAbs + ".meta";
string dstName = fileName;
string dstAbs = Path.Combine(absTargetFolder2, dstName);
string dstMeta = dstAbs + ".meta";
int suffix = 1;
while (usedNames.Contains(dstAbs) || (File.Exists(dstAbs) && !NormalizePath(srcAbs).Equals(NormalizePath(dstAbs), StringComparison.OrdinalIgnoreCase)))
{
dstName = $"{baseName}-{suffix.ToString("D2")}{ext}";
dstAbs = Path.Combine(absTargetFolder2, dstName);
dstMeta = dstAbs + ".meta";
suffix++;
}
if (NormalizePath(srcAbs).Equals(NormalizePath(dstAbs), StringComparison.OrdinalIgnoreCase))
continue;
usedNames.Add(dstAbs);
planned.Add(new PlannedMove {
srcAsset = srcAbs,
srcMeta = srcMeta,
dstAsset = dstAbs,
dstMeta = dstMeta
});
}
}
_plannedMoves = planned;
}
private static string AssetPathToAbs(string assetPath)
{
if (assetPath.StartsWith("Assets/"))
return Path.Combine(Application.dataPath.Substring(0, Application.dataPath.Length - 6), assetPath);
if (assetPath.StartsWith("Assets"))
return Path.Combine(Application.dataPath.Substring(0, Application.dataPath.Length - 6), assetPath);
return assetPath;
}
private void ProcessNextBatch()
{
if (!_isProcessing)
{
EditorApplication.update -= ProcessNextBatch;
CleanupAfterProcessing();
return;
}
int batchSize = 10;
int moved = 0;
while (_plannedMoves.Count > 0 && moved < batchSize)
{
var move = _plannedMoves[0];
_plannedMoves.RemoveAt(0);
if (move.isFolder)
{
if (!NormalizePath(move.srcAsset).Equals(NormalizePath(move.dstAsset), StringComparison.OrdinalIgnoreCase))
{
if (Directory.Exists(move.dstAsset)) Directory.Delete(move.dstAsset, true);
Directory.Move(move.srcAsset, move.dstAsset);
}
if (File.Exists(move.srcMeta) && !NormalizePath(move.srcMeta).Equals(NormalizePath(move.dstMeta), StringComparison.OrdinalIgnoreCase))
{
if (File.Exists(move.dstMeta)) File.Delete(move.dstMeta);
File.Move(move.srcMeta, move.dstMeta);
}
string folderType = Path.GetFileName(Path.GetDirectoryName(move.dstAsset));
if (!_organizedFolders.ContainsKey(folderType)) _organizedFolders[folderType] = 1;
else _organizedFolders[folderType]++;
}
else
{
if (!NormalizePath(move.srcAsset).Equals(NormalizePath(move.dstAsset), StringComparison.OrdinalIgnoreCase))
{
if (File.Exists(move.dstAsset)) File.Delete(move.dstAsset);
if (File.Exists(move.srcAsset))
{
File.Move(move.srcAsset, move.dstAsset);
}
else
{
_errorAssets.Add($"Source file not found: {move.srcAsset}");
moved++;
continue;
}
}
if (File.Exists(move.srcMeta) && !NormalizePath(move.srcMeta).Equals(NormalizePath(move.dstMeta), StringComparison.OrdinalIgnoreCase))
{
if (File.Exists(move.dstMeta)) File.Delete(move.dstMeta);
File.Move(move.srcMeta, move.dstMeta);
}
string folderType = Path.GetFileName(Path.GetDirectoryName(move.dstAsset));
if (!_organizedFolders.ContainsKey(folderType)) _organizedFolders[folderType] = 1;
else _organizedFolders[folderType]++;
}
moved++;
}
_progress = 1f - (_plannedMoves.Count / (float)(1 + _plannedMoves.Count));
if (_plannedMoves.Count == 0)
{
int totalAssets = _organizedFolders.Values.Sum();
_reportTitle = totalAssets > 0
? $"Successfully organized {totalAssets} assets into {_organizedFolders.Count} category folders!"
: "No assets needed to be organized.";
if (_errorAssets.Count > 0)
{
_reportTitle += $" Encountered {_errorAssets.Count} errors.";
}
EditorApplication.update -= ProcessNextBatch;
AssetDatabase.Refresh();
CleanupAfterProcessing();
}
_window.Repaint();
}
private void DeleteEmptySubfolders(string parentFolder)
{
DeleteEmptyFoldersRecursive(parentFolder);
}
private static void DeleteEmptyFoldersRecursive(string root)
{
if (!Directory.Exists(root)) return;
foreach (var dir in Directory.GetDirectories(root))
{
var dirName = Path.GetFileName(dir);
if (dirName.StartsWith(".")) continue;
DeleteEmptyFoldersRecursive(dir.Replace("\\", "/"));
if (IsUnityFolderEmptyStatic(dir.Replace("\\", "/")))
{
string relPath = "Assets" + dir.Replace(Application.dataPath, "").Replace("\\", "/");
AssetDatabase.DeleteAsset(relPath);
}
}
}
private static bool IsUnityFolderEmptyStatic(string folder)
{
var files = Directory.GetFiles(folder);
foreach (var file in files)
{
var fileName = Path.GetFileName(file);
if (fileName == null) continue;
if (fileName.StartsWith(".")) return false;
if (!fileName.EndsWith(".meta", StringComparison.OrdinalIgnoreCase)) return false;
}
var dirs = Directory.GetDirectories(folder);
foreach (var dir in dirs)
{
var dirName = Path.GetFileName(dir);
if (dirName.StartsWith(".")) return false;
if (!IsUnityFolderEmptyStatic(dir.Replace("\\", "/"))) return false;
}
return true;
}
private string GetAssetFolderType(string extension)
{
// Normalize extension to lowercase for case-insensitive comparison
string normalizedExt = extension?.ToLowerInvariant() ?? "";
for (int i = 0; i < AssetFinderAssetGroupDrawer.FILTERS.Length - 1; i++)
{
AssetFinderAssetGroup filter = AssetFinderAssetGroupDrawer.FILTERS[i];
if (filter.extension.Contains(normalizedExt))
{
return filter.name + "s";
}
}
return "Others";
}
private class PlannedMove {
public string srcAsset;
public string srcMeta;
public string dstAsset;
public string dstMeta;
public bool isFolder;
}
private class SpriteAtlasInfo {
public string atlasPath;
public List folders = new List();
public List