This commit is contained in:
jmaniuvc
2025-05-29 09:51:58 +09:00
parent 00257a3d58
commit 282fcfe688
1026 changed files with 291311 additions and 0 deletions

55
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,55 @@
{
"files.exclude":
{
"**/.DS_Store":true,
"**/.git":true,
"**/.gitmodules":true,
"**/*.booproj":true,
"**/*.pidb":true,
"**/*.suo":true,
"**/*.user":true,
"**/*.userprefs":true,
"**/*.unityproj":true,
"**/*.dll":true,
"**/*.exe":true,
"**/*.pdf":true,
"**/*.mid":true,
"**/*.midi":true,
"**/*.wav":true,
"**/*.gif":true,
"**/*.ico":true,
"**/*.jpg":true,
"**/*.jpeg":true,
"**/*.png":true,
"**/*.psd":true,
"**/*.tga":true,
"**/*.tif":true,
"**/*.tiff":true,
"**/*.3ds":true,
"**/*.3DS":true,
"**/*.fbx":true,
"**/*.FBX":true,
"**/*.lxo":true,
"**/*.LXO":true,
"**/*.ma":true,
"**/*.MA":true,
"**/*.obj":true,
"**/*.OBJ":true,
"**/*.asset":true,
"**/*.cubemap":true,
"**/*.flare":true,
"**/*.mat":true,
"**/*.meta":true,
"**/*.prefab":true,
"**/*.unity":true,
"build/":true,
"Build/":true,
"Library/":true,
"library/":true,
"obj/":true,
"Obj/":true,
"ProjectSettings/":true,
"temp/":true,
"Temp/":true
}
}

6
.vsconfig Normal file
View File

