mirror of
https://github.com/muskit/H3VR-TNH-Quality-of-Life-Improvements.git
synced 2026-06-02 20:24:26 -07:00
Initial commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
artifacts/**
|
||||
build/**
|
||||
Documentation/ApiDocs/**
|
||||
.DS_Store
|
||||
.npmrc
|
||||
@@ -0,0 +1,8 @@
|
||||
artifacts/**
|
||||
build/**
|
||||
Documentation/ApiDocs/**
|
||||
.npmrc
|
||||
.npmignore
|
||||
.gitignore
|
||||
QAReport.md
|
||||
QAReport.md.meta
|
||||
@@ -0,0 +1,36 @@
|
||||
# Changelog
|
||||
All notable changes to this package will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.8.0] - 2018-XX-XX
|
||||
- Added more details to bundle detail view.
|
||||
- fix for folder containing only non-asset items.
|
||||
|
||||
## [1.7.0] - 2018-07-23
|
||||
- Sorting dependencies for easier identification.
|
||||
- fix to windows file sharing violation error.
|
||||
- minor bug fixes.
|
||||
|
||||
## [1.6.0] - 2018-04-25
|
||||
- Stopped altering global styles.
|
||||
|
||||
## [1.5.0] - 2018-02-28
|
||||
- Code static analysis cleanup.
|
||||
- Documentation updates.
|
||||
- fix to missing bundle-icon bug.
|
||||
|
||||
## [1.4.0] - 2018-02-23
|
||||
- Added a search bar to the main tab. Searches based on asset name.
|
||||
|
||||
## [1.3.0] - 2018-01-08
|
||||
- serialization fix for inspect tab (was causing entire window to potentially be blank).
|
||||
- documentation fix
|
||||
|
||||
## [1.2.0] - 2017-12-08
|
||||
- Added asmdef to keep browser in its own assembly
|
||||
- minor null check fixes
|
||||
|
||||
## [1.1.4] - 2017-11-09
|
||||
- Initial submission for package distribution
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eae989b9583e0794592d5e9b747bc280
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4fd76bb12fd5fae48a3e952d8020bef4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,173 @@
|
||||
# Unity Asset Bundle Browser tool
|
||||
|
||||
This tool enables the user to view and edit the configuration of asset bundles for their Unity project. It will block editing that would create invalid bundles, and inform you of any issues with existing bundles. It also provides basic build functionality.
|
||||
|
||||
Use this tool as an alternative to selecting assets and setting their asset bundle manually in the inspector. It can be dropped into any Unity project with a version of 5.6 or greater. It will create a new menu item in __Window__ > __AssetBundle Browser__. The bundle configuration, build functionality, and build-bundle inspection are split into three tabs within the new window.
|
||||
|
||||

|
||||
|
||||
### Requires Unity 5.6+
|
||||
|
||||
# Usage - Configure
|
||||
|
||||
This window provides an explorer like interface to managing and modifying asset bundles in your project. When first opened, the tool will parse all bundle data in the background, slowly marking warnings or errors it detects. It does what it can to stay in sync with the project, but cannot always be aware of activity outside the tool. To force a quick pass at error detection, or to update the tool with changes made externally, hit the Refresh button in the upper left.
|
||||
|
||||
The window is broken into four sections: Bundle List, Bundle Details, Asset List, and Asset Details.
|
||||

|
||||
|
||||
### Bundle List
|
||||
|
||||
Left hand pane showing a list of all bundles in the project. Available functionality:
|
||||
|
||||
* Select a bundle or set of bundles to see a list of the assets that will be in the bundle in the Asset List pane.
|
||||
|
||||
* Bundles with variants are a darker grey and can be expanded to show the list of variants.
|
||||
|
||||
* Right-click or slow-double-click to rename bundle or bundle folder.
|
||||
|
||||
* If a bundle has any error, warning, or info message, an icon will appear on the right side. Mouse over the icon for more information.
|
||||
|
||||
* If a bundle has at least one scene in it (making it a scene bundle) and non-scene assets explicitly included, it will be marked as having an error. This bundle will not build until fixed.
|
||||
|
||||
* Bundles with duplicated assets will be marked with a warning (more information on duplication in Asset List section below)
|
||||
|
||||
* Empty bundles will be marked with an info message. For a number of reasons, empty bundles are not very stable and can disappear from this list at times.
|
||||
|
||||
* Folders of bundles will be marked with the highest message from the contained bundles.
|
||||
|
||||
* To fix the duplicated inclusion of assets in bundles, you can:
|
||||
|
||||
* Right click on a single bundle to move all assets determined to be duplicates into a new bundle.
|
||||
|
||||
* Right click on multiple bundles to either move the assets from all selected bundles that are duplicates into a new bundle, or only those that are shared within the selection.
|
||||
|
||||
* You can also drag duplicate assets out of the Asset List pane into the Bundle List to explicitly include them in a bundle. More info on this in the Asset List feature set below.
|
||||
|
||||
* Right click or hit DEL to delete bundles.
|
||||
|
||||
* Drag bundles around to move them into and out of folders, or merge them.
|
||||
|
||||
* Drag assets from the Project Explorer onto bundles to add them.
|
||||
|
||||
* Drag assets onto empty space to create a new bundle.
|
||||
|
||||
* Right click to create new bundles or bundle folders.
|
||||
|
||||
* Right click to "Convert to Variant"
|
||||
|
||||
* This will add a variant (initially called "newvariant") to the selected bundle.
|
||||
|
||||
* All assets currently in selected bundle will be moved into the new variant
|
||||
|
||||
* ComingSoon: Mismatch detection between variants.
|
||||
|
||||
Icons indicate if the bundle is a standard or a scene bundle.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### Bundle Details
|
||||
|
||||
Lower left hand pane showing details of the bundles(s) selected in the Bundle List pane. This pane will show the following information if it is available:
|
||||
|
||||
* Total bundle size. This is a sum of the on-disk size of all assets.
|
||||
|
||||
* Bundles that the current bundle depends on.
|
||||
|
||||
* Assets in that bundle referenced from the current bundle.
|
||||
|
||||
* Assets in the current bundle referencing the asset in the dependency bundle.
|
||||
|
||||
* Any messages (error/warning/info) associated with the current bundle.
|
||||
|
||||
* Select bundles or assets to select assets in the Asset List that are dependent on the selection.
|
||||
|
||||
### Asset List
|
||||
|
||||
Upper right hand pane providing a list of assets contained in whichever bundles are selected in the Bundle List. The search field above this list will match with assets in any bundle. The Asset List will only display matching assets, and the Bundle List will only display bundles that contain matching assets. Available functionality:
|
||||
|
||||
* View all assets anticipated to be included in bundle. Sort asset list by any column header.
|
||||
|
||||
* View assets explicitly included in bundle. These are assets that have been assigned a bundle explicitly. The inspector will reflect the bundle inclusion, and in this view they will say the bundle name next to the asset name.
|
||||
|
||||
* View assets implicitly included in bundle. These assets will say *auto* as the name of the bundle next to the asset name. If looking at these assets in the inspector they will say *None* as the assigned bundle.
|
||||
|
||||
* These assets have been added to the selected bundle(s) due to a dependency on another included asset. Only assets that are not explicitly assigned to a bundle will be implicitly included in any bundles.
|
||||
|
||||
* Note that this list of implicit includes can be incomplete. There are known issues with materials and textures not always showing up correctly.
|
||||
|
||||
* As multiple assets can share dependencies, it is common for a given asset to be implicitly included in multiple bundles. If the tool detects this case, it will mark both the bundle and the asset in question with a warning icon.
|
||||
|
||||
* To fix the duplicate-inclusion warnings, you can manually move assets into a new bundle or right click the bundle and selecting one of the "Move duplicate" options.
|
||||
|
||||
* Drag assets from the Project Explorer into this view to add them to the selected bundle. This is only valid if only one bundle is selected, and the asset type is compatible (scenes onto scene bundles, etc.)
|
||||
|
||||
* Drag assets (explicit or implicit) from the Asset List into the Bundle List (to add them to different bundles, or a newly created bundle).
|
||||
|
||||
* Right click or hit DEL to remove assets from bundles (does not remove assets from project).
|
||||
|
||||
* Select or double-click assets to reveal them in the Project Explorer.
|
||||
|
||||
A note on including folders in bundles. It is possible to assign an asset folder (from the Project Explorer) to a bundle. When viewing this in the browser, the folder itself will be listed as explicit and the contents implicit. This reflects the priority system used to assign assets to bundles. For example, say your game had five prefabs in Assets/Prefabs, and you marked the folder "Prefabs" as being in one bundle, and one of the actual prefabs ("PrefabA") as being in another. Once built, "PrefabA" would be in one bundle, and the other four prefabs would be in the other.
|
||||
|
||||
### Asset Details
|
||||
|
||||
Lower right hand pane showing details of the asset(s) selected in the Asset List pane. This pane cannot be interacted with, but will show the following information if it is available:
|
||||
|
||||
* Full path of asset
|
||||
|
||||
* Reason for implicit inclusion in bundles if it is implicit.
|
||||
|
||||
* Reason for warning if any.
|
||||
|
||||
* Reason for error if any.
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
* *Can't rename or delete a specific bundle.* This is occasionally caused when first adding this tool to an existing project. Please force a reimport of your assets through the Unity menu system to refresh the data.
|
||||
|
||||
### External Tool Integration
|
||||
Other tools that generate asset bundle data can choose to integrate with the browser. Currently the primary example is the [Asset Bundle Graph Tool](https://bitbucket.org/Unity-Technologies/assetbundlegraphtool). If integrations are detected, then a selection bar will appear near the top of the browser. It will allow you to select the Default data source (Unity's AssetDatabase) or an integrated tool. If none are detected, the selector is not present, though you can add it by right-clicking on the tab header and selecting "Custom Sources".
|
||||
|
||||
# Usage - Build
|
||||
|
||||
The Build tab provides basic build functionality to get you started using asset bundles. In most professional scenarios, users will end up needing a more advanced build setup. All are welcome to use the build code in this tool as a starting point for writing their own once this no longer meets their needs. For the most part, the options here are directly tied to the options the engine expects in [BuildAssetBundleOptions](https://docs.unity3d.com/ScriptReference/BuildAssetBundleOptions.html). Interface:
|
||||
|
||||
* *Build Target* - Platform the bundles will be built for
|
||||
|
||||
* *Output Path* - Path for saving built bundles. By default this is AssetBundles/. You can edit the path manually, or by selecting "Browse". To return to the default naming convention, hit "Reset".
|
||||
|
||||
* *Clear Folders* - This will delete all data from the build path folder prior to building.
|
||||
|
||||
* *Copy to StreamingAssets* - After the build is complete, this will copy the results to Assets/StreamingAssets. This can be useful for testing, but would not be used in production.
|
||||
|
||||
* *Advanced Settings*
|
||||
|
||||
* *Compression* - Choose between no compression, standard LZMA, or chunk-based LZ4 compression.
|
||||
|
||||
* *Exclude Type Information* - Do not include type information within the asset bundle
|
||||
|
||||
* *Force Rebuild* - Rebuild bundles needing to be built. This is different than "Clear Folders" as this option will not delete bundles that no longer exist.
|
||||
|
||||
* *Ignore Type Tree Changes* - Ignore the type tree changes when doing the incremental build check.
|
||||
|
||||
* *Append Hash* - Append the hash to the asset bundle name.
|
||||
|
||||
* *Strict Mode* - Do not allow the build to succeed if any errors are reporting during it.
|
||||
|
||||
* *Dry Run Build* - Do a dry run build.
|
||||
|
||||
* *Build* - Executes build.
|
||||
|
||||
# Usage - Inspect
|
||||
This tab enables you to inspect the contents of bundles that have already been built.
|
||||
### Usage
|
||||
* If you use the Browser to build, then the path you built to will automatically be added here.
|
||||
* Click "Add File" or "Add Folder" to add bundles to inspect.
|
||||
* Click the "-" next to each row to remove that file or folder. Note, you cannot remove individual files that were added by adding a folder.
|
||||
* Select any bundle listed to see details:
|
||||
* Name
|
||||
* Size on disk
|
||||
* Source Asset Paths - the assets explicitly added to this bundle. Note this list is incomplete for scene bundles.
|
||||
* Advanced Data - includes information on the Preload Table, Container (explicit assets) and Dependencies.
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 04fe18d2a84db4c46ad123537efe6941
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6ee6573a237b7e46adb95566c600038
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
+83
@@ -0,0 +1,83 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 521ac7d05d42f41408ec95eaedef87cd
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
externalObjects: {}
|
||||
serializedVersion: 5
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
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
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapU: -1
|
||||
wrapV: -1
|
||||
wrapW: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 2
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
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:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
+83
@@ -0,0 +1,83 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63255dcf53aebf34a8b1162ee502ebe1
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
externalObjects: {}
|
||||
serializedVersion: 5
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
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
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapU: -1
|
||||
wrapV: -1
|
||||
wrapW: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 2
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
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:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 356 KiB |
@@ -0,0 +1,77 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3aa429e1ebaddb41ad13211560ea5e8
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
externalObjects: {}
|
||||
serializedVersion: 5
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
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
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapU: -1
|
||||
wrapV: -1
|
||||
wrapW: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 2
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
@@ -0,0 +1,77 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 18223d49715730347b997e6682fe49e5
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
externalObjects: {}
|
||||
serializedVersion: 5
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
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
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapU: -1
|
||||
wrapV: -1
|
||||
wrapW: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 2
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 946ae3b25c7026b49b4fc66e969e5475
|
||||
folderAsset: yes
|
||||
timeCreated: 1507818222
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,246 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AssetBundleBrowser.AssetBundleDataSource;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
[assembly: System.Runtime.CompilerServices.InternalsVisibleToAttribute("Unity.AssetBundleBrowser.Editor.Tests")]
|
||||
|
||||
namespace AssetBundleBrowser
|
||||
{
|
||||
public class AssetBundleBrowserMain : EditorWindow, IHasCustomMenu, ISerializationCallbackReceiver
|
||||
{
|
||||
private static AssetBundleBrowserMain s_instance = null;
|
||||
|
||||
internal static AssetBundleBrowserMain instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_instance == null)
|
||||
s_instance = GetWindow<AssetBundleBrowserMain>();
|
||||
return s_instance;
|
||||
}
|
||||
}
|
||||
|
||||
internal const float kButtonWidth = 150;
|
||||
|
||||
private enum Mode
|
||||
{
|
||||
Browser,
|
||||
Builder,
|
||||
Inspect
|
||||
}
|
||||
|
||||
[SerializeField] private Mode m_Mode;
|
||||
|
||||
[SerializeField] private int m_DataSourceIndex;
|
||||
|
||||
[SerializeField] internal AssetBundleManageTab m_ManageTab;
|
||||
|
||||
[SerializeField] internal AssetBundleBuildTab m_BuildTab;
|
||||
|
||||
[SerializeField] internal AssetBundleInspectTab m_InspectTab;
|
||||
|
||||
private Texture2D m_RefreshTexture;
|
||||
|
||||
private const float k_ToolbarPadding = 15;
|
||||
private const float k_MenubarPadding = 32;
|
||||
|
||||
[MenuItem("Window/AssetBundle Browser", priority = 2050)]
|
||||
private static void ShowWindow()
|
||||
{
|
||||
s_instance = null;
|
||||
instance.titleContent = new GUIContent("AssetBundles");
|
||||
instance.Show();
|
||||
}
|
||||
|
||||
[SerializeField] internal bool multiDataSource = false;
|
||||
|
||||
private List<AssetBundleDataSource.ABDataSource> m_DataSourceList = null;
|
||||
|
||||
public virtual void AddItemsToMenu(GenericMenu menu)
|
||||
{
|
||||
if (menu != null)
|
||||
menu.AddItem(new GUIContent("Custom Sources"), multiDataSource, FlipDataSource);
|
||||
}
|
||||
|
||||
internal void FlipDataSource()
|
||||
{
|
||||
multiDataSource = !multiDataSource;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Rect subPos = GetSubWindowArea();
|
||||
if (m_ManageTab == null)
|
||||
m_ManageTab = new AssetBundleManageTab();
|
||||
m_ManageTab.OnEnable(subPos, this);
|
||||
if (m_BuildTab == null)
|
||||
m_BuildTab = new AssetBundleBuildTab();
|
||||
m_BuildTab.OnEnable(this);
|
||||
if (m_InspectTab == null)
|
||||
m_InspectTab = new AssetBundleInspectTab();
|
||||
m_InspectTab.OnEnable(subPos);
|
||||
|
||||
m_RefreshTexture = EditorGUIUtility.FindTexture("Refresh");
|
||||
|
||||
InitDataSources();
|
||||
}
|
||||
|
||||
private void InitDataSources()
|
||||
{
|
||||
//determine if we are "multi source" or not...
|
||||
multiDataSource = false;
|
||||
m_DataSourceList = new List<AssetBundleDataSource.ABDataSource>();
|
||||
foreach (Type info in AssetBundleDataSource.ABDataSourceProviderUtility.CustomABDataSourceTypes) m_DataSourceList.AddRange(info.GetMethod("CreateDataSources").Invoke(null, null) as List<AssetBundleDataSource.ABDataSource>);
|
||||
|
||||
if (m_DataSourceList.Count > 1)
|
||||
{
|
||||
multiDataSource = true;
|
||||
if (m_DataSourceIndex >= m_DataSourceList.Count)
|
||||
m_DataSourceIndex = 0;
|
||||
AssetBundleModel.Model.DataSource = m_DataSourceList[m_DataSourceIndex];
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (m_BuildTab != null)
|
||||
m_BuildTab.OnDisable();
|
||||
if (m_InspectTab != null)
|
||||
m_InspectTab.OnDisable();
|
||||
}
|
||||
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
}
|
||||
|
||||
private Rect GetSubWindowArea()
|
||||
{
|
||||
float padding = k_MenubarPadding;
|
||||
if (multiDataSource)
|
||||
padding += k_MenubarPadding * 0.5f;
|
||||
Rect subPos = new Rect(0, padding, position.width, position.height - padding);
|
||||
return subPos;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
switch (m_Mode)
|
||||
{
|
||||
case Mode.Builder:
|
||||
break;
|
||||
case Mode.Inspect:
|
||||
break;
|
||||
case Mode.Browser:
|
||||
default:
|
||||
m_ManageTab.Update();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
ModeToggle();
|
||||
|
||||
switch (m_Mode)
|
||||
{
|
||||
case Mode.Builder:
|
||||
m_BuildTab.OnGUI();
|
||||
break;
|
||||
case Mode.Inspect:
|
||||
m_InspectTab.OnGUI(GetSubWindowArea());
|
||||
break;
|
||||
case Mode.Browser:
|
||||
default:
|
||||
m_ManageTab.OnGUI(GetSubWindowArea());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ModeToggle()
|
||||
{
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.Space(k_ToolbarPadding);
|
||||
bool clicked = false;
|
||||
switch (m_Mode)
|
||||
{
|
||||
case Mode.Browser:
|
||||
clicked = GUILayout.Button(m_RefreshTexture);
|
||||
if (clicked)
|
||||
m_ManageTab.ForceReloadData();
|
||||
break;
|
||||
case Mode.Builder:
|
||||
GUILayout.Space(m_RefreshTexture.width + k_ToolbarPadding);
|
||||
break;
|
||||
case Mode.Inspect:
|
||||
clicked = GUILayout.Button(m_RefreshTexture);
|
||||
if (clicked)
|
||||
m_InspectTab.RefreshBundles();
|
||||
break;
|
||||
}
|
||||
|
||||
float toolbarWidth = position.width - k_ToolbarPadding * 4 - m_RefreshTexture.width;
|
||||
//string[] labels = new string[2] { "Configure", "Build"};
|
||||
string[] labels = new string[3] {"Configure", "Build", "Inspect"};
|
||||
m_Mode = (Mode) GUILayout.Toolbar((int) m_Mode, labels, "LargeButton", GUILayout.Width(toolbarWidth));
|
||||
GUILayout.FlexibleSpace();
|
||||
GUILayout.EndHorizontal();
|
||||
if (multiDataSource)
|
||||
{
|
||||
//GUILayout.BeginArea(r);
|
||||
GUILayout.BeginHorizontal();
|
||||
|
||||
using (new EditorGUILayout.HorizontalScope(EditorStyles.toolbar))
|
||||
{
|
||||
GUILayout.Label("Bundle Data Source:");
|
||||
GUILayout.FlexibleSpace();
|
||||
GUIContent c = new GUIContent(string.Format("{0} ({1})", AssetBundleModel.Model.DataSource.Name, AssetBundleModel.Model.DataSource.ProviderName), "Select Asset Bundle Set");
|
||||
if (GUILayout.Button(c, EditorStyles.toolbarPopup))
|
||||
{
|
||||
GenericMenu menu = new GenericMenu();
|
||||
|
||||
for (int index = 0; index < m_DataSourceList.Count; index++)
|
||||
{
|
||||
ABDataSource ds = m_DataSourceList[index];
|
||||
if (ds == null)
|
||||
continue;
|
||||
|
||||
if (index > 0)
|
||||
menu.AddSeparator("");
|
||||
|
||||
int counter = index;
|
||||
menu.AddItem(new GUIContent(string.Format("{0} ({1})", ds.Name, ds.ProviderName)), false,
|
||||
() =>
|
||||
{
|
||||
m_DataSourceIndex = counter;
|
||||
ABDataSource thisDataSource = ds;
|
||||
AssetBundleModel.Model.DataSource = thisDataSource;
|
||||
m_ManageTab.ForceReloadData();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
if (AssetBundleModel.Model.DataSource.IsReadOnly())
|
||||
{
|
||||
GUIStyle tbLabel = new GUIStyle(EditorStyles.toolbar);
|
||||
tbLabel.alignment = TextAnchor.MiddleRight;
|
||||
|
||||
GUILayout.Label("Read Only", tbLabel);
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
//GUILayout.EndArea();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 15478322cbd3d2f4a862552f839978da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,451 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using AssetBundleBrowser.AssetBundleDataSource;
|
||||
|
||||
namespace AssetBundleBrowser
|
||||
{
|
||||
[System.Serializable]
|
||||
internal class AssetBundleBuildTab
|
||||
{
|
||||
private const string k_BuildPrefPrefix = "ABBBuild:";
|
||||
|
||||
private string m_streamingPath = "Assets/StreamingAssets";
|
||||
|
||||
[SerializeField] private bool m_AdvancedSettings;
|
||||
|
||||
[SerializeField] private Vector2 m_ScrollPosition;
|
||||
|
||||
|
||||
private class ToggleData
|
||||
{
|
||||
internal ToggleData(bool s,
|
||||
string title,
|
||||
string tooltip,
|
||||
List<string> onToggles,
|
||||
BuildAssetBundleOptions opt = BuildAssetBundleOptions.None)
|
||||
{
|
||||
if (onToggles.Contains(title))
|
||||
state = true;
|
||||
else
|
||||
state = s;
|
||||
content = new GUIContent(title, tooltip);
|
||||
option = opt;
|
||||
}
|
||||
|
||||
//internal string prefsKey
|
||||
//{ get { return k_BuildPrefPrefix + content.text; } }
|
||||
internal bool state;
|
||||
internal GUIContent content;
|
||||
internal BuildAssetBundleOptions option;
|
||||
}
|
||||
|
||||
private AssetBundleInspectTab m_InspectTab;
|
||||
|
||||
[SerializeField] private BuildTabData m_UserData;
|
||||
|
||||
private List<ToggleData> m_ToggleData;
|
||||
private ToggleData m_ForceRebuild;
|
||||
private ToggleData m_CopyToStreaming;
|
||||
private GUIContent m_TargetContent;
|
||||
private GUIContent m_CompressionContent;
|
||||
|
||||
internal enum CompressOptions
|
||||
{
|
||||
Uncompressed = 0,
|
||||
StandardCompression,
|
||||
ChunkBasedCompression
|
||||
}
|
||||
|
||||
private GUIContent[] m_CompressionOptions =
|
||||
{
|
||||
new GUIContent("No Compression"),
|
||||
new GUIContent("Standard Compression (LZMA)"),
|
||||
new GUIContent("Chunk Based Compression (LZ4)")
|
||||
};
|
||||
|
||||
private int[] m_CompressionValues = {0, 1, 2};
|
||||
|
||||
|
||||
internal AssetBundleBuildTab()
|
||||
{
|
||||
m_AdvancedSettings = false;
|
||||
m_UserData = new BuildTabData();
|
||||
m_UserData.m_OnToggles = new List<string>();
|
||||
m_UserData.m_UseDefaultPath = true;
|
||||
}
|
||||
|
||||
internal void OnDisable()
|
||||
{
|
||||
string dataPath = Path.GetFullPath(".");
|
||||
dataPath = dataPath.Replace("\\", "/");
|
||||
dataPath += "/Library/AssetBundleBrowserBuild.dat";
|
||||
|
||||
BinaryFormatter bf = new BinaryFormatter();
|
||||
FileStream file = File.Create(dataPath);
|
||||
|
||||
bf.Serialize(file, m_UserData);
|
||||
file.Close();
|
||||
}
|
||||
|
||||
internal void OnEnable(EditorWindow parent)
|
||||
{
|
||||
m_InspectTab = (parent as AssetBundleBrowserMain).m_InspectTab;
|
||||
|
||||
//LoadData...
|
||||
string dataPath = Path.GetFullPath(".");
|
||||
dataPath = dataPath.Replace("\\", "/");
|
||||
dataPath += "/Library/AssetBundleBrowserBuild.dat";
|
||||
|
||||
if (File.Exists(dataPath))
|
||||
{
|
||||
BinaryFormatter bf = new BinaryFormatter();
|
||||
FileStream file = File.Open(dataPath, FileMode.Open);
|
||||
BuildTabData data = bf.Deserialize(file) as BuildTabData;
|
||||
if (data != null)
|
||||
m_UserData = data;
|
||||
file.Close();
|
||||
}
|
||||
|
||||
m_ToggleData = new List<ToggleData>();
|
||||
m_ToggleData.Add(new ToggleData(
|
||||
false,
|
||||
"Exclude Type Information",
|
||||
"Do not include type information within the asset bundle (don't write type tree).",
|
||||
m_UserData.m_OnToggles,
|
||||
BuildAssetBundleOptions.DisableWriteTypeTree));
|
||||
m_ToggleData.Add(new ToggleData(
|
||||
false,
|
||||
"Force Rebuild",
|
||||
"Force rebuild the asset bundles",
|
||||
m_UserData.m_OnToggles,
|
||||
BuildAssetBundleOptions.ForceRebuildAssetBundle));
|
||||
m_ToggleData.Add(new ToggleData(
|
||||
false,
|
||||
"Ignore Type Tree Changes",
|
||||
"Ignore the type tree changes when doing the incremental build check.",
|
||||
m_UserData.m_OnToggles,
|
||||
BuildAssetBundleOptions.IgnoreTypeTreeChanges));
|
||||
m_ToggleData.Add(new ToggleData(
|
||||
false,
|
||||
"Append Hash",
|
||||
"Append the hash to the assetBundle name.",
|
||||
m_UserData.m_OnToggles,
|
||||
BuildAssetBundleOptions.AppendHashToAssetBundleName));
|
||||
m_ToggleData.Add(new ToggleData(
|
||||
false,
|
||||
"Strict Mode",
|
||||
"Do not allow the build to succeed if any errors are reporting during it.",
|
||||
m_UserData.m_OnToggles,
|
||||
BuildAssetBundleOptions.StrictMode));
|
||||
m_ToggleData.Add(new ToggleData(
|
||||
false,
|
||||
"Dry Run Build",
|
||||
"Do a dry run build.",
|
||||
m_UserData.m_OnToggles,
|
||||
BuildAssetBundleOptions.DryRunBuild));
|
||||
|
||||
|
||||
m_ForceRebuild = new ToggleData(
|
||||
false,
|
||||
"Clear Folders",
|
||||
"Will wipe out all contents of build directory as well as StreamingAssets/AssetBundles if you are choosing to copy build there.",
|
||||
m_UserData.m_OnToggles);
|
||||
m_CopyToStreaming = new ToggleData(
|
||||
false,
|
||||
"Copy to StreamingAssets",
|
||||
"After build completes, will copy all build content to " + m_streamingPath + " for use in stand-alone player.",
|
||||
m_UserData.m_OnToggles);
|
||||
|
||||
m_TargetContent = new GUIContent("Build Target", "Choose target platform to build for.");
|
||||
m_CompressionContent = new GUIContent("Compression", "Choose no compress, standard (LZMA), or chunk based (LZ4)");
|
||||
|
||||
if (m_UserData.m_UseDefaultPath) ResetPathToDefault();
|
||||
}
|
||||
|
||||
internal void OnGUI()
|
||||
{
|
||||
m_ScrollPosition = EditorGUILayout.BeginScrollView(m_ScrollPosition);
|
||||
bool newState = false;
|
||||
GUIStyle centeredStyle = new GUIStyle(GUI.skin.GetStyle("Label"));
|
||||
centeredStyle.alignment = TextAnchor.UpperCenter;
|
||||
GUILayout.Label(new GUIContent("Example build setup"), centeredStyle);
|
||||
//basic options
|
||||
EditorGUILayout.Space();
|
||||
GUILayout.BeginVertical();
|
||||
|
||||
// build target
|
||||
using (new EditorGUI.DisabledScope(!AssetBundleModel.Model.DataSource.CanSpecifyBuildTarget))
|
||||
{
|
||||
ValidBuildTarget tgt = (ValidBuildTarget) EditorGUILayout.EnumPopup(m_TargetContent, m_UserData.m_BuildTarget);
|
||||
if (tgt != m_UserData.m_BuildTarget)
|
||||
{
|
||||
m_UserData.m_BuildTarget = tgt;
|
||||
if (m_UserData.m_UseDefaultPath)
|
||||
{
|
||||
m_UserData.m_OutputPath = "AssetBundles/";
|
||||
m_UserData.m_OutputPath += m_UserData.m_BuildTarget.ToString();
|
||||
//EditorUserBuildSettings.SetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath", m_OutputPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////output path
|
||||
using (new EditorGUI.DisabledScope(!AssetBundleModel.Model.DataSource.CanSpecifyBuildOutputDirectory))
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
GUILayout.BeginHorizontal();
|
||||
string newPath = EditorGUILayout.TextField("Output Path", m_UserData.m_OutputPath);
|
||||
if (!string.IsNullOrEmpty(newPath) && newPath != m_UserData.m_OutputPath)
|
||||
{
|
||||
m_UserData.m_UseDefaultPath = false;
|
||||
m_UserData.m_OutputPath = newPath;
|
||||
//EditorUserBuildSettings.SetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath", m_OutputPath);
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
if (GUILayout.Button("Browse", GUILayout.MaxWidth(75f)))
|
||||
BrowseForFolder();
|
||||
if (GUILayout.Button("Reset", GUILayout.MaxWidth(75f)))
|
||||
ResetPathToDefault();
|
||||
//if (string.IsNullOrEmpty(m_OutputPath))
|
||||
// m_OutputPath = EditorUserBuildSettings.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath");
|
||||
GUILayout.EndHorizontal();
|
||||
EditorGUILayout.Space();
|
||||
|
||||
newState = GUILayout.Toggle(
|
||||
m_ForceRebuild.state,
|
||||
m_ForceRebuild.content);
|
||||
if (newState != m_ForceRebuild.state)
|
||||
{
|
||||
if (newState)
|
||||
m_UserData.m_OnToggles.Add(m_ForceRebuild.content.text);
|
||||
else
|
||||
m_UserData.m_OnToggles.Remove(m_ForceRebuild.content.text);
|
||||
m_ForceRebuild.state = newState;
|
||||
}
|
||||
|
||||
newState = GUILayout.Toggle(
|
||||
m_CopyToStreaming.state,
|
||||
m_CopyToStreaming.content);
|
||||
if (newState != m_CopyToStreaming.state)
|
||||
{
|
||||
if (newState)
|
||||
m_UserData.m_OnToggles.Add(m_CopyToStreaming.content.text);
|
||||
else
|
||||
m_UserData.m_OnToggles.Remove(m_CopyToStreaming.content.text);
|
||||
m_CopyToStreaming.state = newState;
|
||||
}
|
||||
}
|
||||
|
||||
// advanced options
|
||||
using (new EditorGUI.DisabledScope(!AssetBundleModel.Model.DataSource.CanSpecifyBuildOptions))
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
m_AdvancedSettings = EditorGUILayout.Foldout(m_AdvancedSettings, "Advanced Settings");
|
||||
if (m_AdvancedSettings)
|
||||
{
|
||||
int indent = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = 1;
|
||||
CompressOptions cmp = (CompressOptions) EditorGUILayout.IntPopup(
|
||||
m_CompressionContent,
|
||||
(int) m_UserData.m_Compression,
|
||||
m_CompressionOptions,
|
||||
m_CompressionValues);
|
||||
|
||||
if (cmp != m_UserData.m_Compression) m_UserData.m_Compression = cmp;
|
||||
foreach (ToggleData tog in m_ToggleData)
|
||||
{
|
||||
newState = EditorGUILayout.ToggleLeft(
|
||||
tog.content,
|
||||
tog.state);
|
||||
if (newState != tog.state)
|
||||
{
|
||||
if (newState)
|
||||
m_UserData.m_OnToggles.Add(tog.content.text);
|
||||
else
|
||||
m_UserData.m_OnToggles.Remove(tog.content.text);
|
||||
tog.state = newState;
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUI.indentLevel = indent;
|
||||
}
|
||||
}
|
||||
|
||||
// build.
|
||||
EditorGUILayout.Space();
|
||||
if (GUILayout.Button("Build")) EditorApplication.delayCall += ExecuteBuild;
|
||||
GUILayout.EndVertical();
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private void ExecuteBuild()
|
||||
{
|
||||
if (AssetBundleModel.Model.DataSource.CanSpecifyBuildOutputDirectory)
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_UserData.m_OutputPath))
|
||||
BrowseForFolder();
|
||||
|
||||
if (string.IsNullOrEmpty(m_UserData.m_OutputPath)) //in case they hit "cancel" on the open browser
|
||||
{
|
||||
Debug.LogError("AssetBundle Build: No valid output path for build.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_ForceRebuild.state)
|
||||
{
|
||||
string message = "Do you want to delete all files in the directory " + m_UserData.m_OutputPath;
|
||||
if (m_CopyToStreaming.state)
|
||||
message += " and " + m_streamingPath;
|
||||
message += "?";
|
||||
if (EditorUtility.DisplayDialog("File delete confirmation", message, "Yes", "No"))
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(m_UserData.m_OutputPath))
|
||||
Directory.Delete(m_UserData.m_OutputPath, true);
|
||||
|
||||
if (m_CopyToStreaming.state)
|
||||
if (Directory.Exists(m_streamingPath))
|
||||
Directory.Delete(m_streamingPath, true);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!Directory.Exists(m_UserData.m_OutputPath))
|
||||
Directory.CreateDirectory(m_UserData.m_OutputPath);
|
||||
}
|
||||
|
||||
BuildAssetBundleOptions opt = BuildAssetBundleOptions.None;
|
||||
|
||||
if (AssetBundleModel.Model.DataSource.CanSpecifyBuildOptions)
|
||||
{
|
||||
if (m_UserData.m_Compression == CompressOptions.Uncompressed)
|
||||
opt |= BuildAssetBundleOptions.UncompressedAssetBundle;
|
||||
else if (m_UserData.m_Compression == CompressOptions.ChunkBasedCompression)
|
||||
opt |= BuildAssetBundleOptions.ChunkBasedCompression;
|
||||
foreach (ToggleData tog in m_ToggleData)
|
||||
if (tog.state)
|
||||
opt |= tog.option;
|
||||
}
|
||||
|
||||
ABBuildInfo buildInfo = new ABBuildInfo();
|
||||
|
||||
buildInfo.outputDirectory = m_UserData.m_OutputPath;
|
||||
buildInfo.options = opt;
|
||||
buildInfo.buildTarget = (BuildTarget) m_UserData.m_BuildTarget;
|
||||
buildInfo.onBuild = (assetBundleName) =>
|
||||
{
|
||||
if (m_InspectTab == null)
|
||||
return;
|
||||
m_InspectTab.AddBundleFolder(buildInfo.outputDirectory);
|
||||
m_InspectTab.RefreshBundles();
|
||||
};
|
||||
|
||||
AssetBundleModel.Model.DataSource.BuildAssetBundles(buildInfo);
|
||||
|
||||
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
|
||||
|
||||
if (m_CopyToStreaming.state)
|
||||
DirectoryCopy(m_UserData.m_OutputPath, m_streamingPath);
|
||||
}
|
||||
|
||||
private static void DirectoryCopy(string sourceDirName, string destDirName)
|
||||
{
|
||||
// If the destination directory doesn't exist, create it.
|
||||
if (!Directory.Exists(destDirName)) Directory.CreateDirectory(destDirName);
|
||||
|
||||
foreach (string folderPath in Directory.GetDirectories(sourceDirName, "*", SearchOption.AllDirectories))
|
||||
if (!Directory.Exists(folderPath.Replace(sourceDirName, destDirName)))
|
||||
Directory.CreateDirectory(folderPath.Replace(sourceDirName, destDirName));
|
||||
|
||||
foreach (string filePath in Directory.GetFiles(sourceDirName, "*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
string fileDirName = Path.GetDirectoryName(filePath).Replace("\\", "/");
|
||||
string fileName = Path.GetFileName(filePath);
|
||||
string newFilePath = Path.Combine(fileDirName.Replace(sourceDirName, destDirName), fileName);
|
||||
|
||||
File.Copy(filePath, newFilePath, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void BrowseForFolder()
|
||||
{
|
||||
m_UserData.m_UseDefaultPath = false;
|
||||
string newPath = EditorUtility.OpenFolderPanel("Bundle Folder", m_UserData.m_OutputPath, string.Empty);
|
||||
if (!string.IsNullOrEmpty(newPath))
|
||||
{
|
||||
string gamePath = Path.GetFullPath(".");
|
||||
gamePath = gamePath.Replace("\\", "/");
|
||||
if (newPath.StartsWith(gamePath) && newPath.Length > gamePath.Length)
|
||||
newPath = newPath.Remove(0, gamePath.Length + 1);
|
||||
m_UserData.m_OutputPath = newPath;
|
||||
//EditorUserBuildSettings.SetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath", m_OutputPath);
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetPathToDefault()
|
||||
{
|
||||
m_UserData.m_UseDefaultPath = true;
|
||||
m_UserData.m_OutputPath = "AssetBundles/";
|
||||
m_UserData.m_OutputPath += m_UserData.m_BuildTarget.ToString();
|
||||
//EditorUserBuildSettings.SetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath", m_OutputPath);
|
||||
}
|
||||
|
||||
//Note: this is the provided BuildTarget enum with some entries removed as they are invalid in the dropdown
|
||||
internal enum ValidBuildTarget
|
||||
{
|
||||
//NoTarget = -2, --doesn't make sense
|
||||
//iPhone = -1, --deprecated
|
||||
//BB10 = -1, --deprecated
|
||||
//MetroPlayer = -1, --deprecated
|
||||
StandaloneOSXUniversal = 2,
|
||||
StandaloneOSXIntel = 4,
|
||||
StandaloneWindows = 5,
|
||||
WebPlayer = 6,
|
||||
WebPlayerStreamed = 7,
|
||||
iOS = 9,
|
||||
PS3 = 10,
|
||||
XBOX360 = 11,
|
||||
Android = 13,
|
||||
StandaloneLinux = 17,
|
||||
StandaloneWindows64 = 19,
|
||||
WebGL = 20,
|
||||
WSAPlayer = 21,
|
||||
StandaloneLinux64 = 24,
|
||||
StandaloneLinuxUniversal = 25,
|
||||
WP8Player = 26,
|
||||
StandaloneOSXIntel64 = 27,
|
||||
BlackBerry = 28,
|
||||
Tizen = 29,
|
||||
PSP2 = 30,
|
||||
PS4 = 31,
|
||||
PSM = 32,
|
||||
XboxOne = 33,
|
||||
SamsungTV = 34,
|
||||
N3DS = 35,
|
||||
WiiU = 36,
|
||||
tvOS = 37,
|
||||
Switch = 38
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
internal class BuildTabData
|
||||
{
|
||||
internal List<string> m_OnToggles;
|
||||
internal ValidBuildTarget m_BuildTarget = ValidBuildTarget.StandaloneWindows;
|
||||
internal CompressOptions m_Compression = CompressOptions.StandardCompression;
|
||||
internal string m_OutputPath = string.Empty;
|
||||
internal bool m_UseDefaultPath = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 10d204eff91c2e844b26a7dee989f978
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37cf1ee934b7c254e92e69eb6f19f186
|
||||
folderAsset: yes
|
||||
timeCreated: 1497630438
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,141 @@
|
||||
using UnityEditor;
|
||||
using System;
|
||||
|
||||
namespace AssetBundleBrowser.AssetBundleDataSource
|
||||
{
|
||||
/// <summary>
|
||||
/// Build Info struct used by ABDataSource to pass needed build data around.
|
||||
/// </summary>
|
||||
public partial class ABBuildInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Directory to place build result
|
||||
/// </summary>
|
||||
public string outputDirectory
|
||||
{
|
||||
get { return m_outputDirectory; }
|
||||
set { m_outputDirectory = value; }
|
||||
}
|
||||
|
||||
private string m_outputDirectory;
|
||||
|
||||
/// <summary>
|
||||
/// Standard asset bundle build options.
|
||||
/// </summary>
|
||||
public BuildAssetBundleOptions options
|
||||
{
|
||||
get { return m_options; }
|
||||
set { m_options = value; }
|
||||
}
|
||||
|
||||
private BuildAssetBundleOptions m_options;
|
||||
|
||||
/// <summary>
|
||||
/// Target platform for build.
|
||||
/// </summary>
|
||||
public BuildTarget buildTarget
|
||||
{
|
||||
get { return m_buildTarget; }
|
||||
set { m_buildTarget = value; }
|
||||
}
|
||||
|
||||
private BuildTarget m_buildTarget;
|
||||
|
||||
/// <summary>
|
||||
/// Callback for build event.
|
||||
/// </summary>
|
||||
public Action<string> onBuild
|
||||
{
|
||||
get { return m_onBuild; }
|
||||
set { m_onBuild = value; }
|
||||
}
|
||||
|
||||
private Action<string> m_onBuild;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Interface class used by browser. It is expected to contain all information needed to display predicted bundle layout.
|
||||
/// Any class deriving from this interface AND implementing CreateDataSources() will be picked up by the browser automatically
|
||||
/// and displayed in an in-tool dropdown. By default, that dropdown is hidden if the browser detects no external data sources.
|
||||
/// To turn it on, right click on tab header "AssetBundles" and enable "Custom Sources"
|
||||
///
|
||||
/// Must implement CreateDataSources() to be picked up by the browser.
|
||||
/// public static List<ABDataSource> CreateDataSources();
|
||||
///
|
||||
/// </summary>
|
||||
public partial interface ABDataSource
|
||||
{
|
||||
//// all derived classes must implement the following interface in order to be picked up by the browser.
|
||||
//public static List<ABDataSource> CreateDataSources();
|
||||
|
||||
/// <summary>
|
||||
/// Name of DataSource. Displayed in menu as "Name (ProvidorName)"
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Name of provider for DataSource. Displayed in menu as "Name (ProvidorName)"
|
||||
/// </summary>
|
||||
string ProviderName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Array of paths in bundle.
|
||||
/// </summary>
|
||||
string[] GetAssetPathsFromAssetBundle(string assetBundleName);
|
||||
|
||||
/// <summary>
|
||||
/// Name of bundle explicitly associated with asset at path.
|
||||
/// </summary>
|
||||
string GetAssetBundleName(string assetPath);
|
||||
|
||||
/// <summary>
|
||||
/// Name of bundle associated with asset at path.
|
||||
/// The difference between this and GetAssetBundleName() is for assets unassigned to a bundle, but
|
||||
/// residing inside a folder that is assigned to a bundle. Those assets will implicitly associate
|
||||
/// with the bundle associated with the parent folder.
|
||||
/// </summary>
|
||||
string GetImplicitAssetBundleName(string assetPath);
|
||||
|
||||
/// <summary>
|
||||
/// Array of asset bundle names in project
|
||||
/// </summary>
|
||||
string[] GetAllAssetBundleNames();
|
||||
|
||||
/// <summary>
|
||||
/// If this data source is read only.
|
||||
/// If this returns true, much of the Browsers's interface will be disabled (drag&drop, etc.)
|
||||
/// </summary>
|
||||
bool IsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the asset bundle name (and variant) on a given asset
|
||||
/// </summary>
|
||||
void SetAssetBundleNameAndVariant(string assetPath, string bundleName, string variantName);
|
||||
|
||||
/// <summary>
|
||||
/// Clears out any asset bundle names that do not have assets associated with them.
|
||||
/// </summary>
|
||||
void RemoveUnusedAssetBundleNames();
|
||||
|
||||
/// <summary>
|
||||
/// Signals if this data source can have build target set by tool
|
||||
/// </summary>
|
||||
bool CanSpecifyBuildTarget { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Signals if this data source can have output directory set by tool
|
||||
/// </summary>
|
||||
bool CanSpecifyBuildOutputDirectory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Signals if this data source can have build options set by tool
|
||||
/// </summary>
|
||||
bool CanSpecifyBuildOptions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Executes data source's implementation of asset bundle building.
|
||||
/// Called by "build" button in build tab of tool.
|
||||
/// </summary>
|
||||
bool BuildAssetBundles(ABBuildInfo info);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 919244bce66418940b1be40b992ffb7c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace AssetBundleBrowser.AssetBundleDataSource
|
||||
{
|
||||
internal class ABDataSourceProviderUtility
|
||||
{
|
||||
private static List<Type> s_customNodes;
|
||||
|
||||
internal static List<Type> CustomABDataSourceTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_customNodes == null) s_customNodes = BuildCustomABDataSourceList();
|
||||
return s_customNodes;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Type> BuildCustomABDataSourceList()
|
||||
{
|
||||
List<Type> properList = new List<Type>();
|
||||
properList.Add(null); //empty spot for "default"
|
||||
Assembly[] x = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (Assembly assembly in x)
|
||||
try
|
||||
{
|
||||
List<Type> list = new List<Type>(
|
||||
assembly
|
||||
.GetTypes()
|
||||
.Where(t => t != typeof(ABDataSource))
|
||||
.Where(t => typeof(ABDataSource).IsAssignableFrom(t)));
|
||||
|
||||
|
||||
for (int count = 0; count < list.Count; count++)
|
||||
if (list[count].Name == "AssetDatabaseABDataSource")
|
||||
properList[0] = list[count];
|
||||
else if (list[count] != null)
|
||||
properList.Add(list[count]);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
//assembly which raises exception on the GetTypes() call - ignore it
|
||||
}
|
||||
|
||||
|
||||
return properList;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8b537f111d73db4dac7f6a47a53b8d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.Assertions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
|
||||
namespace AssetBundleBrowser.AssetBundleDataSource
|
||||
{
|
||||
internal class AssetDatabaseABDataSource : ABDataSource
|
||||
{
|
||||
public static List<ABDataSource> CreateDataSources()
|
||||
{
|
||||
AssetDatabaseABDataSource op = new AssetDatabaseABDataSource();
|
||||
List<ABDataSource> retList = new List<ABDataSource>();
|
||||
retList.Add(op);
|
||||
return retList;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return "Default"; }
|
||||
}
|
||||
|
||||
public string ProviderName
|
||||
{
|
||||
get { return "Built-in"; }
|
||||
}
|
||||
|
||||
public string[] GetAssetPathsFromAssetBundle(string assetBundleName)
|
||||
{
|
||||
return AssetDatabase.GetAssetPathsFromAssetBundle(assetBundleName);
|
||||
}
|
||||
|
||||
public string GetAssetBundleName(string assetPath)
|
||||
{
|
||||
AssetImporter importer = AssetImporter.GetAtPath(assetPath);
|
||||
if (importer == null) return string.Empty;
|
||||
string bundleName = importer.assetBundleName;
|
||||
if (importer.assetBundleVariant.Length > 0) bundleName = bundleName + "." + importer.assetBundleVariant;
|
||||
return bundleName;
|
||||
}
|
||||
|
||||
public string GetImplicitAssetBundleName(string assetPath)
|
||||
{
|
||||
return AssetDatabase.GetImplicitAssetBundleName(assetPath);
|
||||
}
|
||||
|
||||
public string[] GetAllAssetBundleNames()
|
||||
{
|
||||
return AssetDatabase.GetAllAssetBundleNames();
|
||||
}
|
||||
|
||||
public bool IsReadOnly()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetAssetBundleNameAndVariant(string assetPath, string bundleName, string variantName)
|
||||
{
|
||||
AssetImporter.GetAtPath(assetPath).SetAssetBundleNameAndVariant(bundleName, variantName);
|
||||
}
|
||||
|
||||
public void RemoveUnusedAssetBundleNames()
|
||||
{
|
||||
AssetDatabase.RemoveUnusedAssetBundleNames();
|
||||
}
|
||||
|
||||
public bool CanSpecifyBuildTarget
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool CanSpecifyBuildOutputDirectory
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool CanSpecifyBuildOptions
|
||||
{
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public bool BuildAssetBundles(ABBuildInfo info)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
Debug.Log("Error in build");
|
||||
return false;
|
||||
}
|
||||
|
||||
AssetBundleManifest buildManifest = BuildPipeline.BuildAssetBundles(info.outputDirectory, info.options, info.buildTarget);
|
||||
if (buildManifest == null)
|
||||
{
|
||||
Debug.Log("Error in build");
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (string assetBundleName in buildManifest.GetAllAssetBundles())
|
||||
if (info.onBuild != null)
|
||||
info.onBuild(assetBundleName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 22084e56372f59e47a3fcd9c3b291923
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,265 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
namespace AssetBundleBrowser
|
||||
{
|
||||
[System.Serializable]
|
||||
internal class AssetBundleManageTab
|
||||
{
|
||||
[SerializeField] private TreeViewState m_BundleTreeState;
|
||||
[SerializeField] private TreeViewState m_AssetListState;
|
||||
[SerializeField] private MultiColumnHeaderState m_AssetListMCHState;
|
||||
[SerializeField] private TreeViewState m_BundleDetailState;
|
||||
|
||||
private Rect m_Position;
|
||||
|
||||
private AssetBundleTree m_BundleTree;
|
||||
private AssetListTree m_AssetList;
|
||||
private MessageList m_MessageList;
|
||||
private BundleDetailList m_DetailsList;
|
||||
private bool m_ResizingHorizontalSplitter = false;
|
||||
private bool m_ResizingVerticalSplitterRight = false;
|
||||
private bool m_ResizingVerticalSplitterLeft = false;
|
||||
private Rect m_HorizontalSplitterRect, m_VerticalSplitterRectRight, m_VerticalSplitterRectLeft;
|
||||
[SerializeField] private float m_HorizontalSplitterPercent;
|
||||
[SerializeField] private float m_VerticalSplitterPercentRight;
|
||||
[SerializeField] private float m_VerticalSplitterPercentLeft;
|
||||
private const float k_SplitterWidth = 3f;
|
||||
private static float s_UpdateDelay = 0f;
|
||||
|
||||
private SearchField m_searchField;
|
||||
|
||||
private EditorWindow m_Parent = null;
|
||||
|
||||
internal AssetBundleManageTab()
|
||||
{
|
||||
m_HorizontalSplitterPercent = 0.4f;
|
||||
m_VerticalSplitterPercentRight = 0.7f;
|
||||
m_VerticalSplitterPercentLeft = 0.85f;
|
||||
}
|
||||
|
||||
internal void OnEnable(Rect pos, EditorWindow parent)
|
||||
{
|
||||
m_Parent = parent;
|
||||
m_Position = pos;
|
||||
m_HorizontalSplitterRect = new Rect(
|
||||
(int) (m_Position.x + m_Position.width * m_HorizontalSplitterPercent),
|
||||
m_Position.y,
|
||||
k_SplitterWidth,
|
||||
m_Position.height);
|
||||
m_VerticalSplitterRectRight = new Rect(
|
||||
m_HorizontalSplitterRect.x,
|
||||
(int) (m_Position.y + m_HorizontalSplitterRect.height * m_VerticalSplitterPercentRight),
|
||||
m_Position.width - m_HorizontalSplitterRect.width - k_SplitterWidth,
|
||||
k_SplitterWidth);
|
||||
m_VerticalSplitterRectLeft = new Rect(
|
||||
m_Position.x,
|
||||
(int) (m_Position.y + m_HorizontalSplitterRect.height * m_VerticalSplitterPercentLeft),
|
||||
m_HorizontalSplitterRect.width - k_SplitterWidth,
|
||||
k_SplitterWidth);
|
||||
|
||||
m_searchField = new SearchField();
|
||||
}
|
||||
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
float t = Time.realtimeSinceStartup;
|
||||
if (t - s_UpdateDelay > 0.1f ||
|
||||
s_UpdateDelay > t) //something went strangely wrong if this second check is true.
|
||||
{
|
||||
s_UpdateDelay = t - 0.001f;
|
||||
|
||||
if (AssetBundleModel.Model.Update()) m_Parent.Repaint();
|
||||
|
||||
if (m_DetailsList != null)
|
||||
m_DetailsList.Update();
|
||||
|
||||
if (m_AssetList != null)
|
||||
m_AssetList.Update();
|
||||
}
|
||||
}
|
||||
|
||||
internal void ForceReloadData()
|
||||
{
|
||||
UpdateSelectedBundles(new List<AssetBundleModel.BundleInfo>());
|
||||
SetSelectedItems(new List<AssetBundleModel.AssetInfo>());
|
||||
m_BundleTree.SetSelection(new int[0]);
|
||||
AssetBundleModel.Model.ForceReloadData(m_BundleTree);
|
||||
m_Parent.Repaint();
|
||||
}
|
||||
|
||||
internal void OnGUI(Rect pos)
|
||||
{
|
||||
m_Position = pos;
|
||||
|
||||
if (m_BundleTree == null)
|
||||
{
|
||||
if (m_AssetListState == null)
|
||||
m_AssetListState = new TreeViewState();
|
||||
|
||||
MultiColumnHeaderState headerState = AssetListTree.CreateDefaultMultiColumnHeaderState(); // multiColumnTreeViewRect.width);
|
||||
if (MultiColumnHeaderState.CanOverwriteSerializedFields(m_AssetListMCHState, headerState))
|
||||
MultiColumnHeaderState.OverwriteSerializedFields(m_AssetListMCHState, headerState);
|
||||
m_AssetListMCHState = headerState;
|
||||
|
||||
|
||||
m_AssetList = new AssetListTree(m_AssetListState, m_AssetListMCHState, this);
|
||||
m_AssetList.Reload();
|
||||
m_MessageList = new MessageList();
|
||||
|
||||
if (m_BundleDetailState == null)
|
||||
m_BundleDetailState = new TreeViewState();
|
||||
m_DetailsList = new BundleDetailList(m_BundleDetailState);
|
||||
m_DetailsList.Reload();
|
||||
|
||||
if (m_BundleTreeState == null)
|
||||
m_BundleTreeState = new TreeViewState();
|
||||
m_BundleTree = new AssetBundleTree(m_BundleTreeState, this);
|
||||
m_BundleTree.Refresh();
|
||||
m_Parent.Repaint();
|
||||
}
|
||||
|
||||
HandleHorizontalResize();
|
||||
HandleVerticalResize();
|
||||
|
||||
|
||||
if (AssetBundleModel.Model.BundleListIsEmpty())
|
||||
{
|
||||
m_BundleTree.OnGUI(m_Position);
|
||||
GUIStyle style = new GUIStyle(GUI.skin.label);
|
||||
style.alignment = TextAnchor.MiddleCenter;
|
||||
style.wordWrap = true;
|
||||
GUI.Label(
|
||||
new Rect(m_Position.x + 1f, m_Position.y + 1f, m_Position.width - 2f, m_Position.height - 2f),
|
||||
new GUIContent(AssetBundleModel.Model.GetEmptyMessage()),
|
||||
style);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Left half
|
||||
Rect bundleTreeRect = new Rect(
|
||||
m_Position.x,
|
||||
m_Position.y,
|
||||
m_HorizontalSplitterRect.x,
|
||||
m_VerticalSplitterRectLeft.y - m_Position.y);
|
||||
|
||||
m_BundleTree.OnGUI(bundleTreeRect);
|
||||
m_DetailsList.OnGUI(new Rect(
|
||||
bundleTreeRect.x,
|
||||
bundleTreeRect.y + bundleTreeRect.height + k_SplitterWidth,
|
||||
bundleTreeRect.width,
|
||||
m_Position.height - bundleTreeRect.height - k_SplitterWidth * 2));
|
||||
|
||||
//Right half.
|
||||
float panelLeft = m_HorizontalSplitterRect.x + k_SplitterWidth;
|
||||
float panelWidth = m_VerticalSplitterRectRight.width - k_SplitterWidth * 2;
|
||||
float searchHeight = 20f;
|
||||
float panelTop = m_Position.y + searchHeight;
|
||||
float panelHeight = m_VerticalSplitterRectRight.y - panelTop;
|
||||
OnGUISearchBar(new Rect(panelLeft, m_Position.y, panelWidth, searchHeight));
|
||||
m_AssetList.OnGUI(new Rect(
|
||||
panelLeft,
|
||||
panelTop,
|
||||
panelWidth,
|
||||
panelHeight));
|
||||
m_MessageList.OnGUI(new Rect(
|
||||
panelLeft,
|
||||
panelTop + panelHeight + k_SplitterWidth,
|
||||
panelWidth,
|
||||
m_Position.height - panelHeight - k_SplitterWidth * 2));
|
||||
|
||||
if (m_ResizingHorizontalSplitter || m_ResizingVerticalSplitterRight || m_ResizingVerticalSplitterLeft)
|
||||
m_Parent.Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUISearchBar(Rect rect)
|
||||
{
|
||||
m_BundleTree.searchString = m_searchField.OnGUI(rect, m_BundleTree.searchString);
|
||||
m_AssetList.searchString = m_BundleTree.searchString;
|
||||
}
|
||||
|
||||
public bool hasSearch
|
||||
{
|
||||
get { return m_BundleTree.hasSearch; }
|
||||
}
|
||||
|
||||
private void HandleHorizontalResize()
|
||||
{
|
||||
m_HorizontalSplitterRect.x = (int) (m_Position.width * m_HorizontalSplitterPercent);
|
||||
m_HorizontalSplitterRect.height = m_Position.height;
|
||||
|
||||
EditorGUIUtility.AddCursorRect(m_HorizontalSplitterRect, MouseCursor.ResizeHorizontal);
|
||||
if (Event.current.type == EventType.MouseDown && m_HorizontalSplitterRect.Contains(Event.current.mousePosition))
|
||||
m_ResizingHorizontalSplitter = true;
|
||||
|
||||
if (m_ResizingHorizontalSplitter)
|
||||
{
|
||||
m_HorizontalSplitterPercent = Mathf.Clamp(Event.current.mousePosition.x / m_Position.width, 0.1f, 0.9f);
|
||||
m_HorizontalSplitterRect.x = (int) (m_Position.width * m_HorizontalSplitterPercent);
|
||||
}
|
||||
|
||||
if (Event.current.type == EventType.MouseUp)
|
||||
m_ResizingHorizontalSplitter = false;
|
||||
}
|
||||
|
||||
private void HandleVerticalResize()
|
||||
{
|
||||
m_VerticalSplitterRectRight.x = m_HorizontalSplitterRect.x;
|
||||
m_VerticalSplitterRectRight.y = (int) (m_HorizontalSplitterRect.height * m_VerticalSplitterPercentRight);
|
||||
m_VerticalSplitterRectRight.width = m_Position.width - m_HorizontalSplitterRect.x;
|
||||
m_VerticalSplitterRectLeft.y = (int) (m_HorizontalSplitterRect.height * m_VerticalSplitterPercentLeft);
|
||||
m_VerticalSplitterRectLeft.width = m_VerticalSplitterRectRight.width;
|
||||
|
||||
|
||||
EditorGUIUtility.AddCursorRect(m_VerticalSplitterRectRight, MouseCursor.ResizeVertical);
|
||||
if (Event.current.type == EventType.MouseDown && m_VerticalSplitterRectRight.Contains(Event.current.mousePosition))
|
||||
m_ResizingVerticalSplitterRight = true;
|
||||
|
||||
EditorGUIUtility.AddCursorRect(m_VerticalSplitterRectLeft, MouseCursor.ResizeVertical);
|
||||
if (Event.current.type == EventType.MouseDown && m_VerticalSplitterRectLeft.Contains(Event.current.mousePosition))
|
||||
m_ResizingVerticalSplitterLeft = true;
|
||||
|
||||
|
||||
if (m_ResizingVerticalSplitterRight)
|
||||
{
|
||||
m_VerticalSplitterPercentRight = Mathf.Clamp(Event.current.mousePosition.y / m_HorizontalSplitterRect.height, 0.2f, 0.98f);
|
||||
m_VerticalSplitterRectRight.y = (int) (m_HorizontalSplitterRect.height * m_VerticalSplitterPercentRight);
|
||||
}
|
||||
else if (m_ResizingVerticalSplitterLeft)
|
||||
{
|
||||
m_VerticalSplitterPercentLeft = Mathf.Clamp(Event.current.mousePosition.y / m_HorizontalSplitterRect.height, 0.25f, 0.98f);
|
||||
m_VerticalSplitterRectLeft.y = (int) (m_HorizontalSplitterRect.height * m_VerticalSplitterPercentLeft);
|
||||
}
|
||||
|
||||
|
||||
if (Event.current.type == EventType.MouseUp)
|
||||
{
|
||||
m_ResizingVerticalSplitterRight = false;
|
||||
m_ResizingVerticalSplitterLeft = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal void UpdateSelectedBundles(IEnumerable<AssetBundleModel.BundleInfo> bundles)
|
||||
{
|
||||
AssetBundleModel.Model.AddBundlesToUpdate(bundles);
|
||||
m_AssetList.SetSelectedBundles(bundles);
|
||||
m_DetailsList.SetItems(bundles);
|
||||
m_MessageList.SetItems(null);
|
||||
}
|
||||
|
||||
internal void SetSelectedItems(IEnumerable<AssetBundleModel.AssetInfo> items)
|
||||
{
|
||||
m_MessageList.SetItems(items);
|
||||
}
|
||||
|
||||
internal void SetAssetListSelection(List<string> assets)
|
||||
{
|
||||
m_AssetList.SetSelection(assets);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f53b921dc74d344a98b8624b7b95a8b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b59abcdb3531bd64ab43572babec263e
|
||||
folderAsset: yes
|
||||
timeCreated: 1488835755
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,766 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.Assertions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using AssetBundleBrowser.AssetBundleDataSource;
|
||||
|
||||
namespace AssetBundleBrowser.AssetBundleModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Static class holding model data for Asset Bundle Browser tool. Data in Model is read from DataSource, but is not pushed.
|
||||
///
|
||||
/// If not using a custom DataSource, then the data comes from the AssetDatabase. If you wish to alter the data from code,
|
||||
/// you should just push changes to the AssetDatabase then tell the Model to Rebuild(). If needed, you can also loop over
|
||||
/// Update() until it returns true to force all sub-items to refresh.
|
||||
///
|
||||
/// </summary>
|
||||
public static class Model
|
||||
{
|
||||
private const string k_NewBundleBaseName = "newbundle";
|
||||
private const string k_NewVariantBaseName = "newvariant";
|
||||
internal static /*const*/ Color k_LightGrey = Color.grey * 1.5f;
|
||||
|
||||
private static ABDataSource s_DataSource;
|
||||
private static BundleFolderConcreteInfo s_RootLevelBundles = new BundleFolderConcreteInfo("", null);
|
||||
private static List<ABMoveData> s_MoveData = new List<ABMoveData>();
|
||||
private static List<BundleInfo> s_BundlesToUpdate = new List<BundleInfo>();
|
||||
private static Dictionary<string, AssetInfo> s_GlobalAssetList = new Dictionary<string, AssetInfo>();
|
||||
private static Dictionary<string, HashSet<string>> s_DependencyTracker = new Dictionary<string, HashSet<string>>();
|
||||
|
||||
private static bool s_InErrorState = false;
|
||||
private const string k_DefaultEmptyMessage = "Drag assets here or right-click to begin creating bundles.";
|
||||
private const string k_ProblemEmptyMessage = "There was a problem parsing the list of bundles. See console.";
|
||||
private static string s_EmptyMessageString;
|
||||
|
||||
private static Texture2D s_folderIcon = null;
|
||||
private static Texture2D s_bundleIcon = null;
|
||||
private static Texture2D s_sceneIcon = null;
|
||||
|
||||
/// <summary>
|
||||
/// If using a custom source of asset bundles, you can implement your own ABDataSource and set it here as the active
|
||||
/// DataSource. This will allow you to use the Browser with data that you provide.
|
||||
///
|
||||
/// If no custom DataSource is provided, then the Browser will create one that feeds off of and into the
|
||||
/// AssetDatabase.
|
||||
///
|
||||
/// </summary>
|
||||
public static ABDataSource DataSource
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_DataSource == null) s_DataSource = new AssetDatabaseABDataSource();
|
||||
return s_DataSource;
|
||||
}
|
||||
set { s_DataSource = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update will loop over bundles that need updating and update them. It will only update one bundle
|
||||
/// per frame and will continue on the same bundle next frame until that bundle is marked as doneUpdating.
|
||||
/// By default, this will cause a very slow collection of dependency data as it will only update one bundle per
|
||||
/// </summary>
|
||||
public static bool Update()
|
||||
{
|
||||
bool shouldRepaint = false;
|
||||
ExecuteAssetMove(false); //this should never do anything. just a safety check.
|
||||
|
||||
//TODO - look into EditorApplication callback functions.
|
||||
|
||||
int size = s_BundlesToUpdate.Count;
|
||||
if (size > 0)
|
||||
{
|
||||
s_BundlesToUpdate[size - 1].Update();
|
||||
s_BundlesToUpdate.RemoveAll(item => item.doneUpdating == true);
|
||||
if (s_BundlesToUpdate.Count == 0)
|
||||
{
|
||||
shouldRepaint = true;
|
||||
foreach (BundleInfo bundle in s_RootLevelBundles.GetChildList()) bundle.RefreshDupeAssetWarning();
|
||||
}
|
||||
}
|
||||
|
||||
return shouldRepaint;
|
||||
}
|
||||
|
||||
internal static void ForceReloadData(TreeView tree)
|
||||
{
|
||||
s_InErrorState = false;
|
||||
Rebuild();
|
||||
tree.Reload();
|
||||
bool doneUpdating = s_BundlesToUpdate.Count == 0;
|
||||
|
||||
EditorUtility.DisplayProgressBar("Updating Bundles", "", 0);
|
||||
int fullBundleCount = s_BundlesToUpdate.Count;
|
||||
while (!doneUpdating && !s_InErrorState)
|
||||
{
|
||||
int currCount = s_BundlesToUpdate.Count;
|
||||
EditorUtility.DisplayProgressBar("Updating Bundles", s_BundlesToUpdate[currCount - 1].displayName, (float) (fullBundleCount - currCount) / (float) fullBundleCount);
|
||||
doneUpdating = Update();
|
||||
}
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears and rebuilds model data.
|
||||
/// </summary>
|
||||
public static void Rebuild()
|
||||
{
|
||||
s_RootLevelBundles = new BundleFolderConcreteInfo("", null);
|
||||
s_MoveData = new List<ABMoveData>();
|
||||
s_BundlesToUpdate = new List<BundleInfo>();
|
||||
s_GlobalAssetList = new Dictionary<string, AssetInfo>();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
internal static void AddBundlesToUpdate(IEnumerable<BundleInfo> bundles)
|
||||
{
|
||||
foreach (BundleInfo bundle in bundles)
|
||||
{
|
||||
bundle.ForceNeedUpdate();
|
||||
s_BundlesToUpdate.Add(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Refresh()
|
||||
{
|
||||
s_EmptyMessageString = k_ProblemEmptyMessage;
|
||||
if (s_InErrorState)
|
||||
return;
|
||||
|
||||
string[] bundleList = ValidateBundleList();
|
||||
if (bundleList != null)
|
||||
{
|
||||
s_EmptyMessageString = k_DefaultEmptyMessage;
|
||||
foreach (string bundleName in bundleList) AddBundleToModel(bundleName);
|
||||
AddBundlesToUpdate(s_RootLevelBundles.GetChildList());
|
||||
}
|
||||
|
||||
if (s_InErrorState)
|
||||
{
|
||||
s_RootLevelBundles = new BundleFolderConcreteInfo("", null);
|
||||
s_EmptyMessageString = k_ProblemEmptyMessage;
|
||||
}
|
||||
}
|
||||
|
||||
internal static string[] ValidateBundleList()
|
||||
{
|
||||
string[] bundleList = DataSource.GetAllAssetBundleNames();
|
||||
bool valid = true;
|
||||
HashSet<string> bundleSet = new HashSet<string>();
|
||||
int index = 0;
|
||||
bool attemptedBundleReset = false;
|
||||
while (index < bundleList.Length)
|
||||
{
|
||||
string name = bundleList[index];
|
||||
if (!bundleSet.Add(name))
|
||||
{
|
||||
LogError("Two bundles share the same name: " + name);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
int lastDot = name.LastIndexOf('.');
|
||||
if (lastDot > -1)
|
||||
{
|
||||
string bunName = name.Substring(0, lastDot);
|
||||
int extraDot = bunName.LastIndexOf('.');
|
||||
if (extraDot > -1)
|
||||
{
|
||||
if (attemptedBundleReset)
|
||||
{
|
||||
string message = "Bundle name '" + bunName + "' contains a period.";
|
||||
message += " Internally Unity keeps 'bundleName' and 'variantName' separate, but externally treat them as 'bundleName.variantName'.";
|
||||
message += " If a bundleName contains a period, the build will (probably) succeed, but this tool cannot tell which portion is bundle and which portion is variant.";
|
||||
LogError(message);
|
||||
valid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!DataSource.IsReadOnly()) DataSource.RemoveUnusedAssetBundleNames();
|
||||
index = 0;
|
||||
bundleSet.Clear();
|
||||
bundleList = DataSource.GetAllAssetBundleNames();
|
||||
attemptedBundleReset = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (bundleList.Contains(bunName))
|
||||
{
|
||||
//there is a bundle.none and a bundle.variant coexisting. Need to fix that or return an error.
|
||||
if (attemptedBundleReset)
|
||||
{
|
||||
valid = false;
|
||||
string message = "Bundle name '" + bunName + "' exists without a variant as well as with variant '" + name.Substring(lastDot + 1) + "'.";
|
||||
message += " That is an illegal state that will not build and must be cleaned up.";
|
||||
LogError(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!DataSource.IsReadOnly()) DataSource.RemoveUnusedAssetBundleNames();
|
||||
index = 0;
|
||||
bundleSet.Clear();
|
||||
bundleList = DataSource.GetAllAssetBundleNames();
|
||||
attemptedBundleReset = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if (valid)
|
||||
return bundleList;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static bool BundleListIsEmpty()
|
||||
{
|
||||
return s_RootLevelBundles.GetChildList().Count() == 0;
|
||||
}
|
||||
|
||||
internal static string GetEmptyMessage()
|
||||
{
|
||||
return s_EmptyMessageString;
|
||||
}
|
||||
|
||||
internal static BundleInfo CreateEmptyBundle(BundleFolderInfo folder = null, string newName = null)
|
||||
{
|
||||
if (folder as BundleVariantFolderInfo != null)
|
||||
return CreateEmptyVariant(folder as BundleVariantFolderInfo);
|
||||
|
||||
folder = folder == null ? s_RootLevelBundles : folder;
|
||||
string name = GetUniqueName(folder, newName);
|
||||
BundleNameData nameData;
|
||||
nameData = new BundleNameData(folder.m_Name.bundleName, name);
|
||||
return AddBundleToFolder(folder, nameData);
|
||||
}
|
||||
|
||||
internal static BundleInfo CreateEmptyVariant(BundleVariantFolderInfo folder)
|
||||
{
|
||||
string name = GetUniqueName(folder, k_NewVariantBaseName);
|
||||
string variantName = folder.m_Name.bundleName + "." + name;
|
||||
BundleNameData nameData = new BundleNameData(variantName);
|
||||
return AddBundleToFolder(folder.parent, nameData);
|
||||
}
|
||||
|
||||
internal static BundleFolderInfo CreateEmptyBundleFolder(BundleFolderConcreteInfo folder = null)
|
||||
{
|
||||
folder = folder == null ? s_RootLevelBundles : folder;
|
||||
string name = GetUniqueName(folder) + "/dummy";
|
||||
BundleNameData nameData = new BundleNameData(folder.m_Name.bundleName, name);
|
||||
return AddFoldersToBundle(s_RootLevelBundles, nameData);
|
||||
}
|
||||
|
||||
private static BundleInfo AddBundleToModel(string name)
|
||||
{
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
BundleNameData nameData = new BundleNameData(name);
|
||||
|
||||
BundleFolderInfo folder = AddFoldersToBundle(s_RootLevelBundles, nameData);
|
||||
BundleInfo currInfo = AddBundleToFolder(folder, nameData);
|
||||
|
||||
return currInfo;
|
||||
}
|
||||
|
||||
private static BundleFolderConcreteInfo AddFoldersToBundle(BundleFolderInfo root, BundleNameData nameData)
|
||||
{
|
||||
BundleInfo currInfo = root;
|
||||
BundleFolderConcreteInfo folder = currInfo as BundleFolderConcreteInfo;
|
||||
int size = nameData.pathTokens.Count;
|
||||
for (int index = 0; index < size; index++)
|
||||
if (folder != null)
|
||||
{
|
||||
currInfo = folder.GetChild(nameData.pathTokens[index]);
|
||||
if (currInfo == null)
|
||||
{
|
||||
currInfo = new BundleFolderConcreteInfo(nameData.pathTokens, index + 1, folder);
|
||||
folder.AddChild(currInfo);
|
||||
}
|
||||
|
||||
folder = currInfo as BundleFolderConcreteInfo;
|
||||
if (folder == null)
|
||||
{
|
||||
s_InErrorState = true;
|
||||
LogFolderAndBundleNameConflict(currInfo.m_Name.fullNativeName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return currInfo as BundleFolderConcreteInfo;
|
||||
}
|
||||
|
||||
private static void LogFolderAndBundleNameConflict(string name)
|
||||
{
|
||||
string message = "Bundle '";
|
||||
message += name;
|
||||
message += "' has a name conflict with a bundle-folder.";
|
||||
message += "Display of bundle data and building of bundles will not work.";
|
||||
message += "\nDetails: If you name a bundle 'x/y', then the result of your build will be a bundle named 'y' in a folder named 'x'. You thus cannot also have a bundle named 'x' at the same level as the folder named 'x'.";
|
||||
LogError(message);
|
||||
}
|
||||
|
||||
private static BundleInfo AddBundleToFolder(BundleFolderInfo root, BundleNameData nameData)
|
||||
{
|
||||
BundleInfo currInfo = root.GetChild(nameData.shortName);
|
||||
if (!string.IsNullOrEmpty(nameData.variant))
|
||||
{
|
||||
if (currInfo == null)
|
||||
{
|
||||
currInfo = new BundleVariantFolderInfo(nameData.bundleName, root);
|
||||
root.AddChild(currInfo);
|
||||
}
|
||||
|
||||
BundleVariantFolderInfo folder = currInfo as BundleVariantFolderInfo;
|
||||
if (folder == null)
|
||||
{
|
||||
string message = "Bundle named " + nameData.shortName;
|
||||
message += " exists both as a standard bundle, and a bundle with variants. ";
|
||||
message += "This message is not supported for display or actual bundle building. ";
|
||||
message += "You must manually fix bundle naming in the inspector.";
|
||||
|
||||
LogError(message);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
currInfo = folder.GetChild(nameData.variant);
|
||||
if (currInfo == null)
|
||||
{
|
||||
currInfo = new BundleVariantDataInfo(nameData.fullNativeName, folder);
|
||||
folder.AddChild(currInfo);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (currInfo == null)
|
||||
{
|
||||
currInfo = new BundleDataInfo(nameData.fullNativeName, root);
|
||||
root.AddChild(currInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
BundleDataInfo dataInfo = currInfo as BundleDataInfo;
|
||||
if (dataInfo == null)
|
||||
{
|
||||
s_InErrorState = true;
|
||||
LogFolderAndBundleNameConflict(nameData.fullNativeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return currInfo;
|
||||
}
|
||||
|
||||
private static string GetUniqueName(BundleFolderInfo folder, string suggestedName = null)
|
||||
{
|
||||
suggestedName = suggestedName == null ? k_NewBundleBaseName : suggestedName;
|
||||
string name = suggestedName;
|
||||
int index = 1;
|
||||
bool foundExisting = folder.GetChild(name) != null;
|
||||
while (foundExisting)
|
||||
{
|
||||
name = suggestedName + index;
|
||||
index++;
|
||||
foundExisting = folder.GetChild(name) != null;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
internal static BundleTreeItem CreateBundleTreeView()
|
||||
{
|
||||
return s_RootLevelBundles.CreateTreeView(-1);
|
||||
}
|
||||
|
||||
internal static AssetTreeItem CreateAssetListTreeView(IEnumerable<BundleInfo> selectedBundles)
|
||||
{
|
||||
AssetTreeItem root = new AssetTreeItem();
|
||||
if (selectedBundles != null)
|
||||
foreach (BundleInfo bundle in selectedBundles)
|
||||
bundle.AddAssetsToNode(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
internal static bool HandleBundleRename(BundleTreeItem item, string newName)
|
||||
{
|
||||
BundleNameData originalName = new BundleNameData(item.bundle.m_Name.fullNativeName);
|
||||
|
||||
int findDot = newName.LastIndexOf('.');
|
||||
int findSlash = newName.LastIndexOf('/');
|
||||
int findBSlash = newName.LastIndexOf('\\');
|
||||
if (findDot == 0 || findSlash == 0 || findBSlash == 0)
|
||||
return false; //can't start a bundle with a / or .
|
||||
|
||||
bool result = item.bundle.HandleRename(newName, 0);
|
||||
|
||||
if (findDot > 0 || findSlash > 0 || findBSlash > 0) item.bundle.parent.HandleChildRename(newName, string.Empty);
|
||||
|
||||
ExecuteAssetMove();
|
||||
|
||||
BundleInfo node = FindBundle(originalName);
|
||||
if (node != null)
|
||||
{
|
||||
string message = "Failed to rename bundle named: ";
|
||||
message += originalName.fullNativeName;
|
||||
message += ". Most likely this is due to the bundle being assigned to a folder in your Assets directory, AND that folder is either empty or only contains assets that are explicitly assigned elsewhere.";
|
||||
Debug.LogError(message);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static void HandleBundleReparent(IEnumerable<BundleInfo> bundles, BundleFolderInfo parent)
|
||||
{
|
||||
parent = parent == null ? s_RootLevelBundles : parent;
|
||||
foreach (BundleInfo bundle in bundles) bundle.HandleReparent(parent.m_Name.bundleName, parent);
|
||||
ExecuteAssetMove();
|
||||
}
|
||||
|
||||
internal static void HandleBundleMerge(IEnumerable<BundleInfo> bundles, BundleDataInfo target)
|
||||
{
|
||||
foreach (BundleInfo bundle in bundles) bundle.HandleDelete(true, target.m_Name.bundleName, target.m_Name.variant);
|
||||
ExecuteAssetMove();
|
||||
}
|
||||
|
||||
internal static void HandleBundleDelete(IEnumerable<BundleInfo> bundles)
|
||||
{
|
||||
List<BundleNameData> nameList = new List<BundleNameData>();
|
||||
foreach (BundleInfo bundle in bundles)
|
||||
{
|
||||
nameList.Add(bundle.m_Name);
|
||||
bundle.HandleDelete(true);
|
||||
}
|
||||
|
||||
ExecuteAssetMove();
|
||||
|
||||
//check to see if any bundles are still there...
|
||||
foreach (BundleNameData name in nameList)
|
||||
{
|
||||
BundleInfo node = FindBundle(name);
|
||||
if (node != null)
|
||||
{
|
||||
string message = "Failed to delete bundle named: ";
|
||||
message += name.fullNativeName;
|
||||
message += ". Most likely this is due to the bundle being assigned to a folder in your Assets directory, AND that folder is either empty or only contains assets that are explicitly assigned elsewhere.";
|
||||
Debug.LogError(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static BundleInfo FindBundle(BundleNameData name)
|
||||
{
|
||||
BundleInfo currNode = s_RootLevelBundles;
|
||||
foreach (string token in name.pathTokens)
|
||||
if (currNode is BundleFolderInfo)
|
||||
{
|
||||
currNode = (currNode as BundleFolderInfo).GetChild(token);
|
||||
if (currNode == null)
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (currNode is BundleFolderInfo)
|
||||
{
|
||||
currNode = (currNode as BundleFolderInfo).GetChild(name.shortName);
|
||||
if (currNode is BundleVariantFolderInfo) currNode = (currNode as BundleVariantFolderInfo).GetChild(name.variant);
|
||||
return currNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal static BundleInfo HandleDedupeBundles(IEnumerable<BundleInfo> bundles, bool onlyOverlappedAssets)
|
||||
{
|
||||
BundleInfo newBundle = CreateEmptyBundle();
|
||||
HashSet<string> dupeAssets = new HashSet<string>();
|
||||
HashSet<string> fullAssetList = new HashSet<string>();
|
||||
|
||||
//if they were just selected, then they may still be updating.
|
||||
bool doneUpdating = s_BundlesToUpdate.Count == 0;
|
||||
while (!doneUpdating)
|
||||
doneUpdating = Update();
|
||||
|
||||
foreach (BundleInfo bundle in bundles)
|
||||
foreach (AssetInfo asset in bundle.GetDependencies())
|
||||
if (onlyOverlappedAssets)
|
||||
{
|
||||
if (!fullAssetList.Add(asset.fullAssetName))
|
||||
dupeAssets.Add(asset.fullAssetName);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (asset.IsMessageSet(MessageSystem.MessageFlag.AssetsDuplicatedInMultBundles))
|
||||
dupeAssets.Add(asset.fullAssetName);
|
||||
}
|
||||
|
||||
if (dupeAssets.Count == 0)
|
||||
return null;
|
||||
|
||||
MoveAssetToBundle(dupeAssets, newBundle.m_Name.bundleName, string.Empty);
|
||||
ExecuteAssetMove();
|
||||
return newBundle;
|
||||
}
|
||||
|
||||
internal static BundleInfo HandleConvertToVariant(BundleDataInfo bundle)
|
||||
{
|
||||
bundle.HandleDelete(true, bundle.m_Name.bundleName, k_NewVariantBaseName);
|
||||
ExecuteAssetMove();
|
||||
BundleVariantFolderInfo root = bundle.parent.GetChild(bundle.m_Name.shortName) as BundleVariantFolderInfo;
|
||||
|
||||
if (root != null)
|
||||
{
|
||||
return root.GetChild(k_NewVariantBaseName);
|
||||
}
|
||||
else
|
||||
{
|
||||
//we got here because the converted bundle was empty.
|
||||
BundleVariantFolderInfo vfolder = new BundleVariantFolderInfo(bundle.m_Name.bundleName, bundle.parent);
|
||||
BundleVariantDataInfo vdata = new BundleVariantDataInfo(bundle.m_Name.bundleName + "." + k_NewVariantBaseName, vfolder);
|
||||
bundle.parent.AddChild(vfolder);
|
||||
vfolder.AddChild(vdata);
|
||||
return vdata;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ABMoveData
|
||||
{
|
||||
internal string assetName;
|
||||
internal string bundleName;
|
||||
internal string variantName;
|
||||
|
||||
internal ABMoveData(string asset, string bundle, string variant)
|
||||
{
|
||||
assetName = asset;
|
||||
bundleName = bundle;
|
||||
variantName = variant;
|
||||
}
|
||||
|
||||
internal void Apply()
|
||||
{
|
||||
if (!DataSource.IsReadOnly()) DataSource.SetAssetBundleNameAndVariant(assetName, bundleName, variantName);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MoveAssetToBundle(AssetInfo asset, string bundleName, string variant)
|
||||
{
|
||||
s_MoveData.Add(new ABMoveData(asset.fullAssetName, bundleName, variant));
|
||||
}
|
||||
|
||||
internal static void MoveAssetToBundle(string assetName, string bundleName, string variant)
|
||||
{
|
||||
s_MoveData.Add(new ABMoveData(assetName, bundleName, variant));
|
||||
}
|
||||
|
||||
internal static void MoveAssetToBundle(IEnumerable<AssetInfo> assets, string bundleName, string variant)
|
||||
{
|
||||
foreach (AssetInfo asset in assets)
|
||||
MoveAssetToBundle(asset, bundleName, variant);
|
||||
}
|
||||
|
||||
internal static void MoveAssetToBundle(IEnumerable<string> assetNames, string bundleName, string variant)
|
||||
{
|
||||
foreach (string assetName in assetNames)
|
||||
MoveAssetToBundle(assetName, bundleName, variant);
|
||||
}
|
||||
|
||||
internal static void ExecuteAssetMove(bool forceAct = true)
|
||||
{
|
||||
int size = s_MoveData.Count;
|
||||
if (forceAct)
|
||||
{
|
||||
if (size > 0)
|
||||
{
|
||||
bool autoRefresh = EditorPrefs.GetBool("kAutoRefresh");
|
||||
EditorPrefs.SetBool("kAutoRefresh", false);
|
||||
AssetDatabase.StartAssetEditing();
|
||||
EditorUtility.DisplayProgressBar("Moving assets to bundles", "", 0);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
EditorUtility.DisplayProgressBar("Moving assets to bundle " + s_MoveData[i].bundleName, System.IO.Path.GetFileNameWithoutExtension(s_MoveData[i].assetName), (float) i / (float) size);
|
||||
s_MoveData[i].Apply();
|
||||
}
|
||||
|
||||
EditorUtility.ClearProgressBar();
|
||||
AssetDatabase.StopAssetEditing();
|
||||
EditorPrefs.SetBool("kAutoRefresh", autoRefresh);
|
||||
s_MoveData.Clear();
|
||||
}
|
||||
|
||||
if (!DataSource.IsReadOnly()) DataSource.RemoveUnusedAssetBundleNames();
|
||||
Refresh();
|
||||
}
|
||||
}
|
||||
|
||||
//this version of CreateAsset is only used for dependent assets.
|
||||
internal static AssetInfo CreateAsset(string name, AssetInfo parent)
|
||||
{
|
||||
if (ValidateAsset(name))
|
||||
{
|
||||
string bundleName = GetBundleName(name);
|
||||
return CreateAsset(name, bundleName, parent);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal static AssetInfo CreateAsset(string name, string bundleName)
|
||||
{
|
||||
if (ValidateAsset(name)) return CreateAsset(name, bundleName, null);
|
||||
return null;
|
||||
}
|
||||
|
||||
private static AssetInfo CreateAsset(string name, string bundleName, AssetInfo parent)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(bundleName))
|
||||
{
|
||||
return new AssetInfo(name, bundleName);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssetInfo info = null;
|
||||
if (!s_GlobalAssetList.TryGetValue(name, out info))
|
||||
{
|
||||
info = new AssetInfo(name, string.Empty);
|
||||
s_GlobalAssetList.Add(name, info);
|
||||
}
|
||||
|
||||
info.AddParent(parent.displayName);
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool ValidateAsset(string name)
|
||||
{
|
||||
if (!name.StartsWith("Assets/"))
|
||||
return false;
|
||||
string ext = System.IO.Path.GetExtension(name);
|
||||
if (ext == ".dll" || ext == ".cs" || ext == ".meta" || ext == ".js" || ext == ".boo")
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static string GetBundleName(string asset)
|
||||
{
|
||||
return DataSource.GetAssetBundleName(asset);
|
||||
}
|
||||
|
||||
internal static int RegisterAsset(AssetInfo asset, string bundle)
|
||||
{
|
||||
if (s_DependencyTracker.ContainsKey(asset.fullAssetName))
|
||||
{
|
||||
s_DependencyTracker[asset.fullAssetName].Add(bundle);
|
||||
int count = s_DependencyTracker[asset.fullAssetName].Count;
|
||||
if (count > 1)
|
||||
asset.SetMessageFlag(MessageSystem.MessageFlag.AssetsDuplicatedInMultBundles, true);
|
||||
return count;
|
||||
}
|
||||
|
||||
HashSet<string> bundles = new HashSet<string>();
|
||||
bundles.Add(bundle);
|
||||
s_DependencyTracker.Add(asset.fullAssetName, bundles);
|
||||
return 1;
|
||||
}
|
||||
|
||||
internal static void UnRegisterAsset(AssetInfo asset, string bundle)
|
||||
{
|
||||
if (s_DependencyTracker == null || asset == null)
|
||||
return;
|
||||
|
||||
if (s_DependencyTracker.ContainsKey(asset.fullAssetName))
|
||||
{
|
||||
s_DependencyTracker[asset.fullAssetName].Remove(bundle);
|
||||
int count = s_DependencyTracker[asset.fullAssetName].Count;
|
||||
switch (count)
|
||||
{
|
||||
case 0:
|
||||
s_DependencyTracker.Remove(asset.fullAssetName);
|
||||
break;
|
||||
case 1:
|
||||
asset.SetMessageFlag(MessageSystem.MessageFlag.AssetsDuplicatedInMultBundles, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static IEnumerable<string> CheckDependencyTracker(AssetInfo asset)
|
||||
{
|
||||
if (s_DependencyTracker.ContainsKey(asset.fullAssetName)) return s_DependencyTracker[asset.fullAssetName];
|
||||
return new HashSet<string>();
|
||||
}
|
||||
|
||||
//TODO - switch local cache server on and utilize this method to stay up to date.
|
||||
//static List<string> m_importedAssets = new List<string>();
|
||||
//static List<string> m_deletedAssets = new List<string>();
|
||||
//static List<KeyValuePair<string, string>> m_movedAssets = new List<KeyValuePair<string, string>>();
|
||||
//class AssetBundleChangeListener : AssetPostprocessor
|
||||
//{
|
||||
// static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
|
||||
// {
|
||||
// m_importedAssets.AddRange(importedAssets);
|
||||
// m_deletedAssets.AddRange(deletedAssets);
|
||||
// for (int i = 0; i < movedAssets.Length; i++)
|
||||
// m_movedAssets.Add(new KeyValuePair<string, string>(movedFromAssetPaths[i], movedAssets[i]));
|
||||
// //m_dirty = true;
|
||||
// }
|
||||
//}
|
||||
|
||||
internal static void LogError(string message)
|
||||
{
|
||||
Debug.LogError("AssetBundleBrowser: " + message);
|
||||
}
|
||||
|
||||
internal static void LogWarning(string message)
|
||||
{
|
||||
Debug.LogWarning("AssetBundleBrowser: " + message);
|
||||
}
|
||||
|
||||
internal static Texture2D GetFolderIcon()
|
||||
{
|
||||
if (s_folderIcon == null)
|
||||
FindBundleIcons();
|
||||
return s_folderIcon;
|
||||
}
|
||||
|
||||
internal static Texture2D GetBundleIcon()
|
||||
{
|
||||
if (s_bundleIcon == null)
|
||||
FindBundleIcons();
|
||||
return s_bundleIcon;
|
||||
}
|
||||
|
||||
internal static Texture2D GetSceneIcon()
|
||||
{
|
||||
if (s_sceneIcon == null)
|
||||
FindBundleIcons();
|
||||
return s_sceneIcon;
|
||||
}
|
||||
|
||||
private static void FindBundleIcons()
|
||||
{
|
||||
s_folderIcon = EditorGUIUtility.FindTexture("Folder Icon");
|
||||
|
||||
string packagePath = System.IO.Path.GetFullPath("Packages/com.unity.assetbundlebrowser");
|
||||
if (System.IO.Directory.Exists(packagePath))
|
||||
{
|
||||
s_bundleIcon = (Texture2D) AssetDatabase.LoadAssetAtPath("Packages/com.unity.assetbundlebrowser/Editor/Icons/ABundleBrowserIconY1756Basic.png", typeof(Texture2D));
|
||||
s_sceneIcon = (Texture2D) AssetDatabase.LoadAssetAtPath("Packages/com.unity.assetbundlebrowser/Editor/Icons/ABundleBrowserIconY1756Scene.png", typeof(Texture2D));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f45129f30a7a9b4caf06fa3cbb90882
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,248 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
|
||||
namespace AssetBundleBrowser.AssetBundleModel
|
||||
{
|
||||
internal sealed class AssetTreeItem : TreeViewItem
|
||||
{
|
||||
private AssetInfo m_asset;
|
||||
|
||||
internal AssetInfo asset
|
||||
{
|
||||
get { return m_asset; }
|
||||
}
|
||||
|
||||
internal AssetTreeItem() : base(-1, -1)
|
||||
{
|
||||
}
|
||||
|
||||
internal AssetTreeItem(AssetInfo a) : base(a != null ? a.fullAssetName.GetHashCode() : Random.Range(int.MinValue, int.MaxValue), 0, a != null ? a.displayName : "failed")
|
||||
{
|
||||
m_asset = a;
|
||||
if (a != null)
|
||||
icon = AssetDatabase.GetCachedIcon(a.fullAssetName) as Texture2D;
|
||||
}
|
||||
|
||||
private Color m_color = new Color(0, 0, 0, 0);
|
||||
|
||||
internal Color itemColor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_color.a == 0.0f && m_asset != null) m_color = m_asset.GetColor();
|
||||
return m_color;
|
||||
}
|
||||
set { m_color = value; }
|
||||
}
|
||||
|
||||
internal Texture2D MessageIcon()
|
||||
{
|
||||
return MessageSystem.GetIcon(HighestMessageLevel());
|
||||
}
|
||||
|
||||
internal MessageType HighestMessageLevel()
|
||||
{
|
||||
return m_asset != null ? m_asset.HighestMessageLevel() : MessageType.Error;
|
||||
}
|
||||
|
||||
internal bool ContainsChild(AssetInfo asset)
|
||||
{
|
||||
bool contains = false;
|
||||
if (children == null)
|
||||
return contains;
|
||||
|
||||
if (asset == null)
|
||||
return false;
|
||||
foreach (TreeViewItem child in children)
|
||||
{
|
||||
AssetTreeItem c = child as AssetTreeItem;
|
||||
if (c != null && c.asset != null && c.asset.fullAssetName == asset.fullAssetName)
|
||||
{
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return contains;
|
||||
}
|
||||
}
|
||||
|
||||
internal class AssetInfo
|
||||
{
|
||||
internal bool isScene { get; set; }
|
||||
internal bool isFolder { get; set; }
|
||||
internal long fileSize;
|
||||
|
||||
private HashSet<string> m_Parents;
|
||||
private string m_AssetName;
|
||||
private string m_DisplayName;
|
||||
private string m_BundleName;
|
||||
private MessageSystem.MessageState m_AssetMessages = new MessageSystem.MessageState();
|
||||
|
||||
internal AssetInfo(string inName, string bundleName = "")
|
||||
{
|
||||
fullAssetName = inName;
|
||||
m_BundleName = bundleName;
|
||||
m_Parents = new HashSet<string>();
|
||||
isScene = false;
|
||||
isFolder = false;
|
||||
}
|
||||
|
||||
internal string fullAssetName
|
||||
{
|
||||
get { return m_AssetName; }
|
||||
set
|
||||
{
|
||||
m_AssetName = value;
|
||||
m_DisplayName = System.IO.Path.GetFileNameWithoutExtension(m_AssetName);
|
||||
|
||||
//TODO - maybe there's a way to ask the AssetDatabase for this size info.
|
||||
FileInfo fileInfo = new System.IO.FileInfo(m_AssetName);
|
||||
if (fileInfo.Exists)
|
||||
fileSize = fileInfo.Length;
|
||||
else
|
||||
fileSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal string displayName
|
||||
{
|
||||
get { return m_DisplayName; }
|
||||
}
|
||||
|
||||
internal string bundleName
|
||||
{
|
||||
get { return string.IsNullOrEmpty(m_BundleName) ? "auto" : m_BundleName; }
|
||||
}
|
||||
|
||||
internal Color GetColor()
|
||||
{
|
||||
if (string.IsNullOrEmpty(m_BundleName))
|
||||
return Model.k_LightGrey;
|
||||
else
|
||||
return Color.white;
|
||||
}
|
||||
|
||||
internal bool IsMessageSet(MessageSystem.MessageFlag flag)
|
||||
{
|
||||
return m_AssetMessages.IsSet(flag);
|
||||
}
|
||||
|
||||
internal void SetMessageFlag(MessageSystem.MessageFlag flag, bool on)
|
||||
{
|
||||
m_AssetMessages.SetFlag(flag, on);
|
||||
}
|
||||
|
||||
internal MessageType HighestMessageLevel()
|
||||
{
|
||||
return m_AssetMessages.HighestMessageLevel();
|
||||
}
|
||||
|
||||
internal IEnumerable<MessageSystem.Message> GetMessages()
|
||||
{
|
||||
List<MessageSystem.Message> messages = new List<MessageSystem.Message>();
|
||||
if (IsMessageSet(MessageSystem.MessageFlag.SceneBundleConflict))
|
||||
{
|
||||
string message = displayName + "\n";
|
||||
if (isScene)
|
||||
message += "Is a scene that is in a bundle with non-scene assets. Scene bundles must have only one or more scene assets.";
|
||||
else
|
||||
message += "Is included in a bundle with a scene. Scene bundles must have only one or more scene assets.";
|
||||
messages.Add(new MessageSystem.Message(message, MessageType.Error));
|
||||
}
|
||||
|
||||
if (IsMessageSet(MessageSystem.MessageFlag.DependencySceneConflict))
|
||||
{
|
||||
string message = displayName + "\n";
|
||||
message += MessageSystem.GetMessage(MessageSystem.MessageFlag.DependencySceneConflict).message;
|
||||
messages.Add(new MessageSystem.Message(message, MessageType.Error));
|
||||
}
|
||||
|
||||
if (IsMessageSet(MessageSystem.MessageFlag.AssetsDuplicatedInMultBundles))
|
||||
{
|
||||
IEnumerable<string> bundleNames = Model.CheckDependencyTracker(this);
|
||||
string message = displayName + "\n" + "Is auto-included in multiple bundles:\n";
|
||||
foreach (string bundleName in bundleNames) message += bundleName + ", ";
|
||||
message = message.Substring(0, message.Length - 2); //remove trailing comma.
|
||||
messages.Add(new MessageSystem.Message(message, MessageType.Warning));
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(m_BundleName) && m_Parents.Count > 0)
|
||||
{
|
||||
//TODO - refine the parent list to only include those in the current asset list
|
||||
string message = displayName + "\n" + "Is auto included in bundle(s) due to parent(s): \n";
|
||||
foreach (string parent in m_Parents) message += parent + ", ";
|
||||
message = message.Substring(0, message.Length - 2); //remove trailing comma.
|
||||
messages.Add(new MessageSystem.Message(message, MessageType.Info));
|
||||
}
|
||||
|
||||
if (m_dependencies != null && m_dependencies.Count > 0)
|
||||
{
|
||||
string message = string.Empty;
|
||||
IOrderedEnumerable<AssetInfo> sortedDependencies = m_dependencies.OrderBy(d => d.bundleName);
|
||||
foreach (AssetInfo dependent in sortedDependencies)
|
||||
if (dependent.bundleName != bundleName)
|
||||
message += dependent.bundleName + " : " + dependent.displayName + "\n";
|
||||
if (string.IsNullOrEmpty(message) == false)
|
||||
{
|
||||
message = message.Insert(0, displayName + "\n" + "Is dependent on other bundle's asset(s) or auto included asset(s): \n");
|
||||
message = message.Substring(0, message.Length - 1); //remove trailing line break.
|
||||
messages.Add(new MessageSystem.Message(message, MessageType.Info));
|
||||
}
|
||||
}
|
||||
|
||||
messages.Add(new MessageSystem.Message(displayName + "\n" + "Path: " + fullAssetName, MessageType.Info));
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
internal void AddParent(string name)
|
||||
{
|
||||
m_Parents.Add(name);
|
||||
}
|
||||
|
||||
internal void RemoveParent(string name)
|
||||
{
|
||||
m_Parents.Remove(name);
|
||||
}
|
||||
|
||||
internal string GetSizeString()
|
||||
{
|
||||
if (fileSize == 0)
|
||||
return "--";
|
||||
return EditorUtility.FormatBytes(fileSize);
|
||||
}
|
||||
|
||||
private List<AssetInfo> m_dependencies = null;
|
||||
|
||||
internal List<AssetInfo> GetDependencies()
|
||||
{
|
||||
//TODO - not sure this refreshes enough. need to build tests around that.
|
||||
if (m_dependencies == null)
|
||||
{
|
||||
m_dependencies = new List<AssetInfo>();
|
||||
if (AssetDatabase.IsValidFolder(m_AssetName))
|
||||
{
|
||||
//if we have a folder, its dependencies were already pulled in through alternate means. no need to GatherFoldersAndFiles
|
||||
//GatherFoldersAndFiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string dep in AssetDatabase.GetDependencies(m_AssetName, true))
|
||||
if (dep != m_AssetName)
|
||||
{
|
||||
AssetInfo asset = Model.CreateAsset(dep, this);
|
||||
if (asset != null)
|
||||
m_dependencies.Add(asset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m_dependencies;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f5923f93f7306f4182aecb2dd06ef8d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0909f7af70db1114abcc90714f37acb2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,630 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using AssetBundleBrowser.AssetBundleModel;
|
||||
|
||||
|
||||
namespace AssetBundleBrowser
|
||||
{
|
||||
internal class AssetBundleTree : TreeView
|
||||
{
|
||||
private AssetBundleManageTab m_Controller;
|
||||
private bool m_ContextOnItem = false;
|
||||
private List<UnityEngine.Object> m_EmptyObjectList = new List<UnityEngine.Object>();
|
||||
|
||||
internal AssetBundleTree(TreeViewState state, AssetBundleManageTab ctrl) : base(state)
|
||||
{
|
||||
AssetBundleModel.Model.Rebuild();
|
||||
m_Controller = ctrl;
|
||||
showBorder = true;
|
||||
}
|
||||
|
||||
protected override bool CanMultiSelect(TreeViewItem item)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool CanRename(TreeViewItem item)
|
||||
{
|
||||
return item != null && item.displayName.Length > 0;
|
||||
}
|
||||
|
||||
protected override bool DoesItemMatchSearch(TreeViewItem item, string search)
|
||||
{
|
||||
BundleTreeItem bundleItem = item as AssetBundleModel.BundleTreeItem;
|
||||
return bundleItem.bundle.DoesItemMatchSearch(search);
|
||||
}
|
||||
|
||||
protected override void RowGUI(RowGUIArgs args)
|
||||
{
|
||||
BundleTreeItem bundleItem = args.item as AssetBundleModel.BundleTreeItem;
|
||||
if (args.item.icon == null)
|
||||
extraSpaceBeforeIconAndLabel = 16f;
|
||||
else
|
||||
extraSpaceBeforeIconAndLabel = 0f;
|
||||
|
||||
Color old = GUI.color;
|
||||
if (bundleItem.bundle as AssetBundleModel.BundleVariantFolderInfo != null)
|
||||
GUI.color = AssetBundleModel.Model.k_LightGrey; //new Color(0.3f, 0.5f, 0.85f);
|
||||
base.RowGUI(args);
|
||||
GUI.color = old;
|
||||
|
||||
MessageSystem.Message message = bundleItem.BundleMessage();
|
||||
if (message.severity != MessageType.None)
|
||||
{
|
||||
float size = args.rowRect.height;
|
||||
float right = args.rowRect.xMax;
|
||||
Rect messageRect = new Rect(right - size, args.rowRect.yMin, size, size);
|
||||
GUI.Label(messageRect, new GUIContent(message.icon, message.message));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RenameEnded(RenameEndedArgs args)
|
||||
{
|
||||
base.RenameEnded(args);
|
||||
if (args.newName.Length > 0 && args.newName != args.originalName)
|
||||
{
|
||||
args.newName = args.newName.ToLower();
|
||||
args.acceptedRename = true;
|
||||
|
||||
BundleTreeItem renamedItem = FindItem(args.itemID, rootItem) as AssetBundleModel.BundleTreeItem;
|
||||
args.acceptedRename = AssetBundleModel.Model.HandleBundleRename(renamedItem, args.newName);
|
||||
ReloadAndSelect(renamedItem.bundle.nameHashCode, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.acceptedRename = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override TreeViewItem BuildRoot()
|
||||
{
|
||||
AssetBundleModel.Model.Refresh();
|
||||
BundleTreeItem root = AssetBundleModel.Model.CreateBundleTreeView();
|
||||
return root;
|
||||
}
|
||||
|
||||
protected override void SelectionChanged(IList<int> selectedIds)
|
||||
{
|
||||
List<BundleInfo> selectedBundles = new List<AssetBundleModel.BundleInfo>();
|
||||
if (selectedIds != null)
|
||||
foreach (int id in selectedIds)
|
||||
{
|
||||
BundleTreeItem item = FindItem(id, rootItem) as AssetBundleModel.BundleTreeItem;
|
||||
if (item != null && item.bundle != null)
|
||||
{
|
||||
item.bundle.RefreshAssetList();
|
||||
selectedBundles.Add(item.bundle);
|
||||
}
|
||||
}
|
||||
|
||||
m_Controller.UpdateSelectedBundles(selectedBundles);
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect rect)
|
||||
{
|
||||
base.OnGUI(rect);
|
||||
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && rect.Contains(Event.current.mousePosition)) SetSelection(new int[0], TreeViewSelectionOptions.FireSelectionChanged);
|
||||
}
|
||||
|
||||
|
||||
protected override void ContextClicked()
|
||||
{
|
||||
if (m_ContextOnItem)
|
||||
{
|
||||
m_ContextOnItem = false;
|
||||
return;
|
||||
}
|
||||
|
||||
List<BundleTreeItem> selectedNodes = new List<AssetBundleModel.BundleTreeItem>();
|
||||
GenericMenu menu = new GenericMenu();
|
||||
|
||||
if (!AssetBundleModel.Model.DataSource.IsReadOnly())
|
||||
{
|
||||
menu.AddItem(new GUIContent("Add new bundle"), false, CreateNewBundle, selectedNodes);
|
||||
menu.AddItem(new GUIContent("Add new folder"), false, CreateFolder, selectedNodes);
|
||||
}
|
||||
|
||||
menu.AddItem(new GUIContent("Reload all data"), false, ForceReloadData, selectedNodes);
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
protected override void ContextClickedItem(int id)
|
||||
{
|
||||
if (AssetBundleModel.Model.DataSource.IsReadOnly()) return;
|
||||
|
||||
m_ContextOnItem = true;
|
||||
List<BundleTreeItem> selectedNodes = new List<AssetBundleModel.BundleTreeItem>();
|
||||
foreach (int nodeID in GetSelection()) selectedNodes.Add(FindItem(nodeID, rootItem) as AssetBundleModel.BundleTreeItem);
|
||||
|
||||
GenericMenu menu = new GenericMenu();
|
||||
|
||||
if (selectedNodes.Count == 1)
|
||||
{
|
||||
if (selectedNodes[0].bundle as AssetBundleModel.BundleFolderConcreteInfo != null)
|
||||
{
|
||||
menu.AddItem(new GUIContent("Add Child/New Bundle"), false, CreateNewBundle, selectedNodes);
|
||||
menu.AddItem(new GUIContent("Add Child/New Folder"), false, CreateFolder, selectedNodes);
|
||||
menu.AddItem(new GUIContent("Add Sibling/New Bundle"), false, CreateNewSiblingBundle, selectedNodes);
|
||||
menu.AddItem(new GUIContent("Add Sibling/New Folder"), false, CreateNewSiblingFolder, selectedNodes);
|
||||
}
|
||||
else if (selectedNodes[0].bundle as AssetBundleModel.BundleVariantFolderInfo != null)
|
||||
{
|
||||
menu.AddItem(new GUIContent("Add Child/New Variant"), false, CreateNewVariant, selectedNodes);
|
||||
menu.AddItem(new GUIContent("Add Sibling/New Bundle"), false, CreateNewSiblingBundle, selectedNodes);
|
||||
menu.AddItem(new GUIContent("Add Sibling/New Folder"), false, CreateNewSiblingFolder, selectedNodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
BundleVariantDataInfo variant = selectedNodes[0].bundle as AssetBundleModel.BundleVariantDataInfo;
|
||||
if (variant == null)
|
||||
{
|
||||
menu.AddItem(new GUIContent("Add Sibling/New Bundle"), false, CreateNewSiblingBundle, selectedNodes);
|
||||
menu.AddItem(new GUIContent("Add Sibling/New Folder"), false, CreateNewSiblingFolder, selectedNodes);
|
||||
menu.AddItem(new GUIContent("Convert to variant"), false, ConvertToVariant, selectedNodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
menu.AddItem(new GUIContent("Add Sibling/New Variant"), false, CreateNewSiblingVariant, selectedNodes);
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedNodes[0].bundle.IsMessageSet(MessageSystem.MessageFlag.AssetsDuplicatedInMultBundles))
|
||||
menu.AddItem(new GUIContent("Move duplicates to new bundle"), false, DedupeAllBundles, selectedNodes);
|
||||
menu.AddItem(new GUIContent("Rename"), false, RenameBundle, selectedNodes);
|
||||
menu.AddItem(new GUIContent("Delete " + selectedNodes[0].displayName), false, DeleteBundles, selectedNodes);
|
||||
}
|
||||
else if (selectedNodes.Count > 1)
|
||||
{
|
||||
menu.AddItem(new GUIContent("Move duplicates shared by selected"), false, DedupeOverlappedBundles, selectedNodes);
|
||||
menu.AddItem(new GUIContent("Move duplicates existing in any selected"), false, DedupeAllBundles, selectedNodes);
|
||||
menu.AddItem(new GUIContent("Delete " + selectedNodes.Count + " selected bundles"), false, DeleteBundles, selectedNodes);
|
||||
}
|
||||
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
|
||||
private void ForceReloadData(object context)
|
||||
{
|
||||
AssetBundleModel.Model.ForceReloadData(this);
|
||||
}
|
||||
|
||||
private void CreateNewSiblingFolder(object context)
|
||||
{
|
||||
List<BundleTreeItem> selectedNodes = context as List<AssetBundleModel.BundleTreeItem>;
|
||||
if (selectedNodes != null && selectedNodes.Count > 0)
|
||||
{
|
||||
AssetBundleModel.BundleFolderConcreteInfo folder = null;
|
||||
folder = selectedNodes[0].bundle.parent as AssetBundleModel.BundleFolderConcreteInfo;
|
||||
CreateFolderUnderParent(folder);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("could not add 'sibling' with no bundles selected");
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateFolder(object context)
|
||||
{
|
||||
AssetBundleModel.BundleFolderConcreteInfo folder = null;
|
||||
List<BundleTreeItem> selectedNodes = context as List<AssetBundleModel.BundleTreeItem>;
|
||||
if (selectedNodes != null && selectedNodes.Count > 0) folder = selectedNodes[0].bundle as AssetBundleModel.BundleFolderConcreteInfo;
|
||||
CreateFolderUnderParent(folder);
|
||||
}
|
||||
|
||||
private void CreateFolderUnderParent(AssetBundleModel.BundleFolderConcreteInfo folder)
|
||||
{
|
||||
BundleFolderInfo newBundle = AssetBundleModel.Model.CreateEmptyBundleFolder(folder);
|
||||
ReloadAndSelect(newBundle.nameHashCode, true);
|
||||
}
|
||||
|
||||
private void RenameBundle(object context)
|
||||
{
|
||||
List<BundleTreeItem> selectedNodes = context as List<AssetBundleModel.BundleTreeItem>;
|
||||
if (selectedNodes != null && selectedNodes.Count > 0) BeginRename(FindItem(selectedNodes[0].bundle.nameHashCode, rootItem));
|
||||
}
|
||||
|
||||
private void CreateNewSiblingBundle(object context)
|
||||
{
|
||||
List<BundleTreeItem> selectedNodes = context as List<AssetBundleModel.BundleTreeItem>;
|
||||
if (selectedNodes != null && selectedNodes.Count > 0)
|
||||
{
|
||||
AssetBundleModel.BundleFolderConcreteInfo folder = null;
|
||||
folder = selectedNodes[0].bundle.parent as AssetBundleModel.BundleFolderConcreteInfo;
|
||||
CreateBundleUnderParent(folder);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("could not add 'sibling' with no bundles selected");
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateNewBundle(object context)
|
||||
{
|
||||
AssetBundleModel.BundleFolderConcreteInfo folder = null;
|
||||
List<BundleTreeItem> selectedNodes = context as List<AssetBundleModel.BundleTreeItem>;
|
||||
if (selectedNodes != null && selectedNodes.Count > 0) folder = selectedNodes[0].bundle as AssetBundleModel.BundleFolderConcreteInfo;
|
||||
CreateBundleUnderParent(folder);
|
||||
}
|
||||
|
||||
private void CreateBundleUnderParent(AssetBundleModel.BundleFolderInfo folder)
|
||||
{
|
||||
BundleInfo newBundle = AssetBundleModel.Model.CreateEmptyBundle(folder);
|
||||
ReloadAndSelect(newBundle.nameHashCode, true);
|
||||
}
|
||||
|
||||
|
||||
private void CreateNewSiblingVariant(object context)
|
||||
{
|
||||
List<BundleTreeItem> selectedNodes = context as List<AssetBundleModel.BundleTreeItem>;
|
||||
if (selectedNodes != null && selectedNodes.Count > 0)
|
||||
{
|
||||
AssetBundleModel.BundleVariantFolderInfo folder = null;
|
||||
folder = selectedNodes[0].bundle.parent as AssetBundleModel.BundleVariantFolderInfo;
|
||||
CreateVariantUnderParent(folder);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("could not add 'sibling' with no bundles selected");
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateNewVariant(object context)
|
||||
{
|
||||
AssetBundleModel.BundleVariantFolderInfo folder = null;
|
||||
List<BundleTreeItem> selectedNodes = context as List<AssetBundleModel.BundleTreeItem>;
|
||||
if (selectedNodes != null && selectedNodes.Count == 1)
|
||||
{
|
||||
folder = selectedNodes[0].bundle as AssetBundleModel.BundleVariantFolderInfo;
|
||||
CreateVariantUnderParent(folder);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateVariantUnderParent(AssetBundleModel.BundleVariantFolderInfo folder)
|
||||
{
|
||||
if (folder != null)
|
||||
{
|
||||
BundleInfo newBundle = AssetBundleModel.Model.CreateEmptyVariant(folder);
|
||||
ReloadAndSelect(newBundle.nameHashCode, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void ConvertToVariant(object context)
|
||||
{
|
||||
List<BundleTreeItem> selectedNodes = context as List<AssetBundleModel.BundleTreeItem>;
|
||||
if (selectedNodes.Count == 1)
|
||||
{
|
||||
BundleDataInfo bundle = selectedNodes[0].bundle as AssetBundleModel.BundleDataInfo;
|
||||
BundleInfo newBundle = AssetBundleModel.Model.HandleConvertToVariant(bundle);
|
||||
int hash = 0;
|
||||
if (newBundle != null)
|
||||
hash = newBundle.nameHashCode;
|
||||
ReloadAndSelect(hash, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void DedupeOverlappedBundles(object context)
|
||||
{
|
||||
DedupeBundles(context, true);
|
||||
}
|
||||
|
||||
private void DedupeAllBundles(object context)
|
||||
{
|
||||
DedupeBundles(context, false);
|
||||
}
|
||||
|
||||
private void DedupeBundles(object context, bool onlyOverlappedAssets)
|
||||
{
|
||||
List<BundleTreeItem> selectedNodes = context as List<AssetBundleModel.BundleTreeItem>;
|
||||
BundleInfo newBundle = AssetBundleModel.Model.HandleDedupeBundles(selectedNodes.Select(item => item.bundle), onlyOverlappedAssets);
|
||||
if (newBundle != null)
|
||||
{
|
||||
List<int> selection = new List<int>();
|
||||
selection.Add(newBundle.nameHashCode);
|
||||
ReloadAndSelect(selection);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (onlyOverlappedAssets)
|
||||
Debug.LogWarning("There were no duplicated assets that existed across all selected bundles.");
|
||||
else
|
||||
Debug.LogWarning("No duplicate assets found after refreshing bundle contents.");
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteBundles(object b)
|
||||
{
|
||||
List<BundleTreeItem> selectedNodes = b as List<AssetBundleModel.BundleTreeItem>;
|
||||
AssetBundleModel.Model.HandleBundleDelete(selectedNodes.Select(item => item.bundle));
|
||||
ReloadAndSelect(new List<int>());
|
||||
}
|
||||
|
||||
protected override void KeyEvent()
|
||||
{
|
||||
if (Event.current.keyCode == KeyCode.Delete && GetSelection().Count > 0)
|
||||
{
|
||||
List<BundleTreeItem> selectedNodes = new List<AssetBundleModel.BundleTreeItem>();
|
||||
foreach (int nodeID in GetSelection()) selectedNodes.Add(FindItem(nodeID, rootItem) as AssetBundleModel.BundleTreeItem);
|
||||
DeleteBundles(selectedNodes);
|
||||
}
|
||||
}
|
||||
|
||||
private class DragAndDropData
|
||||
{
|
||||
internal bool hasBundleFolder = false;
|
||||
internal bool hasScene = false;
|
||||
internal bool hasNonScene = false;
|
||||
internal bool hasVariantChild = false;
|
||||
internal List<AssetBundleModel.BundleInfo> draggedNodes;
|
||||
internal AssetBundleModel.BundleTreeItem targetNode;
|
||||
internal DragAndDropArgs args;
|
||||
internal string[] paths;
|
||||
|
||||
internal DragAndDropData(DragAndDropArgs a)
|
||||
{
|
||||
args = a;
|
||||
draggedNodes = DragAndDrop.GetGenericData("AssetBundleModel.BundleInfo") as List<AssetBundleModel.BundleInfo>;
|
||||
targetNode = args.parentItem as AssetBundleModel.BundleTreeItem;
|
||||
paths = DragAndDrop.paths;
|
||||
|
||||
if (draggedNodes != null)
|
||||
foreach (BundleInfo bundle in draggedNodes)
|
||||
if (bundle as AssetBundleModel.BundleFolderInfo != null)
|
||||
{
|
||||
hasBundleFolder = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
BundleDataInfo dataBundle = bundle as AssetBundleModel.BundleDataInfo;
|
||||
if (dataBundle != null)
|
||||
{
|
||||
if (dataBundle.isSceneBundle)
|
||||
hasScene = true;
|
||||
else
|
||||
hasNonScene = true;
|
||||
|
||||
if (dataBundle as AssetBundleModel.BundleVariantDataInfo != null)
|
||||
hasVariantChild = true;
|
||||
}
|
||||
}
|
||||
else if (DragAndDrop.paths != null)
|
||||
foreach (string assetPath in DragAndDrop.paths)
|
||||
if (AssetDatabase.GetMainAssetTypeAtPath(assetPath) == typeof(SceneAsset))
|
||||
hasScene = true;
|
||||
else
|
||||
hasNonScene = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override DragAndDropVisualMode HandleDragAndDrop(DragAndDropArgs args)
|
||||
{
|
||||
DragAndDropVisualMode visualMode = DragAndDropVisualMode.None;
|
||||
DragAndDropData data = new DragAndDropData(args);
|
||||
|
||||
if (AssetBundleModel.Model.DataSource.IsReadOnly()) return DragAndDropVisualMode.Rejected;
|
||||
|
||||
if (data.hasScene && data.hasNonScene ||
|
||||
data.hasVariantChild)
|
||||
return DragAndDropVisualMode.Rejected;
|
||||
|
||||
switch (args.dragAndDropPosition)
|
||||
{
|
||||
case DragAndDropPosition.UponItem:
|
||||
visualMode = HandleDragDropUpon(data);
|
||||
break;
|
||||
case DragAndDropPosition.BetweenItems:
|
||||
visualMode = HandleDragDropBetween(data);
|
||||
break;
|
||||
case DragAndDropPosition.OutsideItems:
|
||||
if (data.draggedNodes != null)
|
||||
{
|
||||
visualMode = DragAndDropVisualMode.Copy;
|
||||
if (data.args.performDrop)
|
||||
{
|
||||
AssetBundleModel.Model.HandleBundleReparent(data.draggedNodes, null);
|
||||
Reload();
|
||||
}
|
||||
}
|
||||
else if (data.paths != null)
|
||||
{
|
||||
visualMode = DragAndDropVisualMode.Copy;
|
||||
if (data.args.performDrop) DragPathsToNewSpace(data.paths, null);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return visualMode;
|
||||
}
|
||||
|
||||
private DragAndDropVisualMode HandleDragDropUpon(DragAndDropData data)
|
||||
{
|
||||
DragAndDropVisualMode visualMode = DragAndDropVisualMode.Copy; //Move;
|
||||
BundleDataInfo targetDataBundle = data.targetNode.bundle as AssetBundleModel.BundleDataInfo;
|
||||
if (targetDataBundle != null)
|
||||
{
|
||||
if (targetDataBundle.isSceneBundle)
|
||||
{
|
||||
if (data.hasNonScene)
|
||||
return DragAndDropVisualMode.Rejected;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data.hasBundleFolder)
|
||||
return DragAndDropVisualMode.Rejected;
|
||||
else if (data.hasScene && !targetDataBundle.IsEmpty()) return DragAndDropVisualMode.Rejected;
|
||||
}
|
||||
|
||||
|
||||
if (data.args.performDrop)
|
||||
{
|
||||
if (data.draggedNodes != null)
|
||||
{
|
||||
AssetBundleModel.Model.HandleBundleMerge(data.draggedNodes, targetDataBundle);
|
||||
ReloadAndSelect(targetDataBundle.nameHashCode, false);
|
||||
}
|
||||
else if (data.paths != null)
|
||||
{
|
||||
AssetBundleModel.Model.MoveAssetToBundle(data.paths, targetDataBundle.m_Name.bundleName, targetDataBundle.m_Name.variant);
|
||||
AssetBundleModel.Model.ExecuteAssetMove();
|
||||
ReloadAndSelect(targetDataBundle.nameHashCode, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BundleFolderInfo folder = data.targetNode.bundle as AssetBundleModel.BundleFolderInfo;
|
||||
if (folder != null)
|
||||
{
|
||||
if (data.args.performDrop)
|
||||
{
|
||||
if (data.draggedNodes != null)
|
||||
{
|
||||
AssetBundleModel.Model.HandleBundleReparent(data.draggedNodes, folder);
|
||||
Reload();
|
||||
}
|
||||
else if (data.paths != null)
|
||||
{
|
||||
DragPathsToNewSpace(data.paths, folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
visualMode = DragAndDropVisualMode.Rejected; //must be a variantfolder
|
||||
}
|
||||
}
|
||||
|
||||
return visualMode;
|
||||
}
|
||||
|
||||
private DragAndDropVisualMode HandleDragDropBetween(DragAndDropData data)
|
||||
{
|
||||
DragAndDropVisualMode visualMode = DragAndDropVisualMode.Copy; //Move;
|
||||
|
||||
BundleTreeItem parent = data.args.parentItem as AssetBundleModel.BundleTreeItem;
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
BundleVariantFolderInfo variantFolder = parent.bundle as AssetBundleModel.BundleVariantFolderInfo;
|
||||
if (variantFolder != null)
|
||||
return DragAndDropVisualMode.Rejected;
|
||||
|
||||
if (data.args.performDrop)
|
||||
{
|
||||
BundleFolderConcreteInfo folder = parent.bundle as AssetBundleModel.BundleFolderConcreteInfo;
|
||||
if (folder != null)
|
||||
{
|
||||
if (data.draggedNodes != null)
|
||||
{
|
||||
AssetBundleModel.Model.HandleBundleReparent(data.draggedNodes, folder);
|
||||
Reload();
|
||||
}
|
||||
else if (data.paths != null)
|
||||
{
|
||||
DragPathsToNewSpace(data.paths, folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return visualMode;
|
||||
}
|
||||
|
||||
private string[] dragToNewSpacePaths = null;
|
||||
private AssetBundleModel.BundleFolderInfo dragToNewSpaceRoot = null;
|
||||
|
||||
private void DragPathsAsOneBundle()
|
||||
{
|
||||
BundleInfo newBundle = AssetBundleModel.Model.CreateEmptyBundle(dragToNewSpaceRoot);
|
||||
AssetBundleModel.Model.MoveAssetToBundle(dragToNewSpacePaths, newBundle.m_Name.bundleName, newBundle.m_Name.variant);
|
||||
AssetBundleModel.Model.ExecuteAssetMove();
|
||||
ReloadAndSelect(newBundle.nameHashCode, true);
|
||||
}
|
||||
|
||||
private void DragPathsAsManyBundles()
|
||||
{
|
||||
List<int> hashCodes = new List<int>();
|
||||
foreach (string assetPath in dragToNewSpacePaths)
|
||||
{
|
||||
BundleInfo newBundle = AssetBundleModel.Model.CreateEmptyBundle(dragToNewSpaceRoot, System.IO.Path.GetFileNameWithoutExtension(assetPath).ToLower());
|
||||
AssetBundleModel.Model.MoveAssetToBundle(assetPath, newBundle.m_Name.bundleName, newBundle.m_Name.variant);
|
||||
hashCodes.Add(newBundle.nameHashCode);
|
||||
}
|
||||
|
||||
AssetBundleModel.Model.ExecuteAssetMove();
|
||||
ReloadAndSelect(hashCodes);
|
||||
}
|
||||
|
||||
private void DragPathsToNewSpace(string[] paths, AssetBundleModel.BundleFolderInfo root)
|
||||
{
|
||||
dragToNewSpacePaths = paths;
|
||||
dragToNewSpaceRoot = root;
|
||||
if (paths.Length > 1)
|
||||
{
|
||||
GenericMenu menu = new GenericMenu();
|
||||
menu.AddItem(new GUIContent("Create 1 Bundle"), false, DragPathsAsOneBundle);
|
||||
string message = "Create ";
|
||||
message += paths.Length;
|
||||
message += " Bundles";
|
||||
menu.AddItem(new GUIContent(message), false, DragPathsAsManyBundles);
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
else
|
||||
{
|
||||
DragPathsAsManyBundles();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SetupDragAndDrop(SetupDragAndDropArgs args)
|
||||
{
|
||||
if (args.draggedItemIDs == null)
|
||||
return;
|
||||
|
||||
DragAndDrop.PrepareStartDrag();
|
||||
|
||||
List<BundleInfo> selectedBundles = new List<AssetBundleModel.BundleInfo>();
|
||||
foreach (int id in args.draggedItemIDs)
|
||||
{
|
||||
BundleTreeItem item = FindItem(id, rootItem) as AssetBundleModel.BundleTreeItem;
|
||||
selectedBundles.Add(item.bundle);
|
||||
}
|
||||
|
||||
DragAndDrop.paths = null;
|
||||
DragAndDrop.objectReferences = m_EmptyObjectList.ToArray();
|
||||
DragAndDrop.SetGenericData("AssetBundleModel.BundleInfo", selectedBundles);
|
||||
DragAndDrop.visualMode = DragAndDropVisualMode.Copy; //Move;
|
||||
DragAndDrop.StartDrag("AssetBundleTree");
|
||||
}
|
||||
|
||||
protected override bool CanStartDrag(CanStartDragArgs args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void Refresh()
|
||||
{
|
||||
IList<int> selection = GetSelection();
|
||||
Reload();
|
||||
SelectionChanged(selection);
|
||||
}
|
||||
|
||||
private void ReloadAndSelect(int hashCode, bool rename)
|
||||
{
|
||||
List<int> selection = new List<int>();
|
||||
selection.Add(hashCode);
|
||||
ReloadAndSelect(selection);
|
||||
if (rename) BeginRename(FindItem(hashCode, rootItem), 0.25f);
|
||||
}
|
||||
|
||||
private void ReloadAndSelect(IList<int> hashCodes)
|
||||
{
|
||||
Reload();
|
||||
SetSelection(hashCodes, TreeViewSelectionOptions.RevealAndFrame);
|
||||
SelectionChanged(hashCodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54744ac396176e74aaed3c5053b12150
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,447 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using System.Linq;
|
||||
using AssetBundleBrowser.AssetBundleModel;
|
||||
|
||||
//using System;
|
||||
|
||||
|
||||
namespace AssetBundleBrowser
|
||||
{
|
||||
internal class AssetListTree : TreeView
|
||||
{
|
||||
private List<AssetBundleModel.BundleInfo> m_SourceBundles = new List<AssetBundleModel.BundleInfo>();
|
||||
private AssetBundleManageTab m_Controller;
|
||||
private List<Object> m_EmptyObjectList = new List<Object>();
|
||||
|
||||
internal static MultiColumnHeaderState CreateDefaultMultiColumnHeaderState()
|
||||
{
|
||||
return new MultiColumnHeaderState(GetColumns());
|
||||
}
|
||||
|
||||
private static MultiColumnHeaderState.Column[] GetColumns()
|
||||
{
|
||||
MultiColumnHeaderState.Column[] retVal = new MultiColumnHeaderState.Column[]
|
||||
{
|
||||
new MultiColumnHeaderState.Column(),
|
||||
new MultiColumnHeaderState.Column(),
|
||||
new MultiColumnHeaderState.Column(),
|
||||
new MultiColumnHeaderState.Column()
|
||||
};
|
||||
retVal[0].headerContent = new GUIContent("Asset", "Short name of asset. For full name select asset and see message below");
|
||||
retVal[0].minWidth = 50;
|
||||
retVal[0].width = 100;
|
||||
retVal[0].maxWidth = 300;
|
||||
retVal[0].headerTextAlignment = TextAlignment.Left;
|
||||
retVal[0].canSort = true;
|
||||
retVal[0].autoResize = true;
|
||||
|
||||
retVal[1].headerContent = new GUIContent("Bundle", "Bundle name. 'auto' means asset was pulled in due to dependency");
|
||||
retVal[1].minWidth = 50;
|
||||
retVal[1].width = 100;
|
||||
retVal[1].maxWidth = 300;
|
||||
retVal[1].headerTextAlignment = TextAlignment.Left;
|
||||
retVal[1].canSort = true;
|
||||
retVal[1].autoResize = true;
|
||||
|
||||
retVal[2].headerContent = new GUIContent("Size", "Size on disk");
|
||||
retVal[2].minWidth = 30;
|
||||
retVal[2].width = 75;
|
||||
retVal[2].maxWidth = 100;
|
||||
retVal[2].headerTextAlignment = TextAlignment.Left;
|
||||
retVal[2].canSort = true;
|
||||
retVal[2].autoResize = true;
|
||||
|
||||
retVal[3].headerContent = new GUIContent("!", "Errors, Warnings, or Info");
|
||||
retVal[3].minWidth = 16;
|
||||
retVal[3].width = 16;
|
||||
retVal[3].maxWidth = 16;
|
||||
retVal[3].headerTextAlignment = TextAlignment.Left;
|
||||
retVal[3].canSort = true;
|
||||
retVal[3].autoResize = false;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private enum MyColumns
|
||||
{
|
||||
Asset,
|
||||
Bundle,
|
||||
Size,
|
||||
Message
|
||||
}
|
||||
|
||||
internal enum SortOption
|
||||
{
|
||||
Asset,
|
||||
Bundle,
|
||||
Size,
|
||||
Message
|
||||
}
|
||||
|
||||
private SortOption[] m_SortOptions =
|
||||
{
|
||||
SortOption.Asset,
|
||||
SortOption.Bundle,
|
||||
SortOption.Size,
|
||||
SortOption.Message
|
||||
};
|
||||
|
||||
internal AssetListTree(TreeViewState state, MultiColumnHeaderState mchs, AssetBundleManageTab ctrl) : base(state, new MultiColumnHeader(mchs))
|
||||
{
|
||||
m_Controller = ctrl;
|
||||
showBorder = true;
|
||||
showAlternatingRowBackgrounds = true;
|
||||
multiColumnHeader.sortingChanged += OnSortingChanged;
|
||||
}
|
||||
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
bool dirty = false;
|
||||
foreach (BundleInfo bundle in m_SourceBundles) dirty |= bundle.dirty;
|
||||
if (dirty)
|
||||
Reload();
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect rect)
|
||||
{
|
||||
base.OnGUI(rect);
|
||||
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && rect.Contains(Event.current.mousePosition)) SetSelection(new int[0], TreeViewSelectionOptions.FireSelectionChanged);
|
||||
}
|
||||
|
||||
|
||||
protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
|
||||
{
|
||||
IList<TreeViewItem> rows = base.BuildRows(root);
|
||||
SortIfNeeded(root, rows);
|
||||
return rows;
|
||||
}
|
||||
|
||||
internal void SetSelectedBundles(IEnumerable<AssetBundleModel.BundleInfo> bundles)
|
||||
{
|
||||
m_Controller.SetSelectedItems(null);
|
||||
m_SourceBundles = bundles.ToList();
|
||||
SetSelection(new List<int>());
|
||||
Reload();
|
||||
}
|
||||
|
||||
protected override TreeViewItem BuildRoot()
|
||||
{
|
||||
AssetTreeItem root = AssetBundleModel.Model.CreateAssetListTreeView(m_SourceBundles);
|
||||
return root;
|
||||
}
|
||||
|
||||
protected override void RowGUI(RowGUIArgs args)
|
||||
{
|
||||
for (int i = 0; i < args.GetNumVisibleColumns(); ++i)
|
||||
CellGUI(args.GetCellRect(i), args.item as AssetBundleModel.AssetTreeItem, args.GetColumn(i), ref args);
|
||||
}
|
||||
|
||||
private void CellGUI(Rect cellRect, AssetBundleModel.AssetTreeItem item, int column, ref RowGUIArgs args)
|
||||
{
|
||||
Color oldColor = GUI.color;
|
||||
CenterRectUsingSingleLineHeight(ref cellRect);
|
||||
if (column != 3)
|
||||
GUI.color = item.itemColor;
|
||||
|
||||
switch (column)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
Rect iconRect = new Rect(cellRect.x + 1, cellRect.y + 1, cellRect.height - 2, cellRect.height - 2);
|
||||
if (item.icon != null)
|
||||
GUI.DrawTexture(iconRect, item.icon, ScaleMode.ScaleToFit);
|
||||
DefaultGUI.Label(
|
||||
new Rect(cellRect.x + iconRect.xMax + 1, cellRect.y, cellRect.width - iconRect.width, cellRect.height),
|
||||
item.displayName,
|
||||
args.selected,
|
||||
args.focused);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
DefaultGUI.Label(cellRect, item.asset.bundleName, args.selected, args.focused);
|
||||
break;
|
||||
case 2:
|
||||
DefaultGUI.Label(cellRect, item.asset.GetSizeString(), args.selected, args.focused);
|
||||
break;
|
||||
case 3:
|
||||
Texture2D icon = item.MessageIcon();
|
||||
if (icon != null)
|
||||
{
|
||||
Rect iconRect = new Rect(cellRect.x, cellRect.y, cellRect.height, cellRect.height);
|
||||
GUI.DrawTexture(iconRect, icon, ScaleMode.ScaleToFit);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
GUI.color = oldColor;
|
||||
}
|
||||
|
||||
protected override void DoubleClickedItem(int id)
|
||||
{
|
||||
AssetTreeItem assetItem = FindItem(id, rootItem) as AssetBundleModel.AssetTreeItem;
|
||||
if (assetItem != null)
|
||||
{
|
||||
Object o = AssetDatabase.LoadAssetAtPath<Object>(assetItem.asset.fullAssetName);
|
||||
EditorGUIUtility.PingObject(o);
|
||||
Selection.activeObject = o;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSelection(List<string> paths)
|
||||
{
|
||||
List<int> selected = new List<int>();
|
||||
AddIfInPaths(paths, selected, rootItem);
|
||||
SetSelection(selected);
|
||||
}
|
||||
|
||||
private void AddIfInPaths(List<string> paths, List<int> selected, TreeViewItem me)
|
||||
{
|
||||
AssetTreeItem assetItem = me as AssetBundleModel.AssetTreeItem;
|
||||
if (assetItem != null && assetItem.asset != null)
|
||||
if (paths.Contains(assetItem.asset.fullAssetName))
|
||||
if (selected.Contains(me.id) == false)
|
||||
selected.Add(me.id);
|
||||
|
||||
if (me.hasChildren)
|
||||
foreach (TreeViewItem item in me.children)
|
||||
AddIfInPaths(paths, selected, item);
|
||||
}
|
||||
|
||||
protected override void SelectionChanged(IList<int> selectedIds)
|
||||
{
|
||||
if (selectedIds == null)
|
||||
return;
|
||||
|
||||
List<Object> selectedObjects = new List<Object>();
|
||||
List<AssetInfo> selectedAssets = new List<AssetBundleModel.AssetInfo>();
|
||||
foreach (int id in selectedIds)
|
||||
{
|
||||
AssetTreeItem assetItem = FindItem(id, rootItem) as AssetBundleModel.AssetTreeItem;
|
||||
if (assetItem != null)
|
||||
{
|
||||
Object o = AssetDatabase.LoadAssetAtPath<Object>(assetItem.asset.fullAssetName);
|
||||
selectedObjects.Add(o);
|
||||
Selection.activeObject = o;
|
||||
selectedAssets.Add(assetItem.asset);
|
||||
}
|
||||
}
|
||||
|
||||
m_Controller.SetSelectedItems(selectedAssets);
|
||||
Selection.objects = selectedObjects.ToArray();
|
||||
}
|
||||
|
||||
protected override bool CanBeParent(TreeViewItem item)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool CanStartDrag(CanStartDragArgs args)
|
||||
{
|
||||
args.draggedItemIDs = GetSelection();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void SetupDragAndDrop(SetupDragAndDropArgs args)
|
||||
{
|
||||
DragAndDrop.PrepareStartDrag();
|
||||
DragAndDrop.objectReferences = m_EmptyObjectList.ToArray();
|
||||
List<AssetTreeItem> items =
|
||||
new List<AssetBundleModel.AssetTreeItem>(args.draggedItemIDs.Select(id => FindItem(id, rootItem) as AssetBundleModel.AssetTreeItem));
|
||||
DragAndDrop.paths = items.Select(a => a.asset.fullAssetName).ToArray();
|
||||
DragAndDrop.SetGenericData("AssetListTreeSource", this);
|
||||
DragAndDrop.StartDrag("AssetListTree");
|
||||
}
|
||||
|
||||
protected override DragAndDropVisualMode HandleDragAndDrop(DragAndDropArgs args)
|
||||
{
|
||||
if (IsValidDragDrop())
|
||||
{
|
||||
if (args.performDrop)
|
||||
{
|
||||
AssetBundleModel.Model.MoveAssetToBundle(DragAndDrop.paths, m_SourceBundles[0].m_Name.bundleName, m_SourceBundles[0].m_Name.variant);
|
||||
AssetBundleModel.Model.ExecuteAssetMove();
|
||||
foreach (BundleInfo bundle in m_SourceBundles) bundle.RefreshAssetList();
|
||||
m_Controller.UpdateSelectedBundles(m_SourceBundles);
|
||||
}
|
||||
|
||||
return DragAndDropVisualMode.Copy; //Move;
|
||||
}
|
||||
|
||||
return DragAndDropVisualMode.Rejected;
|
||||
}
|
||||
|
||||
protected bool IsValidDragDrop()
|
||||
{
|
||||
//can't do drag & drop if data source is read only
|
||||
if (AssetBundleModel.Model.DataSource.IsReadOnly())
|
||||
return false;
|
||||
|
||||
//can't drag onto none or >1 bundles
|
||||
if (m_SourceBundles.Count == 0 || m_SourceBundles.Count > 1)
|
||||
return false;
|
||||
|
||||
//can't drag nothing
|
||||
if (DragAndDrop.paths == null || DragAndDrop.paths.Length == 0)
|
||||
return false;
|
||||
|
||||
//can't drag into a folder
|
||||
BundleFolderInfo folder = m_SourceBundles[0] as AssetBundleModel.BundleFolderInfo;
|
||||
if (folder != null)
|
||||
return false;
|
||||
|
||||
BundleDataInfo data = m_SourceBundles[0] as AssetBundleModel.BundleDataInfo;
|
||||
if (data == null)
|
||||
return false; // this should never happen.
|
||||
|
||||
AssetListTree thing = DragAndDrop.GetGenericData("AssetListTreeSource") as AssetListTree;
|
||||
if (thing != null)
|
||||
return false;
|
||||
|
||||
if (data.IsEmpty())
|
||||
return true;
|
||||
|
||||
|
||||
if (data.isSceneBundle)
|
||||
{
|
||||
foreach (string assetPath in DragAndDrop.paths)
|
||||
if (AssetDatabase.GetMainAssetTypeAtPath(assetPath) != typeof(SceneAsset) &&
|
||||
!AssetDatabase.IsValidFolder(assetPath))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string assetPath in DragAndDrop.paths)
|
||||
if (AssetDatabase.GetMainAssetTypeAtPath(assetPath) == typeof(SceneAsset))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void ContextClickedItem(int id)
|
||||
{
|
||||
if (AssetBundleModel.Model.DataSource.IsReadOnly()) return;
|
||||
|
||||
List<AssetTreeItem> selectedNodes = new List<AssetBundleModel.AssetTreeItem>();
|
||||
foreach (int nodeID in GetSelection()) selectedNodes.Add(FindItem(nodeID, rootItem) as AssetBundleModel.AssetTreeItem);
|
||||
|
||||
if (selectedNodes.Count > 0)
|
||||
{
|
||||
GenericMenu menu = new GenericMenu();
|
||||
menu.AddItem(new GUIContent("Remove asset(s) from bundle."), false, RemoveAssets, selectedNodes);
|
||||
menu.ShowAsContext();
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveAssets(object obj)
|
||||
{
|
||||
List<AssetTreeItem> selectedNodes = obj as List<AssetBundleModel.AssetTreeItem>;
|
||||
List<AssetInfo> assets = new List<AssetBundleModel.AssetInfo>();
|
||||
//var bundles = new List<AssetBundleModel.BundleInfo>();
|
||||
foreach (AssetTreeItem node in selectedNodes)
|
||||
if (!string.IsNullOrEmpty(node.asset.bundleName))
|
||||
assets.Add(node.asset);
|
||||
AssetBundleModel.Model.MoveAssetToBundle(assets, string.Empty, string.Empty);
|
||||
AssetBundleModel.Model.ExecuteAssetMove();
|
||||
foreach (BundleInfo bundle in m_SourceBundles) bundle.RefreshAssetList();
|
||||
m_Controller.UpdateSelectedBundles(m_SourceBundles);
|
||||
//ReloadAndSelect(new List<int>());
|
||||
}
|
||||
|
||||
protected override void KeyEvent()
|
||||
{
|
||||
if (m_SourceBundles.Count > 0 && Event.current.keyCode == KeyCode.Delete && GetSelection().Count > 0)
|
||||
{
|
||||
List<AssetTreeItem> selectedNodes = new List<AssetBundleModel.AssetTreeItem>();
|
||||
foreach (int nodeID in GetSelection()) selectedNodes.Add(FindItem(nodeID, rootItem) as AssetBundleModel.AssetTreeItem);
|
||||
|
||||
RemoveAssets(selectedNodes);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSortingChanged(MultiColumnHeader multiColumnHeader)
|
||||
{
|
||||
SortIfNeeded(rootItem, GetRows());
|
||||
}
|
||||
|
||||
private void SortIfNeeded(TreeViewItem root, IList<TreeViewItem> rows)
|
||||
{
|
||||
if (rows.Count <= 1)
|
||||
return;
|
||||
|
||||
if (multiColumnHeader.sortedColumnIndex == -1)
|
||||
return;
|
||||
|
||||
SortByColumn();
|
||||
|
||||
rows.Clear();
|
||||
for (int i = 0; i < root.children.Count; i++)
|
||||
rows.Add(root.children[i]);
|
||||
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void SortByColumn()
|
||||
{
|
||||
int[] sortedColumns = multiColumnHeader.state.sortedColumns;
|
||||
|
||||
if (sortedColumns.Length == 0)
|
||||
return;
|
||||
|
||||
List<AssetTreeItem> assetList = new List<AssetBundleModel.AssetTreeItem>();
|
||||
foreach (TreeViewItem item in rootItem.children) assetList.Add(item as AssetBundleModel.AssetTreeItem);
|
||||
IOrderedEnumerable<AssetTreeItem> orderedItems = InitialOrder(assetList, sortedColumns);
|
||||
|
||||
rootItem.children = orderedItems.Cast<TreeViewItem>().ToList();
|
||||
}
|
||||
|
||||
private IOrderedEnumerable<AssetBundleModel.AssetTreeItem> InitialOrder(IEnumerable<AssetBundleModel.AssetTreeItem> myTypes, int[] columnList)
|
||||
{
|
||||
SortOption sortOption = m_SortOptions[columnList[0]];
|
||||
bool ascending = multiColumnHeader.IsSortedAscending(columnList[0]);
|
||||
switch (sortOption)
|
||||
{
|
||||
case SortOption.Asset:
|
||||
return myTypes.Order(l => l.displayName, ascending);
|
||||
case SortOption.Size:
|
||||
return myTypes.Order(l => l.asset.fileSize, ascending);
|
||||
case SortOption.Message:
|
||||
return myTypes.Order(l => l.HighestMessageLevel(), ascending);
|
||||
case SortOption.Bundle:
|
||||
default:
|
||||
return myTypes.Order(l => l.asset.bundleName, ascending);
|
||||
}
|
||||
}
|
||||
|
||||
private void ReloadAndSelect(IList<int> hashCodes)
|
||||
{
|
||||
Reload();
|
||||
SetSelection(hashCodes);
|
||||
SelectionChanged(hashCodes);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class MyExtensionMethods
|
||||
{
|
||||
internal static IOrderedEnumerable<T> Order<T, TKey>(this IEnumerable<T> source, System.Func<T, TKey> selector, bool ascending)
|
||||
{
|
||||
if (ascending)
|
||||
return source.OrderBy(selector);
|
||||
else
|
||||
return source.OrderByDescending(selector);
|
||||
}
|
||||
|
||||
internal static IOrderedEnumerable<T> ThenBy<T, TKey>(this IOrderedEnumerable<T> source, System.Func<T, TKey> selector, bool ascending)
|
||||
{
|
||||
if (ascending)
|
||||
return source.ThenBy(selector);
|
||||
else
|
||||
return source.ThenByDescending(selector);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a3caba452c4f3a543b831e94871aebc6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,300 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using AssetBundleBrowser.AssetBundleModel;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
|
||||
namespace AssetBundleBrowser
|
||||
{
|
||||
internal class BundleDetailItem : TreeViewItem
|
||||
{
|
||||
internal BundleDetailItem(int id, int depth, string displayName, MessageType type) : base(id, depth, displayName)
|
||||
{
|
||||
MessageLevel = type;
|
||||
}
|
||||
|
||||
internal MessageType MessageLevel { get; set; }
|
||||
}
|
||||
|
||||
internal class TogglePathTreeViewItem : TreeViewItem
|
||||
{
|
||||
private static bool m_DisplayAlt = false;
|
||||
|
||||
private string m_DisplayNamePrefix;
|
||||
private string m_Path;
|
||||
|
||||
public string Path
|
||||
{
|
||||
get { return m_Path; }
|
||||
}
|
||||
|
||||
public string DisplayNamePrefix
|
||||
{
|
||||
get { return m_DisplayNamePrefix; }
|
||||
}
|
||||
|
||||
public TogglePathTreeViewItem(int id, int depth, string displayName, string path)
|
||||
{
|
||||
base.depth = depth;
|
||||
base.id = id;
|
||||
base.displayName = displayName;
|
||||
m_Path = path;
|
||||
m_DisplayNamePrefix = "";
|
||||
}
|
||||
|
||||
public TogglePathTreeViewItem(int id, int depth, string displayNamePrefix, string displayName, string path)
|
||||
{
|
||||
base.depth = depth;
|
||||
base.id = id;
|
||||
base.displayName = displayName;
|
||||
m_Path = path;
|
||||
m_DisplayNamePrefix = displayNamePrefix;
|
||||
}
|
||||
|
||||
public override string displayName
|
||||
{
|
||||
get
|
||||
{
|
||||
// TODO this is a bit unresponsive here in large projects, see if can be better elsewhere
|
||||
Event e = Event.current;
|
||||
if (e.alt && e.type == EventType.MouseDown)
|
||||
m_DisplayAlt = !m_DisplayAlt;
|
||||
|
||||
return m_DisplayNamePrefix + (m_DisplayAlt ? m_Path : base.displayName);
|
||||
}
|
||||
set { base.displayName = value; }
|
||||
}
|
||||
}
|
||||
|
||||
internal class BundleDetailList : TreeView
|
||||
{
|
||||
private HashSet<BundleDataInfo> m_Selecteditems;
|
||||
private Rect m_TotalRect;
|
||||
|
||||
private const float k_DoubleIndent = 32f;
|
||||
private const string k_SizeHeader = "Size: ";
|
||||
private const string k_DependencyHeader = "Dependent On:";
|
||||
private const string k_DependencyEmpty = k_DependencyHeader + " - None";
|
||||
private const string k_MessageHeader = "Messages:";
|
||||
private const string k_MessageEmpty = k_MessageHeader + " - None";
|
||||
private const string k_ReferencedPrefix = "- ";
|
||||
|
||||
|
||||
internal BundleDetailList(TreeViewState state) : base(state)
|
||||
{
|
||||
m_Selecteditems = new HashSet<BundleDataInfo>();
|
||||
showBorder = true;
|
||||
}
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
bool dirty = false;
|
||||
foreach (BundleDataInfo bundle in m_Selecteditems) dirty |= bundle.dirty;
|
||||
if (dirty)
|
||||
{
|
||||
Reload();
|
||||
ExpandAll(2);
|
||||
}
|
||||
}
|
||||
|
||||
protected override TreeViewItem BuildRoot()
|
||||
{
|
||||
TreeViewItem root = new TreeViewItem(-1, -1);
|
||||
root.children = new List<TreeViewItem>();
|
||||
if (m_Selecteditems != null)
|
||||
foreach (BundleDataInfo bundle in m_Selecteditems)
|
||||
root.AddChild(AppendBundleToTree(bundle));
|
||||
return root;
|
||||
}
|
||||
|
||||
protected override void RowGUI(RowGUIArgs args)
|
||||
{
|
||||
if (args.item as BundleDetailItem != null)
|
||||
{
|
||||
EditorGUI.HelpBox(
|
||||
new Rect(args.rowRect.x + k_DoubleIndent, args.rowRect.y, args.rowRect.width - k_DoubleIndent, args.rowRect.height),
|
||||
args.item.displayName,
|
||||
(args.item as BundleDetailItem).MessageLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
Color old = GUI.color;
|
||||
if (args.item.depth == 1 &&
|
||||
(args.item.displayName == k_MessageEmpty || args.item.displayName == k_DependencyEmpty))
|
||||
GUI.color = Model.k_LightGrey;
|
||||
base.RowGUI(args);
|
||||
GUI.color = old;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect rect)
|
||||
{
|
||||
m_TotalRect = rect;
|
||||
base.OnGUI(rect);
|
||||
}
|
||||
|
||||
protected override float GetCustomRowHeight(int row, TreeViewItem item)
|
||||
{
|
||||
if (item as BundleDetailItem != null)
|
||||
{
|
||||
float height = DefaultStyles.backgroundEven.CalcHeight(new GUIContent(item.displayName), m_TotalRect.width);
|
||||
return height + 3f;
|
||||
}
|
||||
|
||||
return base.GetCustomRowHeight(row, item);
|
||||
}
|
||||
|
||||
|
||||
protected override void SelectionChanged(IList<int> selectedIds)
|
||||
{
|
||||
base.SelectionChanged(selectedIds);
|
||||
List<string> pathList = new List<string>();
|
||||
|
||||
for (int i = 0; i < selectedIds.Count; ++i)
|
||||
{
|
||||
TreeViewItem item = FindItem(selectedIds[i], rootItem);
|
||||
if (item != null) AddDependentAssetsRecursive(item, pathList);
|
||||
}
|
||||
|
||||
AssetBundleBrowserMain.instance.m_ManageTab.SetAssetListSelection(pathList);
|
||||
}
|
||||
|
||||
private void AddDependentAssetsRecursive(TreeViewItem item, List<string> pathList)
|
||||
{
|
||||
TogglePathTreeViewItem pathItem = item as TogglePathTreeViewItem;
|
||||
if (pathItem != null)
|
||||
if (string.IsNullOrEmpty(pathItem.DisplayNamePrefix) == false && pathList.Contains(pathItem.Path) == false)
|
||||
pathList.Add(pathItem.Path);
|
||||
|
||||
if (item.hasChildren)
|
||||
for (int i = 0; i < item.children.Count; ++i)
|
||||
AddDependentAssetsRecursive(item.children[i], pathList);
|
||||
}
|
||||
|
||||
protected override void DoubleClickedItem(int id)
|
||||
{
|
||||
base.DoubleClickedItem(id);
|
||||
TreeViewItem item = FindItem(id, rootItem);
|
||||
if (item != null)
|
||||
{
|
||||
TogglePathTreeViewItem pathItem = item as TogglePathTreeViewItem;
|
||||
if (pathItem != null)
|
||||
{
|
||||
Object o = AssetDatabase.LoadAssetAtPath<Object>(pathItem.Path);
|
||||
if (o != null)
|
||||
{
|
||||
Selection.activeObject = o;
|
||||
EditorGUIUtility.PingObject(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static TreeViewItem AppendBundleToTree(BundleDataInfo bundle)
|
||||
{
|
||||
string itemName = bundle.m_Name.fullNativeName;
|
||||
TreeViewItem bunRoot = new TreeViewItem(itemName.GetHashCode(), 0, itemName);
|
||||
|
||||
string str = itemName + k_SizeHeader;
|
||||
TreeViewItem sz = new TreeViewItem(str.GetHashCode(), 1, k_SizeHeader + bundle.TotalSize());
|
||||
|
||||
str = itemName + k_DependencyHeader;
|
||||
TreeViewItem dependency = new TreeViewItem(str.GetHashCode(), 1, k_DependencyEmpty);
|
||||
List<BundleDependencyInfo> depList = bundle.GetBundleDependencies();
|
||||
if (depList.Count > 0)
|
||||
{
|
||||
dependency.displayName = k_DependencyHeader;
|
||||
foreach (BundleDependencyInfo dep in bundle.GetBundleDependencies())
|
||||
{
|
||||
str = itemName + dep.m_BundleName;
|
||||
TreeViewItem newItem = new TreeViewItem(str.GetHashCode(), 2, dep.m_BundleName);
|
||||
newItem.icon = Model.GetBundleIcon();
|
||||
dependency.AddChild(newItem);
|
||||
|
||||
Dictionary<string, TogglePathTreeViewItem> toAssetItems = new Dictionary<string, TogglePathTreeViewItem>();
|
||||
|
||||
for (int i = 0; i < dep.m_FromAssets.Count; ++i)
|
||||
{
|
||||
TogglePathTreeViewItem item = null;
|
||||
|
||||
if (!toAssetItems.TryGetValue(dep.m_ToAssets[i].fullAssetName, out item))
|
||||
{
|
||||
str = itemName + dep.m_BundleName + dep.m_ToAssets[i].displayName;
|
||||
item = new TogglePathTreeViewItem(str.GetHashCode(), 3, "/" + dep.m_ToAssets[i].displayName, "/" + dep.m_ToAssets[i].fullAssetName);
|
||||
item.icon = AssetDatabase.GetCachedIcon(dep.m_ToAssets[i].fullAssetName) as Texture2D;
|
||||
newItem.AddChild(item);
|
||||
toAssetItems.Add(dep.m_ToAssets[i].fullAssetName, item);
|
||||
}
|
||||
|
||||
str = str + dep.m_FromAssets[i].displayName;
|
||||
TreeViewItem refItem = new TogglePathTreeViewItem(str.GetHashCode(), 4, k_ReferencedPrefix,
|
||||
dep.m_FromAssets[i].displayName, dep.m_FromAssets[i].fullAssetName);
|
||||
refItem.icon = AssetDatabase.GetCachedIcon(dep.m_FromAssets[i].fullAssetName) as Texture2D;
|
||||
item.AddChild(refItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
str = itemName + k_MessageHeader;
|
||||
TreeViewItem msg = new TreeViewItem(str.GetHashCode(), 1, k_MessageEmpty);
|
||||
if (bundle.HasMessages())
|
||||
{
|
||||
msg.displayName = k_MessageHeader;
|
||||
List<MessageSystem.Message> currMessages = bundle.GetMessages();
|
||||
|
||||
foreach (MessageSystem.Message currMsg in currMessages)
|
||||
{
|
||||
str = itemName + currMsg.message;
|
||||
msg.AddChild(new BundleDetailItem(str.GetHashCode(), 2, currMsg.message, currMsg.severity));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bunRoot.AddChild(sz);
|
||||
bunRoot.AddChild(dependency);
|
||||
bunRoot.AddChild(msg);
|
||||
|
||||
return bunRoot;
|
||||
}
|
||||
|
||||
|
||||
internal void SetItems(IEnumerable<BundleInfo> items)
|
||||
{
|
||||
m_Selecteditems.Clear();
|
||||
foreach (BundleInfo item in items) CollectBundles(item);
|
||||
SetSelection(new List<int>());
|
||||
Reload();
|
||||
ExpandAll(2);
|
||||
}
|
||||
|
||||
internal void CollectBundles(BundleInfo bundle)
|
||||
{
|
||||
BundleDataInfo bunData = bundle as BundleDataInfo;
|
||||
if (bunData != null)
|
||||
{
|
||||
m_Selecteditems.Add(bunData);
|
||||
}
|
||||
else
|
||||
{
|
||||
BundleFolderInfo bunFolder = bundle as BundleFolderInfo;
|
||||
foreach (BundleInfo bun in bunFolder.GetChildList()) CollectBundles(bun);
|
||||
}
|
||||
}
|
||||
|
||||
internal void ExpandAll(int maximumDepth)
|
||||
{
|
||||
List<int> expanded = new List<int>(GetExpanded());
|
||||
FindItems(rootItem, maximumDepth, expanded);
|
||||
SetExpanded(expanded);
|
||||
}
|
||||
|
||||
internal void FindItems(TreeViewItem item, int maximumDepth, List<int> expanded)
|
||||
{
|
||||
if (item.depth >= maximumDepth || !item.hasChildren)
|
||||
return;
|
||||
|
||||
expanded.Add(item.id);
|
||||
for (int i = 0; i < item.children.Count; ++i) FindItems(item.children[i], maximumDepth, expanded);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37b2e435499908144b0d282ed2719039
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fabc705ac6bb6b746ac5ade02708d0b9
|
||||
folderAsset: yes
|
||||
timeCreated: 1491494491
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
+77
@@ -0,0 +1,77 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0af1b5107fe5c2428026d9f032c64c0
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
externalObjects: {}
|
||||
serializedVersion: 5
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
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
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapU: -1
|
||||
wrapV: -1
|
||||
wrapW: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 2
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
+77
@@ -0,0 +1,77 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c52be92045472a4bb2d2fa348b08d6d
|
||||
TextureImporter:
|
||||
fileIDToRecycleName: {}
|
||||
externalObjects: {}
|
||||
serializedVersion: 5
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 1
|
||||
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
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: -1
|
||||
aniso: -1
|
||||
mipBias: -1
|
||||
wrapU: -1
|
||||
wrapV: -1
|
||||
wrapW: -1
|
||||
nPOTScale: 1
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 0
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spritePixelsToUnits: 100
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 0
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 0
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 2
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
physicsShape: []
|
||||
spritePackingTag:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d1c1f5fd30f85c4f971269388fad046
|
||||
folderAsset: yes
|
||||
timeCreated: 1497983785
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,460 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using System.Linq;
|
||||
|
||||
namespace AssetBundleBrowser
|
||||
{
|
||||
[System.Serializable]
|
||||
internal class AssetBundleInspectTab
|
||||
{
|
||||
private Rect m_Position;
|
||||
|
||||
[SerializeField] private InspectTabData m_Data;
|
||||
|
||||
|
||||
private Dictionary<string, List<string>> m_BundleList;
|
||||
private InspectBundleTree m_BundleTreeView;
|
||||
[SerializeField] private TreeViewState m_BundleTreeState;
|
||||
|
||||
internal Editor m_Editor = null;
|
||||
|
||||
private SingleBundleInspector m_SingleInspector;
|
||||
|
||||
/// <summary>
|
||||
/// Collection of loaded asset bundle records indexed by bundle name
|
||||
/// </summary>
|
||||
private Dictionary<string, AssetBundleRecord> m_loadedAssetBundles;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the record for a loaded asset bundle by name if it exists in our container.
|
||||
/// </summary>
|
||||
/// <returns>Asset bundle record instance if loaded, otherwise null.</returns>
|
||||
/// <param name="bundleName">Name of the loaded asset bundle, excluding the variant extension</param>
|
||||
private AssetBundleRecord GetLoadedBundleRecordByName(string bundleName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(bundleName)) return null;
|
||||
|
||||
if (!m_loadedAssetBundles.ContainsKey(bundleName)) return null;
|
||||
|
||||
return m_loadedAssetBundles[bundleName];
|
||||
}
|
||||
|
||||
internal AssetBundleInspectTab()
|
||||
{
|
||||
m_BundleList = new Dictionary<string, List<string>>();
|
||||
m_SingleInspector = new SingleBundleInspector();
|
||||
m_loadedAssetBundles = new Dictionary<string, AssetBundleRecord>();
|
||||
}
|
||||
|
||||
internal void OnEnable(Rect pos)
|
||||
{
|
||||
m_Position = pos;
|
||||
if (m_Data == null)
|
||||
m_Data = new InspectTabData();
|
||||
|
||||
//LoadData...
|
||||
string dataPath = Path.GetFullPath(".");
|
||||
dataPath = dataPath.Replace("\\", "/");
|
||||
dataPath += "/Library/AssetBundleBrowserInspect.dat";
|
||||
|
||||
if (File.Exists(dataPath))
|
||||
{
|
||||
BinaryFormatter bf = new BinaryFormatter();
|
||||
FileStream file = File.Open(dataPath, FileMode.Open);
|
||||
InspectTabData data = bf.Deserialize(file) as InspectTabData;
|
||||
if (data != null)
|
||||
m_Data = data;
|
||||
file.Close();
|
||||
}
|
||||
|
||||
|
||||
if (m_BundleList == null)
|
||||
m_BundleList = new Dictionary<string, List<string>>();
|
||||
|
||||
if (m_BundleTreeState == null)
|
||||
m_BundleTreeState = new TreeViewState();
|
||||
m_BundleTreeView = new InspectBundleTree(m_BundleTreeState, this);
|
||||
|
||||
|
||||
RefreshBundles();
|
||||
}
|
||||
|
||||
internal void OnDisable()
|
||||
{
|
||||
ClearData();
|
||||
|
||||
string dataPath = Path.GetFullPath(".");
|
||||
dataPath = dataPath.Replace("\\", "/");
|
||||
dataPath += "/Library/AssetBundleBrowserInspect.dat";
|
||||
|
||||
BinaryFormatter bf = new BinaryFormatter();
|
||||
FileStream file = File.Create(dataPath);
|
||||
|
||||
bf.Serialize(file, m_Data);
|
||||
file.Close();
|
||||
}
|
||||
|
||||
internal void OnGUI(Rect pos)
|
||||
{
|
||||
m_Position = pos;
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
GUIStyle style = new GUIStyle(GUI.skin.label);
|
||||
style.alignment = TextAnchor.MiddleCenter;
|
||||
style.wordWrap = true;
|
||||
GUI.Label(
|
||||
new Rect(m_Position.x + 1f, m_Position.y + 1f, m_Position.width - 2f, m_Position.height - 2f),
|
||||
new GUIContent("Inspector unavailable while in PLAY mode"),
|
||||
style);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnGUIEditor();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUIEditor()
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
GUILayout.BeginHorizontal();
|
||||
|
||||
if (GUILayout.Button("Add File", GUILayout.MaxWidth(75f))) BrowseForFile();
|
||||
if (GUILayout.Button("Add Folder", GUILayout.MaxWidth(75f))) BrowseForFolder();
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (m_BundleList.Count > 0)
|
||||
{
|
||||
int halfWidth = (int) (m_Position.width / 2.0f);
|
||||
m_BundleTreeView.OnGUI(new Rect(m_Position.x, m_Position.y + 30, halfWidth, m_Position.height - 30));
|
||||
m_SingleInspector.OnGUI(new Rect(m_Position.x + halfWidth, m_Position.y + 30, halfWidth, m_Position.height - 30));
|
||||
}
|
||||
}
|
||||
|
||||
internal void RemoveBundlePath(string pathToRemove)
|
||||
{
|
||||
UnloadBundle(pathToRemove);
|
||||
m_Data.RemovePath(pathToRemove);
|
||||
}
|
||||
|
||||
internal void RemoveBundleFolder(string pathToRemove)
|
||||
{
|
||||
List<string> paths = null;
|
||||
if (m_BundleList.TryGetValue(pathToRemove, out paths))
|
||||
foreach (string p in paths)
|
||||
UnloadBundle(p);
|
||||
m_Data.RemoveFolder(pathToRemove);
|
||||
}
|
||||
|
||||
private void BrowseForFile()
|
||||
{
|
||||
string newPath = EditorUtility.OpenFilePanelWithFilters("Bundle Folder", string.Empty, new string[] { });
|
||||
if (!string.IsNullOrEmpty(newPath))
|
||||
{
|
||||
string gamePath = Path.GetFullPath("."); //TODO - FileUtil.GetProjectRelativePath??
|
||||
gamePath = gamePath.Replace("\\", "/");
|
||||
if (newPath.StartsWith(gamePath))
|
||||
newPath = newPath.Remove(0, gamePath.Length + 1);
|
||||
|
||||
m_Data.AddPath(newPath);
|
||||
|
||||
RefreshBundles();
|
||||
}
|
||||
}
|
||||
|
||||
//TODO - this is largely copied from BuildTab, should maybe be shared code.
|
||||
private void BrowseForFolder(string folderPath = null)
|
||||
{
|
||||
folderPath = EditorUtility.OpenFolderPanel("Bundle Folder", string.Empty, string.Empty);
|
||||
if (!string.IsNullOrEmpty(folderPath))
|
||||
{
|
||||
string gamePath = Path.GetFullPath("."); //TODO - FileUtil.GetProjectRelativePath??
|
||||
gamePath = gamePath.Replace("\\", "/");
|
||||
if (folderPath.Length > gamePath.Length && folderPath.StartsWith(gamePath))
|
||||
folderPath = folderPath.Remove(0, gamePath.Length + 1);
|
||||
|
||||
AddBundleFolder(folderPath);
|
||||
|
||||
RefreshBundles();
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddBundleFolder(string folderPath)
|
||||
{
|
||||
m_Data.AddFolder(folderPath);
|
||||
}
|
||||
|
||||
private void ClearData()
|
||||
{
|
||||
m_SingleInspector.SetBundle(null);
|
||||
|
||||
if (null != m_loadedAssetBundles)
|
||||
{
|
||||
List<AssetBundleRecord> records = new List<AssetBundleRecord>(m_loadedAssetBundles.Values);
|
||||
foreach (AssetBundleRecord record in records) record.bundle.Unload(true);
|
||||
|
||||
m_loadedAssetBundles.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
internal void RefreshBundles()
|
||||
{
|
||||
ClearData();
|
||||
|
||||
|
||||
if (m_Data.BundlePaths == null)
|
||||
return;
|
||||
|
||||
//find assets
|
||||
if (m_BundleList == null)
|
||||
m_BundleList = new Dictionary<string, List<string>>();
|
||||
|
||||
m_BundleList.Clear();
|
||||
List<string> pathsToRemove = new List<string>();
|
||||
foreach (string filePath in m_Data.BundlePaths)
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
AddBundleToList(string.Empty, filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("Expected bundle not found: " + filePath);
|
||||
pathsToRemove.Add(filePath);
|
||||
}
|
||||
|
||||
foreach (string path in pathsToRemove) m_Data.RemovePath(path);
|
||||
pathsToRemove.Clear();
|
||||
|
||||
foreach (InspectTabData.BundleFolderData folder in m_Data.BundleFolders)
|
||||
if (Directory.Exists(folder.path))
|
||||
{
|
||||
AddFilePathToList(folder.path, folder.path);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("Expected folder not found: " + folder);
|
||||
pathsToRemove.Add(folder.path);
|
||||
}
|
||||
|
||||
foreach (string path in pathsToRemove) m_Data.RemoveFolder(path);
|
||||
|
||||
m_BundleTreeView.Reload();
|
||||
}
|
||||
|
||||
private void AddBundleToList(string parent, string bundlePath)
|
||||
{
|
||||
List<string> bundles = null;
|
||||
m_BundleList.TryGetValue(parent, out bundles);
|
||||
|
||||
if (bundles == null)
|
||||
{
|
||||
bundles = new List<string>();
|
||||
m_BundleList.Add(parent, bundles);
|
||||
}
|
||||
|
||||
bundles.Add(bundlePath);
|
||||
}
|
||||
|
||||
private void AddFilePathToList(string rootPath, string path)
|
||||
{
|
||||
string[] notAllowedExtensions = new string[] {".meta", ".manifest", ".dll", ".cs", ".exe", ".js"};
|
||||
foreach (string file in Directory.GetFiles(path))
|
||||
{
|
||||
string ext = Path.GetExtension(file);
|
||||
if (!notAllowedExtensions.Contains(ext))
|
||||
{
|
||||
string f = file.Replace('\\', '/');
|
||||
if (File.Exists(file) && !m_Data.FolderIgnoresFile(rootPath, f)) AddBundleToList(rootPath, f);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (string dir in Directory.GetDirectories(path)) AddFilePathToList(rootPath, dir);
|
||||
}
|
||||
|
||||
internal Dictionary<string, List<string>> BundleList
|
||||
{
|
||||
get { return m_BundleList; }
|
||||
}
|
||||
|
||||
|
||||
internal void SetBundleItem(IList<InspectTreeItem> selected)
|
||||
{
|
||||
//m_SelectedBundleTreeItems = selected;
|
||||
if (selected == null || selected.Count == 0 || selected[0] == null)
|
||||
{
|
||||
m_SingleInspector.SetBundle(null);
|
||||
}
|
||||
else if (selected.Count == 1)
|
||||
{
|
||||
AssetBundle bundle = LoadBundle(selected[0].bundlePath);
|
||||
m_SingleInspector.SetBundle(bundle, selected[0].bundlePath, m_Data, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_SingleInspector.SetBundle(null);
|
||||
|
||||
//perhaps there should be a way to set a message in the inspector, to tell it...
|
||||
//var style = GUI.skin.label;
|
||||
//style.alignment = TextAnchor.MiddleCenter;
|
||||
//style.wordWrap = true;
|
||||
//GUI.Label(
|
||||
// inspectorRect,
|
||||
// new GUIContent("Multi-select inspection not supported"),
|
||||
// style);
|
||||
}
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
internal class InspectTabData
|
||||
{
|
||||
[SerializeField] private List<string> m_BundlePaths = new List<string>();
|
||||
[SerializeField] private List<BundleFolderData> m_BundleFolders = new List<BundleFolderData>();
|
||||
|
||||
internal IList<string> BundlePaths
|
||||
{
|
||||
get { return m_BundlePaths.AsReadOnly(); }
|
||||
}
|
||||
|
||||
internal IList<BundleFolderData> BundleFolders
|
||||
{
|
||||
get { return m_BundleFolders.AsReadOnly(); }
|
||||
}
|
||||
|
||||
internal void AddPath(string newPath)
|
||||
{
|
||||
if (!m_BundlePaths.Contains(newPath))
|
||||
{
|
||||
BundleFolderData possibleFolderData = FolderDataContainingFilePath(newPath);
|
||||
if (possibleFolderData == null)
|
||||
m_BundlePaths.Add(newPath);
|
||||
else
|
||||
possibleFolderData.ignoredFiles.Remove(newPath);
|
||||
}
|
||||
}
|
||||
|
||||
internal void AddFolder(string newPath)
|
||||
{
|
||||
if (!BundleFolderContains(newPath))
|
||||
m_BundleFolders.Add(new BundleFolderData(newPath));
|
||||
}
|
||||
|
||||
internal void RemovePath(string pathToRemove)
|
||||
{
|
||||
m_BundlePaths.Remove(pathToRemove);
|
||||
}
|
||||
|
||||
internal void RemoveFolder(string pathToRemove)
|
||||
{
|
||||
m_BundleFolders.Remove(BundleFolders.FirstOrDefault(bfd => bfd.path == pathToRemove));
|
||||
}
|
||||
|
||||
internal bool FolderIgnoresFile(string folderPath, string filePath)
|
||||
{
|
||||
if (BundleFolders == null)
|
||||
return false;
|
||||
BundleFolderData bundleFolderData = BundleFolders.FirstOrDefault(bfd => bfd.path == folderPath);
|
||||
return bundleFolderData != null && bundleFolderData.ignoredFiles.Contains(filePath);
|
||||
}
|
||||
|
||||
internal BundleFolderData FolderDataContainingFilePath(string filePath)
|
||||
{
|
||||
foreach (BundleFolderData bundleFolderData in BundleFolders)
|
||||
if (Path.GetFullPath(filePath).StartsWith(Path.GetFullPath(bundleFolderData.path)))
|
||||
return bundleFolderData;
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool BundleFolderContains(string folderPath)
|
||||
{
|
||||
foreach (BundleFolderData bundleFolderData in BundleFolders)
|
||||
if (Path.GetFullPath(bundleFolderData.path) == Path.GetFullPath(folderPath))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
internal class BundleFolderData
|
||||
{
|
||||
[SerializeField] internal string path;
|
||||
|
||||
[SerializeField] private List<string> m_ignoredFiles;
|
||||
|
||||
internal List<string> ignoredFiles
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_ignoredFiles == null)
|
||||
m_ignoredFiles = new List<string>();
|
||||
return m_ignoredFiles;
|
||||
}
|
||||
}
|
||||
|
||||
internal BundleFolderData(string p)
|
||||
{
|
||||
path = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the bundle at the specified path, loading it if necessary.
|
||||
/// Unloads previously loaded bundles if necessary when dealing with variants.
|
||||
/// </summary>
|
||||
/// <returns>Returns the loaded bundle, null if it could not be loaded.</returns>
|
||||
/// <param name="path">Path of bundle to get</param>
|
||||
private AssetBundle LoadBundle(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path)) return null;
|
||||
|
||||
string extension = Path.GetExtension(path);
|
||||
|
||||
string bundleName = path.Substring(0, path.Length - extension.Length);
|
||||
|
||||
// Check if we have a record for this bundle
|
||||
AssetBundleRecord record = GetLoadedBundleRecordByName(bundleName);
|
||||
AssetBundle bundle = null;
|
||||
if (null != record)
|
||||
{
|
||||
// Unload existing bundle if variant names differ, otherwise use existing bundle
|
||||
if (!record.path.Equals(path))
|
||||
UnloadBundle(bundleName);
|
||||
else
|
||||
bundle = record.bundle;
|
||||
}
|
||||
|
||||
if (null == bundle)
|
||||
{
|
||||
// Load the bundle
|
||||
bundle = AssetBundle.LoadFromFile(path);
|
||||
if (null == bundle) return null;
|
||||
|
||||
m_loadedAssetBundles[bundleName] = new AssetBundleRecord(path, bundle);
|
||||
|
||||
// Load the bundle's assets
|
||||
string[] assetNames = bundle.GetAllAssetNames();
|
||||
foreach (string name in assetNames) bundle.LoadAsset(name);
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unloads the bundle with the given name.
|
||||
/// </summary>
|
||||
/// <param name="bundleName">Name of the bundle to unload without variant extension</param>
|
||||
private void UnloadBundle(string bundleName)
|
||||
{
|
||||
AssetBundleRecord record = GetLoadedBundleRecordByName(bundleName);
|
||||
if (null == record) return;
|
||||
|
||||
record.bundle.Unload(true);
|
||||
m_loadedAssetBundles.Remove(bundleName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6578866792bb814088661383f4a7471
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,60 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace AssetBundleBrowser
|
||||
{
|
||||
/// <summary>
|
||||
/// This class maintains a record of a loaded asset bundle, allowing us
|
||||
/// to associate the full path of an asset bundle with the actual bundle,
|
||||
/// so that we can:
|
||||
///
|
||||
/// 1. distinguish between bundle variants, which, when loaded
|
||||
/// resolve to the same name.
|
||||
///
|
||||
/// 2. Differentiate between the same asset bundles built for different platforms.
|
||||
///
|
||||
/// ex:
|
||||
///
|
||||
/// Two asset bundle variants:
|
||||
///
|
||||
/// - variant one: mycylinder.one
|
||||
/// - variant two: mycylinder.two
|
||||
///
|
||||
/// Will Resolve to "mycylinder" when loaded.
|
||||
///
|
||||
/// Likewise,
|
||||
///
|
||||
/// - iOS: AssetBundles/iOS/myBundle
|
||||
/// - Android: AssetBundle/Android/myBundle
|
||||
///
|
||||
/// Will both resolve to "mybundle" when loaded.
|
||||
///
|
||||
/// </summary>
|
||||
internal class AssetBundleRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// Full path of the asset bundle.
|
||||
/// </summary>
|
||||
internal string path { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the loaded asset bundle associated with the path.
|
||||
/// </summary>
|
||||
internal AssetBundle bundle { get; private set; }
|
||||
|
||||
internal AssetBundleRecord(string path, AssetBundle bundle)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path) ||
|
||||
null == bundle)
|
||||
{
|
||||
string msg = string.Format("AssetBundleRecord encountered invalid parameters path={0}, bundle={1}",
|
||||
path,
|
||||
bundle);
|
||||
|
||||
throw new System.ArgumentException(msg);
|
||||
}
|
||||
|
||||
this.path = path;
|
||||
this.bundle = bundle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0544e0691b210254c9301d8fb106c04a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,127 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.IO;
|
||||
|
||||
namespace AssetBundleBrowser
|
||||
{
|
||||
internal class SingleBundleInspector
|
||||
{
|
||||
internal static string currentPath { get; set; }
|
||||
|
||||
|
||||
internal SingleBundleInspector()
|
||||
{
|
||||
}
|
||||
|
||||
private Editor m_Editor = null;
|
||||
|
||||
private Rect m_Position;
|
||||
|
||||
[SerializeField] private Vector2 m_ScrollPosition;
|
||||
|
||||
private AssetBundleInspectTab m_assetBundleInspectTab = null;
|
||||
private AssetBundleInspectTab.InspectTabData m_inspectTabData = null;
|
||||
|
||||
internal void SetBundle(AssetBundle bundle, string path = "", AssetBundleInspectTab.InspectTabData inspectTabData = null, AssetBundleInspectTab assetBundleInspectTab = null)
|
||||
{
|
||||
//static var...
|
||||
currentPath = path;
|
||||
m_inspectTabData = inspectTabData;
|
||||
m_assetBundleInspectTab = assetBundleInspectTab;
|
||||
|
||||
//members
|
||||
m_Editor = null;
|
||||
if (bundle != null) m_Editor = Editor.CreateEditor(bundle);
|
||||
}
|
||||
|
||||
internal void OnGUI(Rect pos)
|
||||
{
|
||||
m_Position = pos;
|
||||
|
||||
DrawBundleData();
|
||||
}
|
||||
|
||||
private void DrawBundleData()
|
||||
{
|
||||
if (m_Editor != null)
|
||||
{
|
||||
GUILayout.BeginArea(m_Position);
|
||||
m_ScrollPosition = EditorGUILayout.BeginScrollView(m_ScrollPosition);
|
||||
m_Editor.OnInspectorGUI();
|
||||
EditorGUILayout.EndScrollView();
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(currentPath))
|
||||
{
|
||||
GUIStyle style = new GUIStyle(GUI.skin.label);
|
||||
style.alignment = TextAnchor.MiddleCenter;
|
||||
style.wordWrap = true;
|
||||
GUI.Label(m_Position, new GUIContent("Invalid bundle selected"), style);
|
||||
|
||||
if (m_inspectTabData != null && GUI.Button(new Rect(new Vector2(m_Position.position.x + m_Position.width / 2f - 37.5f, m_Position.position.y + m_Position.height / 2f + 15), new Vector2(75, 30)), "Ignore file"))
|
||||
{
|
||||
AssetBundleInspectTab.InspectTabData.BundleFolderData possibleFolderData = m_inspectTabData.FolderDataContainingFilePath(currentPath);
|
||||
if (possibleFolderData != null)
|
||||
{
|
||||
if (!possibleFolderData.ignoredFiles.Contains(currentPath))
|
||||
possibleFolderData.ignoredFiles.Add(currentPath);
|
||||
|
||||
if (m_assetBundleInspectTab != null)
|
||||
m_assetBundleInspectTab.RefreshBundles();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CustomEditor(typeof(AssetBundle))]
|
||||
internal class AssetBundleEditor : Editor
|
||||
{
|
||||
internal bool pathFoldout = false;
|
||||
internal bool advancedFoldout = false;
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
AssetBundle bundle = target as AssetBundle;
|
||||
|
||||
using (new EditorGUI.DisabledScope(true))
|
||||
{
|
||||
GUIStyle leftStyle = new GUIStyle(GUI.skin.GetStyle("Label"));
|
||||
leftStyle.alignment = TextAnchor.UpperLeft;
|
||||
GUILayout.Label(new GUIContent("Name: " + bundle.name), leftStyle);
|
||||
|
||||
long fileSize = -1;
|
||||
if (!string.IsNullOrEmpty(SingleBundleInspector.currentPath) && File.Exists(SingleBundleInspector.currentPath))
|
||||
{
|
||||
FileInfo fileInfo = new FileInfo(SingleBundleInspector.currentPath);
|
||||
fileSize = fileInfo.Length;
|
||||
}
|
||||
|
||||
if (fileSize < 0)
|
||||
GUILayout.Label(new GUIContent("Size: unknown"), leftStyle);
|
||||
else
|
||||
GUILayout.Label(new GUIContent("Size: " + EditorUtility.FormatBytes(fileSize)), leftStyle);
|
||||
|
||||
string[] assetNames = bundle.GetAllAssetNames();
|
||||
pathFoldout = EditorGUILayout.Foldout(pathFoldout, "Source Asset Paths");
|
||||
if (pathFoldout)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
foreach (string asset in assetNames)
|
||||
EditorGUILayout.LabelField(asset);
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
|
||||
advancedFoldout = EditorGUILayout.Foldout(advancedFoldout, "Advanced Data");
|
||||
}
|
||||
|
||||
if (advancedFoldout)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
base.OnInspectorGUI();
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3573b64da26ccbb44a0ad5b58ba36d25
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,132 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace AssetBundleBrowser
|
||||
{
|
||||
internal class InspectTreeItem : TreeViewItem
|
||||
{
|
||||
internal string bundlePath { get; private set; }
|
||||
|
||||
internal InspectTreeItem(string path, int depth) : base(path.GetHashCode(), depth, path)
|
||||
{
|
||||
bundlePath = path;
|
||||
}
|
||||
|
||||
internal InspectTreeItem(string path, int depth, string prettyName) : base(path.GetHashCode(), depth, prettyName)
|
||||
{
|
||||
bundlePath = path;
|
||||
}
|
||||
|
||||
internal InspectTreeItem(string path, string parentPath, int depth, string prettyName) : base((path + parentPath).GetHashCode(), depth, prettyName)
|
||||
{
|
||||
bundlePath = path;
|
||||
}
|
||||
}
|
||||
|
||||
internal class InspectBundleTree : TreeView
|
||||
{
|
||||
private AssetBundleInspectTab m_InspectTab;
|
||||
|
||||
internal InspectBundleTree(TreeViewState s, AssetBundleInspectTab parent) : base(s)
|
||||
{
|
||||
m_InspectTab = parent;
|
||||
showBorder = true;
|
||||
}
|
||||
|
||||
protected override TreeViewItem BuildRoot()
|
||||
{
|
||||
TreeViewItem root = new TreeViewItem(-1, -1);
|
||||
root.children = new List<TreeViewItem>();
|
||||
if (m_InspectTab == null)
|
||||
Debug.Log("Unknown problem in AssetBundle Browser Inspect tab. Restart Browser and try again, or file ticket on github.");
|
||||
else
|
||||
foreach (KeyValuePair<string, List<string>> folder in m_InspectTab.BundleList)
|
||||
if (string.IsNullOrEmpty(folder.Key))
|
||||
{
|
||||
foreach (string path in folder.Value)
|
||||
root.AddChild(new InspectTreeItem(path, 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
TreeViewItem folderItem = new TreeViewItem(folder.Key.GetHashCode(), 0, folder.Key);
|
||||
foreach (string path in folder.Value)
|
||||
{
|
||||
string prettyName = path;
|
||||
if (path.StartsWith(folder.Key)) //how could it not?
|
||||
prettyName = path.Remove(0, folder.Key.Length + 1);
|
||||
|
||||
folderItem.AddChild(new InspectTreeItem(path, folder.Key, 1, prettyName));
|
||||
}
|
||||
|
||||
root.AddChild(folderItem);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect rect)
|
||||
{
|
||||
base.OnGUI(rect);
|
||||
if (Event.current.type == EventType.MouseDown && Event.current.button == 0 && rect.Contains(Event.current.mousePosition)) SetSelection(new int[0], TreeViewSelectionOptions.FireSelectionChanged);
|
||||
}
|
||||
|
||||
protected override void RowGUI(RowGUIArgs args)
|
||||
{
|
||||
base.RowGUI(args);
|
||||
if (args.item.depth == 0)
|
||||
{
|
||||
int width = 16;
|
||||
Rect edgeRect = new Rect(args.rowRect.xMax - width, args.rowRect.y, width, args.rowRect.height);
|
||||
if (GUI.Button(edgeRect, "-"))
|
||||
{
|
||||
if (GetSelection().Contains(args.item.id))
|
||||
{
|
||||
IList<int> selection = GetSelection();
|
||||
foreach (int id in selection)
|
||||
{
|
||||
TreeViewItem item = FindItem(id, rootItem);
|
||||
if (item.depth == 0)
|
||||
RemoveItem(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveItem(args.item);
|
||||
}
|
||||
|
||||
m_InspectTab.RefreshBundles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveItem(TreeViewItem item)
|
||||
{
|
||||
InspectTreeItem inspectItem = item as InspectTreeItem;
|
||||
if (inspectItem != null)
|
||||
m_InspectTab.RemoveBundlePath(inspectItem.bundlePath);
|
||||
else
|
||||
m_InspectTab.RemoveBundleFolder(item.displayName);
|
||||
}
|
||||
|
||||
protected override void SelectionChanged(IList<int> selectedIds)
|
||||
{
|
||||
base.SelectionChanged(selectedIds);
|
||||
|
||||
if (selectedIds == null)
|
||||
return;
|
||||
|
||||
if (selectedIds.Count > 0)
|
||||
m_InspectTab.SetBundleItem(FindRows(selectedIds).Select(tvi => tvi as InspectTreeItem).ToList());
|
||||
//m_InspectTab.SetBundleItem(FindItem(selectedIds[0], rootItem) as InspectTreeItem);
|
||||
else
|
||||
m_InspectTab.SetBundleItem(null);
|
||||
}
|
||||
|
||||
protected override bool CanMultiSelect(TreeViewItem item)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eae882f6f29224b4092f04878a38a0cb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,112 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using AssetBundleBrowser.AssetBundleModel;
|
||||
|
||||
namespace AssetBundleBrowser
|
||||
{
|
||||
internal class MessageList
|
||||
{
|
||||
private Vector2 m_ScrollPosition = Vector2.zero;
|
||||
|
||||
private GUIStyle[] m_Style = new GUIStyle[2];
|
||||
|
||||
private IEnumerable<AssetBundleModel.AssetInfo> m_Selecteditems;
|
||||
private List<MessageSystem.Message> m_Messages;
|
||||
|
||||
private Vector2 m_Dimensions = new Vector2(0, 0);
|
||||
private const float k_ScrollbarPadding = 16f;
|
||||
private const float k_BorderSize = 1f;
|
||||
|
||||
|
||||
internal MessageList()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
m_Style[0] = "OL EntryBackOdd";
|
||||
m_Style[1] = "OL EntryBackEven";
|
||||
m_Style[0].wordWrap = true;
|
||||
m_Style[1].wordWrap = true;
|
||||
m_Style[0].padding = new RectOffset(32, 0, 1, 4);
|
||||
m_Style[1].padding = new RectOffset(32, 0, 1, 4);
|
||||
m_Messages = new List<MessageSystem.Message>();
|
||||
}
|
||||
|
||||
internal void OnGUI(Rect fullPos)
|
||||
{
|
||||
DrawOutline(fullPos, 1f);
|
||||
|
||||
Rect pos = new Rect(fullPos.x + k_BorderSize, fullPos.y + k_BorderSize, fullPos.width - 2 * k_BorderSize, fullPos.height - 2 * k_BorderSize);
|
||||
|
||||
|
||||
if (m_Dimensions.y == 0 || m_Dimensions.x != pos.width - k_ScrollbarPadding)
|
||||
{
|
||||
//recalculate height.
|
||||
m_Dimensions.x = pos.width - k_ScrollbarPadding;
|
||||
m_Dimensions.y = 0;
|
||||
foreach (MessageSystem.Message message in m_Messages) m_Dimensions.y += m_Style[0].CalcHeight(new GUIContent(message.message), m_Dimensions.x);
|
||||
}
|
||||
|
||||
m_ScrollPosition = GUI.BeginScrollView(pos, m_ScrollPosition, new Rect(0, 0, m_Dimensions.x, m_Dimensions.y));
|
||||
int counter = 0;
|
||||
float runningHeight = 0.0f;
|
||||
foreach (MessageSystem.Message message in m_Messages)
|
||||
{
|
||||
int index = counter % 2;
|
||||
GUIContent content = new GUIContent(message.message);
|
||||
float height = m_Style[index].CalcHeight(content, m_Dimensions.x);
|
||||
|
||||
GUI.Box(new Rect(0, runningHeight, m_Dimensions.x, height), content, m_Style[index]);
|
||||
GUI.DrawTexture(new Rect(0, runningHeight, 32f, 32f), message.icon);
|
||||
//TODO - cleanup formatting issues and switch to HelpBox
|
||||
//EditorGUI.HelpBox(new Rect(0, runningHeight, m_dimensions.x, height), message.message, (MessageType)message.severity);
|
||||
|
||||
counter++;
|
||||
runningHeight += height;
|
||||
}
|
||||
|
||||
GUI.EndScrollView();
|
||||
}
|
||||
|
||||
internal void SetItems(IEnumerable<AssetBundleModel.AssetInfo> items)
|
||||
{
|
||||
m_Selecteditems = items;
|
||||
CollectMessages();
|
||||
}
|
||||
|
||||
internal void CollectMessages()
|
||||
{
|
||||
m_Messages.Clear();
|
||||
m_Dimensions.y = 0f;
|
||||
if (m_Selecteditems != null)
|
||||
foreach (AssetInfo asset in m_Selecteditems)
|
||||
m_Messages.AddRange(asset.GetMessages());
|
||||
}
|
||||
|
||||
internal static void DrawOutline(Rect rect, float size)
|
||||
{
|
||||
Color color = new Color(0.6f, 0.6f, 0.6f, 1.333f);
|
||||
if (EditorGUIUtility.isProSkin)
|
||||
{
|
||||
color.r = 0.12f;
|
||||
color.g = 0.12f;
|
||||
color.b = 0.12f;
|
||||
}
|
||||
|
||||
if (Event.current.type != EventType.Repaint)
|
||||
return;
|
||||
|
||||
Color orgColor = GUI.color;
|
||||
GUI.color = GUI.color * color;
|
||||
GUI.DrawTexture(new Rect(rect.x, rect.y, rect.width, size), EditorGUIUtility.whiteTexture);
|
||||
GUI.DrawTexture(new Rect(rect.x, rect.yMax - size, rect.width, size), EditorGUIUtility.whiteTexture);
|
||||
GUI.DrawTexture(new Rect(rect.x, rect.y + 1, size, rect.height - 2 * size), EditorGUIUtility.whiteTexture);
|
||||
GUI.DrawTexture(new Rect(rect.xMax - size, rect.y + 1, size, rect.height - 2 * size), EditorGUIUtility.whiteTexture);
|
||||
|
||||
GUI.color = orgColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e706f069c95acf9439828b4a3144c2a7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.Assertions;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
|
||||
namespace AssetBundleBrowser
|
||||
{
|
||||
internal class MessageSystem
|
||||
{
|
||||
private static Texture2D s_ErrorIcon = null;
|
||||
private static Texture2D s_WarningIcon = null;
|
||||
private static Texture2D s_InfoIcon = null;
|
||||
private static Dictionary<MessageFlag, Message> s_MessageLookup = null;
|
||||
|
||||
[Flags]
|
||||
internal enum MessageFlag
|
||||
{
|
||||
None = 0x0,
|
||||
|
||||
Info = 0x80, //this flag is only used to check bits, not set.
|
||||
EmptyBundle = 0x81,
|
||||
EmptyFolder = 0x82,
|
||||
|
||||
Warning = 0x8000, //this flag is only used to check bits, not set.
|
||||
WarningInChildren = 0x8100,
|
||||
AssetsDuplicatedInMultBundles = 0x8200,
|
||||
VariantBundleMismatch = 0x8400,
|
||||
|
||||
Error = 0x800000, //this flag is only used to check bits, not set.
|
||||
ErrorInChildren = 0x810000,
|
||||
SceneBundleConflict = 0x820000,
|
||||
DependencySceneConflict = 0x840000
|
||||
}
|
||||
|
||||
internal class MessageState
|
||||
{
|
||||
//I have an enum and a set of enums to make some logic cleaner.
|
||||
// The enum has masks for Error/Warning/Info that won't ever be in the set
|
||||
// this allows for easy checking of IsSet for error rather than specific errors.
|
||||
private MessageFlag m_MessageFlags;
|
||||
private HashSet<MessageFlag> m_MessageSet;
|
||||
|
||||
|
||||
internal MessageState()
|
||||
{
|
||||
m_MessageFlags = MessageFlag.None;
|
||||
m_MessageSet = new HashSet<MessageFlag>();
|
||||
}
|
||||
|
||||
internal void Clear()
|
||||
{
|
||||
m_MessageFlags = MessageFlag.None;
|
||||
m_MessageSet.Clear();
|
||||
}
|
||||
|
||||
internal void SetFlag(MessageFlag flag, bool on)
|
||||
{
|
||||
if (flag == MessageFlag.Info || flag == MessageFlag.Warning || flag == MessageFlag.Error)
|
||||
return;
|
||||
|
||||
if (on)
|
||||
{
|
||||
m_MessageFlags |= flag;
|
||||
m_MessageSet.Add(flag);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_MessageFlags &= ~flag;
|
||||
m_MessageSet.Remove(flag);
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsSet(MessageFlag flag)
|
||||
{
|
||||
return (m_MessageFlags & flag) == flag;
|
||||
}
|
||||
|
||||
internal bool HasMessages()
|
||||
{
|
||||
return m_MessageFlags != MessageFlag.None;
|
||||
}
|
||||
|
||||
internal MessageType HighestMessageLevel()
|
||||
{
|
||||
if (IsSet(MessageFlag.Error))
|
||||
return MessageType.Error;
|
||||
if (IsSet(MessageFlag.Warning))
|
||||
return MessageType.Warning;
|
||||
if (IsSet(MessageFlag.Info))
|
||||
return MessageType.Info;
|
||||
return MessageType.None;
|
||||
}
|
||||
|
||||
internal MessageFlag HighestMessageFlag()
|
||||
{
|
||||
MessageFlag high = MessageFlag.None;
|
||||
foreach (MessageFlag f in m_MessageSet)
|
||||
if (f > high)
|
||||
high = f;
|
||||
return high;
|
||||
}
|
||||
|
||||
internal List<Message> GetMessages()
|
||||
{
|
||||
List<Message> msgs = new List<Message>();
|
||||
foreach (MessageFlag f in m_MessageSet) msgs.Add(GetMessage(f));
|
||||
return msgs;
|
||||
}
|
||||
}
|
||||
|
||||
internal static Texture2D GetIcon(MessageType sev)
|
||||
{
|
||||
if (sev == MessageType.Error)
|
||||
return GetErrorIcon();
|
||||
else if (sev == MessageType.Warning)
|
||||
return GetWarningIcon();
|
||||
else if (sev == MessageType.Info)
|
||||
return GetInfoIcon();
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Texture2D GetErrorIcon()
|
||||
{
|
||||
if (s_ErrorIcon == null)
|
||||
FindMessageIcons();
|
||||
return s_ErrorIcon;
|
||||
}
|
||||
|
||||
private static Texture2D GetWarningIcon()
|
||||
{
|
||||
if (s_WarningIcon == null)
|
||||
FindMessageIcons();
|
||||
return s_WarningIcon;
|
||||
}
|
||||
|
||||
private static Texture2D GetInfoIcon()
|
||||
{
|
||||
if (s_InfoIcon == null)
|
||||
FindMessageIcons();
|
||||
return s_InfoIcon;
|
||||
}
|
||||
|
||||
private static void FindMessageIcons()
|
||||
{
|
||||
s_ErrorIcon = EditorGUIUtility.FindTexture("console.errorIcon");
|
||||
s_WarningIcon = EditorGUIUtility.FindTexture("console.warnicon");
|
||||
s_InfoIcon = EditorGUIUtility.FindTexture("console.infoIcon");
|
||||
}
|
||||
|
||||
internal class Message
|
||||
{
|
||||
internal Message(string msg, MessageType sev)
|
||||
{
|
||||
message = msg;
|
||||
severity = sev;
|
||||
}
|
||||
|
||||
internal MessageType severity;
|
||||
internal string message;
|
||||
|
||||
internal Texture2D icon
|
||||
{
|
||||
get { return GetIcon(severity); }
|
||||
}
|
||||
}
|
||||
|
||||
internal static Message GetMessage(MessageFlag lookup)
|
||||
{
|
||||
if (s_MessageLookup == null)
|
||||
InitMessages();
|
||||
|
||||
Message msg = null;
|
||||
s_MessageLookup.TryGetValue(lookup, out msg);
|
||||
if (msg == null)
|
||||
msg = s_MessageLookup[MessageFlag.None];
|
||||
return msg;
|
||||
}
|
||||
|
||||
private static void InitMessages()
|
||||
{
|
||||
s_MessageLookup = new Dictionary<MessageFlag, Message>();
|
||||
|
||||
s_MessageLookup.Add(MessageFlag.None, new Message(string.Empty, MessageType.None));
|
||||
s_MessageLookup.Add(MessageFlag.EmptyBundle, new Message("This bundle is empty. Empty bundles cannot get saved with the scene and will disappear from this list if Unity restarts or if various other bundle rename or move events occur.", MessageType.Info));
|
||||
s_MessageLookup.Add(MessageFlag.EmptyFolder, new Message("This folder is either empty or contains only empty children. Empty bundles cannot get saved with the scene and will disappear from this list if Unity restarts or if various other bundle rename or move events occur.", MessageType.Info));
|
||||
s_MessageLookup.Add(MessageFlag.WarningInChildren, new Message("Warning in child(ren)", MessageType.Warning));
|
||||
s_MessageLookup.Add(MessageFlag.AssetsDuplicatedInMultBundles, new Message("Assets being pulled into this bundle due to dependencies are also being pulled into another bundle. This will cause duplication in memory", MessageType.Warning));
|
||||
s_MessageLookup.Add(MessageFlag.VariantBundleMismatch, new Message("Variants of a given bundle must have exactly the same assets between them based on a Name.Extension (without Path) comparison. These bundle variants fail that check.", MessageType.Warning));
|
||||
s_MessageLookup.Add(MessageFlag.ErrorInChildren, new Message("Error in child(ren)", MessageType.Error));
|
||||
s_MessageLookup.Add(MessageFlag.SceneBundleConflict, new Message("A bundle with one or more scenes must only contain scenes. This bundle has scenes and non-scene assets.", MessageType.Error));
|
||||
s_MessageLookup.Add(MessageFlag.DependencySceneConflict, new Message("The folder added to this bundle has pulled in scenes and non-scene assets. A bundle must only have one type or the other.", MessageType.Error));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d88fac5a61f1ec947afdfb1d3124030c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "Unity.AssetBundleBrowser.Editor",
|
||||
"references": [
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4b8fc492eac8184fb9a42684cf8c464
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
# Quality Report
|
||||
|
||||
## QA Owner: @davidla
|
||||
|
||||
## Test strategy
|
||||
* Unit test coverage:
|
||||
* Bundle renaming
|
||||
* Bundle folder movement
|
||||
* Bundle creation
|
||||
* Asset movement
|
||||
* Internal bundle updating
|
||||
* Bundle merge and delete
|
||||
* Basic variant functionality
|
||||
* Internal data (model) stability
|
||||
|
||||
## Package Status
|
||||
* open bugs: https://github.com/Unity-Technologies/AssetBundles-Browser/issues
|
||||
* package has been openly available on github since April, officially released on github and the asset store since June. Users have been able to report bugs through github (see above link) and give feedback in the asset store. Current asset store rating is 5 stars after 19 votes.
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b596f3cdbada8164aa95baafaa47dd5c
|
||||
timeCreated: 1511283002
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,10 @@
|
||||
# Unity Asset Bundle Browser tool
|
||||
|
||||
This tool enables the user to view and edit the configuration of asset bundles for their Unity project. It will block editing that would create invalid bundles, and inform you of any issues with existing bundles. It also provides basic build functionality.
|
||||
|
||||
This tool is intended to replace the current workflow of selecting assets and setting their asset bundle manually in the inspector. It can be dropped into any Unity project with a version of 5.6 or greater. It will create a new menu item in *Window->AssetBundle Browser*.
|
||||
|
||||
## Full Documentation
|
||||
#### Official Released Features
|
||||
See [the official manual page](https://docs.unity3d.com/Manual/AssetBundles-Browser.html) or view the included [project manual page](Documentation/com.unity.assetbundlebrowser.md)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ed8819a06e39ed4499b36b63173497f
|
||||
timeCreated: 1507818222
|
||||
licenseType: Pro
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,5 @@
|
||||
Asset Bundle Browser copyright © 2017 Unity Technologies ApS
|
||||
|
||||
Licensed under the Unity Companion License for Unity-dependent projects--see [Unity Companion License](http://www.unity3d.com/legal/licenses/Unity_Companion_License).
|
||||
|
||||
Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions.
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 117b47f9a23bec44b9cff2e62d6a3222
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
{
|
||||
"name": "com.unity.assetbundlebrowser",
|
||||
"displayName": "Asset Bundle Browser",
|
||||
"version": "1.7.0",
|
||||
"unity": "2018.1",
|
||||
"description": "The Asset Bundle Browser tool enables the user to view and edit the configuration of asset bundles for their Unity project. It will block editing that would create invalid bundles, and inform you of any issues with existing bundles. It also provides basic build functionality.\n\nUse this tool as an alternative to selecting assets and setting their asset bundle manually in the inspector. It can be dropped into any Unity project with a version of 5.6 or greater. It will create a new menu item in Window > AssetBundle Browser. The bundle configuration, build functionality, and built-bundle inspection are split into three tabs within the new window.",
|
||||
"keywords": ["asset", "bundle", "bundles", "assetbundles"],
|
||||
"category": "Asset Bundles",
|
||||
"dependencies": {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4581cc1523d2d3e489a8bd6c62aaa345
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user