@@ -0,0 +1,6 @@
{
"version": "1.0",
"components": [
"Microsoft.VisualStudio.Workload.ManagedGame"
]
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b22dfca284c7bd54b8c2d60926037322
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,15 @@
{
"name": "AssetCleaner",
"references": [],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": false,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: de141fd48bce7594e9474fe440f5ea10
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7f4dfe9d348c0ff4cb525a0b2c48fc34
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f45f9c9770a8371428fce5031e1c7c5c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,110 @@
fileFormatVersion: 2
guid: bfec65de7ee2e114199f83507d375a6f
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Standalone
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Android
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 B

View File

@@ -0,0 +1,110 @@
fileFormatVersion: 2
guid: f4b3e5a29ec20194a8ba1d6cf7b1b037
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Standalone
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Android
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@@ -0,0 +1,110 @@
fileFormatVersion: 2
guid: 3fec3bf4c4f5e2d48835dbe2f21aa7d0
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Standalone
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Android
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1,110 @@
fileFormatVersion: 2
guid: 07bb28c80f378c64194dd46c34b2d6c1
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Standalone
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Android
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -0,0 +1,110 @@
fileFormatVersion: 2
guid: c5fd88366c67daf4585631953fa79779
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Standalone
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Android
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

View File

@@ -0,0 +1,110 @@
fileFormatVersion: 2
guid: e0208e3a37267be46ba2d10fad510cef
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Standalone
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Android
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@@ -0,0 +1,110 @@
fileFormatVersion: 2
guid: e755b5ef4cb427f469ba580ae9956237
TextureImporter:
fileIDToRecycleName: {}
externalObjects: {}
serializedVersion: 9
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -100
wrapU: 1
wrapV: 1
wrapW: -1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 2
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Standalone
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Android
maxTextureSize: 32
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 2
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4848aa5846cd6264c984dcc6cd9d72af
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,82 @@
using System;
using System.Linq;
namespace Asset_Cleaner {
[Serializable]
class AufSerializableData {
public const int CurrentVersion = 1;
public int Version;
public bool MarkRed;
public int RebuildCacheOnDemand;
public int UpdateUnusedAssetsOnDemand;
public bool ShowInfoBox;
public string IgnorePathContainsCombined;
public bool IgnoreMaterial;
public bool IgnoreScriptable;
public bool IgnoreSprite;
static int BoolToInt(bool val) {
return val ? 2 : 1;
}
static bool IntToBool(int val, bool defaultVal) {
switch (val) {
case 2:
return true;
case 1:
return false;
default:
return defaultVal;
}
}
public static AufSerializableData Default() {
return new AufSerializableData {
Version = CurrentVersion,
MarkRed = true,
ShowInfoBox = true,
IgnorePathContainsCombined = "Gizmos;Resources;Editor;Asset Cleaner;Asset Usage Finder;",
IgnoreMaterial = false,
IgnoreScriptable = true,
IgnoreSprite = false,
RebuildCacheOnDemand = 2,
UpdateUnusedAssetsOnDemand = 2,
};
}
public static void OnSerialize(in Config src, out AufSerializableData result) {
result = new AufSerializableData();
result.Version = CurrentVersion;
result.MarkRed = src.MarkRed;
result.ShowInfoBox = src.ShowInfoBox;
result.IgnorePathContainsCombined = src.IgnorePathContainsCombined;
result.IgnoreMaterial = src.IgnoreMaterial;
result.IgnoreScriptable = src.IgnoreScriptable;
result.IgnoreSprite = src.IgnoreSprite;
result.RebuildCacheOnDemand = BoolToInt(src.RebuildCacheOnDemand);
result.UpdateUnusedAssetsOnDemand = BoolToInt(src.UpdateUnusedAssetsOnDemand);
}
public static void OnDeserialize(in AufSerializableData src, ref Config result) {
var def = Default();
result.MarkRed = src.MarkRed;
result.IgnorePathContainsCombined = src.IgnorePathContainsCombined;
result.ShowInfoBox = src.ShowInfoBox;
result.IgnorePathContains = result.IgnorePathContainsCombined
.Split(';')
.Select(s => s.Trim())
.Where(s => !string.IsNullOrWhiteSpace(s))
.ToArray();
result.IgnoreMaterial = src.IgnoreMaterial;
result.IgnoreScriptable = src.IgnoreScriptable;
result.IgnoreSprite = src.IgnoreSprite;
result.RebuildCacheOnDemand = IntToBool(src.RebuildCacheOnDemand, def.RebuildCacheOnDemand == 2);
result.UpdateUnusedAssetsOnDemand = IntToBool(src.UpdateUnusedAssetsOnDemand, def.UpdateUnusedAssetsOnDemand == 2);
}
public bool Valid() {
return Version == CurrentVersion || IgnorePathContainsCombined == null;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 14980ecc573d44c59b5f9323603c7bdf
timeCreated: 1588518687

View File

@@ -0,0 +1,105 @@
using System;
using System.Linq;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
#pragma warning disable 649
namespace Asset_Cleaner {
class CleanerStyleAsset : ScriptableObject {
[Serializable]
public class Style {
public Color RedHighlight = new Color(1, 0, 0, 1f);
public GUIContent Lock;
public GUIStyle LockBtn = new GUIStyle();
public GUIStyle SampleBtn = new GUIStyle();
public GUIContent Unlock;
public GUIStyle UnlockBtn = new GUIStyle();
public GUIContent RemoveFile;
public GUIContent RemoveScene;
public GUIStyle RowMainAssetBtn = new GUIStyle();
public GUIStyle RemoveUnusedBtn = new GUIStyle();
public GUIStyle CurrentBtn = new GUIStyle();
public GUIContent ArrowL;
public GUIContent ArrowR;
public GUIStyle ArrowBtn = new GUIStyle();
public float SceneIndent1 = 20f;
public float SceneIndent2 = 20f;
public GUIStyle ProjectViewCounterLabel;
public GUIContent MultiSelect;
public static bool TryFindSelf(out Style value) {
const string typeName = nameof(CleanerStyleAsset);
var guids = AssetDatabase.FindAssets($"t:{typeName}");
if (!guids.Any()) {
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
}
Asr.IsTrue(guids.Length > 0, $"No '{typeName}' assets found");
var res = guids.Select(AssetDatabase.GUIDToAssetPath).Select(t => (CleanerStyleAsset) AssetDatabase.LoadAssetAtPath(t, typeof(CleanerStyleAsset))).FirstOrDefault();
if (res == null) {
value = default;
return false;
}
value = EditorGUIUtility.isProSkin ? res.Pro : res.Personal;
return value != null;
}
}
#pragma warning disable 0649
public Style Pro;
public Style Personal;
#pragma warning restore
[CustomEditor(typeof(CleanerStyleAsset))]
class Editor : UnityEditor.Editor {
public override void OnInspectorGUI() {
#if false
if (GUILayout.Button("Update Btn backgrounds")) {
var targ = (CleanerStyleAsset) target;
Set(targ.Pro);
}
#endif
EditorGUI.BeginChangeCheck();
base.OnInspectorGUI();
if (EditorGUI.EndChangeCheck())
UnityEditorInternal.InternalEditorUtility.RepaintAllViews();
}
#if false
static void Set(Style style) {
var st = style;
var sample = st.SampleBtn;
foreach (var btn in new[] {
st.LockBtn,
st.UnlockBtn,
st.RowMainAssetBtn,
st.RemoveUnusedBtn,
st.CurrentBtn,
st.ArrowBtn,
}) {
btn.normal = sample.normal;
btn.hover = sample.hover;
btn.active = sample.active;
btn.focused = sample.focused;
btn.onNormal = sample.onNormal;
btn.onHover = sample.onHover;
btn.onActive = sample.onActive;
btn.onFocused = sample.onFocused;
}
}
#endif
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 16578cbec6a24ac4d951b5f328a970ca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
namespace Asset_Cleaner {
enum FindModeEnum {
None = 0,
File = 1,
Scene = 2,
Stage = 3,
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 34e14d6333c649f4fb74ccdb6c86d8b6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f94bcb0962724eeea17b007a6ddfd885
timeCreated: 1596213773

View File

@@ -0,0 +1,284 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditorInternal;
using Debug = UnityEngine.Debug;
using Object = UnityEngine.Object;
namespace Asset_Cleaner {
class BacklinkStore {
public bool Initialized { get; private set; }
public Dictionary<string, long> UnusedFiles { get; private set; }
public Dictionary<string, long> UnusedScenes { get; private set; }
public Dictionary<string, BwMeta> Backward { get; private set; }
public Dictionary<string, UnusedQty> FoldersWithQty { get; private set; }
Dictionary<string, FwMeta> _forward;
List<string> Folders { get; set; }
public void Init() {
FoldersWithQty = new Dictionary<string, UnusedQty>();
_forward = new Dictionary<string, FwMeta>();
Backward = new Dictionary<string, BwMeta>();
var defaultAss = typeof(DefaultAsset);
var asmdefAss = typeof(AssemblyDefinitionAsset);
var paths = AssetDatabase.GetAllAssetPaths()
.Distinct()
.Where(s => s.StartsWith("Assets") || s.StartsWith("ProjectSettings"))
.Where(p => {
var t = AssetDatabase.GetMainAssetTypeAtPath(p);
return !t.IsAssignableFromInverse(defaultAss) && !t.IsAssignableFromInverse(asmdefAss);
})
.ToArray();
var i = 0f;
var total = (float) paths.Length;
foreach (var path in paths) {
_FillFwAndBacklinks(path);
var percent = i * 100f / total;
if (Math.Abs(percent % 5f) < 0.01f) {
if (EditorUtility.DisplayCancelableProgressBar(
"Please wait...",
"Building the cache...", percent))
Debug.LogError("Cache build aborted");
}
i++;
}
EditorUtility.ClearProgressBar();
// FillFoldersWithQtyByPaths
List<string> foldersAll = new List<string>();
foreach (var path in paths) {
var folders = GetAllFoldersFromPath(path);
foldersAll.AddRange(folders);
}
Folders = foldersAll.Distinct().OrderBy(p => p).ToList();
UpdateUnusedAssets();
Initialized = true;
}
void _FillFwAndBacklinks(string path) {
var dependencies = _Dependencies(path);
var hs = new FwMeta {Dependencies = new HashSet<string>(dependencies)};
_forward.Add(path, hs);
foreach (var backPath in dependencies) {
if (!Backward.TryGetValue(backPath, out var val)) {
val = new BwMeta();
val.Lookup = new HashSet<string>();
Backward.Add(backPath, val);
}
val.Lookup.Add(path);
}
}
void UpdateFoldersWithQtyByPath(string path) {
var folders = GetAllFoldersFromPath(path);
foreach (var folder in folders) {
if (!Folders.Exists(p => p == folder))
Folders.Add(folder);
}
}
static List<string> GetAllFoldersFromPath(string p) {
var result = new List<string>();
var i = p.IndexOf('/', 0);
while (i > 0) {
var item = p.Substring(0, i);
result.Add(item);
i = p.IndexOf('/', i + 1);
}
return result.Distinct().ToList();
}
public void UpdateUnusedAssets() {
var all = new HashSet<string>(_forward.Keys);
var withBacklinks =
new HashSet<string>(Backward.Where(kv => kv.Value.Lookup.Count > 0).Select(kv => kv.Key));
all.ExceptWith(withBacklinks);
all.RemoveWhere(SearchUtils.IsFileIgrnoredBySettings);
var unusedAssets = all;
var scenes = unusedAssets.Where(s =>
AssetDatabase.GetMainAssetTypeAtPath(s).IsAssignableFromInverse(typeof(SceneAsset))).ToArray();
unusedAssets.ExceptWith(scenes);
var files = unusedAssets;
UnusedFiles = new Dictionary<string, long>();
foreach (var file in files) UnusedFiles[file] = new FileInfo(file).Length;
UnusedScenes = new Dictionary<string, long>();
foreach (var scene in scenes) UnusedScenes[scene] = new FileInfo(scene).Length;
// UpdateFoldersWithQty();
foreach (var folder in Folders) {
var unusedFilesQty = UnusedFiles.Count(p => p.Key.StartsWith(folder));
var unusedScenesQty = UnusedScenes.Count(p => p.Key.StartsWith(folder));
long size = 0;
size = UnusedFiles.Where((p => p.Key.StartsWith(folder))).Sum(p => p.Value);
size += UnusedScenes.Where(p => p.Key.StartsWith(folder)).Sum(p => p.Value);
FoldersWithQty.TryGetValue(folder, out var folderWithQty);
if (folderWithQty == null) {
FoldersWithQty.Add(folder, new UnusedQty(unusedFilesQty, unusedScenesQty, size));
}
else {
folderWithQty.UnusedFilesQty = unusedFilesQty;
folderWithQty.UnusedScenesQty = unusedScenesQty;
folderWithQty.UnusedSize = size;
}
}
}
public void Remove(string path) {
if (!_forward.TryGetValue(path, out var fwMeta))
return;
foreach (var dependency in fwMeta.Dependencies) {
if (!Backward.TryGetValue(dependency, out var dep)) continue;
dep.Lookup.Remove(path);
}
_forward.Remove(path);
UpdateFoldersWithQtyByPath(path);
}
public void Replace(string src, string dest) {
_Upd(_forward);
_Upd(Backward);
UpdateFoldersWithQtyByPath(dest);
void _Upd<T>(Dictionary<string, T> dic) {
if (!dic.TryGetValue(src, out var refs)) return;
dic.Remove(src);
dic.Add(dest, refs);
}
}
public void RebuildFor(string path, bool remove) {
if (!_forward.TryGetValue(path, out var fwMeta)) {
fwMeta = new FwMeta();
_forward.Add(path, fwMeta);
}
else if (remove) {
foreach (var dependency in fwMeta.Dependencies) {
if (!Backward.TryGetValue(dependency, out var backDep)) continue;
backDep.Lookup.Remove(path);
}
fwMeta.Dependencies = null;
}
var dependencies = _Dependencies(path);
fwMeta.Dependencies = new HashSet<string>(dependencies);
foreach (var backPath in dependencies) {
if (!Backward.TryGetValue(backPath, out var bwMeta)) {
bwMeta = new BwMeta {Lookup = new HashSet<string>()};
Backward.Add(backPath, bwMeta);
}
else if (remove)
bwMeta.Lookup.Remove(path);
bwMeta.Lookup.Add(path);
}
if (!remove) {
UpdateFoldersWithQtyByPath(path);
}
}
static string[] _Dependencies(string s) {
if (s[0] == 'A')
return AssetDatabase.GetDependencies(s, false);
var obj = LoadAllOrMain(s)[0];
return GetDependenciesManualPaths().ToArray();
Object[] LoadAllOrMain(string assetPath) {
// prevents error "Do not use readobjectthreaded on scene objects!"
return typeof(SceneAsset) == AssetDatabase.GetMainAssetTypeAtPath(assetPath)
? new[] {AssetDatabase.LoadMainAssetAtPath(assetPath)}
: AssetDatabase.LoadAllAssetsAtPath(assetPath);
}
IEnumerable<string> GetDependenciesManualPaths() {
if (obj is EditorBuildSettings) {
foreach (var scene in EditorBuildSettings.scenes)
yield return scene.path;
}
using (var so = new SerializedObject(obj)) {
var props = so.GetIterator();
while (props.Next(true)) {
switch (props.propertyType) {
case SerializedPropertyType.ObjectReference:
var propsObjectReferenceValue = props.objectReferenceValue;
if (!propsObjectReferenceValue) continue;
var assetPath = AssetDatabase.GetAssetPath(propsObjectReferenceValue);
yield return assetPath;
break;
#if later
case SerializedPropertyType.Generic:
case SerializedPropertyType.ExposedReference:
case SerializedPropertyType.ManagedReference:
break;
#endif
default:
continue;
}
}
}
}
}
class FwMeta {
public HashSet<string> Dependencies;
}
public class BwMeta {
public HashSet<string> Lookup;
}
public class UnusedQty {
public int UnusedFilesQty;
public int UnusedScenesQty;
public long UnusedSize;
public UnusedQty() {
Init(0, 0, 0);
}
public UnusedQty(int unusedFilesQty, int unusedScenesQty, long unusedSize) {
Init(unusedFilesQty, unusedScenesQty, unusedSize);
}
private void Init(int unusedFilesQty, int unusedScenesQty, long unusedSize) {
UnusedFilesQty = unusedFilesQty;
UnusedScenesQty = unusedScenesQty;
UnusedSize = unusedSize;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 004e5680782f4e679ae93027d2d40023
timeCreated: 1577106223

View File

@@ -0,0 +1,23 @@
namespace Asset_Cleaner {
class Config {
// serialized
public bool MarkRed;
public string IgnorePathContainsCombined;
public bool ShowInfoBox;
public bool RebuildCacheOnDemand;
public bool UpdateUnusedAssetsOnDemand;
// todo make type array
public bool IgnoreMaterial;
public bool IgnoreScriptable;
public bool IgnoreSprite;
// serialized only while window is opened
public bool Locked;
// non-serialized
public string[] IgnorePathContains;
public string InitializationTime;
public bool PendingUpdateUnusedAssets;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a6ff796e4dab4324887a303cba29eed4
timeCreated: 1577117228

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
namespace Asset_Cleaner {
[Serializable]
class PersistentUndoRedoState {
public List<SelectionEntry> History = new List<SelectionEntry>();
public int Id;
public void Deconstruct(out List<SelectionEntry> list, out int id) {
id = Id;
list = History;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5414db21dfea4d7eaff8ce4c8f106c8d
timeCreated: 1596213717

View File

@@ -0,0 +1,6 @@
namespace Asset_Cleaner {
class UndoRedoState {
public bool UndoEnabled;
public bool RedoEnabled;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c3ac9efa32c8491aa8a11749f8020d96
timeCreated: 1596213776

View File

@@ -0,0 +1,14 @@
using UnityEngine;
namespace Asset_Cleaner {
class WindowData {
public bool ExpandFiles;
public bool ExpandScenes;
public Vector2 ScrollPos;
public CleanerStyleAsset.Style Style;
public GUIContent SceneFoldout;
public PrevClick Click;
public AufWindow Window;
public FindModeEnum FindFrom;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8481f043b2414c08a45957dcd6035fbe
timeCreated: 1575305740

View File

@@ -0,0 +1,11 @@
using Leopotam.Ecs;
namespace Asset_Cleaner {
class InSceneResult : IEcsAutoReset {
public string ScenePath;
public void Reset() {
ScenePath = default;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 013577ffef1140109e8367f2a5f75fa6
timeCreated: 1577269723

View File

@@ -0,0 +1,18 @@
using UnityEngine;
namespace Asset_Cleaner {
public struct PrevClick {
const float DoubleClickTime = 0.5f;
Object _target;
float _timeClicked;
public PrevClick(Object target) {
_target = target;
_timeClicked = Time.realtimeSinceStartup;
}
public bool IsDoubleClick(Object o) {
return _target == o && Time.realtimeSinceStartup - _timeClicked < DoubleClickTime;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 31c0f204d1b044caaa02a0726f11a270
timeCreated: 1576087327

View File

@@ -0,0 +1,19 @@
using Leopotam.Ecs;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Asset_Cleaner {
class Result : IEcsAutoReset {
public string FilePath;
public Object File;
public Object MainFile;
public GameObject RootGo;
public void Reset() {
FilePath = default;
File = default;
MainFile = default;
RootGo = default;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 46f4fd24243adc749b377209f744d628
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,20 @@
using Leopotam.Ecs;
using UnityEngine.SceneManagement;
namespace Asset_Cleaner {
class SceneDetails : IEcsAutoReset {
public string Path;
public Scene Scene;
public bool SearchRequested;
public bool SearchDone;
public bool WasOpened;
public void Reset() {
Path = default;
Scene = default;
SearchRequested = default;
SearchDone = default;
WasOpened = default;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 14749f86c2c14febb592442508f8c23a
timeCreated: 1577266613

View File

@@ -0,0 +1,11 @@
using Leopotam.Ecs;
namespace Asset_Cleaner {
class SceneResult : IEcsAutoReset {
public string PathNicified;
public void Reset() {
PathNicified = default;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 338e9e86c9274f91808429753a912d4b
timeCreated: 1576083610

View File

@@ -0,0 +1,26 @@
using System.Collections.Generic;
using Leopotam.Ecs;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Asset_Cleaner {
class SearchArg : IEcsAutoReset {
public Object Target;
public Object Main;
public string FilePath;
public Option<Object[]> SubAssets;
public Scene Scene;
public List<string> UnusedAssetsFiltered;
public List<string> UnusedScenesFiltered;
public void Reset() {
UnusedAssetsFiltered = default;
UnusedScenesFiltered = default;
Target = default;
Main = default;
SubAssets = default;
Scene = default;
FilePath = default;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4a6b25ca9296c9e41affadc9ccecb1f1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,32 @@
using System.Collections.Generic;
using Leopotam.Ecs;
using UnityEditor;
using UnityEngine;
namespace Asset_Cleaner {
class SearchResultGui : IEcsAutoReset {
public SerializedObject SerializedObject;
public List<PropertyData> Properties;
public GUIContent Label;
public string TransformPath;
public void Reset() {
SerializedObject?.Dispose();
SerializedObject = default;
if (Properties != default)
foreach (var propertyData in Properties) {
propertyData.Property.Dispose();
}
Properties = default;
Label = default;
TransformPath = default;
}
public class PropertyData {
public SerializedProperty Property;
public GUIContent Content;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 14f26ff078c04c5685fdbb3ec3f92382
timeCreated: 1576168925

View File

@@ -0,0 +1,17 @@
using Leopotam.Ecs;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Asset_Cleaner {
class SelectionChanged : IEcsAutoReset {
public Object Target;
public Scene Scene;
public FindModeEnum From;
public void Reset() {
Target = default;
Scene = default;
From = default;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1d0dc4d007bb4ebbbeea6626197d17b9
timeCreated: 1575365553

View File

@@ -0,0 +1,31 @@
using System;
using UnityEditor;
using Object = UnityEngine.Object;
namespace Asset_Cleaner {
[Serializable]
class SelectionEntry {
public bool IsGuids;
public string[] Guids;
public Object[] SceneObjects;
public bool Valid() {
if (IsGuids) {
foreach (var guid in Guids) {
var path = AssetDatabase.GUIDToAssetPath(guid);
if (!string.IsNullOrEmpty(path))
return true;
}
return false;
}
foreach (var sceneObject in SceneObjects)
if (sceneObject)
return true;
return false;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2b4476be23474ea4b20262c0ee59983c
timeCreated: 1596139978

View File

@@ -0,0 +1,41 @@
using System;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace Asset_Cleaner {
static class IgnoreTypes {
public static bool Check(string path, out Type type) {
type = AssetDatabase.GetMainAssetTypeAtPath(path);
if (type == null) return false;
var conf = Globals<Config>.Value;
if (conf == null) return false;
if (type.IsAssignableFromInverse(typeof(MonoScript))) return true;
if (type.IsAssignableFromInverse(typeof(DefaultAsset))) return true;
if (conf.IgnoreScriptable && type.IsAssignableFromInverse(typeof(ScriptableObject))) return true;
if (type.IsAssignableFromInverse(typeof(Shader))) return true;
if (type.IsAssignableFromInverse(typeof(ComputeShader))) return true;
if (type.IsAssignableFromInverse(typeof(ShaderVariantCollection))) return true;
#if UNITY_2019_3_OR_NEWER
if (type.IsAssignableFromInverse(typeof(UnityEngine.Experimental.Rendering.RayTracingShader))) return true; // todo: track of Experimental namespace
#endif
if (type.IsAssignableFromInverse(typeof(TextAsset))) return true;
if (type.IsAssignableFromInverse(typeof(AssemblyDefinitionAsset))) return true;
if (type.IsAssignableFromInverse(typeof(UnityEngine.U2D.SpriteAtlas))) return true;
if (conf.IgnoreMaterial && type.IsAssignableFromInverse(typeof(Material))) {
return true;
}
if (conf.IgnoreSprite && type.Name.Contains("Texture2D")) {
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 34f1d98d6e464e5ebee0df9de721af39
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4b15d89b453047218955641734d4ec9e
timeCreated: 1596213484

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cbedea52d931409a8b62256873637225
timeCreated: 1596214557

View File

@@ -0,0 +1,103 @@
using System.Diagnostics;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;
namespace Asset_Cleaner {
class AufWindow : EditorWindow {
[SerializeField] PersistentUndoRedoState _persistentUndo;
[MenuItem("Window/- Asset Cleaner %L")]
static void OpenActiveWindow() {
GetWindow<AufWindow>();
}
// restore window state after recompilation
void OnEnable() {
var wd = Globals<WindowData>.Value = new WindowData();
wd.Window = this;
wd.Window.titleContent = new GUIContent("Asset Cleaner v1.26");
var firstTime = _persistentUndo == null;
Globals<PersistentUndoRedoState>.Value = _persistentUndo ?? new PersistentUndoRedoState();
Globals<BacklinkStore>.Value = new BacklinkStore();
var config = Globals<Config>.Value = new Config();
PersistenceUtils.Load(ref config);
if (firstTime || !config.RebuildCacheOnDemand)
Globals<BacklinkStore>.Value.Init();
EditorApplication.update += Upd;
EditorApplication.projectWindowItemOnGUI += ProjectViewGui.OnProjectWindowItemOnGui;
AufCtx.TryInitWorld();
// need to close window in case of Asset Cleaner uninstalled
if (!CleanerStyleAsset.Style.TryFindSelf(out wd.Style))
ForceClose();
}
void OnGUI() {
var store = Globals<BacklinkStore>.Value;
if (!store.Initialized) {
// prevent further window GUI rendering
var config = Globals<Config>.Value;
if (!GUILayout.Button("Initialize Cache")) return;
var stopwatch = new Stopwatch();
stopwatch.Start();
store.Init();
stopwatch.Stop();
config.InitializationTime = $"Initialized in {stopwatch.Elapsed.TotalSeconds:N} s";
AufCtx.World.NewEntityWith(out RequestRepaintEvt _);
}
if (Globals<Config>.Value.PendingUpdateUnusedAssets && GUILayout.Button ("Update unused assets")) {
ProcessAllAssets.ForceUpdateUnusedAssets ();
}
AufCtx.OnGuiGroup.Run();
}
static void Upd() {
if (AufCtx.World == null) {
AufCtx.DestroyWorld();
return;
}
AufCtx.UndoGroup.Run();
if (!Globals<BacklinkStore>.Value.Initialized) return;
AufCtx.UpdateGroup.Run();
}
bool _closing;
void ForceClose() {
if (_closing) return;
_closing = true;
Close();
EditorWindow.DestroyImmediate(this);
}
void OnDisable() {
if (!AufCtx.Destroyed) {
AufCtx.UndoGroup.Destroy();
AufCtx.UpdateGroup.Destroy();
AufCtx.OnGuiGroup.Destroy();
AufCtx.DestroyWorld();
}
_persistentUndo = Globals<PersistentUndoRedoState>.Value;
Globals<Config>.Value = default;
Globals<PersistentUndoRedoState>.Value = default;
Globals<WindowData>.Value = default;
EditorApplication.update -= Upd;
EditorApplication.projectWindowItemOnGUI -= ProjectViewGui.OnProjectWindowItemOnGui;
// need to close window in case of Asset Cleaner uninstalled
if (!CleanerStyleAsset.Style.TryFindSelf(out _))
ForceClose();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 44ebd7bbb2bb41b4ba0187901e8d583f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,39 @@
using UnityEditor;
namespace Asset_Cleaner {
class ProcessAllAssets : AssetPostprocessor {
static void OnPostprocessAllAssets (string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) {
if (!AufCtx.InitStarted) return;
if (!Globals<BacklinkStore>.Value.Initialized) return;
var store = Globals<BacklinkStore>.Value;
if (store == null) {
return;
}
var length = movedAssets.Length;
for (var i = 0; i < length; i++)
store.Replace (movedFromAssetPaths[i], movedAssets[i]);
foreach (var path in deletedAssets)
store.Remove (path);
foreach (var path in importedAssets)
store.RebuildFor (path, true);
var config = Globals<Config>.Value;
if (config.UpdateUnusedAssetsOnDemand) {
config.PendingUpdateUnusedAssets = true;
}
else {
ForceUpdateUnusedAssets ();
}
}
public static void ForceUpdateUnusedAssets () {
Globals<Config>.Value.PendingUpdateUnusedAssets = false;
var store = Globals<BacklinkStore>.Value;
store.UpdateUnusedAssets ();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 17c1f845fa88d38448c4cf65e9745f30
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,47 @@
using UnityEditor;
using UnityEngine;
namespace Asset_Cleaner {
static class ProjectViewGui {
static CleanerStyleAsset.Style _style = Globals<WindowData>.Value.Style;
public static void OnProjectWindowItemOnGui(string guid, Rect rect) {
if (!Globals<Config>.Value.MarkRed) return;
var store = Globals<BacklinkStore>.Value;
if (!store.Initialized) return;
var path = AssetDatabase.GUIDToAssetPath(guid);
ShowRowQuantity(rect, path, store);
long size = 0;
var _ = store.UnusedFiles.TryGetValue(path, out size) || store.UnusedScenes.TryGetValue(path, out size);
if (SearchUtils.IsUnused(path)) {
var buf = GUI.color;
{
GUI.color = _style.RedHighlight;
GUI.Box(rect, string.Empty);
}
GUI.color = buf;
GUI.Label(rect, CommonUtils.BytesToString(size), _style.ProjectViewCounterLabel);
}
}
static void ShowRowQuantity(Rect rect, string path, BacklinkStore backlinkStore) {
if (!AssetDatabase.IsValidFolder(path))
return;
backlinkStore.FoldersWithQty.TryGetValue(path, out var folderWithQty);
var cntFiles = folderWithQty?.UnusedFilesQty ?? 0;
var cntScenes = folderWithQty?.UnusedScenesQty ?? 0;
long size = folderWithQty?.UnusedSize ?? 0;
if (cntFiles == 0 && cntScenes == 0) return;
var countStr = cntFiles + cntScenes > 0 ? $"{cntFiles} | {cntScenes} ({CommonUtils.BytesToString(size)})" : "";
GUI.Label(rect, countStr, _style.ProjectViewCounterLabel);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a17718cc645348b0bc4ed6d7e514ab30
timeCreated: 1589032770

View File

@@ -0,0 +1,56 @@
using System;
using Leopotam.Ecs;
using UnityEngine;
using static Asset_Cleaner.AufCtx;
namespace Asset_Cleaner {
class SysProcessSearch : IEcsRunSystem {
EcsFilter<SelectionChanged> _from = null;
EcsFilter<Result, SearchResultGui, InSceneResult> SceneResultRows = null;
EcsFilter<SceneResult, SceneDetails> ScenePaths = null;
EcsFilter<SearchArg>.Exclude<InSceneResult> SearchArgMain = null;
EcsFilter<Result, SearchResultGui, FileResultTag> FileResultRows = null;
public void Run() {
if (_from.IsEmpty())
return;
SearchArgMain.AllDestroy();
ScenePaths.AllDestroy();
FileResultRows.AllDestroy();
SceneResultRows.AllDestroy();
var wd = Globals<WindowData>.Value;
if (wd.Window)
wd.Window.Repaint();
foreach (var i in _from.Out(out var get1, out _)) {
var t1 = get1[i];
if (!t1.Target) continue;
wd.FindFrom = t1.From;
try {
switch (t1.From) {
case FindModeEnum.Scene:
World.NewEntityWith(out SearchArg st);
SearchUtils.Init(st, t1.Target, t1.Scene);
SearchUtils.InScene(st, t1.Scene);
break;
case FindModeEnum.File:
World.NewEntityWith(out SearchArg arg);
SearchUtils.Init(arg, t1.Target);
SearchUtils.FilesThatReference(arg);
SearchUtils.ScenesThatContain(t1.Target);
break;
}
}
catch (Exception e) {
Debug.LogException(e);
}
}
_from.AllUnset<SelectionChanged>();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: afd561b0c1384bcc82976f2624afa895
timeCreated: 1577287503

View File

@@ -0,0 +1,29 @@
using Leopotam.Ecs;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace Asset_Cleaner {
class RequestRepaintEvt { }
class SysRepaintWindow : IEcsRunSystem, IEcsInitSystem {
EcsFilter<RequestRepaintEvt> Repaint = null;
public void Init() {
var wd = Globals<WindowData>.Value;
wd.SceneFoldout = new GUIContent(AssetPreview.GetMiniTypeThumbnail(typeof(SceneAsset)));
wd.ExpandScenes = true;
wd.ExpandFiles = true;
wd.ScrollPos = Vector2.zero;
}
public void Run() {
var wd = Globals<WindowData>.Value;
if (Repaint.IsEmpty()) return;
wd.Window.Repaint();
InternalEditorUtility.RepaintAllViews();
Repaint.AllDestroy();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 673a2486762b4e3da784f1657b90bb93
timeCreated: 1577288020

View File

@@ -0,0 +1,44 @@
using Leopotam.Ecs;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
namespace Asset_Cleaner {
class SceneToClose : IEcsAutoReset {
public Scene Scene;
public int SelectionId;
public bool ForceClose;
public void Reset() {
ForceClose = default;
Scene = default;
SelectionId = default;
}
}
class SysSceneCleanup : IEcsRunSystem, IEcsDestroySystem {
EcsFilter<SceneToClose> ScenesToClose = default;
public void Run() {
if (ScenesToClose.IsEmpty()) return;
var selectionId = Globals<PersistentUndoRedoState>.Value.Id;
foreach (var i in ScenesToClose.Out(out var g1, out var entities)) {
var s = g1[i].Scene;
if (g1[i].SelectionId == selectionId && !g1[i].ForceClose) continue;
if (Selection.activeGameObject && Selection.activeGameObject.scene == s) continue;
if (s.isLoaded) EditorSceneManager.CloseScene(s, removeScene: true);
entities[i].Destroy();
}
}
// close scenes on window close
public void Destroy() {
foreach (var i in ScenesToClose.Out(out var g1, out _)) {
var s = g1[i].Scene;
if (s.isLoaded) EditorSceneManager.CloseScene(s, removeScene: true);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d42cae4aa3ff4500948ddd623a270120
timeCreated: 1596142629

View File

@@ -0,0 +1,191 @@
using System.Linq;
using System.Runtime.InteropServices;
using Leopotam.Ecs;
using UnityEditor;
using UnityEngine;
using static Asset_Cleaner.AufCtx;
namespace Asset_Cleaner {
class CleanupPrevArg { }
class UndoEvt { }
class RedoEvt { }
class SysUndoRedoSelection : IEcsRunSystem, IEcsInitSystem, IEcsDestroySystem {
EcsFilter<UndoEvt> UndoEvt = default;
EcsFilter<RedoEvt> RedoEvt = default;
bool _preventHistoryInsert;
bool _preventSelectionSet;
public void Init() {
Undo.undoRedoPerformed += OnUndoRedoPerformed;
Selection.selectionChanged += OnSelectionChanged;
Globals<UndoRedoState>.Value = new UndoRedoState();
if (Globals<PersistentUndoRedoState>.Value.History.Count > 0)
_preventHistoryInsert = true;
OnSelectionChanged(); //init selection
}
public void Destroy() {
Undo.undoRedoPerformed -= OnUndoRedoPerformed;
Selection.selectionChanged -= OnSelectionChanged;
Globals<UndoRedoState>.Value = default;
}
public void Run() {
MouseInput();
if (UndoEvt.IsEmpty() && RedoEvt.IsEmpty()) return;
Counters(undo: !UndoEvt.IsEmpty(), redo: !RedoEvt.IsEmpty(), false);
_preventHistoryInsert = true;
if (!_preventSelectionSet) {
(var history, var id) = Globals<PersistentUndoRedoState>.Value;
SelectionEntry entry = history[id];
if (entry.Valid())
Selection.objects = entry.IsGuids
? entry.Guids.Select(AssetDatabase.GUIDToAssetPath).Select(AssetDatabase.LoadAssetAtPath<Object>).Where(obj => obj).ToArray()
: entry.SceneObjects;
}
_preventSelectionSet = false;
UndoEvt.AllDestroy();
RedoEvt.AllDestroy();
}
static void Counters(bool undo, bool redo, bool insertToHistory) {
var state = Globals<PersistentUndoRedoState>.Value;
World.NewEntityWith(out RequestRepaintEvt _);
const int MinId = 0;
if (insertToHistory) {
var entry = new SelectionEntry();
var count = state.History.Count - 1 - state.Id;
if (count > 0)
state.History.RemoveRange(state.Id + 1, count);
state.History.Add(entry);
state.Id = MaxId();
if (Selection.assetGUIDs.Length > 0) {
entry.IsGuids = true;
entry.Guids = Selection.assetGUIDs;
}
else {
entry.SceneObjects = Selection.objects;
}
}
if (undo) {
// loop to skip invalid
while (true) {
state.Id -= 1;
if (state.Id < MinId) break;
if (state.History[state.Id].Valid()) break;
}
}
if (redo) {
// loop to skip invalid
while (true) {
state.Id += 1;
if (state.Id > MaxId()) break;
if (state.History[state.Id].Valid()) break;
}
}
state.Id = Mathf.Clamp(state.Id, MinId, MaxId());
var undoRedoState = Globals<UndoRedoState>.Value;
undoRedoState.UndoEnabled = state.Id != MinId;
undoRedoState.RedoEnabled = state.Id != MaxId();
int MaxId() => Mathf.Max(0, state.History.Count - 1);
}
void OnSelectionChanged() {
World.NewEntityWith(out RequestRepaintEvt _);
if (Globals<Config>.Value == null || Globals<Config>.Value.Locked) return;
Counters(undo: false, redo: false, insertToHistory: !_preventHistoryInsert);
_preventHistoryInsert = false;
World.NewEntityWith(out SelectionChanged comp);
World.NewEntityWith(out CleanupPrevArg _);
var go = Selection.activeGameObject;
if (go && go.scene.IsValid()) {
comp.From = FindModeEnum.Scene;
comp.Target = go;
comp.Scene = go.scene;
}
else {
var guids = Selection.assetGUIDs;
// comp.Guids = Selection.assetGUIDs;
bool any = guids != null && guids.Length > 0;
if (any) {
comp.From = FindModeEnum.File;
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
comp.Target = AssetDatabase.LoadAssetAtPath<Object>(path);
}
else {
comp.From = FindModeEnum.None;
comp.Target = null;
}
}
}
// prevents selection history flooding
void OnUndoRedoPerformed() {
// below is a hackish way to catch Undo/Redo from editor
//if (AufCtx.Destroyed) return;
if (!Undo.GetCurrentGroupName().Equals("Selection Change")) return;
var evt = Event.current;
if (evt == null) return;
if (evt.rawType != EventType.KeyDown) return;
switch (evt.keyCode) {
case KeyCode.Z:
World.NewEntityWith(out UndoEvt _);
_preventSelectionSet = true; // prevent manual Selection set
break;
case KeyCode.Y:
World.NewEntityWith(out RedoEvt _);
_preventSelectionSet = true;
break;
}
}
void MouseInput() {
if (_nextClick > EditorApplication.timeSinceStartup) return;
var any = false;
if (Pressed(0x5)) {
World.NewEntityWith(out UndoEvt _);
any = true;
}
if (Pressed(0x6)) {
World.NewEntityWith(out RedoEvt _);
any = true;
}
if (any)
_nextClick = EditorApplication.timeSinceStartup + 0.25;
}
#if UNITY_EDITOR_WIN
[DllImport("USER32.dll")]
static extern short GetKeyState(int keycode);
#else
static short GetKeyState(int keycode) => 0;
#endif
double _nextClick;
// 5 back, 6 fw
static bool Pressed(int keyCode) => (GetKeyState(keyCode) & 0x100) != 0;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6e6a2f49ce174cabbc560ea8e3e9975c
timeCreated: 1596008857

View File

@@ -0,0 +1,978 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Leopotam.Ecs;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
namespace Asset_Cleaner {
class FileResultTag { }
enum TargetTypeEnum {
File = 0,
Directory = 1,
Scene = 2,
ObjectInScene = 3,
ObjectInStage = 4
}
class SysWindowGui : IEcsRunSystem, IEcsInitSystem {
EcsFilter<Result, SearchResultGui, InSceneResult> SceneResultRows = null;
EcsFilter<SceneResult, SceneDetails> ScenePaths = null;
EcsFilter<SearchArg>.Exclude<InSceneResult> SearchArgMain = null;
EcsFilter<Result, SearchResultGui, FileResultTag> FileResultRows = null;
public void Init() {
BacklinkStoreDirty(true);
VisualSettingDirty(true);
}
public void Run() {
var windowData = Globals<WindowData>.Value;
_toolbarSelection = GUILayout.Toolbar(_toolbarSelection, _toolbarStrings, GUILayout.ExpandWidth(false));
var conf = Globals<Config>.Value;
switch (_toolbarSelection) {
case 0: {
ShowTabMain(conf, windowData);
break;
}
case 1: {
ShowTabSettings(conf);
break;
}
}
}
string[] _toolbarStrings = {"Main", "Settings"};
const int _progressBarShowFromLevel = 10;
int _toolbarSelection = 0;
int _settingIgnoredPathsHash1;
bool BacklinkStoreDirty(bool set) {
var res = Hash() != _settingIgnoredPathsHash1;
if (set) _settingIgnoredPathsHash1 = Hash();
return res;
int Hash() {
var conf = Globals<Config>.Value;
return DirtyUtils.HashCode(conf.IgnorePathContainsCombined,
conf.IgnoreMaterial,
conf.IgnoreScriptable,
conf.IgnoreSprite);
}
}
int _settingCodeHash1;
bool VisualSettingDirty(bool set) {
var res = Hash() != _settingCodeHash1;
if (set) _settingCodeHash1 = Hash();
return res;
int Hash() {
var conf = Globals<Config>.Value;
return DirtyUtils.HashCode(
conf.MarkRed,
conf.ShowInfoBox,
conf.RebuildCacheOnDemand,
conf.UpdateUnusedAssetsOnDemand);
}
}
void ShowTabSettings(Config conf) {
using (new EditorGUILayout.VerticalScope()) {
EditorGUILayout.Space();
var enabled = GUI.enabled;
GUI.enabled = true;
using (new EditorGUILayout.VerticalScope()) {
conf.MarkRed = GUILayout.Toggle(conf.MarkRed, "Display counters and red overlay in Project View");
conf.ShowInfoBox = GUILayout.Toggle(conf.ShowInfoBox, "Help suggestions");
conf.RebuildCacheOnDemand = GUILayout.Toggle(conf.RebuildCacheOnDemand, "Rebuild cache on demand (when scripts are updated often)");
conf.UpdateUnusedAssetsOnDemand = GUILayout.Toggle(conf.UpdateUnusedAssetsOnDemand, "Update unused assets on demand");
EditorGUILayout.Space();
conf.IgnoreMaterial = GUILayout.Toggle(conf.IgnoreMaterial, "Skip Materials");
conf.IgnoreScriptable = GUILayout.Toggle(conf.IgnoreScriptable, "Skip ScriptableObjects");
conf.IgnoreSprite = GUILayout.Toggle(conf.IgnoreSprite, "Skip Sprites");
}
EditorGUILayout.Space();
GUI.enabled = enabled;
EditorGUILayout.LabelField("Skip Path(s) contains:");
conf.IgnorePathContainsCombined = GUILayout.TextArea(conf.IgnorePathContainsCombined);
using (new EditorGUILayout.HorizontalScope(GUILayout.ExpandWidth(false))) {
EditorGUILayout.Space();
var previous = GUI.enabled;
GUI.enabled = BacklinkStoreDirty(false) || VisualSettingDirty(false);
if (GUILayout.Button("Apply")) {
conf.IgnorePathContains = conf.IgnorePathContainsCombined.Split(';')
.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray();
Apply();
}
GUI.enabled = previous;
var selectedGuids = Selection.assetGUIDs;
var assetPaths = new List<string>();
if (selectedGuids.Length > 0) {
foreach (var guid in selectedGuids) {
var realAssetPath = AssetDatabase.GUIDToAssetPath(guid);
var obj = AssetDatabase.LoadAssetAtPath<Object>(realAssetPath);
var assetPath = realAssetPath.Replace("Assets/", string.Empty);
if (obj is DefaultAsset &&
!conf.IgnorePathContains.Any(p => (StringComparer.Ordinal.Equals(p, assetPath)))) {
assetPaths.Add(assetPath);
}
}
}
GUI.enabled = (assetPaths.Count > 0);
var foldersList = string.Join(", ", assetPaths);
if (GUILayout.Button("Add Selected Path")) {
var choice = EditorUtility.DisplayDialog(
title: "Asset Cleaner",
message:
$"Do you really want to add these folder(s) to ignored list: \"{foldersList}\"?",
ok: "Ignore",
cancel: "Cancel");
if (choice) {
conf.IgnorePathContainsCombined += $"{foldersList};";
conf.IgnorePathContains = conf.IgnorePathContainsCombined.Split(';')
.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray();
Apply();
}
}
GUI.enabled = true;
if (GUILayout.Button("Reset")) {
var choice = EditorUtility.DisplayDialog(
title: "Asset Cleaner",
message:
$"Do you really want to reset to the factory settings?",
ok: "Reset",
cancel: "Cancel");
if (choice) {
var serializable = AufSerializableData.Default();
AufSerializableData.OnDeserialize(in serializable, ref conf);
Apply();
}
}
GUI.enabled = previous;
}
EditorGUILayout.Space();
EditorGUILayout.LabelField(conf.InitializationTime);
var buf = GUI.enabled;
GUI.enabled = Selection.objects.Length > 0;
if (GUILayout.Button("Reserialize selected assets", GUILayout.ExpandWidth(false))) {
var paths = Selection.objects.Select(AssetDatabase.GetAssetPath);
AssetDatabase.ForceReserializeAssets(paths);
EditorApplication.ExecuteMenuItem("File/Save Project");
AssetDatabase.Refresh();
}
GUI.enabled = buf;
EditorGUILayout.Space();
}
void Apply() {
var rebuild = BacklinkStoreDirty(true);
VisualSettingDirty(true);
PersistenceUtils.Save(in conf);
AufCtx.World.NewEntityWith(out RequestRepaintEvt _);
if (rebuild)
Globals<BacklinkStore>.Value.UpdateUnusedAssets();
InternalEditorUtility.RepaintAllViews();
}
}
void ShowTabMain(Config conf, WindowData windowData) {
var store = Globals<BacklinkStore>.Value;
EditorGUIUtility.labelWidth = windowData.Window.position.width * .7f;
int Hash() => DirtyUtils.HashCode(conf.Locked);
var active = SearchArgMain.Get1[0];
if (conf.Locked && (windowData.FindFrom == FindModeEnum.File &&
(active == null || active.Main == null || !AssetDatabase.Contains(active.Main)))) {
conf.Locked = false;
AufCtx.World.NewEntityWith(out RequestRepaintEvt _);
}
var style = windowData.Style;
var hash = Hash();
if (hash != Hash()) {
PersistenceUtils.Save(in conf);
AufCtx.World.NewEntityWith(out RequestRepaintEvt _);
}
// if (Globals<WindowData>.Get() == null) return;
EditorGUILayout.Space();
SearchArg arg = default;
foreach (var i in SearchArgMain) {
arg = SearchArgMain.Get1[i];
if (arg != null && arg.Main != null) {
break;
}
}
if (arg == default) {
GUI.enabled = false;
GUILayout.TextArea("No items selected. Select an item in a scene or project.");
GUI.enabled = true;
return;
}
var targetTypeEnum = GetTargetType(windowData, arg?.Main);
BacklinkStore.UnusedQty unusedQty = new BacklinkStore.UnusedQty(0, 0, 0);
using (new EditorGUILayout.HorizontalScope()) {
var enabledBuf = GUI.enabled;
var selectedGuids = Selection.assetGUIDs;
var undoRedoState = Globals<UndoRedoState>.Value;
GUI.enabled = selectedGuids != null && !conf.Locked && undoRedoState.UndoEnabled;
if (GUILayout.Button(style.ArrowL, style.ArrowBtn)) {
AufCtx.World.NewEntityWith(out UndoEvt _);
}
GUI.enabled = selectedGuids != null && !conf.Locked && undoRedoState.RedoEnabled;
if (GUILayout.Button(style.ArrowR, style.ArrowBtn)) {
AufCtx.World.NewEntityWith(out RedoEvt _);
}
GUI.enabled = enabledBuf;
if (conf.Locked) {
if (GUILayout.Button(style.Lock, style.LockBtn)) {
AufCtx.World.NewEntityWith(out SelectionChanged selectionChanged);
conf.Locked = false;
if (Selection.activeObject != arg.Target) {
selectionChanged.From = FindModeEnum.Scene;
selectionChanged.Scene = SceneManager.GetActiveScene();
selectionChanged.Target = Selection.activeObject;
}
else if (Selection.assetGUIDs is string[] guids) {
// todo show info box multiple selection is unsupported
if (guids.Length > 0) {
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
selectionChanged.Target = AssetDatabase.LoadAssetAtPath<Object>(path);
switch (Selection.selectionChanged.Target) {
case DefaultAsset _:
selectionChanged.From = FindModeEnum.File;
break;
case GameObject go when go.scene.isLoaded:
selectionChanged.From = FindModeEnum.Scene;
selectionChanged.Scene = SceneManager.GetActiveScene();
break;
default:
selectionChanged.From = FindModeEnum.File;
break;
}
}
else if (Selection.activeObject is GameObject go && go.scene.isLoaded) {
selectionChanged.From = FindModeEnum.Scene;
selectionChanged.Target = Selection.activeObject;
selectionChanged.Scene = SceneManager.GetActiveScene();
}
}
}
}
else {
var enabled = GUI.enabled;
GUI.enabled = selectedGuids != null && selectedGuids.Length == 1;
if (GUILayout.Button(style.Unlock, style.UnlockBtn)) {
conf.Locked = true;
}
GUI.enabled = enabled;
}
unusedQty = ShowObjectName(store, windowData, targetTypeEnum, arg, selectedGuids);
}
bool isMultiSelect = Selection.assetGUIDs != null && Selection.assetGUIDs.Length > 1;
if (conf.ShowInfoBox) {
if (isMultiSelect && (unusedQty.UnusedFilesQty + unusedQty.UnusedScenesQty > 0)) {
var msgUnusedFiles = (unusedQty.UnusedFilesQty > 0)
? $"unused files ({unusedQty.UnusedFilesQty}),"
: "";
var msgUnusedScenes = (unusedQty.UnusedScenesQty > 0)
? $"unused scenes ({unusedQty.UnusedScenesQty}),"
: "";
var msgMultiSelect = $"This multi-selection contains: " +
msgUnusedFiles + msgUnusedScenes +
$"\nYou could delete them pressing corresponding button to the right.";
EditorGUILayout.HelpBox(msgMultiSelect, MessageType.Info);
}
else if (TryGetHelpInfo(arg, out var msg, out var msgType)) {
EditorGUILayout.HelpBox(msg, msgType);
}
}
if (targetTypeEnum != TargetTypeEnum.Directory && !isMultiSelect) {
var windowData2 = Globals<WindowData>.Value;
EditorGUILayout.BeginVertical();
{
windowData2.ScrollPos = EditorGUILayout.BeginScrollView(windowData2.ScrollPos);
{
RenderRows(windowData2);
EditorGUILayout.Space();
}
EditorGUILayout.EndScrollView();
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
}
}
static bool TryGetHelpInfo(SearchArg arg, out string msg, out MessageType msgType) {
msgType = MessageType.Info;
if (arg == null) {
msg = default;
return false;
}
var path = arg.FilePath;
if (string.IsNullOrEmpty(path)) {
msg = default;
return false;
}
if (SearchUtils.IgnoredPaths(path, out var subPath)) {
msg = $"Paths containing '{subPath}' are ignored. You could add or remove it in Settings tab";
return true;
}
if (SearchUtils.IgnoredNonAssets(path) && !path.Eq("Assets")) {
msg = $"Asset is outside of Assets folder";
return true;
}
if (IgnoreTypes.Check(path, out var type)) {
if (AssetDatabase.IsValidFolder(path)) {
var scenes = arg.UnusedScenesFiltered?.Count;
var files = arg.UnusedAssetsFiltered?.Count;
if (scenes == 0 && files == 0) {
msg = default;
return false;
}
var b = new StringBuilder();
b.Append("This directory contains: ");
var any = false;
if (files > 0) {
any = true;
b.Append($"unused files ({files})");
}
if (scenes > 0) {
if (any)
b.Append(", ");
b.Append($"unused scenes ({scenes})");
}
b.Append(
".\nYou could delete them pressing corresponding button to the right.\nIf you don't want it to be inspected, please add it to Ignore list in the Settings tab");
msg = b.ToString();
return true;
}
msg = $"Assets of type '{type.Name}' are ignored. Please contact support if you need to change it";
return true;
}
// if (Filters.ScenePaths.GetEntitiesCount() == 0 && Filters.FileResultRows.GetEntitiesCount() == 0) {
if (SearchUtils.IsUnused(path)) {
type = AssetDatabase.GetMainAssetTypeAtPath(path);
var name = type.IsAssignableFromInverse(typeof(SceneAsset)) ? "scene" : "file";
msg =
$"This {name} has no explicit serialized usages and potentially could be removed. If you don't want it to be inspected, please add the containing folder to ignore list";
return true;
}
msgType = default;
msg = default;
return false;
}
static TargetTypeEnum GetTargetType(WindowData windowData1, Object obj) {
if (obj == null) return TargetTypeEnum.File;
var targetTypeEnum = TargetTypeEnum.Directory;
var path = AssetDatabase.GetAssetPath(obj);
switch (windowData1.FindFrom) {
case FindModeEnum.File when obj is DefaultAsset:
targetTypeEnum = TargetTypeEnum.Directory;
break;
case FindModeEnum.File when path.LastIndexOf(".unity", StringComparison.Ordinal) != -1:
targetTypeEnum = TargetTypeEnum.Scene;
break;
case FindModeEnum.File:
targetTypeEnum = TargetTypeEnum.File;
break;
case FindModeEnum.Scene:
targetTypeEnum = TargetTypeEnum.ObjectInScene;
break;
case FindModeEnum.Stage:
targetTypeEnum = TargetTypeEnum.ObjectInStage;
break;
}
return targetTypeEnum;
}
GUIContent _contentBuf = new GUIContent();
GUIContent _buf2 = new GUIContent();
BacklinkStore.UnusedQty ShowObjectName(BacklinkStore store, WindowData windowData, TargetTypeEnum targetTypeEnum, SearchArg arg, string[] selectedGuids) {
float TextWidth() {
_buf2.text = _contentBuf.text;
return 20f + GUI.skin.button.CalcSize(_buf2).x;
}
if (arg == null || arg.Main == null) return new BacklinkStore.UnusedQty();
bool isMultiSelect = selectedGuids != null && selectedGuids.Length > 1;
if (_contentBuf == null) {
_contentBuf = new GUIContent {tooltip = $"Click to ping"};
}
_contentBuf.image = isMultiSelect
? windowData.Style.MultiSelect.image
: AssetPreview.GetMiniThumbnail(arg.Target);
_contentBuf.text = string.Empty;
_contentBuf.tooltip = string.Empty;
if (!isMultiSelect) {
switch (targetTypeEnum) {
case TargetTypeEnum.Directory:
if (!SearchArgMain.IsEmpty()) {
_contentBuf.text = $"{arg.Main.name} (Folder)";
if (GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn,
GUILayout.MinWidth(TextWidth()))) {
EditorGUIUtility.PingObject(arg.Main);
}
if (AskDeleteUnusedFiles(arg, arg.UnusedAssetsFiltered, windowData))
return new BacklinkStore.UnusedQty();
if (AskDeleteUnusedScenes(arg, arg.UnusedScenesFiltered, windowData))
return new BacklinkStore.UnusedQty();
}
break;
case TargetTypeEnum.File:
_contentBuf.text = $"{arg.Main.name} (File Asset)";
if (GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn,
GUILayout.MinWidth(TextWidth()))) {
EditorGUIUtility.PingObject(arg.Main);
}
bool hasUnusedFile = SearchUtils.IsUnused(arg.FilePath);
var previous = GUI.enabled;
GUI.enabled = hasUnusedFile;
if (GUILayout.Button(windowData.Style.RemoveFile,
windowData.Style.RemoveUnusedBtn)) {
var choice = EditorUtility.DisplayDialog(
title: "Asset Cleaner",
message:
$"Do you really want to remove file: \"{arg.Main.name}\"?",
ok: "Remove",
cancel: "Cancel");
if (choice) {
EditorApplication.ExecuteMenuItem("File/Save Project");
DeleteWithMeta(arg.FilePath);
AssetDatabase.Refresh();
SearchUtils.Upd(arg);
}
}
GUI.enabled = previous;
break;
case TargetTypeEnum.Scene:
_contentBuf.text = $"{arg.Main.name} (Scene)";
if (GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn,
GUILayout.MinWidth(TextWidth()))) {
EditorGUIUtility.PingObject(arg.Main);
}
bool hasUnusedScene = SearchUtils.IsUnused(arg.FilePath);
previous = GUI.enabled;
GUI.enabled = hasUnusedScene;
if (GUILayout.Button(windowData.Style.RemoveScene,
windowData.Style.RemoveUnusedBtn)) {
var choice = EditorUtility.DisplayDialog(
title: "Asset Cleaner",
message:
$"Do you really want to remove scene: {arg.Main.name}?",
ok: "Remove",
cancel: "Cancel");
if (choice) {
EditorApplication.ExecuteMenuItem("File/Save Project");
DeleteWithMeta(arg.FilePath);
AssetDatabase.Refresh();
SearchUtils.Upd(arg);
}
}
GUI.enabled = previous;
break;
case TargetTypeEnum.ObjectInScene:
_contentBuf.text = $"{arg.Main.name} (Object in Scene)";
if (GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn,
GUILayout.MinWidth(TextWidth()))) {
EditorGUIUtility.PingObject(arg.Main);
}
break;
case TargetTypeEnum.ObjectInStage:
_contentBuf.image = AssetPreview.GetMiniThumbnail(arg.Target);
_contentBuf.text = $"{arg.Main.name} (Object in Staging)";
if (GUILayout.Button(_contentBuf,
windowData.Style.RemoveUnusedBtn)) {
EditorGUIUtility.PingObject(arg.Main);
}
break;
default:
if (GUILayout.Button($"{arg.Main.name} (Unknown Object Type)",
windowData.Style.RemoveUnusedBtn)) {
EditorGUIUtility.PingObject(arg.Main);
}
break;
}
}
else {
var unusedAssets = new List<string>();
var unusedScenes = new List<string>();
foreach (var guid in selectedGuids) {
var path = AssetDatabase.GUIDToAssetPath(guid);
if (store.UnusedFiles.TryGetValue(path, out _))
unusedAssets.Add(path);
else if (store.UnusedScenes.TryGetValue(path, out _))
unusedScenes.Add(path);
if (store.FoldersWithQty.TryGetValue(path, out _)) {
SearchArg searchArg = new SearchArg() {
FilePath = path,
Target = AssetDatabase.LoadAssetAtPath<DefaultAsset>(path),
Main = AssetDatabase.LoadAssetAtPath<DefaultAsset>(path)
};
SearchUtils.Upd(searchArg);
foreach (var unusedAssetPath in searchArg.UnusedAssetsFiltered)
if (store.UnusedFiles.TryGetValue(unusedAssetPath, out _))
unusedAssets.Add(unusedAssetPath);
foreach (var unusedScenePath in searchArg.UnusedScenesFiltered)
if (store.UnusedScenes.TryGetValue(unusedScenePath, out _))
unusedScenes.Add(unusedScenePath);
}
}
unusedAssets = unusedAssets.Distinct().ToList();
unusedScenes = unusedScenes.Distinct().ToList();
var assetSize = unusedAssets.Sum(p => new FileInfo(p).Length);
var sceneSize = unusedScenes.Sum(p => new FileInfo(p).Length);
_contentBuf.text =
$"Assets: {unusedAssets.Count} ({CommonUtils.BytesToString(assetSize)}), Scenes: {unusedScenes.Count} ({CommonUtils.BytesToString(sceneSize)})";
;
GUILayout.Button(_contentBuf, windowData.Style.CurrentBtn, GUILayout.MinWidth(TextWidth()));
if (AskDeleteUnusedFiles(arg, unusedAssets, windowData))
return new BacklinkStore.UnusedQty();
if (AskDeleteUnusedScenes(arg, unusedScenes, windowData))
return new BacklinkStore.UnusedQty();
return new BacklinkStore.UnusedQty(unusedAssets.Count, unusedScenes.Count, assetSize + sceneSize);
}
return new BacklinkStore.UnusedQty();
}
static bool AskDeleteUnusedFiles(SearchArg arg, List<string> unusedAssets, WindowData windowData) {
if (arg == null || unusedAssets == null) return false;
var hasUnusedAssets = unusedAssets.Count > 0;
var previous = GUI.enabled;
GUI.enabled = hasUnusedAssets;
var guiContentRemoveAssets = windowData.Style.RemoveFile;
if (GUILayout.Button(guiContentRemoveAssets,
windowData.Style.RemoveUnusedBtn)) {
var choice = EditorUtility.DisplayDialog(
title: "Asset Cleaner",
message:
$"Do you really want to remove {unusedAssets.Count} asset(s)?",
ok: "Remove",
cancel: "Cancel");
if (choice) {
EditorApplication.ExecuteMenuItem("File/Save Project");
var i = 0f;
var total = (float) unusedAssets.Count;
foreach (var f in unusedAssets) {
var path = Application.dataPath.Replace("Assets", f);
DeleteWithMeta(path);
var percent = i * 100 / total;
if (total >= _progressBarShowFromLevel) {
if (Math.Abs(percent % 5f) < 0.01f) {
if (EditorUtility.DisplayCancelableProgressBar(
"Please wait...",
"Deleting assets...", percent))
throw new Exception("Deleting aborted");
}
i++;
}
}
if (total >= _progressBarShowFromLevel) {
EditorUtility.ClearProgressBar();
}
AssetDatabase.Refresh();
SearchUtils.Upd(arg);
}
GUI.enabled = previous;
return true;
}
GUI.enabled = previous;
return false;
}
static void DeleteWithMeta(string path) {
FileUtil.DeleteFileOrDirectory(path);
var metaPath = AssetDatabase.GetTextMetaFilePathFromAssetPath(path);
if (!string.IsNullOrEmpty(metaPath))
FileUtil.DeleteFileOrDirectory(metaPath);
}
static bool AskDeleteUnusedScenes(SearchArg arg, List<string> unusedScenes, WindowData windowData) {
if (arg == null || unusedScenes == null) return false;
var hasUnusedScenes = unusedScenes.Count > 0;
var previous = GUI.enabled;
GUI.enabled = hasUnusedScenes;
var guiContentRemoveScenes = windowData.Style.RemoveScene;
if (GUILayout.Button(guiContentRemoveScenes,
windowData.Style.RemoveUnusedBtn)) {
var choice = EditorUtility.DisplayDialog(
title: "Asset Cleaner",
message:
$"Do you really want to remove {unusedScenes.Count} scene(s)?",
ok: "Remove",
cancel: "Cancel");
if (choice) {
EditorApplication.ExecuteMenuItem("File/Save Project");
var i = 0f;
var total = (float) unusedScenes.Count;
foreach (var scene in unusedScenes) {
var path = Application.dataPath.Replace("Assets", scene);
DeleteWithMeta(path);
if (total >= _progressBarShowFromLevel) {
var percent = i * 100 / total;
if (Math.Abs(percent % 5f) < 0.01f) {
if (EditorUtility.DisplayCancelableProgressBar(
"Please wait...",
"Deleting scenes...", percent))
throw new Exception("Deleting aborted");
}
i++;
}
}
if (total >= _progressBarShowFromLevel) {
EditorUtility.ClearProgressBar();
}
AssetDatabase.Refresh();
SearchUtils.Upd(arg);
}
GUI.enabled = previous;
return true;
}
GUI.enabled = previous;
return false;
}
void RenderRows(WindowData windowData) {
// todo show spinner until scene is loaded,
if (FileResultRows.GetEntitiesCount() > 0) {
windowData.ExpandFiles =
EditorGUILayout.Foldout(windowData.ExpandFiles,
$"Usages in Project: {FileResultRows.GetEntitiesCount()}");
}
if (SearchArgMain.IsEmpty())
return;
if (windowData.ExpandFiles && windowData.FindFrom == FindModeEnum.File)
foreach (var i in FileResultRows.Out(out var get1, out var get2, out _, out _))
DrawRowFile(get1[i], get2[i], windowData);
var sceneMessage = $"Usages in Scenes: {ScenePaths.GetEntitiesCount()}";
if (ScenePaths.GetEntitiesCount() > 0) {
windowData.ExpandScenes =
EditorGUILayout.Foldout(windowData.ExpandScenes, sceneMessage);
}
if (!windowData.ExpandScenes) return;
if (windowData.ExpandScenes && windowData.FindFrom == FindModeEnum.Scene) {
foreach (var (grp, indices) in SceneResultRows.Out(out _, out var get2, out _, out _)
.GroupBy1(ResultComp.Instance)) {
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
var count = 0;
foreach (var i in indices) {
if (count++ == 0)
if (GUILayout.Button(get2[i].Label, windowData.Style.RowMainAssetBtn)) {
if (windowData.Click.IsDoubleClick(grp.RootGo)) {
// _selectionChangedByArrows = false;
Selection.activeObject = grp.RootGo;
}
else
EditorGUIUtility.PingObject(grp.RootGo);
windowData.Click = new PrevClick(grp.RootGo);
}
DrawRowScene(get2[i]);
}
}
}
}
using (new GUILayout.HorizontalScope()) {
GUILayout.Space(windowData.Style.SceneIndent1);
using (new EditorGUILayout.VerticalScope()) {
foreach (var i1 in ScenePaths.Out(out var get1, out var get2, out _)) {
windowData.SceneFoldout.text = get1[i1].PathNicified;
var details = get2[i1];
details.SearchRequested = details.Scene.isLoaded;
details.SearchRequested = EditorGUILayout.Foldout(details.SearchRequested,
windowData.SceneFoldout, EditorStyles.foldout);
if (details.SearchRequested && details.Scene.isLoaded && !details.SearchDone) {
var mainArg = SearchArgMain.GetSingle();
mainArg.Scene = SceneManager.GetSceneByPath(details.Scene.path);
SearchUtils.InScene(mainArg, details.Scene);
details.SearchDone = true;
}
if (!details.SearchRequested) {
if (!details.Scene.isLoaded) continue;
if (!details.WasOpened) {
// to clean up on selection change
AufCtx.World.NewEntityWith(out SceneToClose comp);
comp.Scene = details.Scene;
comp.ForceClose = true;
}
foreach (var row in SceneResultRows.Out(out _, out _, out var get3, out var entities)) {
if (!get3[row].ScenePath.Eq(details.Path))
continue;
entities[row].Destroy();
}
details.SearchDone = false;
}
else {
if (!details.Scene.isLoaded) {
details.Scene = EditorSceneManager.OpenScene(details.Path, OpenSceneMode.Additive);
// to clean up on selection change
AufCtx.World.NewEntityWith(out SceneToClose comp);
comp.Scene = details.Scene;
comp.SelectionId = Globals<PersistentUndoRedoState>.Value.Id;
#if UNITY_2019_1_OR_NEWER
EditorSceneManager.SetSceneCullingMask(details.Scene, 0);
#endif
details.SearchRequested = true;
// todo Scope component
#if later
if (details.Scene.isLoaded)
EditorSceneManager.CloseScene(details.Scene, false);
#endif
}
else if (SceneResultRows.IsEmpty())
EditorGUILayout.LabelField("No in-scene dependencies found.");
else
using (new GUILayout.HorizontalScope()) {
GUILayout.Space(windowData.Style.SceneIndent2);
using (new EditorGUILayout.VerticalScope())
foreach (var (grp, indices) in SceneResultRows
.Out(out var g1, out var g2, out var g3, out _)
.GroupBy1(ResultComp.Instance)) {
var any = false;
foreach (var i3 in indices) {
if (!g3[i3].ScenePath.Eq(details.Path))
continue;
any = true;
break;
}
if (!any)
continue;
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
var count = 0;
foreach (var i2 in indices) {
if (!g3[i2].ScenePath.Eq(details.Path))
continue;
if (count++ == 0) {
Result comp = g1[i2];
if (GUILayout.Button(g2[i2].Label,
windowData.Style.RowMainAssetBtn)) {
if (windowData.Click.IsDoubleClick(grp.RootGo)) {
// _selectionChangedByArrows = false;
Selection.activeObject = comp.RootGo;
}
else
EditorGUIUtility.PingObject(comp.RootGo);
windowData.Click = new PrevClick(comp.RootGo);
}
}
DrawRowScene(g2[i2]);
}
}
}
}
}
}
}
}
}
class ResultComp : IEqualityComparer<Result> {
public static ResultComp Instance { get; } = new ResultComp();
public bool Equals(Result x, Result y) => GetHashCode(x) == GetHashCode(y);
public int GetHashCode(Result obj) => obj.RootGo.GetInstanceID();
}
static void DrawRowScene(SearchResultGui gui) {
EditorGUI.BeginChangeCheck();
// if (data.TargetGo || data.TargetComponent)
foreach (var prop in gui.Properties) {
{
var locked = prop.Property.objectReferenceValue is MonoScript;
var f = GUI.enabled;
if (locked) GUI.enabled = false;
EditorGUILayout.PropertyField(prop.Property, prop.Content, false);
if (locked) GUI.enabled = f;
}
}
if (EditorGUI.EndChangeCheck())
gui.SerializedObject.ApplyModifiedProperties();
}
static void DrawRowFile(Result data, SearchResultGui gui, WindowData windowData) {
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) {
var buf = GUI.color;
var pingGo = data.MainFile == null ? data.RootGo : data.MainFile;
if (GUILayout.Button(gui.Label, windowData.Style.RowMainAssetBtn)) {
if (windowData.Click.IsDoubleClick(pingGo)) {
// _selectionChangedByArrows = false;
Selection.activeObject = pingGo;
}
else {
EditorGUIUtility.PingObject(pingGo);
}
windowData.Click = new PrevClick(pingGo);
}
GUI.color = buf;
EditorGUI.BeginChangeCheck();
if (data.File) {
foreach (var prop in gui.Properties) {
using (new EditorGUILayout.HorizontalScope()) {
var locked = prop.Property.objectReferenceValue is MonoScript;
var f = GUI.enabled;
if (locked) GUI.enabled = false;
EditorGUILayout.PropertyField(prop.Property, prop.Content, false);
if (locked) GUI.enabled = f;
}
}
}
if (EditorGUI.EndChangeCheck()) {
gui.SerializedObject.ApplyModifiedProperties();
// dependency.SerializedObject.Update();
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 87ac41283aa347c4aece096c90076c7f
timeCreated: 1596213420

View File

@@ -0,0 +1,10 @@
Asset Cleaner PRO - Clean | Find References
Third-Party Notices
This asset is governed by the Asset Store EULA; however, the following components are governed by the licenses indicated below:
A. [LeoECS] [MIT License] [license and copyright/accreditation details below]
Copyright (c) 2017 - 2020 Leopotam leopotam@gmail.com
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 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.

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1f955e95e9069b4439aa4fc90e0ee0ab
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8f9623ba76cbf214994b7b865c65f362
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,34 @@
using System.Diagnostics;
using UnityEngine.Assertions;
namespace Asset_Cleaner {
static class FLAGS {
// cleanup in release
public const string DEBUG = "DEBUG1";
public const string M_DISABLE_POOLING = "M_DISABLE_POOLING";
}
static class Asr {
#line hidden
[Conditional(FLAGS.DEBUG)]
public static void AreEqual(int a, int b) {
Assert.AreEqual(a, b);
}
[Conditional(FLAGS.DEBUG)]
public static void IsTrue(bool b, string format = null) {
Assert.IsTrue(b, format);
}
[Conditional(FLAGS.DEBUG)]
public static void IsFalse(bool b, string format = null) {
Assert.IsFalse(b, format);
}
[Conditional(FLAGS.DEBUG)]
public static void IsNotNull(object target, string format = null) {
Assert.IsNotNull(target, format);
}
#line default
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 53c5eeeb89d541a5b26f906f93ffe650
timeCreated: 1581424097

View File

@@ -0,0 +1,41 @@
using Leopotam.Ecs;
namespace Asset_Cleaner {
static class AufCtx {
public static EcsWorld World;
public static EcsSystems UndoGroup;
public static EcsSystems UpdateGroup;
public static EcsSystems OnGuiGroup;
internal static bool InitStarted { get; private set; }
internal static bool Destroyed { get; private set; }
internal static void TryInitWorld() {
if (InitStarted) return;
InitStarted = true;
World = new EcsWorld();
(OnGuiGroup = new EcsSystems(World)
.Add(new SysWindowGui())).Init();
(UndoGroup = new EcsSystems(World)
.Add(new SysUndoRedoSelection())
).Init();
(UpdateGroup = new EcsSystems(World)
.Add(new SysRepaintWindow())
.Add(new SysProcessSearch())
.Add(new SysSceneCleanup())
).Init();
}
internal static void DestroyWorld() {
if (!InitStarted) return;
InitStarted = false;
Destroyed = true;
Asr.IsFalse(__GlobalsCounter.HasAnyValue());
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 999cc1329c8dd5743b39fe17eb02a7aa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,23 @@
using System;
namespace Asset_Cleaner {
public static class CommonUtils {
static string[] _suffix = {"B", "KB", "MB", "GB", "TB"};
public static string BytesToString(long byteCount) {
if (byteCount == 0)
return $"0 {_suffix[0]}";
var bytes = Math.Abs(byteCount);
var place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
double num;
if (place == 0 || place == 1) { // display B, KB in MB
num = Math.Round(bytes / Math.Pow(1024, 2), 4);
return $"{Math.Sign(byteCount) * num:N} {_suffix[2]}";
}
num = Math.Round(bytes / Math.Pow(1024, place), 1);
return $"{Math.Sign(byteCount) * num:F0} {_suffix[place]}";
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4a0fa79cb05e495a8dc80a251a33970c
timeCreated: 1595072385

View File

@@ -0,0 +1,46 @@
using System.Runtime.CompilerServices;
namespace Asset_Cleaner {
static class DirtyUtils {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int HashCode<T1>(in T1 v1) {
var hash = v1.GetHashCode();
hash = (hash * 397);
return hash;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int HashCode<T1, T2>(in T1 v1, in T2 v2) {
var hash = v1.GetHashCode();
hash = (hash * 397) ^ v2.GetHashCode();
return hash;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int HashCode<T1, T2, T3>(in T1 v1, in T2 v2, in T3 v3) {
var hash = v1.GetHashCode();
hash = (hash * 397) ^ v2.GetHashCode();
hash = (hash * 397) ^ v3.GetHashCode();
return hash;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int HashCode<T1, T2, T3, T4>(in T1 v1, in T2 v2, in T3 v3, in T4 v4) {
var hash = v1.GetHashCode();
hash = (hash * 397) ^ v2.GetHashCode();
hash = (hash * 397) ^ v3.GetHashCode();
hash = (hash * 397) ^ v4.GetHashCode();
return hash;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int HashCode<T1, T2, T3, T4, T5>(in T1 v1, in T2 v2, in T3 v3, in T4 v4, in T5 v5) {
var hash = v1.GetHashCode();
hash = (hash * 397) ^ v2.GetHashCode();
hash = (hash * 397) ^ v3.GetHashCode();
hash = (hash * 397) ^ v4.GetHashCode();
hash = (hash * 397) ^ v5.GetHashCode();
return hash;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3fd8e20db38143e3a3c51ea733bd0bab
timeCreated: 1577270369

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Leopotam.Ecs;
namespace Asset_Cleaner {
static class EcsUtils {
public static IEnumerable<(T Group, IEnumerable<int> Indices)> GroupBy1<T, T1, T2>(this EcsFilter<T, T1, T2> f, IEqualityComparer<T> comp)
where T : class
where T1 : class
where T2 : class {
foreach (var group in Inner().GroupBy(tuple => tuple.Group, comp))
yield return (group.Key, group.Select(g => g.EcsIndex));
IEnumerable<(T Group, int EcsIndex)> Inner() {
var get1 = f.Get1;
foreach (var i in f) yield return (get1[i], i);
}
}
public static EcsFilter<T> Out<T>(this EcsFilter<T> filter, out T[] get1, out EcsEntity[] entities) where T : class {
get1 = filter.Get1;
entities = filter.Entities;
return filter;
}
public static EcsFilter<T1, T2> Out<T1, T2>(this EcsFilter<T1, T2> filter, out T1[] get1, out T2[] get2, out EcsEntity[] entities)
where T1 : class where T2 : class {
get1 = filter.Get1;
get2 = filter.Get2;
entities = filter.Entities;
return filter;
}
public static EcsFilter<T1, T2, T3> Out<T1, T2, T3>(this EcsFilter<T1, T2, T3> filter, out T1[] get1, out T2[] get2, out T3[] get3, out EcsEntity[] entities)
where T1 : class where T2 : class where T3 : class {
get1 = filter.Get1;
get2 = filter.Get2;
get3 = filter.Get3;
entities = filter.Entities;
return filter;
}
public static void AllDestroy(this EcsFilter f) {
var ecsEntities = f.Entities;
foreach (var i in f)
ecsEntities[i].Destroy();
}
public static void AllUnset<T>(this EcsFilter f) where T : class {
var e = f.Entities;
foreach (var i in f)
e[i].Unset<T>();
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d81e9bb0744bc4448ddb9bbd93bffd3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,17 @@
using System.Runtime.CompilerServices;
using Leopotam.Ecs;
namespace Asset_Cleaner {
static class Ext {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Eq(this string s1, string s2) => (s1 == s2);
// public static bool Eq(this string s1, string s2) => StringComparer.Ordinal.Equals(s1, s2);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T GetSingle<T>(this EcsFilter<T> f) where T : class {
Asr.AreEqual(f.GetEntitiesCount(), 1);
return f.Get1[0];
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 66f6b6922016ea04797deac8d082bc80
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,29 @@
namespace Asset_Cleaner {
static class Globals<T> where T : class {
static T _instance;
public static T Value {
get {
Asr.IsFalse(_instance == null);
return _instance;
}
set {
var was = HasValue();
_instance = value;
// keep counter to check during deinitialization if all Globals are cleared
if (was && !HasValue())
__GlobalsCounter.Counter -= 1;
if (!was && HasValue())
__GlobalsCounter.Counter += 1;
bool HasValue() => _instance != null;
}
}
}
static class __GlobalsCounter {
internal static int Counter;
public static bool HasAnyValue() => Counter > 0;
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dd0464bfdb1b4253ba131c7140510c81
timeCreated: 1596137163

View File

@@ -0,0 +1,124 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Asset_Cleaner {
readonly struct Option<T> : IEquatable<Option<T>>, IComparable<Option<T>> {
// ReSharper disable once StaticMemberInGenericType
static readonly bool IsValueType;
public bool HasValue { get; }
T Value { get; }
public static implicit operator Option<T>(T arg) {
if (!IsValueType) return ReferenceEquals(arg, null) ? new Option<T>() : new Option<T>(arg, true);
#if M_WARN
if (arg.Equals(default(T)))
Warn.Warning($"{arg} has default value");
#endif
return new Option<T>(arg, true);
}
static Option() {
IsValueType = typeof(T).IsValueType;
}
public void GetOrFail(out T value) {
if (!TryGet(out value))
Fail($"Option<{typeof(T).Name}> has no value");
}
public T GetOrFail() {
if (!TryGet(out var value))
Fail($"Option<{typeof(T).Name}> has no value");
return value;
}
[Conditional("DEBUG1")]
static void Fail(string format = null) {
throw new Exception(format);
}
public bool TryGet(out T value) {
if (!HasValue) {
value = default(T);
return false;
}
value = Value;
return true;
}
internal Option(T value, bool hasValue) {
Value = value;
HasValue = hasValue;
}
public T ValueOr(T alternative) {
return HasValue ? Value : alternative;
}
// for debug purposes
public override string ToString() {
if (!HasValue) return "None";
return Value == null ? "Some(null)" : $"Some({Value})";
}
#region eq comparers boilerplate
public bool Equals(Option<T> other) {
if (!HasValue && !other.HasValue)
return true;
if (HasValue && other.HasValue)
return EqualityComparer<T>.Default.Equals(Value, other.Value);
return false;
}
public override bool Equals(object obj) {
return obj is Option<T> && Equals((Option<T>) obj);
}
public static bool operator ==(Option<T> left, Option<T> right) {
return left.Equals(right);
}
public static bool operator !=(Option<T> left, Option<T> right) {
return !left.Equals(right);
}
public override int GetHashCode() {
if (!HasValue) return 0;
return IsValueType || Value != null ? Value.GetHashCode() : 1;
}
public int CompareTo(Option<T> other) {
if (HasValue && !other.HasValue) return 1;
if (!HasValue && other.HasValue) return -1;
return Comparer<T>.Default.Compare(Value, other.Value);
}
public static bool operator <(Option<T> left, Option<T> right) {
return left.CompareTo(right) < 0;
}
public static bool operator <=(Option<T> left, Option<T> right) {
return left.CompareTo(right) <= 0;
}
public static bool operator >(Option<T> left, Option<T> right) {
return left.CompareTo(right) > 0;
}
public static bool operator >=(Option<T> left, Option<T> right) {
return left.CompareTo(right) >= 0;
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 098c45ccebe14f5caa28635386021c94
timeCreated: 1591801098

View File

@@ -0,0 +1,57 @@
using System.IO;
using UnityEngine;
namespace Asset_Cleaner {
static class PersistenceUtils {
public static void Load(ref Config result) {
var serializable = Deserialize();
AufSerializableData.OnDeserialize(in serializable, ref result);
}
public static void Save(in Config src) {
AufSerializableData.OnSerialize(in src, out var serializable);
var json = JsonUtility.ToJson(serializable);
File.WriteAllText(Path, json);
}
static AufSerializableData Deserialize() {
AufSerializableData serializableData;
string json;
if (!File.Exists(Path)) {
// not exists - write new
serializableData = AufSerializableData.Default();
json = JsonUtility.ToJson(serializableData);
File.WriteAllText(Path, json);
}
else {
// exists
json = File.ReadAllText(Path);
if (string.IsNullOrEmpty(json)) {
// but corrupted - overwrite with new
serializableData = AufSerializableData.Default();
json = JsonUtility.ToJson(serializableData);
File.WriteAllText(Path, json);
}
serializableData = JsonUtility.FromJson<AufSerializableData>(json);
if (serializableData.Valid())
return serializableData;
serializableData = AufSerializableData.Default();
json = JsonUtility.ToJson(serializableData);
File.WriteAllText(Path, json);
}
return serializableData;
}
static string Path => $"{Application.temporaryCachePath}/AssetCleaner_{AufSerializableData.CurrentVersion}.json";
// [MenuItem("Tools/LogPath")]
static void Log() {
Debug.Log(Application.temporaryCachePath);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 764a6ef52ca2456eb7c276bbcd0929f0
timeCreated: 1589466213

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
namespace Asset_Cleaner {
class Pool<T> : IDisposable where T : class {
Func<T> _ctor;
readonly Stack<T> _stack;
// todo place asserts on app quit
Action<T> _reset;
Action<T> _destroy;
static Action<T> Empty = _ => { };
public Pool(Func<T> ctor, Action<T> reset, Action<T> destroy = null) {
_ctor = ctor;
#if !M_DISABLE_POOLING
_destroy = destroy ?? Empty;
_reset = reset;
_stack = new Stack<T>();
#endif
}
public T Get() {
#if M_DISABLE_POOLING
return _ctor.Invoke();
#else
T element;
if (_stack.Count == 0) {
element = _ctor();
}
else {
element = _stack.Pop();
}
return element;
#endif
}
public void Release(ref T element) {
#if !M_DISABLE_POOLING
Asr.IsFalse(_stack.Count > 0 && ReferenceEquals(_stack.Peek(), element),
"Internal error. Trying to release object that is already released to pool. ");
_reset.Invoke(element);
_stack.Push(element);
#endif
element = null;
}
public void Dispose() {
#if !M_DISABLE_POOLING
while (_stack.Count > 0) {
var t = _stack.Pop();
_destroy.Invoke(t);
}
#endif
}
public _Scope GetScoped(out T tmp) {
tmp = Get();
return new _Scope(this, ref tmp);
}
public struct _Scope : IDisposable {
Pool<T> _pool;
T _val;
internal _Scope(Pool<T> pool, ref T val) {
_pool = pool;
_val = val;
}
public void Dispose() {
_pool.Release(ref _val);
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More