After feedback patch 1

This commit is contained in:
Ali Sharoz 2025-06-03 04:27:14 +05:00
parent adc1e94b00
commit 2c851159d6
52 changed files with 19985 additions and 4393 deletions

24
Assets/ForcedArabic.cs Normal file
View File

@ -0,0 +1,24 @@
using UnityEngine;
using TMPro;
using ArabicSupport; // Make sure this script is in your project
[RequireComponent(typeof(TextMeshProUGUI))]
public class ForcedArabic : MonoBehaviour
{
[TextArea(2, 5)]
public string rawArabicInput = "العربية"; // You can paste Arabic here
public bool showTashkeel = true;
public bool useHinduNumbers = true;
private TextMeshProUGUI textMeshPro;
void Start()
{
textMeshPro = GetComponent<TextMeshProUGUI>();
// textMeshPro.isRightToLeftText = true;
string fixedText = ArabicFixer.Fix(rawArabicInput, showTashkeel, useHinduNumbers);
textMeshPro.text = fixedText;
}
}

View File

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

View File

@ -140,46 +140,46 @@ MonoBehaviour:
english: All of the above
arabic: "\u0643\u0644 \u0645\u0627 \u0633\u0628\u0642"
- key: whatwerethecluessummary
english: "\U0001F4CC Typos & Spelling Errors The email had spelling mistakes
or odd grammar \u2014 a common phishing sign. \U0001F4CC Urgency & Pressure
It used urgent language to pressure you into acting quickly. \U0001F4CC Link
Inspection The link preview didn\u2019t match the real website \u2014 inspect
links before clicking. \U0001F4CC Unfamiliar or Unexpected Content The email
seemed unrelated to your work \u2014 phishing often comes out of nowhere.
\U0001F4CC Generic Greetings It used a generic greeting like Dear user instead
of your name. \U0001F4CC Unusual Requests It asked for sensitive info like
passwords \u2014 companies don\u2019t do this by email."
arabic: "\U0001F4CC \u0623\u062E\u0637\u0627\u0621 \u0625\u0645\u0644\u0627\u0626\u064A\u0629
\u0648\u0646\u062D\u0648\u064A\u0629 \u062A\u0636\u0645\u0651\u0646 \u0627\u0644\u0628\u0631\u064A\u062F
\u0623\u062E\u0637\u0627\u0621 \u0641\u064A \u0627\u0644\u062A\u0647\u062C\u0626\u0629
\u0623\u0648 \u0642\u0648\u0627\u0639\u062F \u0644\u063A\u0648\u064A\u0629
\u063A\u0631\u064A\u0628\u0629 \u2014 \u0648\u0647\u0630\u0627 \u0645\u0646
\u0639\u0644\u0627\u0645\u0627\u062A \u0627\u0644\u062A\u0635\u064A\u0651\u062F
\u0627\u0644\u0634\u0627\u0626\u0639\u0629. \U0001F4CC \u0627\u0644\u0627\u0633\u062A\u0639\u062C\u0627\u0644
\u0648\u0627\u0644\u0636\u063A\u0637 \u0627\u0633\u062A\u062E\u062F\u0645 \u0644\u063A\u0629
\u0645\u0644\u062D\u0651\u0629 \u0644\u0644\u0636\u063A\u0637 \u0639\u0644\u064A\u0643
\u0643\u064A \u062A\u062A\u0635\u0631\u0641 \u0628\u0633\u0631\u0639\u0629.
\U0001F4CC \u0641\u062D\u0635 \u0627\u0644\u0631\u0627\u0628\u0637 \u0644\u0645
\u064A\u062A\u0637\u0627\u0628\u0642 \u0645\u0639\u0627\u064A\u0646\u0629 \u0627\u0644\u0631\u0627\u0628\u0637
english: "Typos & Spelling Errors The email had spelling mistakes or odd grammar
\u2014 a common phishing sign. Urgency & Pressure It used urgent language
to pressure you into acting quickly. Link Inspection The link preview didn\u2019t
match the real website \u2014 inspect links before clicking. Unfamiliar or
Unexpected Content The email seemed unrelated to your work \u2014 phishing
often comes out of nowhere. Generic Greetings It used a generic greeting like
Dear user instead of your name. Unusual Requests It asked for sensitive info
like passwords \u2014 companies don\u2019t do this by email.email."
arabic: "\u0623\u062E\u0637\u0627\u0621 \u0625\u0645\u0644\u0627\u0626\u064A\u0629
\u0648\u0646\u062D\u0648\u064A\u0629 \u0627\u062D\u062A\u0648\u0649 \u0627\u0644\u0628\u0631\u064A\u062F
\u0627\u0644\u0625\u0644\u0643\u062A\u0631\u0648\u0646\u064A \u0639\u0644\u0649
\u0623\u062E\u0637\u0627\u0621 \u0625\u0645\u0644\u0627\u0626\u064A\u0629 \u0623\u0648
\u0646\u062D\u0648\u064A\u0629 \u063A\u0631\u064A\u0628\u0629 \u2014 \u0648\u0647\u064A
\u0639\u0644\u0627\u0645\u0629 \u0634\u0627\u0626\u0639\u0629 \u0639\u0644\u0649
\u0627\u0644\u062A\u0635\u064A\u062F \u0627\u0644\u0627\u062D\u062A\u064A\u0627\u0644\u064A.
\u0627\u0644\u0627\u0633\u062A\u0639\u062C\u0627\u0644 \u0648\u0627\u0644\u0636\u063A\u0637
\u0627\u0633\u062A\u062E\u062F\u0645 \u0644\u063A\u0629 \u0639\u0627\u062C\u0644\u0629
\u0644\u0644\u0636\u063A\u0637 \u0639\u0644\u064A\u0643 \u0645\u0646 \u0623\u062C\u0644
\u0627\u0644\u062A\u0635\u0631\u0641 \u0628\u0633\u0631\u0639\u0629. \u0641\u062D\u0635
\u0627\u0644\u0631\u0648\u0627\u0628\u0637 \u0645\u0639\u0627\u064A\u0646\u0629
\u0627\u0644\u0631\u0627\u0628\u0637 \u0644\u0645 \u062A\u062A\u0637\u0627\u0628\u0642
\u0645\u0639 \u0627\u0644\u0645\u0648\u0642\u0639 \u0627\u0644\u062D\u0642\u064A\u0642\u064A
\u2014 \u0627\u0641\u062D\u0635 \u0627\u0644\u0631\u0648\u0627\u0628\u0637
\u0642\u0628\u0644 \u0627\u0644\u0646\u0642\u0631 \u0639\u0644\u064A\u0647\u0627.
\U0001F4CC \u0645\u062D\u062A\u0648\u0649 \u063A\u064A\u0631 \u0645\u0623\u0644\u0648\u0641
\u0623\u0648 \u063A\u064A\u0631 \u0645\u062A\u0648\u0642\u0639 \u0628\u062F\u0627
\u0627\u0644\u0628\u0631\u064A\u062F \u063A\u064A\u0631 \u0645\u0631\u062A\u0628\u0637
\u0628\u0639\u0645\u0644\u0643 \u2014 \u0641\u0627\u0644\u062A\u0635\u064A\u0651\u062F
\u063A\u0627\u0644\u0628\u0627\u064B \u0645\u0627 \u064A\u0623\u062A\u064A
\u0645\u0646 \u0645\u0635\u062F\u0631 \u063A\u064A\u0631 \u0645\u062A\u0648\u0642\u0639.
\U0001F4CC \u062A\u062D\u064A\u0629 \u0639\u0627\u0645\u0629 \u0627\u0633\u062A\u062E\u062F\u0645
\u062A\u062D\u064A\u0629 \u0639\u0627\u0645\u0629 \u0645\u062B\u0644 \"\u0639\u0632\u064A\u0632\u064A
\u0645\u062D\u062A\u0648\u0649 \u063A\u064A\u0631 \u0645\u0623\u0644\u0648\u0641
\u0623\u0648 \u063A\u064A\u0631 \u0645\u062A\u0648\u0642\u0639 \u0628\u062F\u0649
\u0627\u0644\u0628\u0631\u064A\u062F \u0627\u0644\u0625\u0644\u0643\u062A\u0631\u0648\u0646\u064A
\u063A\u064A\u0631 \u0645\u0631\u062A\u0628\u0637 \u0628\u0639\u0645\u0644\u0643
\u2014 \u063A\u0627\u0644\u0628\u064B\u0627 \u0645\u0627 \u064A\u0623\u062A\u064A
\u0627\u0644\u062A\u0635\u064A\u062F \u0627\u0644\u0627\u062D\u062A\u064A\u0627\u0644\u064A
\u0628\u0634\u0643\u0644 \u0645\u0641\u0627\u062C\u0626. \u062A\u062D\u064A\u0627\u062A
\u0639\u0627\u0645\u0629 \u0627\u0633\u062A\u062E\u062F\u0645 \u062A\u062D\u064A\u0629
\u0639\u0627\u0645\u0629 \u0645\u062B\u0644 \"\u0639\u0632\u064A\u0632\u064A
\u0627\u0644\u0645\u0633\u062A\u062E\u062F\u0645\" \u0628\u062F\u0644\u0627\u064B
\u0645\u0646 \u0627\u0633\u0645\u0643 \u0627\u0644\u062D\u0642\u064A\u0642\u064A.
\U0001F4CC \u0637\u0644\u0628\u0627\u062A \u063A\u064A\u0631 \u0645\u0639\u062A\u0627\u062F\u0629
\u0637\u0644\u0628 \u0645\u0639\u0644\u0648\u0645\u0627\u062A \u062D\u0633\u0627\u0633\u0629
\u0645\u062B\u0644 \u0643\u0644\u0645\u0627\u062A \u0627\u0644\u0645\u0631\u0648\u0631
\u2014 \u0627\u0644\u0634\u0631\u0643\u0627\u062A \u0644\u0627 \u062A\u0637\u0644\u0628
\u0630\u0644\u0643 \u0639\u0628\u0631 \u0627\u0644\u0628\u0631\u064A\u062F
\u0627\u0644\u0625\u0644\u0643\u062A\u0631\u0648\u0646\u064A. "
\u0645\u0646 \u0627\u0633\u0645\u0643. \u0637\u0644\u0628\u0627\u062A \u063A\u064A\u0631
\u0645\u0639\u062A\u0627\u062F\u0629 \u0637\u0644\u0628 \u0645\u0639\u0644\u0648\u0645\u0627\u062A
\u062D\u0633\u0627\u0633\u0629 \u0645\u062B\u0644 \u0643\u0644\u0645\u0627\u062A
\u0627\u0644\u0645\u0631\u0648\u0631 \u2014 \u0627\u0644\u0634\u0631\u0643\u0627\u062A
\u0644\u0627 \u062A\u0641\u0639\u0644 \u0630\u0644\u0643 \u0639\u0628\u0631
\u0627\u0644\u0628\u0631\u064A\u062F \u0627\u0644\u0625\u0644\u0643\u062A\u0631\u0648\u0646\u064A.\u0648\u0646\u064A. "
- key: howtobesafesummary
english: "\u2022 <b>Verify the sender\u2019s email address carefully</b> Phishing
emails often use addresses that look legit but contain subtle misspellings.

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: c1764d69117881843b761dc14ca276d4
folderAsset: yes
timeCreated: 1561225368
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 19f677a9eb83d3942af6d4c5fa8dbeee
folderAsset: yes
timeCreated: 1520032274
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,29 @@
{
"name": "AssetUsageDetector.Editor",
"rootNamespace": "",
"references": [
"Unity.Addressables"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [
{
"name": "com.unity.addressables",
"expression": "0.0.0",
"define": "ASSET_USAGE_ADDRESSABLES"
},
{
"name": "com.unity.visualeffectgraph",
"expression": "0.0.0",
"define": "ASSET_USAGE_VFX_GRAPH"
}
],
"noEngineReferences": false
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2c0dea52dcdb16e4e9b13f8dacc1590f
timeCreated: 1520032279
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,207 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace AssetUsageDetectorNamespace
{
public partial class AssetUsageDetector
{
#region Helper Classes
private class CacheEntry
{
public enum Result { Unknown = 0, No = 1, Yes = 2 };
public string hash;
public string[] dependencies;
public long[] fileSizes;
public bool verified;
public Result searchResult;
public CacheEntry( string path )
{
Verify( path );
}
public CacheEntry( string hash, string[] dependencies, long[] fileSizes )
{
this.hash = hash;
this.dependencies = dependencies;
this.fileSizes = fileSizes;
}
public void Verify( string path )
{
string hash = AssetDatabase.GetAssetDependencyHash( path ).ToString();
if( this.hash != hash )
{
this.hash = hash;
Refresh( path );
}
verified = true;
}
public void Refresh( string path )
{
dependencies = AssetDatabase.GetDependencies( path, false );
if( fileSizes == null || fileSizes.Length != dependencies.Length )
fileSizes = new long[dependencies.Length];
int length = dependencies.Length;
for( int i = 0; i < length; i++ )
{
if( !string.IsNullOrEmpty( dependencies[i] ) )
{
FileInfo assetFile = new FileInfo( dependencies[i] );
fileSizes[i] = assetFile.Exists ? assetFile.Length : 0L;
}
else
{
// This dependency is empty which causes issues when passed to FileInfo constructor
// Find a non-empty dependency and move it to this index
for( int j = length - 1; j > i; j--, length-- )
{
if( !string.IsNullOrEmpty( dependencies[j] ) )
{
dependencies[i--] = dependencies[j];
break;
}
}
length--;
}
}
if( length != fileSizes.Length )
{
Array.Resize( ref dependencies, length );
Array.Resize( ref fileSizes, length );
}
}
}
#endregion
// An optimization to fetch the dependencies of an asset only once (key is the path of the asset)
private Dictionary<string, CacheEntry> assetDependencyCache;
private CacheEntry lastRefreshedCacheEntry;
private string CachePath { get { return Application.dataPath + "/../Library/AssetUsageDetector.cache"; } } // Path of the cache file
public void SaveCache()
{
if( assetDependencyCache == null )
return;
try
{
using( FileStream stream = new FileStream( CachePath, FileMode.Create ) )
using( BinaryWriter writer = new BinaryWriter( stream ) )
{
writer.Write( assetDependencyCache.Count );
foreach( var keyValuePair in assetDependencyCache )
{
CacheEntry cacheEntry = keyValuePair.Value;
string[] dependencies = cacheEntry.dependencies;
long[] fileSizes = cacheEntry.fileSizes;
writer.Write( keyValuePair.Key );
writer.Write( cacheEntry.hash );
writer.Write( dependencies.Length );
for( int i = 0; i < dependencies.Length; i++ )
{
writer.Write( dependencies[i] );
writer.Write( fileSizes[i] );
}
}
}
}
catch( Exception e )
{
Debug.LogException( e );
}
}
private void LoadCache()
{
if( File.Exists( CachePath ) )
{
using( FileStream stream = new FileStream( CachePath, FileMode.Open, FileAccess.Read ) )
using( BinaryReader reader = new BinaryReader( stream ) )
{
try
{
int cacheSize = reader.ReadInt32();
assetDependencyCache = new Dictionary<string, CacheEntry>( cacheSize );
for( int i = 0; i < cacheSize; i++ )
{
string assetPath = reader.ReadString();
string hash = reader.ReadString();
int dependenciesLength = reader.ReadInt32();
string[] dependencies = new string[dependenciesLength];
long[] fileSizes = new long[dependenciesLength];
for( int j = 0; j < dependenciesLength; j++ )
{
dependencies[j] = reader.ReadString();
fileSizes[j] = reader.ReadInt64();
}
assetDependencyCache[assetPath] = new CacheEntry( hash, dependencies, fileSizes );
}
}
catch( Exception e )
{
assetDependencyCache = null;
Debug.LogWarning( "Couldn't load cache (probably cache format has changed in an update), will regenerate cache.\n" + e.ToString() );
}
}
}
// Generate cache for all assets for the first time
if( assetDependencyCache == null )
{
assetDependencyCache = new Dictionary<string, CacheEntry>( 1024 * 8 );
string[] allAssets = AssetDatabase.GetAllAssetPaths();
if( allAssets.Length > 0 )
{
double startTime = EditorApplication.timeSinceStartup;
try
{
for( int i = 0; i < allAssets.Length; i++ )
{
if( i % 30 == 0 && EditorUtility.DisplayCancelableProgressBar( "Please wait...", "Generating cache for the first time (optional)", (float) i / allAssets.Length ) )
{
EditorUtility.ClearProgressBar();
Debug.LogWarning( "Initial cache generation cancelled, cache will be generated on the fly as more and more assets are searched." );
break;
}
assetDependencyCache[allAssets[i]] = new CacheEntry( allAssets[i] );
}
EditorUtility.ClearProgressBar();
Debug.Log( "Cache generated in " + ( EditorApplication.timeSinceStartup - startTime ).ToString( "F2" ) + " seconds" );
Debug.Log( "You can always reset the cache by deleting " + Path.GetFullPath( CachePath ) );
SaveCache();
}
catch( Exception e )
{
EditorUtility.ClearProgressBar();
Debug.LogException( e );
}
}
}
}
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,298 @@
using UnityEditor;
using UnityEngine;
namespace AssetUsageDetectorNamespace
{
public static class AssetUsageDetectorSettings
{
private static readonly GUILayoutOption GL_WIDTH_60 = GUILayout.Width( 60f );
#region Colors
private static Color? m_settingsHeaderColor = null;
public static Color SettingsHeaderColor
{
get { if( m_settingsHeaderColor == null ) m_settingsHeaderColor = GetColor( "AUD_SettingsHeaderTint", Color.cyan ); return m_settingsHeaderColor.Value; }
set { if( m_settingsHeaderColor == value ) return; m_settingsHeaderColor = value; SetColor( "AUD_SettingsHeaderTint", value ); }
}
private static Color? m_searchResultGroupHeaderColor = null;
public static Color SearchResultGroupHeaderColor
{
get { if( m_searchResultGroupHeaderColor == null ) m_searchResultGroupHeaderColor = GetColor( "AUD_ResultGroupHeaderTint", Color.cyan ); return m_searchResultGroupHeaderColor.Value; }
set { if( m_searchResultGroupHeaderColor == value ) return; m_searchResultGroupHeaderColor = value; SetColor( "AUD_ResultGroupHeaderTint", value ); }
}
private static Color? m_rootRowsBackgroundColor = null;
public static Color RootRowsBackgroundColor
{
get { if( m_rootRowsBackgroundColor == null ) m_rootRowsBackgroundColor = GetColor( "AUD_RootRowsTint", EditorGUIUtility.isProSkin ? new Color( 0f, 1f, 1f, 0.15f ) : new Color( 0f, 1f, 1f, 0.25f ) ); return m_rootRowsBackgroundColor.Value; }
set { if( m_rootRowsBackgroundColor == value ) return; m_rootRowsBackgroundColor = value; SetColor( "AUD_RootRowsTint", value ); }
}
private static Color? m_rootRowsBorderColor = null;
public static Color RootRowsBorderColor
{
get { if( m_rootRowsBorderColor == null ) m_rootRowsBorderColor = GetColor( "AUD_RootRowsBorderColor", EditorGUIUtility.isProSkin ? new Color( 0.15f, 0.15f, 0.15f, 1f ) : new Color( 0.375f, 0.375f, 0.375f, 1f ) ); return m_rootRowsBorderColor.Value; }
set { if( m_rootRowsBorderColor == value ) return; m_rootRowsBorderColor = value; SetColor( "AUD_RootRowsBorderColor", value ); }
}
private static Color? m_mainReferencesBackgroundColor = null;
public static Color MainReferencesBackgroundColor
{
get { if( m_mainReferencesBackgroundColor == null ) m_mainReferencesBackgroundColor = GetColor( "AUD_MainRefRowsTint", EditorGUIUtility.isProSkin ? new Color( 0f, 0.35f, 0f, 1f ) : new Color( 0.25f, 0.75f, 0.25f, 1f ) ); return m_mainReferencesBackgroundColor.Value; }
set { if( m_mainReferencesBackgroundColor == value ) return; m_mainReferencesBackgroundColor = value; SetColor( "AUD_MainRefRowsTint", value ); }
}
private static Color? m_selectedRowsParentTint = null;
public static Color SelectedRowParentsTint
{
get { if( m_selectedRowsParentTint == null ) m_selectedRowsParentTint = GetColor( "AUD_SelectedRowParentsTint", EditorGUIUtility.isProSkin ? new Color( 0.36f, 0.36f, 0.18f, 1f ) : new Color( 0.825f, 0.825f, 0.55f, 1f ) ); return m_selectedRowsParentTint.Value; }
set { if( m_selectedRowsParentTint == value ) return; m_selectedRowsParentTint = value; SetColor( "AUD_SelectedRowParentsTint", value ); }
}
private static Color? m_selectedRowOccurrencesColor = null;
public static Color SelectedRowOccurrencesColor
{
get { if( m_selectedRowOccurrencesColor == null ) m_selectedRowOccurrencesColor = GetColor( "AUD_SelectedRowOccurrencesTint", EditorGUIUtility.isProSkin ? new Color( 0f, 0.3f, 0.75f, 1f ) : new Color( 0.25f, 0.75f, 1f, 1f ) ); return m_selectedRowOccurrencesColor.Value; }
set { if( m_selectedRowOccurrencesColor == value ) return; m_selectedRowOccurrencesColor = value; SetColor( "AUD_SelectedRowOccurrencesTint", value ); }
}
private static Color? m_treeLinesColor = null;
public static Color TreeLinesColor
{
get { if( m_treeLinesColor == null ) m_treeLinesColor = GetColor( "AUD_TreeLinesColor", EditorGUIUtility.isProSkin ? new Color( 0.65f, 0.65f, 0.65f, 1f ) : new Color( 0.375f, 0.375f, 0.375f, 1f ) ); return m_treeLinesColor.Value; }
set { if( m_treeLinesColor == value ) return; m_treeLinesColor = value; SetColor( "AUD_TreeLinesColor", value ); }
}
private static Color? m_highlightedTreeLinesColor = null;
public static Color HighlightedTreeLinesColor
{
get { if( m_highlightedTreeLinesColor == null ) m_highlightedTreeLinesColor = GetColor( "AUD_HighlightTreeLinesColor", Color.cyan ); return m_highlightedTreeLinesColor.Value; }
set { if( m_highlightedTreeLinesColor == value ) return; m_highlightedTreeLinesColor = value; SetColor( "AUD_HighlightTreeLinesColor", value ); }
}
private static Color? m_searchMatchingTextColor = null;
public static Color SearchMatchingTextColor
{
get { if( m_searchMatchingTextColor == null ) m_searchMatchingTextColor = GetColor( "AUD_SearchTextColor", Color.red ); return m_searchMatchingTextColor.Value; }
set { if( m_searchMatchingTextColor == value ) return; m_searchMatchingTextColor = value; SetColor( "AUD_SearchTextColor", value ); ForEachAssetUsageDetectorWindow( ( window ) => window.OnSettingsChanged( highlightedSearchTextColorChanged: true ) ); }
}
private static Color? m_tooltipDescriptionTextColor = null;
public static Color TooltipDescriptionTextColor
{
get { if( m_tooltipDescriptionTextColor == null ) m_tooltipDescriptionTextColor = GetColor( "AUD_TooltipUsageTextColor", EditorGUIUtility.isProSkin ? new Color( 0f, 0.9f, 0.9f, 1f ) : new Color( 0.9f, 0f, 0f, 1f ) ); return m_tooltipDescriptionTextColor.Value; }
set { if( m_tooltipDescriptionTextColor == value ) return; m_tooltipDescriptionTextColor = value; SetColor( "AUD_TooltipUsageTextColor", value ); ForEachAssetUsageDetectorWindow( ( window ) => window.OnSettingsChanged( tooltipDescriptionsColorChanged: true ) ); }
}
#endregion
#region Size Adjustments
private static float? m_extraRowHeight = null;
public static float ExtraRowHeight
{
get { if( m_extraRowHeight == null ) m_extraRowHeight = EditorPrefs.GetFloat( "AUD_ExtraRowHeight", 0f ); return m_extraRowHeight.Value; }
set { if( m_extraRowHeight == value ) return; m_extraRowHeight = value; EditorPrefs.SetFloat( "AUD_ExtraRowHeight", value ); ForEachAssetUsageDetectorWindow( ( window ) => window.OnSettingsChanged() ); }
}
#endregion
#region Other Settings
private static bool? m_showRootAssetName = null;
public static bool ShowRootAssetName
{
get { if( m_showRootAssetName == null ) m_showRootAssetName = EditorPrefs.GetBool( "AUD_ShowRootAssetName", true ); return m_showRootAssetName.Value; }
set { if( m_showRootAssetName == value ) return; m_showRootAssetName = value; EditorPrefs.SetBool( "AUD_ShowRootAssetName", value ); }
}
private static bool? m_pingClickedObjects = null;
public static bool PingClickedObjects
{
get { if( m_pingClickedObjects == null ) m_pingClickedObjects = EditorPrefs.GetBool( "AUD_PingClickedObj", true ); return m_pingClickedObjects.Value; }
set { if( m_pingClickedObjects == value ) return; m_pingClickedObjects = value; EditorPrefs.SetBool( "AUD_PingClickedObj", value ); }
}
private static bool? m_selectClickedObjects = null;
public static bool SelectClickedObjects
{
get { if( m_selectClickedObjects == null ) m_selectClickedObjects = EditorPrefs.GetBool( "AUD_SelectClickedObj", false ); return m_selectClickedObjects.Value; }
set { if( m_selectClickedObjects == value ) return; m_selectClickedObjects = value; EditorPrefs.SetBool( "AUD_SelectClickedObj", value ); }
}
private static bool? m_selectDoubleClickedObjects = null;
public static bool SelectDoubleClickedObjects
{
get { if( m_selectDoubleClickedObjects == null ) m_selectDoubleClickedObjects = EditorPrefs.GetBool( "AUD_SelectDoubleClickedObj", true ); return m_selectDoubleClickedObjects.Value; }
set { if( m_selectDoubleClickedObjects == value ) return; m_selectDoubleClickedObjects = value; EditorPrefs.SetBool( "AUD_SelectDoubleClickedObj", value ); }
}
private static bool? m_markUsedAssetsSubAssetsAsUsed = null;
public static bool MarkUsedAssetsSubAssetsAsUsed
{
get { if( m_markUsedAssetsSubAssetsAsUsed == null ) m_markUsedAssetsSubAssetsAsUsed = EditorPrefs.GetBool( "AUD_MarkUsedAssetsSubAssetsAsUsed", true ); return m_markUsedAssetsSubAssetsAsUsed.Value; }
set { if( m_markUsedAssetsSubAssetsAsUsed == value ) return; m_markUsedAssetsSubAssetsAsUsed = value; EditorPrefs.SetBool( "AUD_MarkUsedAssetsSubAssetsAsUsed", value ); }
}
private static bool? m_showUnityTooltip = null;
public static bool ShowUnityTooltip
{
get { if( m_showUnityTooltip == null ) m_showUnityTooltip = EditorPrefs.GetBool( "AUD_ShowUnityTooltip", false ); return m_showUnityTooltip.Value; }
set { if( m_showUnityTooltip == value ) return; m_showUnityTooltip = value; EditorPrefs.SetBool( "AUD_ShowUnityTooltip", value ); }
}
private static bool? m_showCustomTooltip = null;
public static bool ShowCustomTooltip
{
get { if( m_showCustomTooltip == null ) m_showCustomTooltip = EditorPrefs.GetBool( "AUD_ShowCustomTooltip", true ); return m_showCustomTooltip.Value; }
set { if( m_showCustomTooltip == value ) return; m_showCustomTooltip = value; EditorPrefs.SetBool( "AUD_ShowCustomTooltip", value ); ForEachAssetUsageDetectorWindow( ( window ) => window.OnSettingsChanged() ); }
}
private static float? m_customTooltipDelay = null;
public static float CustomTooltipDelay
{
get { if( m_customTooltipDelay == null ) m_customTooltipDelay = EditorPrefs.GetFloat( "AUD_CustomTooltipDelay", 0.7f ); return m_customTooltipDelay.Value; }
set { if( m_customTooltipDelay == value ) return; m_customTooltipDelay = value; EditorPrefs.SetFloat( "AUD_CustomTooltipDelay", value ); }
}
private static bool? m_showTreeLines = null;
public static bool ShowTreeLines
{
get { if( m_showTreeLines == null ) m_showTreeLines = EditorPrefs.GetBool( "AUD_ShowTreeLines", true ); return m_showTreeLines.Value; }
set { if( m_showTreeLines == value ) return; m_showTreeLines = value; EditorPrefs.SetBool( "AUD_ShowTreeLines", value ); }
}
private static bool? m_applySelectedRowParentsTintToRootRows = null;
public static bool ApplySelectedRowParentsTintToRootRows
{
get { if( m_applySelectedRowParentsTintToRootRows == null ) m_applySelectedRowParentsTintToRootRows = EditorPrefs.GetBool( "AUD_SelectedRowParentsTintAtRoot", true ); return m_applySelectedRowParentsTintToRootRows.Value; }
set { if( m_applySelectedRowParentsTintToRootRows == value ) return; m_applySelectedRowParentsTintToRootRows = value; EditorPrefs.SetBool( "AUD_SelectedRowParentsTintAtRoot", value ); }
}
#endregion
#if UNITY_2018_3_OR_NEWER
[SettingsProvider]
public static SettingsProvider CreatePreferencesGUI()
{
return new SettingsProvider( "Project/yasirkula/Asset Usage Detector", SettingsScope.Project )
{
guiHandler = ( searchContext ) => PreferencesGUI(),
keywords = new System.Collections.Generic.HashSet<string>() { "Asset", "Usage", "Detector" }
};
}
#endif
#if !UNITY_2018_3_OR_NEWER
[PreferenceItem( "Asset Usage Detector" )]
#endif
public static void PreferencesGUI()
{
float labelWidth = EditorGUIUtility.labelWidth;
#if UNITY_2018_3_OR_NEWER
EditorGUIUtility.labelWidth += 60f;
#else
EditorGUIUtility.labelWidth += 20f;
#endif
EditorGUI.BeginChangeCheck();
ShowRootAssetName = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Show Root Asset's Name For Sub-Assets (Requires Refresh)", ShowRootAssetName );
EditorGUILayout.Space();
PingClickedObjects = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Ping Clicked Objects", PingClickedObjects );
SelectClickedObjects = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Select Clicked Objects", SelectClickedObjects );
SelectDoubleClickedObjects = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Select Double Clicked Objects", SelectDoubleClickedObjects );
EditorGUILayout.Space();
MarkUsedAssetsSubAssetsAsUsed = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Hide unused sub-assets in \"Unused Objects\" list if their parent assets are used (Requires Refresh)", MarkUsedAssetsSubAssetsAsUsed );
EditorGUILayout.Space();
ShowUnityTooltip = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Show Unity Tooltip", ShowUnityTooltip );
ShowCustomTooltip = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Show Custom Tooltip", ShowCustomTooltip );
EditorGUI.indentLevel++;
CustomTooltipDelay = FloatField( "Delay", CustomTooltipDelay, 0.7f );
EditorGUI.indentLevel--;
TooltipDescriptionTextColor = ColorField( "Tooltip Descriptions Text Color", TooltipDescriptionTextColor, EditorGUIUtility.isProSkin ? new Color( 0f, 0.9f, 0.9f, 1f ) : new Color( 0.9f, 0f, 0f, 1f ) );
EditorGUILayout.Space();
ExtraRowHeight = Mathf.Max( 0f, FloatField( "Extra Row Height", ExtraRowHeight, 0f ) );
EditorGUILayout.Space();
SettingsHeaderColor = ColorField( "Settings Header Color", SettingsHeaderColor, Color.cyan );
SearchResultGroupHeaderColor = ColorField( "Group Header Color", SearchResultGroupHeaderColor, Color.cyan );
RootRowsBackgroundColor = ColorField( "Root Rows Background Color", RootRowsBackgroundColor, EditorGUIUtility.isProSkin ? new Color( 0f, 1f, 1f, 0.15f ) : new Color( 0f, 1f, 1f, 0.25f ) );
RootRowsBorderColor = ColorField( "Root Rows Border Color", RootRowsBorderColor, EditorGUIUtility.isProSkin ? new Color( 0.15f, 0.15f, 0.15f, 1f ) : new Color( 0.375f, 0.375f, 0.375f, 1f ) );
MainReferencesBackgroundColor = ColorField( "Main References Background Color", MainReferencesBackgroundColor, EditorGUIUtility.isProSkin ? new Color( 0f, 0.35f, 0f, 1f ) : new Color( 0.25f, 0.75f, 0.25f, 1f ) );
SelectedRowParentsTint = ColorField( "Selected Row Parents Tint", SelectedRowParentsTint, EditorGUIUtility.isProSkin ? new Color( 0.36f, 0.36f, 0.18f, 1f ) : new Color( 0.825f, 0.825f, 0.55f, 1f ) );
EditorGUI.indentLevel++;
ApplySelectedRowParentsTintToRootRows = !EditorGUILayout.Toggle( "Ignore Root Rows", !ApplySelectedRowParentsTintToRootRows );
EditorGUI.indentLevel--;
SelectedRowOccurrencesColor = ColorField( "Selected Row All Occurrences Tint", SelectedRowOccurrencesColor, EditorGUIUtility.isProSkin ? new Color( 0f, 0.3f, 0.75f, 1f ) : new Color( 0.25f, 0.75f, 1f, 1f ) );
SearchMatchingTextColor = ColorField( "Matching Search Text Color", SearchMatchingTextColor, Color.red );
ShowTreeLines = AssetUsageDetectorWindow.WordWrappingToggleLeft( "Show Tree Lines", ShowTreeLines );
EditorGUI.indentLevel++;
TreeLinesColor = ColorField( "Normal Color", TreeLinesColor, EditorGUIUtility.isProSkin ? new Color( 0.65f, 0.65f, 0.65f, 1f ) : new Color( 0.375f, 0.375f, 0.375f, 1f ) );
HighlightedTreeLinesColor = ColorField( "Highlighted Color", HighlightedTreeLinesColor, Color.cyan );
EditorGUI.indentLevel--;
EditorGUIUtility.labelWidth = labelWidth;
if( EditorGUI.EndChangeCheck() )
ForEachAssetUsageDetectorWindow( ( window ) => window.Repaint() );
}
private static Color ColorField( string label, Color value, Color defaultValue )
{
GUILayout.BeginHorizontal();
Color result = EditorGUILayout.ColorField( label, value );
if( GUILayout.Button( "Reset", GL_WIDTH_60 ) )
result = defaultValue;
GUILayout.EndHorizontal();
return result;
}
private static float FloatField( string label, float value, float defaultValue )
{
GUILayout.BeginHorizontal();
float result = EditorGUILayout.FloatField( label, value );
if( GUILayout.Button( "Reset", GL_WIDTH_60 ) )
result = defaultValue;
GUILayout.EndHorizontal();
return result;
}
private static Color GetColor( string pref, Color defaultColor )
{
if( EditorGUIUtility.isProSkin )
pref += "_Pro";
if( !EditorPrefs.HasKey( pref ) )
return defaultColor;
string[] parts = EditorPrefs.GetString( pref ).Split( ';' );
return new Color32( byte.Parse( parts[0] ), byte.Parse( parts[1] ), byte.Parse( parts[2] ), byte.Parse( parts[3] ) );
}
private static void SetColor( string pref, Color32 value )
{
if( EditorGUIUtility.isProSkin )
pref += "_Pro";
EditorPrefs.SetString( pref, string.Concat( value.r.ToString(), ";", value.g.ToString(), ";", value.b.ToString(), ";", value.a.ToString() ) );
}
private static void ForEachAssetUsageDetectorWindow( System.Action<AssetUsageDetectorWindow> action )
{
foreach( AssetUsageDetectorWindow window in Resources.FindObjectsOfTypeAll<AssetUsageDetectorWindow>() )
{
if( window )
action( window );
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 13295073724765e45aa3b77486e515f4
timeCreated: 1639982865
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,812 @@
// Asset Usage Detector - by Suleyman Yasir KULA (yasirkula@gmail.com)
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Reflection;
using Object = UnityEngine.Object;
#if UNITY_2021_2_OR_NEWER
using PrefabStage = UnityEditor.SceneManagement.PrefabStage;
using PrefabStageUtility = UnityEditor.SceneManagement.PrefabStageUtility;
#elif UNITY_2018_3_OR_NEWER
using PrefabStage = UnityEditor.Experimental.SceneManagement.PrefabStage;
using PrefabStageUtility = UnityEditor.Experimental.SceneManagement.PrefabStageUtility;
#endif
namespace AssetUsageDetectorNamespace
{
public enum Phase { Setup, Processing, Complete };
public class AssetUsageDetectorWindow : EditorWindow, IHasCustomMenu
{
private enum WindowFilter { AlwaysReturnActive, ReturnActiveIfNotLocked, AlwaysReturnNew };
private const string PREFS_SEARCH_SCENES = "AUD_SceneSearch";
private const string PREFS_SEARCH_SCENE_LIGHTING_SETTINGS = "AUD_LightingSettingsSearch";
private const string PREFS_SEARCH_ASSETS = "AUD_AssetsSearch";
private const string PREFS_SEARCH_PROJECT_SETTINGS = "AUD_ProjectSettingsSearch";
private const string PREFS_DONT_SEARCH_SOURCE_ASSETS = "AUD_AssetsExcludeSrc";
private const string PREFS_SEARCH_DEPTH_LIMIT = "AUD_Depth";
private const string PREFS_SEARCH_FIELDS = "AUD_Fields";
private const string PREFS_SEARCH_PROPERTIES = "AUD_Properties";
private const string PREFS_SEARCH_NON_SERIALIZABLES = "AUD_NonSerializables";
private const string PREFS_SEARCH_UNUSED_MATERIAL_PROPERTIES = "AUD_SearchUnusedMaterialProps";
private const string PREFS_LAZY_SCENE_SEARCH = "AUD_LazySceneSearch";
private const string PREFS_ADDRESSABLES_SUPPORT = "AUD_AddressablesSupport";
private const string PREFS_CALCULATE_UNUSED_OBJECTS = "AUD_FindUnusedObjs";
private const string PREFS_HIDE_DUPLICATE_ROWS = "AUD_HideDuplicates";
private const string PREFS_HIDE_REDUNDANT_PREFAB_VARIANT_LINKS = "AUD_HideRedundantPVariantLinks";
private const string PREFS_SHOW_PROGRESS = "AUD_Progress";
private static readonly GUIContent windowTitle = new GUIContent( "Asset Usage Detector" );
private static readonly Vector2 windowMinSize = new Vector2( 325f, 220f );
private static readonly GUILayoutOption GL_WIDTH_12 = GUILayout.Width( 12f );
private GUIStyle lockButtonStyle;
private readonly AssetUsageDetector core = new AssetUsageDetector();
private SearchResult searchResult; // Overall search results
// This isn't readonly so that it can be serialized
private List<ObjectToSearch> objectsToSearch = new List<ObjectToSearch>() { new ObjectToSearch( null ) };
#pragma warning disable 0649
[SerializeField] // Since titleContent persists between Editor sessions, so should the IsLocked property because otherwise, "[L]" in title becomes confusing when the EditorWindow isn't actually locked
private bool m_isLocked;
private bool IsLocked
{
get { return m_isLocked; }
set
{
if( m_isLocked != value )
{
m_isLocked = value;
titleContent = value ? new GUIContent( "[L] " + windowTitle.text, EditorGUIUtility.IconContent( "InspectorLock" ).image ) : windowTitle;
}
}
}
#pragma warning restore 0649
private Phase currentPhase = Phase.Setup;
private bool searchInOpenScenes = true; // Scenes currently open in Hierarchy view
private bool searchInScenesInBuild = true; // Scenes in build
private bool searchInScenesInBuildTickedOnly = true; // Scenes in build (ticked only or not)
private bool searchInAllScenes = true; // All scenes (including scenes that are not in build)
private bool searchInSceneLightingSettings = true; // Window-Rendering-Lighting settings
private bool searchInAssetsFolder = true; // Assets in Project window
private bool dontSearchInSourceAssets = true; // objectsToSearch won't be searched for internal references
private bool searchInProjectSettings = true; // Player Settings, Graphics Settings etc.
private List<Object> searchInAssetsSubset = new List<Object>() { null }; // If not empty, only these assets are searched for references
private List<Object> excludedAssets = new List<Object>() { null }; // These assets won't be searched for references
private List<Object> excludedScenes = new List<Object>() { null }; // These scenes won't be searched for references
private int searchDepthLimit = 4; // Depth limit for recursively searching variables of objects
private bool lazySceneSearch = true;
#if ASSET_USAGE_ADDRESSABLES
private bool addressablesSupport = false;
#endif
private bool searchNonSerializableVariables = true;
private bool searchUnusedMaterialProperties = true;
private bool calculateUnusedObjects = false;
private bool hideDuplicateRows = true;
private bool hideReduntantPrefabVariantLinks = true;
private bool noAssetDatabaseChanges = false;
private bool showDetailedProgressBar = true;
private BindingFlags fieldModifiers, propertyModifiers;
private SearchRefactoring searchRefactoring = null; // Its value can be assigned via ShowAndSearch
private readonly ObjectToSearchListDrawer objectsToSearchDrawer = new ObjectToSearchListDrawer();
private readonly ObjectListDrawer searchInAssetsSubsetDrawer = new ObjectListDrawer( "Search following asset(s) only:", false );
private readonly ObjectListDrawer excludedAssetsDrawer = new ObjectListDrawer( "Don't search following asset(s):", false );
private readonly ObjectListDrawer excludedScenesDrawer = new ObjectListDrawer( "Don't search in following scene(s):", false );
private bool drawObjectsToSearchSection = true;
private Vector2 scrollPosition = Vector2.zero;
private bool shouldRepositionSelf;
private Rect windowTargetPosition;
void IHasCustomMenu.AddItemsToMenu( GenericMenu contextMenu )
{
contextMenu.AddItem( new GUIContent( "Lock" ), IsLocked, () => IsLocked = !IsLocked );
contextMenu.AddSeparator( "" );
#if UNITY_2018_3_OR_NEWER
contextMenu.AddItem( new GUIContent( "Settings" ), false, () => SettingsService.OpenProjectSettings( "Project/yasirkula/Asset Usage Detector" ) );
#else
contextMenu.AddItem( new GUIContent( "Settings" ), false, () =>
{
System.Type preferencesWindowType = typeof( EditorWindow ).Assembly.GetType( "UnityEditor.PreferencesWindow" );
preferencesWindowType.GetMethod( "ShowPreferencesWindow", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static ).Invoke( null, null );
EditorWindow preferencesWindow = GetWindow( preferencesWindowType );
if( (bool) preferencesWindowType.GetField( "m_RefreshCustomPreferences", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ).GetValue( preferencesWindow ) )
{
preferencesWindowType.GetMethod( "AddCustomSections", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ).Invoke( preferencesWindow, null );
preferencesWindowType.GetField( "m_RefreshCustomPreferences", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ).SetValue( preferencesWindow, false );
}
int targetSectionIndex = -1;
System.Collections.IList sections = (System.Collections.IList) preferencesWindowType.GetField( "m_Sections", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ).GetValue( preferencesWindow );
for( int i = 0; i < sections.Count; i++ )
{
if( ( (GUIContent) sections[i].GetType().GetField( "content", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ).GetValue( sections[i] ) ).text == "Asset Usage Detector" )
{
targetSectionIndex = i;
break;
}
}
if( targetSectionIndex >= 0 )
preferencesWindowType.GetProperty( "selectedSectionIndex", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ).SetValue( preferencesWindow, targetSectionIndex, null );
} );
#endif
if( currentPhase == Phase.Setup )
{
contextMenu.AddSeparator( "" );
contextMenu.AddItem( new GUIContent( "Refresh Sub-Assets of Searched Objects" ), false, () =>
{
for( int i = objectsToSearch.Count - 1; i >= 0; i-- )
objectsToSearch[i].RefreshSubAssets();
} );
}
else if( currentPhase == Phase.Complete )
{
if( searchResult != null && searchResult.NumberOfGroups > 0 )
{
contextMenu.AddSeparator( "" );
contextMenu.AddItem( new GUIContent( "Collapse All" ), false, searchResult.CollapseAllSearchResultGroups );
}
}
}
// Shows lock button at the top-right corner
// Credit: http://leahayes.co.uk/2013/04/30/adding-the-little-padlock-button-to-your-editorwindow.html
private void ShowButton( Rect position )
{
if( lockButtonStyle == null )
lockButtonStyle = "IN LockButton";
IsLocked = GUI.Toggle( position, IsLocked, GUIContent.none, lockButtonStyle );
}
private static AssetUsageDetectorWindow GetWindow( WindowFilter filter )
{
AssetUsageDetectorWindow[] windows = Resources.FindObjectsOfTypeAll<AssetUsageDetectorWindow>();
AssetUsageDetectorWindow window = System.Array.Find( windows, ( w ) => w && !w.IsLocked );
if( !window )
window = System.Array.Find( windows, ( w ) => w );
if( window && ( filter == WindowFilter.AlwaysReturnActive || ( !window.IsLocked && filter == WindowFilter.ReturnActiveIfNotLocked ) ) )
{
window.Show();
window.Focus();
return window;
}
Rect? windowTargetPosition = null;
if( window )
{
Rect position = window.position;
position.position += new Vector2( 50f, 50f );
windowTargetPosition = position;
}
window = CreateInstance<AssetUsageDetectorWindow>();
window.titleContent = windowTitle;
window.minSize = windowMinSize;
if( windowTargetPosition.HasValue )
{
window.shouldRepositionSelf = true;
window.windowTargetPosition = windowTargetPosition.Value;
}
window.Show( true );
window.Focus();
return window;
}
[MenuItem( "Window/Asset Usage Detector/Active Window" )]
private static void OpenActiveWindow()
{
GetWindow( WindowFilter.AlwaysReturnActive );
}
[MenuItem( "Window/Asset Usage Detector/New Window" )]
private static void OpenNewWindow()
{
GetWindow( WindowFilter.AlwaysReturnNew );
}
// Quickly initiate search for the selected assets
[MenuItem( "GameObject/Search for References/This Object Only", priority = 49 )]
[MenuItem( "Assets/Search for References", priority = 1000 )]
private static void SearchSelectedAssetReferences( MenuCommand command )
{
// This happens when this button is clicked via hierarchy's right click context menu
// and is called once for each object in the selection. We don't want that, we want
// the function to be called only once
if( command.context )
{
EditorApplication.update -= CallSearchSelectedAssetReferencesOnce;
EditorApplication.update += CallSearchSelectedAssetReferencesOnce;
}
else
ShowAndSearch( Selection.objects );
}
[MenuItem( "GameObject/Search for References/Include Children", priority = 49 )]
private static void SearchSelectedAssetReferencesWithChildren( MenuCommand command )
{
if( command.context )
{
EditorApplication.update -= CallSearchSelectedAssetReferencesWithChildrenOnce;
EditorApplication.update += CallSearchSelectedAssetReferencesWithChildrenOnce;
}
else
ShowAndSearch( Selection.objects, true );
}
// Show the menu item only if there is a selection in the Editor
[MenuItem( "GameObject/Search for References/This Object Only", validate = true )]
[MenuItem( "GameObject/Search for References/Include Children", validate = true )]
[MenuItem( "Assets/Search for References", validate = true )]
private static bool SearchSelectedAssetReferencesValidate( MenuCommand command )
{
return Selection.objects.Length > 0;
}
// Quickly show the AssetUsageDetector window and initiate a search
public static void ShowAndSearch( IEnumerable<Object> searchObjects, bool? shouldSearchChildren = null )
{
GetWindow( WindowFilter.ReturnActiveIfNotLocked ).ShowAndSearchInternal( searchObjects, null, shouldSearchChildren );
}
// Quickly show the AssetUsageDetector window and initiate a search
public static void ShowAndSearch( AssetUsageDetector.Parameters searchParameters, bool? shouldSearchChildren = null )
{
if( searchParameters == null )
{
Debug.LogError( "searchParameters can't be null!" );
return;
}
GetWindow( WindowFilter.ReturnActiveIfNotLocked ).ShowAndSearchInternal( searchParameters.objectsToSearch, searchParameters, shouldSearchChildren );
}
private static void CallSearchSelectedAssetReferencesOnce()
{
EditorApplication.update -= CallSearchSelectedAssetReferencesOnce;
SearchSelectedAssetReferences( new MenuCommand( null ) );
}
private static void CallSearchSelectedAssetReferencesWithChildrenOnce()
{
EditorApplication.update -= CallSearchSelectedAssetReferencesWithChildrenOnce;
SearchSelectedAssetReferencesWithChildren( new MenuCommand( null ) );
}
private void ShowAndSearchInternal( IEnumerable<Object> searchObjects, AssetUsageDetector.Parameters searchParameters, bool? shouldSearchChildren )
{
if( !ReturnToSetupPhase() )
{
Debug.LogError( "Need to reset the previous search first!" );
return;
}
objectsToSearch.Clear();
if( searchObjects != null )
{
foreach( Object obj in searchObjects )
objectsToSearch.Add( new ObjectToSearch( obj, shouldSearchChildren ) );
}
if( searchParameters != null )
{
ParseSceneSearchMode( searchParameters.searchInScenes );
searchInSceneLightingSettings = searchParameters.searchInSceneLightingSettings;
searchInAssetsFolder = searchParameters.searchInAssetsFolder;
dontSearchInSourceAssets = searchParameters.dontSearchInSourceAssets;
searchInProjectSettings = searchParameters.searchInProjectSettings;
searchDepthLimit = searchParameters.searchDepthLimit;
fieldModifiers = searchParameters.fieldModifiers;
propertyModifiers = searchParameters.propertyModifiers;
searchNonSerializableVariables = searchParameters.searchNonSerializableVariables;
searchUnusedMaterialProperties = searchParameters.searchUnusedMaterialProperties;
searchRefactoring = searchParameters.searchRefactoring;
lazySceneSearch = searchParameters.lazySceneSearch;
#if ASSET_USAGE_ADDRESSABLES
addressablesSupport = searchParameters.addressablesSupport;
#endif
calculateUnusedObjects = searchParameters.calculateUnusedObjects;
hideDuplicateRows = searchParameters.hideDuplicateRows;
hideReduntantPrefabVariantLinks = searchParameters.hideReduntantPrefabVariantLinks;
noAssetDatabaseChanges = searchParameters.noAssetDatabaseChanges;
showDetailedProgressBar = searchParameters.showDetailedProgressBar;
searchInAssetsSubset.Clear();
if( searchParameters.searchInAssetsSubset != null )
{
foreach( Object obj in searchParameters.searchInAssetsSubset )
searchInAssetsSubset.Add( obj );
}
excludedAssets.Clear();
if( searchParameters.excludedAssetsFromSearch != null )
{
foreach( Object obj in searchParameters.excludedAssetsFromSearch )
excludedAssets.Add( obj );
}
excludedScenes.Clear();
if( searchParameters.excludedScenesFromSearch != null )
{
foreach( Object obj in searchParameters.excludedScenesFromSearch )
excludedScenes.Add( obj );
}
}
InitiateSearch();
Repaint();
}
private void Awake()
{
LoadPrefs();
}
private void OnEnable()
{
if( currentPhase == Phase.Complete && AssetUsageDetectorSettings.ShowCustomTooltip )
wantsMouseMove = wantsMouseEnterLeaveWindow = true; // These values aren't preserved during domain reload on Unity 2020.3.0f1
#if UNITY_2018_3_OR_NEWER
PrefabStage.prefabStageClosing -= ReplacePrefabStageObjectsWithAssets;
PrefabStage.prefabStageClosing += ReplacePrefabStageObjectsWithAssets;
#endif
}
private void OnDisable()
{
#if UNITY_2018_3_OR_NEWER
PrefabStage.prefabStageClosing -= ReplacePrefabStageObjectsWithAssets;
#endif
SearchResultTooltip.Hide();
}
private void OnDestroy()
{
if( core != null )
core.SaveCache();
SavePrefs();
if( searchResult != null && currentPhase == Phase.Complete )
searchResult.RestoreInitialSceneSetup();
}
private void SavePrefs()
{
EditorPrefs.SetInt( PREFS_SEARCH_SCENES, (int) GetSceneSearchMode( false ) );
EditorPrefs.SetBool( PREFS_SEARCH_SCENE_LIGHTING_SETTINGS, searchInSceneLightingSettings );
EditorPrefs.SetBool( PREFS_SEARCH_ASSETS, searchInAssetsFolder );
EditorPrefs.SetBool( PREFS_DONT_SEARCH_SOURCE_ASSETS, dontSearchInSourceAssets );
EditorPrefs.SetBool( PREFS_SEARCH_PROJECT_SETTINGS, searchInProjectSettings );
EditorPrefs.SetInt( PREFS_SEARCH_DEPTH_LIMIT, searchDepthLimit );
EditorPrefs.SetInt( PREFS_SEARCH_FIELDS, (int) fieldModifiers );
EditorPrefs.SetInt( PREFS_SEARCH_PROPERTIES, (int) propertyModifiers );
EditorPrefs.SetBool( PREFS_SEARCH_NON_SERIALIZABLES, searchNonSerializableVariables );
EditorPrefs.SetBool( PREFS_SEARCH_UNUSED_MATERIAL_PROPERTIES, searchUnusedMaterialProperties );
EditorPrefs.SetBool( PREFS_LAZY_SCENE_SEARCH, lazySceneSearch );
#if ASSET_USAGE_ADDRESSABLES
EditorPrefs.SetBool( PREFS_ADDRESSABLES_SUPPORT, addressablesSupport );
#endif
EditorPrefs.SetBool( PREFS_CALCULATE_UNUSED_OBJECTS, calculateUnusedObjects );
EditorPrefs.SetBool( PREFS_HIDE_DUPLICATE_ROWS, hideDuplicateRows );
EditorPrefs.SetBool( PREFS_HIDE_REDUNDANT_PREFAB_VARIANT_LINKS, hideReduntantPrefabVariantLinks );
EditorPrefs.SetBool( PREFS_SHOW_PROGRESS, showDetailedProgressBar );
}
private void LoadPrefs()
{
ParseSceneSearchMode( (SceneSearchMode) EditorPrefs.GetInt( PREFS_SEARCH_SCENES, (int) ( SceneSearchMode.OpenScenes | SceneSearchMode.ScenesInBuildSettingsTickedOnly | SceneSearchMode.AllScenes ) ) );
searchInSceneLightingSettings = EditorPrefs.GetBool( PREFS_SEARCH_SCENE_LIGHTING_SETTINGS, true );
searchInAssetsFolder = EditorPrefs.GetBool( PREFS_SEARCH_ASSETS, true );
dontSearchInSourceAssets = EditorPrefs.GetBool( PREFS_DONT_SEARCH_SOURCE_ASSETS, true );
searchInProjectSettings = EditorPrefs.GetBool( PREFS_SEARCH_PROJECT_SETTINGS, true );
searchDepthLimit = EditorPrefs.GetInt( PREFS_SEARCH_DEPTH_LIMIT, 4 );
fieldModifiers = (BindingFlags) EditorPrefs.GetInt( PREFS_SEARCH_FIELDS, (int) ( BindingFlags.Public | BindingFlags.NonPublic ) );
propertyModifiers = (BindingFlags) EditorPrefs.GetInt( PREFS_SEARCH_PROPERTIES, (int) ( BindingFlags.Public | BindingFlags.NonPublic ) );
searchNonSerializableVariables = EditorPrefs.GetBool( PREFS_SEARCH_NON_SERIALIZABLES, true );
searchUnusedMaterialProperties = EditorPrefs.GetBool( PREFS_SEARCH_UNUSED_MATERIAL_PROPERTIES, true );
lazySceneSearch = EditorPrefs.GetBool( PREFS_LAZY_SCENE_SEARCH, true );
#if ASSET_USAGE_ADDRESSABLES
addressablesSupport = EditorPrefs.GetBool( PREFS_ADDRESSABLES_SUPPORT, false );
#endif
calculateUnusedObjects = EditorPrefs.GetBool( PREFS_CALCULATE_UNUSED_OBJECTS, false );
hideDuplicateRows = EditorPrefs.GetBool( PREFS_HIDE_DUPLICATE_ROWS, true );
hideReduntantPrefabVariantLinks = EditorPrefs.GetBool( PREFS_HIDE_REDUNDANT_PREFAB_VARIANT_LINKS, true );
showDetailedProgressBar = EditorPrefs.GetBool( PREFS_SHOW_PROGRESS, true );
}
private SceneSearchMode GetSceneSearchMode( bool hideOptionsInPlayMode )
{
SceneSearchMode sceneSearchMode = SceneSearchMode.None;
if( searchInOpenScenes )
sceneSearchMode |= SceneSearchMode.OpenScenes;
if( !hideOptionsInPlayMode || !EditorApplication.isPlaying )
{
if( searchInScenesInBuild )
sceneSearchMode |= searchInScenesInBuildTickedOnly ? SceneSearchMode.ScenesInBuildSettingsTickedOnly : SceneSearchMode.ScenesInBuildSettingsAll;
if( searchInAllScenes )
sceneSearchMode |= SceneSearchMode.AllScenes;
}
return sceneSearchMode;
}
private void ParseSceneSearchMode( SceneSearchMode sceneSearchMode )
{
searchInOpenScenes = ( sceneSearchMode & SceneSearchMode.OpenScenes ) == SceneSearchMode.OpenScenes;
searchInScenesInBuild = ( sceneSearchMode & SceneSearchMode.ScenesInBuildSettingsAll ) == SceneSearchMode.ScenesInBuildSettingsAll || ( sceneSearchMode & SceneSearchMode.ScenesInBuildSettingsTickedOnly ) == SceneSearchMode.ScenesInBuildSettingsTickedOnly;
searchInScenesInBuildTickedOnly = ( sceneSearchMode & SceneSearchMode.ScenesInBuildSettingsAll ) != SceneSearchMode.ScenesInBuildSettingsAll;
searchInAllScenes = ( sceneSearchMode & SceneSearchMode.AllScenes ) == SceneSearchMode.AllScenes;
}
private void Update()
{
if( shouldRepositionSelf )
{
shouldRepositionSelf = false;
position = windowTargetPosition;
}
}
private void OnGUI()
{
// Make the window scrollable
scrollPosition = EditorGUILayout.BeginScrollView( scrollPosition, Utilities.GL_EXPAND_WIDTH, Utilities.GL_EXPAND_HEIGHT );
GUILayout.BeginVertical();
if( currentPhase == Phase.Processing )
{
// If we are stuck at this phase, then we have encountered an exception
GUILayout.Label( ". . . Search in progress or something went wrong (check console) . . ." );
if( GUILayout.Button( "RETURN", Utilities.GL_HEIGHT_30 ) )
{
ReturnToSetupPhase();
GUIUtility.ExitGUI();
}
}
else if( currentPhase == Phase.Setup )
{
DrawObjectsToSearchSection();
GUILayout.Space( 10f );
Color c = GUI.backgroundColor;
GUI.backgroundColor = AssetUsageDetectorSettings.SettingsHeaderColor;
GUILayout.Box( "<b>SEARCH IN</b>", Utilities.BoxGUIStyle, Utilities.GL_EXPAND_WIDTH );
GUI.backgroundColor = c;
searchInAssetsFolder = WordWrappingToggleLeft( "Project window (Assets folder)", searchInAssetsFolder );
if( searchInAssetsFolder )
{
GUILayout.BeginHorizontal();
GUILayout.Space( 35f );
GUILayout.BeginVertical();
searchInAssetsSubsetDrawer.Draw( searchInAssetsSubset );
excludedAssetsDrawer.Draw( excludedAssets );
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
GUILayout.Space( 5f );
dontSearchInSourceAssets = WordWrappingToggleLeft( "Don't search \"SEARCHED OBJECTS\" themselves for references", dontSearchInSourceAssets );
searchUnusedMaterialProperties = WordWrappingToggleLeft( "Search unused material properties (e.g. normal map of a material that no longer uses normal mapping)", searchUnusedMaterialProperties );
Utilities.DrawSeparatorLine();
if( searchInAllScenes && !EditorApplication.isPlaying )
GUI.enabled = false;
searchInOpenScenes = WordWrappingToggleLeft( "Currently open (loaded) scene(s)", searchInOpenScenes );
if( !EditorApplication.isPlaying )
{
searchInScenesInBuild = WordWrappingToggleLeft( "Scenes in Build Settings", searchInScenesInBuild );
if( searchInScenesInBuild )
{
GUILayout.BeginHorizontal();
GUILayout.Space( 35f );
searchInScenesInBuildTickedOnly = EditorGUILayout.ToggleLeft( "Ticked only", searchInScenesInBuildTickedOnly, Utilities.GL_WIDTH_100 );
searchInScenesInBuildTickedOnly = !EditorGUILayout.ToggleLeft( "All", !searchInScenesInBuildTickedOnly, Utilities.GL_WIDTH_100 );
GUILayout.EndHorizontal();
}
GUI.enabled = true;
searchInAllScenes = WordWrappingToggleLeft( "All scenes in the project", searchInAllScenes );
}
GUILayout.BeginHorizontal();
GUILayout.Space( 35f );
GUILayout.BeginVertical();
excludedScenesDrawer.Draw( excludedScenes );
GUILayout.EndVertical();
GUILayout.EndHorizontal();
EditorGUI.BeginDisabledGroup( !searchInOpenScenes && !searchInScenesInBuild && !searchInAllScenes );
searchInSceneLightingSettings = WordWrappingToggleLeft( "Scene Lighting Settings (WARNING: This may change the active scene during search)", searchInSceneLightingSettings );
EditorGUI.EndDisabledGroup();
Utilities.DrawSeparatorLine();
searchInProjectSettings = WordWrappingToggleLeft( "Project Settings (Player Settings, Graphics Settings etc.)", searchInProjectSettings );
GUILayout.Space( 10f );
GUI.backgroundColor = AssetUsageDetectorSettings.SettingsHeaderColor;
GUILayout.Box( "<b>SETTINGS</b>", Utilities.BoxGUIStyle, Utilities.GL_EXPAND_WIDTH );
GUI.backgroundColor = c;
#if ASSET_USAGE_ADDRESSABLES
EditorGUI.BeginDisabledGroup( addressablesSupport );
#endif
lazySceneSearch = WordWrappingToggleLeft( "Lazy scene search: scenes are searched in detail only when they are manually refreshed (faster search)", lazySceneSearch );
#if ASSET_USAGE_ADDRESSABLES
EditorGUI.EndDisabledGroup();
addressablesSupport = WordWrappingToggleLeft( "Addressables support (Experimental) (WARNING: 'Lazy scene search' will be disabled) (slower search)", addressablesSupport );
#endif
calculateUnusedObjects = WordWrappingToggleLeft( "Calculate unused objects", calculateUnusedObjects );
hideDuplicateRows = WordWrappingToggleLeft( "Hide duplicate rows in search results", hideDuplicateRows );
#if UNITY_2018_3_OR_NEWER
hideReduntantPrefabVariantLinks = WordWrappingToggleLeft( "Hide redundant prefab variant links (when the same value is assigned to the same Component of a prefab and its variant(s))", hideReduntantPrefabVariantLinks );
#endif
noAssetDatabaseChanges = WordWrappingToggleLeft( "I haven't modified any assets/scenes since the last search (faster search)", noAssetDatabaseChanges );
showDetailedProgressBar = WordWrappingToggleLeft( "Update search progress bar more often (cancelable search) (slower search)", showDetailedProgressBar );
GUILayout.Space( 10f );
// Don't let the user press the GO button without any valid search location
if( !searchInAllScenes && !searchInOpenScenes && !searchInScenesInBuild && !searchInAssetsFolder && !searchInProjectSettings )
GUI.enabled = false;
if( GUILayout.Button( "GO!", Utilities.GL_HEIGHT_30 ) )
{
InitiateSearch();
GUIUtility.ExitGUI();
}
GUILayout.Space( 5f );
}
else if( currentPhase == Phase.Complete )
{
// Draw the results of the search
GUI.enabled = false;
DrawObjectsToSearchSection();
if( drawObjectsToSearchSection )
GUILayout.Space( 10f );
GUI.enabled = true;
if( GUILayout.Button( "Reset Search", Utilities.GL_HEIGHT_30 ) )
{
ReturnToSetupPhase();
GUIUtility.ExitGUI();
}
if( searchResult == null )
{
EditorGUILayout.HelpBox( "ERROR: searchResult is null", MessageType.Error );
return;
}
else if( !searchResult.SearchCompletedSuccessfully )
EditorGUILayout.HelpBox( "ERROR: search was interrupted, check the logs for more info", MessageType.Error );
if( searchResult.NumberOfGroups == 0 )
{
GUILayout.Space( 10f );
GUILayout.Box( "No references found...", Utilities.BoxGUIStyle, Utilities.GL_EXPAND_WIDTH );
}
else
{
noAssetDatabaseChanges = WordWrappingToggleLeft( "I haven't modified any assets/scenes since the last search (faster Refresh)", noAssetDatabaseChanges );
EditorGUILayout.Space();
scrollPosition.y = searchResult.DrawOnGUI( this, scrollPosition.y, noAssetDatabaseChanges );
}
}
if( Event.current.type == EventType.MouseLeaveWindow )
{
SearchResultTooltip.Hide();
if( searchResult != null )
searchResult.CancelDelayedTreeViewTooltip();
}
GUILayout.EndVertical();
EditorGUILayout.EndScrollView();
}
private void DrawObjectsToSearchSection()
{
Color c = GUI.backgroundColor;
GUI.backgroundColor = AssetUsageDetectorSettings.SettingsHeaderColor;
GUILayout.Box( "<b>SEARCHED OBJECTS</b>", Utilities.BoxGUIStyle, Utilities.GL_EXPAND_WIDTH );
GUI.backgroundColor = c;
Rect searchedObjectsHeaderRect = GUILayoutUtility.GetLastRect();
searchedObjectsHeaderRect.x += 5f;
searchedObjectsHeaderRect.yMin += ( searchedObjectsHeaderRect.height - EditorGUIUtility.singleLineHeight ) * 0.5f;
searchedObjectsHeaderRect.height = EditorGUIUtility.singleLineHeight;
drawObjectsToSearchSection = EditorGUI.Foldout( searchedObjectsHeaderRect, drawObjectsToSearchSection, GUIContent.none, true );
if( drawObjectsToSearchSection )
objectsToSearchDrawer.Draw( objectsToSearch );
}
public static bool WordWrappingToggleLeft( string label, bool value )
{
GUILayout.BeginHorizontal();
bool result = EditorGUILayout.ToggleLeft( GUIContent.none, value, GL_WIDTH_12 );
if( GUILayout.Button( label, EditorStyles.wordWrappedLabel ) )
{
GUI.FocusControl( null );
result = !value;
}
GUILayout.EndHorizontal();
return result;
}
private void InitiateSearch()
{
currentPhase = Phase.Processing;
SavePrefs();
#if UNITY_2018_3_OR_NEWER
ReplacePrefabStageObjectsWithAssets( PrefabStageUtility.GetCurrentPrefabStage() );
#endif
// Start searching
searchResult = core.Run( new AssetUsageDetector.Parameters()
{
objectsToSearch = !objectsToSearch.IsEmpty() ? new ObjectToSearchEnumerator( objectsToSearch ).ToArray() : null,
searchInScenes = GetSceneSearchMode( true ),
searchInSceneLightingSettings = searchInSceneLightingSettings,
searchInAssetsFolder = searchInAssetsFolder,
searchInAssetsSubset = !searchInAssetsSubset.IsEmpty() ? searchInAssetsSubset.ToArray() : null,
excludedAssetsFromSearch = !excludedAssets.IsEmpty() ? excludedAssets.ToArray() : null,
dontSearchInSourceAssets = dontSearchInSourceAssets,
excludedScenesFromSearch = !excludedScenes.IsEmpty() ? excludedScenes.ToArray() : null,
searchInProjectSettings = searchInProjectSettings,
//fieldModifiers = fieldModifiers,
//propertyModifiers = propertyModifiers,
//searchDepthLimit = searchDepthLimit,
//searchNonSerializableVariables = searchNonSerializableVariables,
searchUnusedMaterialProperties = searchUnusedMaterialProperties,
searchRefactoring = searchRefactoring,
#if ASSET_USAGE_ADDRESSABLES
lazySceneSearch = lazySceneSearch && !addressablesSupport,
addressablesSupport = addressablesSupport,
#else
lazySceneSearch = lazySceneSearch,
#endif
calculateUnusedObjects = calculateUnusedObjects,
hideDuplicateRows = hideDuplicateRows,
hideReduntantPrefabVariantLinks = hideReduntantPrefabVariantLinks,
noAssetDatabaseChanges = noAssetDatabaseChanges,
showDetailedProgressBar = showDetailedProgressBar
} );
currentPhase = Phase.Complete;
// We really don't want SearchRefactoring to affect next searches unless the search is initiated via ShowAndSearch again
searchRefactoring = null;
if( AssetUsageDetectorSettings.ShowCustomTooltip )
wantsMouseMove = wantsMouseEnterLeaveWindow = true;
}
#if UNITY_2018_3_OR_NEWER
// Try replacing searched objects who are part of currently open prefab stage with their corresponding prefab assets
public void ReplacePrefabStageObjectsWithAssets( PrefabStage prefabStage )
{
if( prefabStage == null || !prefabStage.stageHandle.IsValid() )
return;
#if UNITY_2020_1_OR_NEWER
GameObject prefabAsset = AssetDatabase.LoadAssetAtPath<GameObject>( prefabStage.assetPath );
#else
GameObject prefabAsset = AssetDatabase.LoadAssetAtPath<GameObject>( prefabStage.prefabAssetPath );
#endif
if( prefabAsset == null || prefabAsset.Equals( null ) )
return;
for( int i = 0; i < objectsToSearch.Count; i++ )
{
Object obj = objectsToSearch[i].obj;
if( obj != null && !obj.Equals( null ) && obj is GameObject && prefabStage.IsPartOfPrefabContents( (GameObject) obj ) )
{
GameObject prefabStageObjectSource = ( (GameObject) obj ).FollowSymmetricHierarchy( prefabStage.prefabContentsRoot, prefabAsset );
if( prefabStageObjectSource != null )
objectsToSearch[i].obj = prefabStageObjectSource;
List<ObjectToSearch.SubAsset> subAssets = objectsToSearch[i].subAssets;
for( int j = 0; j < subAssets.Count; j++ )
{
obj = subAssets[j].subAsset;
if( obj != null && !obj.Equals( null ) && obj is GameObject && prefabStage.IsPartOfPrefabContents( (GameObject) obj ) )
{
prefabStageObjectSource = ( (GameObject) obj ).FollowSymmetricHierarchy( prefabStage.prefabContentsRoot, prefabAsset );
if( prefabStageObjectSource != null )
subAssets[j].subAsset = prefabStageObjectSource;
}
}
}
}
}
#endif
private bool ReturnToSetupPhase()
{
if( searchResult != null && !EditorApplication.isPlaying && !searchResult.RestoreInitialSceneSetup() )
return false;
searchResult = null;
currentPhase = Phase.Setup;
wantsMouseMove = wantsMouseEnterLeaveWindow = false;
SearchResultTooltip.Hide();
return true;
}
internal void OnSettingsChanged( bool highlightedSearchTextColorChanged = false, bool tooltipDescriptionsColorChanged = false )
{
if( searchResult == null )
return;
wantsMouseMove = wantsMouseEnterLeaveWindow = AssetUsageDetectorSettings.ShowCustomTooltip;
for( int i = searchResult.NumberOfGroups - 1; i >= 0; i-- )
{
if( searchResult[i].treeView != null )
{
searchResult[i].treeView.rowHeight = EditorGUIUtility.singleLineHeight + AssetUsageDetectorSettings.ExtraRowHeight;
searchResult[i].treeView.OnSettingsChanged( highlightedSearchTextColorChanged, tooltipDescriptionsColorChanged );
}
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 271a22c69c3d96c4dbdd04cca415a840
timeCreated: 1520032279
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,130 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace AssetUsageDetectorNamespace
{
public class EmptyEnumerator<T> : IEnumerable<T>, IEnumerator<T>
{
public T Current { get { return default( T ); } }
object IEnumerator.Current { get { return Current; } }
public void Dispose() { }
public void Reset() { }
public bool MoveNext()
{
return false;
}
public IEnumerator<T> GetEnumerator()
{
return this;
}
IEnumerator IEnumerable.GetEnumerator()
{
return this;
}
}
public class ObjectToSearchEnumerator : IEnumerable<Object>
{
public class Enumerator : IEnumerator<Object>
{
public Object Current
{
get
{
if( subAssetIndex < 0 )
return source[index].obj;
return source[index].subAssets[subAssetIndex].subAsset;
}
}
object IEnumerator.Current { get { return Current; } }
private List<ObjectToSearch> source;
private int index;
private int subAssetIndex;
public Enumerator( List<ObjectToSearch> source )
{
this.source = source;
Reset();
}
public void Dispose()
{
source = null;
}
public bool MoveNext()
{
if( subAssetIndex < -1 )
{
subAssetIndex = -1;
if( ++index >= source.Count )
return false;
// Skip folder assets in the enumeration, AssetUsageDetector expands encountered folders automatically
// and we don't want that to happen as source[index].subAssets already contains the folder's contents
if( !source[index].obj.IsFolder() )
return true;
}
List<ObjectToSearch.SubAsset> subAssets = source[index].subAssets;
if( subAssets != null )
{
while( ++subAssetIndex < subAssets.Count && !subAssets[subAssetIndex].shouldSearch )
continue;
if( subAssetIndex < subAssets.Count )
return true;
}
subAssetIndex = -2;
return MoveNext();
}
public void Reset()
{
index = -1;
subAssetIndex = -2;
}
}
private readonly List<ObjectToSearch> source;
public ObjectToSearchEnumerator( List<ObjectToSearch> source )
{
this.source = source;
}
public IEnumerator<Object> GetEnumerator()
{
return new Enumerator( source );
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public Object[] ToArray()
{
int count = 0;
foreach( Object obj in this )
count++;
Object[] result = new Object[count];
int index = 0;
foreach( Object obj in this )
result[index++] = obj;
return result;
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 894047c47ce45cf40939dae24afcc72b
timeCreated: 1562079461
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,252 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace AssetUsageDetectorNamespace
{
public abstract class ListDrawer<T>
{
private readonly string label;
private readonly bool acceptSceneObjects;
protected ListDrawer( string label, bool acceptSceneObjects )
{
this.label = label;
this.acceptSceneObjects = acceptSceneObjects;
}
// Exposes a list on GUI
public bool Draw( List<T> list )
{
bool hasChanged = false;
bool guiEnabled = GUI.enabled;
Event ev = Event.current;
GUILayout.BeginHorizontal();
GUILayout.Label( label );
if( guiEnabled )
{
// Handle drag & drop references to array
// Credit: https://answers.unity.com/answers/657877/view.html
if( ( ev.type == EventType.DragPerform || ev.type == EventType.DragUpdated ) && GUILayoutUtility.GetLastRect().Contains( ev.mousePosition ) )
{
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
if( ev.type == EventType.DragPerform )
{
DragAndDrop.AcceptDrag();
Object[] draggedObjects = DragAndDrop.objectReferences;
if( draggedObjects.Length > 0 )
{
for( int i = 0; i < draggedObjects.Length; i++ )
{
if( draggedObjects[i] != null && !draggedObjects[i].Equals( null ) )
{
bool replacedNullElement = false;
for( int j = 0; j < list.Count; j++ )
{
if( IsElementNull( list[j] ) )
{
list[j] = CreateElement( draggedObjects[i] );
replacedNullElement = true;
break;
}
}
if( !replacedNullElement )
list.Add( CreateElement( draggedObjects[i] ) );
hasChanged = true;
}
}
}
}
ev.Use();
}
else if( ev.type == EventType.ContextClick && GUILayoutUtility.GetLastRect().Contains( ev.mousePosition ) )
{
GenericMenu contextMenu = new GenericMenu();
contextMenu.AddItem( new GUIContent( "Clear" ), false, () =>
{
list.Clear();
list.Add( CreateElement( null ) );
} );
contextMenu.ShowAsContext();
ev.Use();
}
if( GUILayout.Button( "+", Utilities.GL_WIDTH_25 ) )
list.Insert( 0, CreateElement( null ) );
}
GUILayout.EndHorizontal();
for( int i = 0; i < list.Count; i++ )
{
T element = list[i];
GUI.changed = false;
GUILayout.BeginHorizontal();
Object prevObject = GetObjectFromElement( element );
Object newObject = EditorGUILayout.ObjectField( "", prevObject, typeof( Object ), acceptSceneObjects );
if( GUI.changed )
{
hasChanged = true;
SetObjectOfElement( list, i, newObject );
}
if( guiEnabled )
{
if( GUILayout.Button( "+", Utilities.GL_WIDTH_25 ) )
list.Insert( i + 1, CreateElement( null ) );
if( GUILayout.Button( "-", Utilities.GL_WIDTH_25 ) )
{
if( element != null && !element.Equals( null ) )
hasChanged = true;
// Lists with no elements look ugly, always keep a dummy null variable
if( list.Count > 1 )
list.RemoveAt( i-- );
else
list[0] = CreateElement( null );
}
}
GUILayout.EndHorizontal();
PostElementDrawer( element );
}
return hasChanged;
}
protected abstract T CreateElement( Object source );
protected abstract Object GetObjectFromElement( T element );
protected abstract void SetObjectOfElement( List<T> list, int index, Object value );
protected abstract bool IsElementNull( T element );
protected abstract void PostElementDrawer( T element );
}
public class ObjectListDrawer : ListDrawer<Object>
{
public ObjectListDrawer( string label, bool acceptSceneObjects ) : base( label, acceptSceneObjects )
{
}
protected override Object CreateElement( Object source )
{
return source;
}
protected override Object GetObjectFromElement( Object element )
{
return element;
}
protected override void SetObjectOfElement( List<Object> list, int index, Object value )
{
list[index] = value;
}
protected override bool IsElementNull( Object element )
{
return element == null || element.Equals( null );
}
protected override void PostElementDrawer( Object element )
{
}
}
public class ObjectToSearchListDrawer : ListDrawer<ObjectToSearch>
{
public ObjectToSearchListDrawer() : base( "Find references of:", true )
{
}
protected override ObjectToSearch CreateElement( Object source )
{
return new ObjectToSearch( source );
}
protected override Object GetObjectFromElement( ObjectToSearch element )
{
return element.obj;
}
protected override void SetObjectOfElement( List<ObjectToSearch> list, int index, Object value )
{
list[index].obj = value;
list[index].RefreshSubAssets();
}
protected override bool IsElementNull( ObjectToSearch element )
{
return element == null || element.obj == null || element.obj.Equals( null );
}
protected override void PostElementDrawer( ObjectToSearch element )
{
List<ObjectToSearch.SubAsset> subAssetsToSearch = element.subAssets;
if( subAssetsToSearch.Count > 0 )
{
GUILayout.BeginHorizontal();
// 0-> all toggles off, 1-> mixed, 2-> all toggles on
bool toggleAllSubAssets = subAssetsToSearch[0].shouldSearch;
bool mixedToggle = false;
for( int j = 1; j < subAssetsToSearch.Count; j++ )
{
if( subAssetsToSearch[j].shouldSearch != toggleAllSubAssets )
{
mixedToggle = true;
break;
}
}
if( mixedToggle )
EditorGUI.showMixedValue = true;
GUI.changed = false;
toggleAllSubAssets = EditorGUILayout.Toggle( toggleAllSubAssets, Utilities.GL_WIDTH_25 );
if( GUI.changed )
{
for( int j = 0; j < subAssetsToSearch.Count; j++ )
subAssetsToSearch[j].shouldSearch = toggleAllSubAssets;
}
EditorGUI.showMixedValue = false;
element.showSubAssetsFoldout = EditorGUILayout.Foldout( element.showSubAssetsFoldout, "Include sub-assets in search:", true );
GUILayout.EndHorizontal();
if( element.showSubAssetsFoldout )
{
for( int j = 0; j < subAssetsToSearch.Count; j++ )
{
GUILayout.BeginHorizontal();
subAssetsToSearch[j].shouldSearch = EditorGUILayout.Toggle( subAssetsToSearch[j].shouldSearch, Utilities.GL_WIDTH_25 );
bool guiEnabled = GUI.enabled;
GUI.enabled = false;
EditorGUILayout.ObjectField( string.Empty, subAssetsToSearch[j].subAsset, typeof( Object ), true );
GUI.enabled = guiEnabled;
GUILayout.EndHorizontal();
}
}
}
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 88a4a4e861026b2498a437ce1e12b054
timeCreated: 1568758673
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace AssetUsageDetectorNamespace
{
[Serializable]
public class ObjectToSearch
{
[Serializable]
public class SubAsset
{
public Object subAsset;
public bool shouldSearch;
public SubAsset( Object subAsset, bool shouldSearch )
{
this.subAsset = subAsset;
this.shouldSearch = shouldSearch;
}
}
public Object obj;
public List<SubAsset> subAssets;
public bool showSubAssetsFoldout;
private static HashSet<Object> currentSubAssets;
public ObjectToSearch( Object obj, bool? shouldSearchChildren = null )
{
this.obj = obj;
RefreshSubAssets( shouldSearchChildren );
}
public void RefreshSubAssets( bool? shouldSearchChildren = null )
{
if( subAssets == null )
subAssets = new List<SubAsset>();
else
subAssets.Clear();
if( currentSubAssets == null )
currentSubAssets = new HashSet<Object>();
else
currentSubAssets.Clear();
AddSubAssets( obj, false, shouldSearchChildren );
currentSubAssets.Clear();
}
private void AddSubAssets( Object target, bool includeTarget, bool? shouldSearchChildren )
{
if( target == null || target.Equals( null ) )
return;
if( !target.IsAsset() )
{
GameObject go = target as GameObject;
if( !go || !go.scene.IsValid() )
return;
// If this is a scene object, add its child objects to the sub-assets list
// but don't include them in the search by default
Transform goTransform = go.transform;
Transform[] children = go.GetComponentsInChildren<Transform>( true );
for( int i = 0; i < children.Length; i++ )
{
if( ReferenceEquals( children[i], goTransform ) )
continue;
subAssets.Add( new SubAsset( children[i].gameObject, shouldSearchChildren ?? false ) );
}
}
else
{
if( !AssetDatabase.IsMainAsset( target ) || target is SceneAsset )
return;
if( includeTarget )
{
if( currentSubAssets.Add( target ) )
subAssets.Add( new SubAsset( target, shouldSearchChildren ?? true ) );
}
else
{
// If asset is a directory, add all of its contents as sub-assets recursively
if( target.IsFolder() )
{
foreach( string filePath in Utilities.EnumerateFolderContents( target ) )
AddSubAssets( AssetDatabase.LoadAssetAtPath<Object>( filePath ), true, shouldSearchChildren );
return;
}
}
// Find sub-asset(s) of the asset (if any)
Object[] assets = AssetDatabase.LoadAllAssetsAtPath( AssetDatabase.GetAssetPath( target ) );
for( int i = 0; i < assets.Length; i++ )
{
Object asset = assets[i];
if( asset == null || asset.Equals( null ) || asset is Component || asset == target )
continue;
#if UNITY_2018_3_OR_NEWER
// Nested prefabs in prefab assets add an additional native object of type 'UnityEngine.PrefabInstance' to the prefab. Managed type of that native type
// is UnityEngine.Object (i.e. GetType() returns UnityEngine.Object, not UnityEngine.PrefabInstance). There are no possible references to these native
// objects so skip them (we're checking for UnityEngine.Prefab because it includes other native types like UnityEngine.PrefabCreation, as well)
if( target is GameObject && asset.GetType() == typeof( Object ) && asset.ToString().Contains( "(UnityEngine.Prefab" ) )
continue;
#endif
if( currentSubAssets.Add( asset ) )
subAssets.Add( new SubAsset( asset, shouldSearchChildren ?? true ) );
}
}
}
}
}

View File

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

View File

@ -0,0 +1,382 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using Object = UnityEngine.Object;
namespace AssetUsageDetectorNamespace
{
public delegate void SearchRefactoring( SearchMatch match );
public abstract class SearchMatch
{
public readonly object Source;
public readonly Object Context; // Almost always equal to Source. This is the Object that needs to be dirtied (if not null) to notify Unity of changes to Value
public Object Value { get; private set; }
protected SearchMatch( object source, Object value )
{
Source = source;
Context = source as Object;
Value = value;
}
protected SearchMatch( object source, Object value, Object context ) : this( source, value )
{
Context = context;
}
public void ChangeValue( Object newValue )
{
if( newValue == Value )
return;
if( Context && ( Context.hideFlags & HideFlags.NotEditable ) == HideFlags.NotEditable )
{
Debug.LogWarning( "Can't change value of read-only Object: " + Context, Context );
return;
}
try
{
bool setContextDirty;
if( ChangeValue( newValue, out setContextDirty ) )
OnValueChanged( newValue, setContextDirty );
}
catch( Exception e )
{
Debug.LogException( e );
}
}
protected abstract bool ChangeValue( Object newValue, out bool setContextDirty );
public void OnValueChanged( Object newValue, bool setContextDirty = true )
{
Value = newValue;
if( setContextDirty )
{
if( Context )
{
if( AssetDatabase.Contains( Context ) )
EditorUtility.SetDirty( Context );
else if( !EditorApplication.isPlaying )
{
EditorUtility.SetDirty( Context );
if( Context is Component )
EditorSceneManager.MarkSceneDirty( ( (Component) Context ).gameObject.scene );
else if( Context is GameObject )
EditorSceneManager.MarkSceneDirty( ( (GameObject) Context ).scene );
else
EditorSceneManager.MarkAllScenesDirty();
}
}
else if( !EditorApplication.isPlaying )
EditorSceneManager.MarkAllScenesDirty();
}
}
}
public abstract class GenericSearchMatch : SearchMatch
{
public delegate void SetterFunction( Object newValue );
public readonly SetterFunction Setter;
internal GenericSearchMatch( object source, Object value, SetterFunction setter ) : base( source, value ) { Setter = setter; }
internal GenericSearchMatch( object source, Object value, Object context, SetterFunction setter ) : base( source, value, context ) { Setter = setter; }
protected override bool ChangeValue( Object newValue, out bool setContextDirty )
{
Setter( newValue );
setContextDirty = true;
return true;
}
}
public abstract class ReadOnlySearchMatch : SearchMatch
{
internal ReadOnlySearchMatch( object source, Object value ) : base( source, value ) { }
protected override bool ChangeValue( Object newValue, out bool setContextDirty )
{
Debug.LogWarning( "Can't change value of " + GetType().Name );
setContextDirty = false;
return false;
}
}
/// <summary>
/// - Source: Object whose SerializedProperty points to Value
/// - Value: Referenced object
/// - SerializedProperty: The SerializedProperty that points to Value
/// </summary>
public class SerializedPropertyMatch : SearchMatch
{
public readonly SerializedProperty SerializedProperty; // Next or NextVisible mustn't be called with this SerializedProperty
internal SerializedPropertyMatch( Object source, Object value, SerializedProperty property ) : base( source, value ) { SerializedProperty = property; }
protected override bool ChangeValue( Object newValue, out bool setContextDirty )
{
setContextDirty = true;
switch( SerializedProperty.propertyType )
{
case SerializedPropertyType.ObjectReference:
SerializedProperty.objectReferenceValue = newValue;
if( SerializedProperty.objectReferenceValue != newValue )
{
Debug.LogWarning( "Couldn't cast " + newValue.GetType() + " to " + SerializedProperty.type );
SerializedProperty.objectReferenceValue = Value;
return false;
}
break;
case SerializedPropertyType.ExposedReference:
SerializedProperty.exposedReferenceValue = newValue;
if( SerializedProperty.exposedReferenceValue != newValue )
{
Debug.LogWarning( "Couldn't cast " + newValue.GetType() + " to " + SerializedProperty.type );
SerializedProperty.exposedReferenceValue = Value;
return false;
}
break;
#if UNITY_2019_3_OR_NEWER
case SerializedPropertyType.ManagedReference: SerializedProperty.managedReferenceValue = newValue; break;
#endif
}
SerializedProperty.serializedObject.ApplyModifiedPropertiesWithoutUndo();
return true;
}
}
/// <summary>
/// - Source: Object whose variable points to Value
/// - Value: Referenced object
/// - Variable: FieldInfo, PropertyInfo or IEnumerable (ChangeValue may not work for all IEnumerables)
/// </summary>
public class ReflectionMatch : SearchMatch
{
public readonly object Variable;
internal ReflectionMatch( object source, Object value, object variable ) : base( source, value ) { Variable = variable; }
protected override bool ChangeValue( Object newValue, out bool setContextDirty )
{
setContextDirty = true;
if( Variable is FieldInfo )
( (FieldInfo) Variable ).SetValue( Source, newValue );
else if( Variable is PropertyInfo )
{
PropertyInfo property = (PropertyInfo) Variable;
if( !property.CanWrite )
{
Debug.LogWarning( "Property is read-only: " + property.DeclaringType.FullName + "." + property.Name );
return false;
}
property.SetValue( Source, newValue, null );
}
else if( Variable is IList )
{
IList list = (IList) Variable;
for( int i = list.Count - 1; i >= 0; i-- )
{
if( ReferenceEquals( list[i], Value ) )
list[i] = newValue;
}
}
else if( Variable is IDictionary )
{
IDictionary dictionary = (IDictionary) Variable;
bool dictionaryModified;
do
{
dictionaryModified = false;
foreach( object dictKey in dictionary.Keys )
{
object dictValue = dictionary[dictKey];
if( ReferenceEquals( dictKey, Value ) )
{
dictionary.Remove( dictKey );
if( newValue )
dictionary[newValue] = dictValue;
dictionaryModified = true;
break;
}
else if( ReferenceEquals( dictValue, Value ) )
{
dictionary[dictKey] = newValue;
dictionaryModified = true;
break;
}
}
} while( dictionaryModified );
}
else
{
Debug.LogWarning( "Can't change value of " + Variable.GetType().Name );
return false;
}
return true;
}
}
/// <summary>
/// - Source: MonoImporter (for scripts) or ShaderImporter
/// - Value: Default value assigned to Source's specified variable in the Inspector
/// - Variable: The variable of Source that Value is assigned to as default value
/// - MonoScriptAllVariables: All variables of Source script if it's MonoImporter
/// </summary>
public class AssetImporterDefaultValueMatch : SearchMatch
{
public readonly string Variable;
public readonly VariableGetterHolder[] MonoScriptAllVariables;
internal AssetImporterDefaultValueMatch( Object source, Object value, string variable, VariableGetterHolder[] monoScriptAllVariables ) : base( source, value )
{
Variable = variable;
MonoScriptAllVariables = monoScriptAllVariables;
}
protected override bool ChangeValue( Object newValue, out bool setContextDirty )
{
setContextDirty = false;
if( Source is MonoImporter )
{
MonoImporter monoImporter = (MonoImporter) Source;
List<string> variableNames = new List<string>( 8 );
List<Object> variableValues = new List<Object>( 8 );
for( int i = 0; i < MonoScriptAllVariables.Length; i++ )
{
if( MonoScriptAllVariables[i].isSerializable && !MonoScriptAllVariables[i].IsProperty )
{
Object variableDefaultValue = monoImporter.GetDefaultReference( MonoScriptAllVariables[i].Name );
if( variableDefaultValue == Value && MonoScriptAllVariables[i].Name == Variable )
variableDefaultValue = newValue;
variableNames.Add( MonoScriptAllVariables[i].Name );
variableValues.Add( variableDefaultValue );
}
}
monoImporter.SetDefaultReferences( variableNames.ToArray(), variableValues.ToArray() );
EditorApplication.delayCall += () => AssetDatabase.ImportAsset( monoImporter.assetPath ); // If code recompiles during search, it will break the search. Give it a 1 frame delay
}
else if( Source is ShaderImporter )
{
ShaderImporter shaderImporter = (ShaderImporter) Source;
Shader shader = shaderImporter.GetShader();
List<string> textureNames = new List<string>( 16 );
List<Texture> textureValues = new List<Texture>( 16 );
#if UNITY_2018_1_OR_NEWER
List<string> nonModifiableTextureNames = new List<string>( 16 );
List<Texture> nonModifiableTextureValues = new List<Texture>( 16 );
#endif
int shaderPropertyCount = ShaderUtil.GetPropertyCount( shader );
for( int i = 0; i < shaderPropertyCount; i++ )
{
if( ShaderUtil.GetPropertyType( shader, i ) != ShaderUtil.ShaderPropertyType.TexEnv )
continue;
string propertyName = ShaderUtil.GetPropertyName( shader, i );
#if UNITY_2018_1_OR_NEWER
if( ShaderUtil.IsShaderPropertyNonModifiableTexureProperty( shader, i ) )
{
Texture propertyDefaultValue = shaderImporter.GetNonModifiableTexture( propertyName );
if( propertyDefaultValue == Value && propertyName == Variable )
propertyDefaultValue = (Texture) newValue;
nonModifiableTextureNames.Add( propertyName );
nonModifiableTextureValues.Add( propertyDefaultValue );
}
else
#endif
{
Texture propertyDefaultValue = shaderImporter.GetDefaultTexture( propertyName );
if( propertyDefaultValue == Value && propertyName == Variable )
propertyDefaultValue = (Texture) newValue;
textureNames.Add( propertyName );
textureValues.Add( propertyDefaultValue );
}
}
shaderImporter.SetDefaultTextures( textureNames.ToArray(), textureValues.ToArray() );
#if UNITY_2018_1_OR_NEWER
shaderImporter.SetNonModifiableTextures( nonModifiableTextureNames.ToArray(), nonModifiableTextureValues.ToArray() );
#endif
AssetDatabase.ImportAsset( shaderImporter.assetPath );
}
else
{
Debug.LogWarning( "Can't change default value of: " + Source.GetType() );
return false;
}
return true;
}
}
/// <summary>
/// - Source: Animation, Animator, AnimatorStateMachine, AnimatorState, AnimatorControllerLayer, BlendTree, PlayableDirector* or AnimationClip*
/// - Context: If Source is AnimatorControllerLayer, then its RuntimeAnimatorController. Otherwise, equal to Source
/// - Value: AnimationClip, AnimatorController or AvatarMask used in Source (*for PlayableDirector and AnimationClip, it can be any Object value)
/// </summary>
public class AnimationSystemMatch : GenericSearchMatch
{
internal AnimationSystemMatch( object source, Object value, SetterFunction setter ) : base( source, value, setter ) { }
internal AnimationSystemMatch( object source, Object value, Object context, SetterFunction setter ) : base( source, value, context, setter ) { }
}
/// <summary>
/// - Source: GameObject, AnimatorStateMachine or AnimatorState
/// - Value: The attached behaviour's source script (C# script or DLL, i.e. MonoScript)
/// - Behaviour: The attached behaviour (MonoBehaviour or StateMachineBehaviour)
/// </summary>
public class BehaviourUsageMatch : ReadOnlySearchMatch
{
public readonly Object Behaviour;
internal BehaviourUsageMatch( Object source, MonoScript value, Object behaviour ) : base( source, value ) { Behaviour = behaviour; }
}
/// <summary>
/// - Source: GameObject Instance
/// - Value: Prefab of that GameObject
/// </summary>
public class PrefabMatch : ReadOnlySearchMatch
{
internal PrefabMatch( Object source, Object value ) : base( source, value ) { }
}
/// <summary>
/// - Source: Object that references Value
/// - Value: Matched object
/// </summary>
public class OtherSearchMatch : GenericSearchMatch
{
internal OtherSearchMatch( object source, Object value, SetterFunction setter ) : base( source, value, setter ) { }
internal OtherSearchMatch( object source, Object value, Object context, SetterFunction setter ) : base( source, value, context, setter ) { }
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3d15ef8bd8f7c7c4e8d228c99713b7eb
timeCreated: 1641132238
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,91 @@
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace AssetUsageDetectorNamespace
{
public class SearchResultTooltip : EditorWindow
{
private static SearchResultTooltip mainWindow;
private static string tooltip;
private static GUIStyle m_style;
internal static GUIStyle Style
{
get
{
if( m_style == null )
{
m_style = (GUIStyle) typeof( EditorStyles ).GetProperty( "tooltip", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static ).GetValue( null, null );
m_style.richText = true;
}
return m_style;
}
}
public static void Show( Rect sourcePosition, string tooltip )
{
Vector2 preferredSize = Style.CalcSize( new GUIContent( tooltip ) ) + Style.contentOffset + new Vector2( Style.padding.horizontal + Style.margin.horizontal, Style.padding.vertical + Style.margin.vertical );
Rect preferredPosition;
Rect positionLeft = new Rect( sourcePosition.position - new Vector2( preferredSize.x, 0f ), preferredSize );
Rect screenFittedPositionLeft = Utilities.GetScreenFittedRect( positionLeft );
Vector2 positionOffset = positionLeft.position - screenFittedPositionLeft.position;
Vector2 sizeOffset = positionLeft.size - screenFittedPositionLeft.size;
if( positionOffset.sqrMagnitude <= 400f && sizeOffset.sqrMagnitude <= 400f )
preferredPosition = screenFittedPositionLeft;
else
{
Rect positionRight = new Rect( sourcePosition.position + new Vector2( sourcePosition.width, 0f ), preferredSize );
Rect screenFittedPositionRight = Utilities.GetScreenFittedRect( positionRight );
Vector2 positionOffset2 = positionRight.position - screenFittedPositionRight.position;
Vector2 sizeOffset2 = positionRight.size - screenFittedPositionRight.size;
if( positionOffset2.magnitude + sizeOffset2.magnitude < positionOffset.magnitude + sizeOffset.magnitude )
preferredPosition = screenFittedPositionRight;
else
preferredPosition = screenFittedPositionLeft;
}
// Don't lose focus to the previous window
EditorWindow prevFocusedWindow = focusedWindow;
if( !mainWindow )
{
mainWindow = CreateInstance<SearchResultTooltip>();
mainWindow.ShowPopup();
}
SearchResultTooltip.tooltip = tooltip;
mainWindow.minSize = preferredPosition.size;
mainWindow.position = preferredPosition;
mainWindow.Repaint();
if( prevFocusedWindow )
prevFocusedWindow.Focus();
}
public static void Hide()
{
if( mainWindow )
{
mainWindow.Close();
mainWindow = null;
}
}
private void OnGUI()
{
// If somehow the tooltip isn't automatically closed, allow closing it by clicking on it
if( Event.current.type == EventType.MouseDown )
{
Hide();
GUIUtility.ExitGUI();
}
GUI.Label( new Rect( Vector2.zero, position.size ), tooltip, Style );
}
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c8bd4351b5024324ca5974ebcad1dde3
timeCreated: 1639247551
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: b3903f1d3149e9b4990e49206f8255c8
timeCreated: 1638690728
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,565 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
#if UNITY_2018_1_OR_NEWER
using Unity.Collections;
#endif
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
#if UNITY_2018_3_OR_NEWER && !UNITY_2021_2_OR_NEWER
using PrefabStage = UnityEditor.Experimental.SceneManagement.PrefabStage;
using PrefabStageUtility = UnityEditor.Experimental.SceneManagement.PrefabStageUtility;
#endif
namespace AssetUsageDetectorNamespace
{
public static class Utilities
{
// A set of commonly used Unity types
private static readonly HashSet<Type> primitiveUnityTypes = new HashSet<Type>()
{
typeof( string ), typeof( Vector4 ), typeof( Vector3 ), typeof( Vector2 ), typeof( Rect ),
typeof( Quaternion ), typeof( Color ), typeof( Color32 ), typeof( LayerMask ), typeof( Bounds ),
typeof( Matrix4x4 ), typeof( AnimationCurve ), typeof( Gradient ), typeof( RectOffset ),
typeof( bool[] ), typeof( byte[] ), typeof( sbyte[] ), typeof( char[] ), typeof( decimal[] ),
typeof( double[] ), typeof( float[] ), typeof( int[] ), typeof( uint[] ), typeof( long[] ),
typeof( ulong[] ), typeof( short[] ), typeof( ushort[] ), typeof( string[] ),
typeof( Vector4[] ), typeof( Vector3[] ), typeof( Vector2[] ), typeof( Rect[] ),
typeof( Quaternion[] ), typeof( Color[] ), typeof( Color32[] ), typeof( LayerMask[] ), typeof( Bounds[] ),
typeof( Matrix4x4[] ), typeof( AnimationCurve[] ), typeof( Gradient[] ), typeof( RectOffset[] ),
typeof( List<bool> ), typeof( List<byte> ), typeof( List<sbyte> ), typeof( List<char> ), typeof( List<decimal> ),
typeof( List<double> ), typeof( List<float> ), typeof( List<int> ), typeof( List<uint> ), typeof( List<long> ),
typeof( List<ulong> ), typeof( List<short> ), typeof( List<ushort> ), typeof( List<string> ),
typeof( List<Vector4> ), typeof( List<Vector3> ), typeof( List<Vector2> ), typeof( List<Rect> ),
typeof( List<Quaternion> ), typeof( List<Color> ), typeof( List<Color32> ), typeof( List<LayerMask> ), typeof( List<Bounds> ),
typeof( List<Matrix4x4> ), typeof( List<AnimationCurve> ), typeof( List<Gradient> ), typeof( List<RectOffset> ),
#if UNITY_2017_2_OR_NEWER
typeof( Vector3Int ), typeof( Vector2Int ), typeof( RectInt ), typeof( BoundsInt ),
typeof( Vector3Int[] ), typeof( Vector2Int[] ), typeof( RectInt[] ), typeof( BoundsInt[] ),
typeof( List<Vector3Int> ), typeof( List<Vector2Int> ), typeof( List<RectInt> ), typeof( List<BoundsInt> )
#endif
};
private static readonly string reflectionNamespace = typeof( Assembly ).Namespace;
#if UNITY_2018_1_OR_NEWER
private static readonly string nativeCollectionsNamespace = typeof( NativeArray<int> ).Namespace;
#endif
private static MethodInfo screenFittedRectGetter;
private static readonly HashSet<string> folderContentsSet = new HashSet<string>();
internal static readonly StringBuilder stringBuilder = new StringBuilder( 400 );
public static readonly GUILayoutOption GL_EXPAND_WIDTH = GUILayout.ExpandWidth( true );
public static readonly GUILayoutOption GL_EXPAND_HEIGHT = GUILayout.ExpandHeight( true );
public static readonly GUILayoutOption GL_WIDTH_25 = GUILayout.Width( 25 );
public static readonly GUILayoutOption GL_WIDTH_100 = GUILayout.Width( 100 );
public static readonly GUILayoutOption GL_WIDTH_250 = GUILayout.Width( 250 );
public static readonly GUILayoutOption GL_HEIGHT_0 = GUILayout.Height( 0 );
public static readonly GUILayoutOption GL_HEIGHT_2 = GUILayout.Height( 2 );
public static readonly GUILayoutOption GL_HEIGHT_30 = GUILayout.Height( 30 );
public static readonly GUILayoutOption GL_HEIGHT_35 = GUILayout.Height( 35 );
public static readonly GUILayoutOption GL_HEIGHT_40 = GUILayout.Height( 40 );
private static GUIStyle m_boxGUIStyle; // GUIStyle used to draw the results of the search
public static GUIStyle BoxGUIStyle
{
get
{
if( m_boxGUIStyle == null )
{
m_boxGUIStyle = new GUIStyle( EditorStyles.helpBox )
{
alignment = TextAnchor.MiddleCenter,
font = EditorStyles.label.font,
richText = true
};
Color textColor = GUI.skin.button.normal.textColor;
m_boxGUIStyle.normal.textColor = textColor;
m_boxGUIStyle.hover.textColor = textColor;
m_boxGUIStyle.focused.textColor = textColor;
m_boxGUIStyle.active.textColor = textColor;
#if !UNITY_2019_1_OR_NEWER || UNITY_2019_3_OR_NEWER
// On 2019.1 and 2019.2 versions, GUI.skin.button.fontSize returns 0 on some devices
// https://forum.unity.com/threads/asset-usage-detector-find-references-to-an-asset-object-open-source.408134/page-3#post-7285954
m_boxGUIStyle.fontSize = ( m_boxGUIStyle.fontSize + GUI.skin.button.fontSize ) / 2;
#endif
}
return m_boxGUIStyle;
}
}
// Check if object is an asset or a Scene object
public static bool IsAsset( this object obj )
{
return obj is Object && AssetDatabase.Contains( (Object) obj );
}
public static bool IsAsset( this Object obj )
{
return AssetDatabase.Contains( obj );
}
// Check if object is a folder asset
public static bool IsFolder( this Object obj )
{
return obj is DefaultAsset && AssetDatabase.IsValidFolder( AssetDatabase.GetAssetPath( obj ) );
}
// Returns an enumerator to iterate through all asset paths in the folder
public static IEnumerable<string> EnumerateFolderContents( Object folderAsset )
{
string[] folderContents = AssetDatabase.FindAssets( "", new string[] { AssetDatabase.GetAssetPath( folderAsset ) } );
if( folderContents == null )
return new EmptyEnumerator<string>();
folderContentsSet.Clear();
for( int i = 0; i < folderContents.Length; i++ )
{
string filePath = AssetDatabase.GUIDToAssetPath( folderContents[i] );
if( !string.IsNullOrEmpty( filePath ) && !AssetDatabase.IsValidFolder( filePath ) )
folderContentsSet.Add( filePath );
}
return folderContentsSet;
}
public static void GetObjectsToSelectAndPing( this Object obj, out Object selection, out Object pingTarget )
{
if( obj == null || obj.Equals( null ) )
{
selection = pingTarget = null;
return;
}
if( obj is Component )
obj = ( (Component) obj ).gameObject;
selection = pingTarget = obj;
if( obj.IsAsset() )
{
if( obj is GameObject )
{
// Pinging a prefab only works if the pinged object is the root of the prefab or a direct child of it. Pinging any grandchildren
// of the prefab doesn't work; in which case, traverse the parent hierarchy until a pingable parent is reached
#if UNITY_2018_3_OR_NEWER
Transform objTR = ( (GameObject) obj ).transform.root;
PrefabAssetType prefabAssetType = PrefabUtility.GetPrefabAssetType( objTR.gameObject );
if( prefabAssetType == PrefabAssetType.Regular || prefabAssetType == PrefabAssetType.Variant )
{
string assetPath = AssetDatabase.GetAssetPath( objTR.gameObject );
PrefabStage openPrefabStage = PrefabStageUtility.GetCurrentPrefabStage();
#if UNITY_2020_1_OR_NEWER
if( openPrefabStage != null && openPrefabStage.stageHandle.IsValid() && assetPath == openPrefabStage.assetPath )
#else
if( openPrefabStage != null && openPrefabStage.stageHandle.IsValid() && assetPath == openPrefabStage.prefabAssetPath )
#endif
{
GameObject prefabStageGO = FollowSymmetricHierarchy( (GameObject) obj, ( (GameObject) obj ).transform.root.gameObject, openPrefabStage.prefabContentsRoot );
if( prefabStageGO != null )
{
objTR = prefabStageGO.transform;
selection = objTR.gameObject;
}
}
#if UNITY_2019_1_OR_NEWER
else if( obj != objTR.gameObject )
selection = objTR.gameObject;
#endif
}
else if( prefabAssetType == PrefabAssetType.Model )
{
objTR = ( (GameObject) obj ).transform;
while( objTR.parent != null && objTR.parent.parent != null )
objTR = objTR.parent;
}
#else
Transform objTR = ( (GameObject) obj ).transform;
while( objTR.parent != null && objTR.parent.parent != null )
objTR = objTR.parent;
#endif
pingTarget = objTR.gameObject;
}
else if( ( obj.hideFlags & ( HideFlags.HideInInspector | HideFlags.HideInHierarchy ) ) != HideFlags.None )
{
// Can't ping assets that are hidden from Project window (e.g. animator states of AnimatorController), ping the main asset at that path instead
pingTarget = AssetDatabase.LoadMainAssetAtPath( AssetDatabase.GetAssetPath( obj ) );
}
else if( !AssetDatabase.IsMainAsset( obj ) && Array.IndexOf( AssetDatabase.LoadAllAssetRepresentationsAtPath( AssetDatabase.GetAssetPath( obj ) ), obj ) < 0 )
{
// VFX Graph assets' nodes are serialized as part of the graph but they are invisible in the Project window even though their hideFlags is None (I don't know how)
pingTarget = AssetDatabase.LoadMainAssetAtPath( AssetDatabase.GetAssetPath( obj ) );
}
}
}
// We are passing "go"s root Transform to thisRoot parameter. If we use go.transform.root instead, when we are in prefab mode on
// newer Unity versions, it points to the preview scene at the root of the prefab stage instead of pointing to the actual root of "go"
public static GameObject FollowSymmetricHierarchy( this GameObject go, GameObject thisRoot, GameObject symmetricRoot )
{
Transform target = go.transform;
Transform root1 = thisRoot.transform;
Transform root2 = symmetricRoot.transform;
while( root1 != target )
{
Transform temp = target;
while( temp.parent != root1 )
temp = temp.parent;
Transform newRoot2;
int siblingIndex = temp.GetSiblingIndex();
if( siblingIndex < root2.childCount )
{
newRoot2 = root2.GetChild( siblingIndex );
if( newRoot2.name != temp.name )
newRoot2 = root2.Find( temp.name );
}
else
newRoot2 = root2.Find( temp.name );
if( newRoot2 == null )
return null;
root2 = newRoot2;
root1 = temp;
}
return root2.gameObject;
}
// Returns -1 if t1 is above t2 in Hierarchy, 1 if t1 is below t2 in Hierarchy and 0 if they are the same object
public static int CompareHierarchySiblingIndices( Transform t1, Transform t2 )
{
Transform parent1 = t1.parent;
Transform parent2 = t2.parent;
if( parent1 == parent2 )
return t1.GetSiblingIndex() - t2.GetSiblingIndex();
int deltaHierarchyDepth = 0;
for( ; parent1; parent1 = parent1.parent )
deltaHierarchyDepth++;
for( ; parent2; parent2 = parent2.parent )
deltaHierarchyDepth--;
for( ; deltaHierarchyDepth > 0; deltaHierarchyDepth-- )
{
t1 = t1.parent;
if( t1 == t2 )
return 1;
}
for( ; deltaHierarchyDepth < 0; deltaHierarchyDepth++ )
{
t2 = t2.parent;
if( t1 == t2 )
return -1;
}
while( t1.parent != t2.parent )
{
t1 = t1.parent;
t2 = t2.parent;
}
return t1.GetSiblingIndex() - t2.GetSiblingIndex();
}
// Check if the field is serializable
public static bool IsSerializable( this FieldInfo fieldInfo )
{
// See Serialization Rules: https://docs.unity3d.com/Manual/script-Serialization.html
if( fieldInfo.IsInitOnly )
return false;
#if UNITY_2019_3_OR_NEWER
// SerializeReference makes even System.Object fields serializable
if( Attribute.IsDefined( fieldInfo, typeof( SerializeReference ) ) )
return true;
#endif
if( ( !fieldInfo.IsPublic || fieldInfo.IsNotSerialized ) && !Attribute.IsDefined( fieldInfo, typeof( SerializeField ) ) )
return false;
return IsTypeSerializable( fieldInfo.FieldType );
}
// Check if the property is serializable
public static bool IsSerializable( this PropertyInfo propertyInfo )
{
return IsTypeSerializable( propertyInfo.PropertyType );
}
// Check if type is serializable
private static bool IsTypeSerializable( Type type )
{
// see Serialization Rules: https://docs.unity3d.com/Manual/script-Serialization.html
if( typeof( Object ).IsAssignableFrom( type ) )
return true;
if( type.IsArray )
{
if( type.GetArrayRank() != 1 )
return false;
type = type.GetElementType();
if( typeof( Object ).IsAssignableFrom( type ) )
return true;
}
else if( type.IsGenericType )
{
// Generic types are allowed on 2020.1 and later
#if UNITY_2020_1_OR_NEWER
if( type.GetGenericTypeDefinition() == typeof( List<> ) )
{
type = type.GetGenericArguments()[0];
if( typeof( Object ).IsAssignableFrom( type ) )
return true;
}
#else
if( type.GetGenericTypeDefinition() != typeof( List<> ) )
return false;
type = type.GetGenericArguments()[0];
if( typeof( Object ).IsAssignableFrom( type ) )
return true;
#endif
}
#if !UNITY_2020_1_OR_NEWER
if( type.IsGenericType )
return false;
#endif
return Attribute.IsDefined( type, typeof( SerializableAttribute ), false );
}
// Check if instances of this type should be searched for references
public static bool IsIgnoredUnityType( this Type type )
{
if( type.IsPrimitive || primitiveUnityTypes.Contains( type ) || type.IsEnum )
return true;
#if UNITY_2018_1_OR_NEWER
// Searching NativeArrays for reference can throw InvalidOperationException if the collection is disposed
if( type.Namespace == nativeCollectionsNamespace )
return true;
#endif
// Searching assembly variables for reference throws InvalidCastException on .NET 4.0 runtime
if( typeof( Type ).IsAssignableFrom( type ) || type.Namespace == reflectionNamespace )
return true;
// Searching pointers or ref variables for reference throws ArgumentException
if( type.IsPointer || type.IsByRef )
return true;
return false;
}
// Get <get> function for a field
public static VariableGetVal CreateGetter( this FieldInfo fieldInfo, Type type )
{
// Commented the IL generator code below because it might actually be slower than simply using reflection
// Credit: https://www.codeproject.com/Articles/14560/Fast-Dynamic-Property-Field-Accessors
//DynamicMethod dm = new DynamicMethod( "Get" + fieldInfo.Name, fieldInfo.FieldType, new Type[] { typeof( object ) }, type );
//ILGenerator il = dm.GetILGenerator();
//// Load the instance of the object (argument 0) onto the stack
//il.Emit( OpCodes.Ldarg_0 );
//// Load the value of the object's field (fi) onto the stack
//il.Emit( OpCodes.Ldfld, fieldInfo );
//// return the value on the top of the stack
//il.Emit( OpCodes.Ret );
//return (VariableGetVal) dm.CreateDelegate( typeof( VariableGetVal ) );
return fieldInfo.GetValue;
}
// Get <get> function for a property
public static VariableGetVal CreateGetter( this PropertyInfo propertyInfo )
{
// Can't use PropertyWrapper (which uses CreateDelegate) for property getters of structs
if( propertyInfo.DeclaringType.IsValueType )
{
return !propertyInfo.CanRead ? (VariableGetVal) null : ( obj ) =>
{
try
{
return propertyInfo.GetValue( obj, null );
}
catch
{
// Property getters may return various kinds of exceptions if their backing fields are not initialized (yet)
return null;
}
};
}
Type GenType = typeof( PropertyWrapper<,> ).MakeGenericType( propertyInfo.DeclaringType, propertyInfo.PropertyType );
return ( (IPropertyAccessor) Activator.CreateInstance( GenType, propertyInfo.GetGetMethod( true ) ) ).GetValue;
}
// Check if all open scenes are saved (not dirty)
public static bool AreScenesSaved()
{
for( int i = 0; i < SceneManager.sceneCount; i++ )
{
Scene scene = SceneManager.GetSceneAt( i );
if( scene.isDirty || string.IsNullOrEmpty( scene.path ) )
return false;
}
return true;
}
// Returns file extension in lowercase (period not included)
public static string GetFileExtension( string path )
{
int extensionIndex = path.LastIndexOf( '.' );
if( extensionIndex < 0 || extensionIndex >= path.Length - 1 )
return "";
stringBuilder.Length = 0;
for( extensionIndex++; extensionIndex < path.Length; extensionIndex++ )
{
char ch = path[extensionIndex];
if( ch >= 65 && ch <= 90 ) // A-Z
ch += (char) 32; // Converted to a-z
stringBuilder.Append( ch );
}
return stringBuilder.ToString();
}
// Draw horizontal line inside OnGUI
public static void DrawSeparatorLine()
{
GUILayout.Space( 4f );
GUILayout.Box( "", GL_HEIGHT_2, GL_EXPAND_WIDTH );
GUILayout.Space( 4f );
}
// Restricts the given Rect within the screen's bounds
public static Rect GetScreenFittedRect( Rect originalRect )
{
if( screenFittedRectGetter == null )
screenFittedRectGetter = typeof( EditorWindow ).Assembly.GetType( "UnityEditor.ContainerWindow" ).GetMethod( "FitRectToScreen", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static );
return (Rect) screenFittedRectGetter.Invoke( null, new object[3] { originalRect, true, true } );
}
// Check if all the objects inside the list are null
public static bool IsEmpty( this List<ObjectToSearch> objectsToSearch )
{
if( objectsToSearch == null )
return true;
for( int i = 0; i < objectsToSearch.Count; i++ )
{
if( objectsToSearch[i].obj != null && !objectsToSearch[i].obj.Equals( null ) )
return false;
}
return true;
}
// Check if all the objects inside the list are null
public static bool IsEmpty( this List<Object> objects )
{
if( objects == null )
return true;
for( int i = 0; i < objects.Count; i++ )
{
if( objects[i] != null && !objects[i].Equals( null ) )
return false;
}
return true;
}
// Check if all the objects that are enumerated are null
public static bool IsEmpty( this IEnumerable<Object> objects )
{
if( objects == null )
return true;
using( IEnumerator<Object> enumerator = objects.GetEnumerator() )
{
while( enumerator.MoveNext() )
{
if( enumerator.Current != null && !enumerator.Current.Equals( null ) )
return false;
}
}
return true;
}
// Returns true is str starts with prefix
public static bool StartsWithFast( this string str, string prefix )
{
int aLen = str.Length;
int bLen = prefix.Length;
int ap = 0; int bp = 0;
while( ap < aLen && bp < bLen && str[ap] == prefix[bp] )
{
ap++;
bp++;
}
return bp == bLen;
}
// Returns true is str ends with postfix
public static bool EndsWithFast( this string str, string postfix )
{
int ap = str.Length - 1;
int bp = postfix.Length - 1;
while( ap >= 0 && bp >= 0 && str[ap] == postfix[bp] )
{
ap--;
bp--;
}
return bp < 0;
}
public static bool ContainsFast<T>( this List<T> list, T element )
{
if( !( element is ValueType ) )
{
for( int i = list.Count - 1; i >= 0; i-- )
{
if( ReferenceEquals( list[i], element ) )
return true;
}
}
else
{
for( int i = list.Count - 1; i >= 0; i-- )
{
if( element.Equals( list[i] ) )
return true;
}
}
return false;
}
}
}

View File

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

View File

@ -0,0 +1,83 @@
using System;
using System.Reflection;
using System.Text;
using UnityEngine;
namespace AssetUsageDetectorNamespace
{
// Delegate to get the value of a variable (either field or property)
public delegate object VariableGetVal( object obj );
// Custom struct to hold a variable, its important properties and its getter function
public struct VariableGetterHolder
{
public readonly MemberInfo variable;
public readonly bool isSerializable;
private readonly VariableGetVal getter;
public string Name { get { return variable.Name; } }
public bool IsProperty { get { return variable is PropertyInfo; } }
public VariableGetterHolder( FieldInfo fieldInfo, VariableGetVal getter, bool isSerializable )
{
this.variable = fieldInfo;
this.isSerializable = isSerializable;
this.getter = getter;
}
public VariableGetterHolder( PropertyInfo propertyInfo, VariableGetVal getter, bool isSerializable )
{
this.variable = propertyInfo;
this.isSerializable = isSerializable;
this.getter = getter;
}
public object Get( object obj )
{
try
{
return getter( obj );
}
catch( Exception e )
{
StringBuilder sb = Utilities.stringBuilder;
sb.Length = 0;
sb.Append( "Error while getting the value of (" ).Append( IsProperty ? ( (PropertyInfo) variable ).PropertyType : ( (FieldInfo) variable ).FieldType ).Append( ") " )
.Append( variable.DeclaringType ).Append( "." ).Append( Name ).Append( ": " ).Append( e );
Debug.LogError( sb.ToString() );
return null;
}
}
}
// Credit: http://stackoverflow.com/questions/724143/how-do-i-create-a-delegate-for-a-net-property
public interface IPropertyAccessor
{
object GetValue( object source );
}
// A wrapper class for properties to get their values more efficiently
public class PropertyWrapper<TObject, TValue> : IPropertyAccessor where TObject : class
{
private readonly Func<TObject, TValue> getter;
public PropertyWrapper( MethodInfo getterMethod )
{
getter = (Func<TObject, TValue>) Delegate.CreateDelegate( typeof( Func<TObject, TValue> ), getterMethod );
}
public object GetValue( object obj )
{
try
{
return getter( (TObject) obj );
}
catch
{
// Property getters may return various kinds of exceptions if their backing fields are not initialized (yet)
return null;
}
}
}
}

View File

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

View File

@ -0,0 +1,4 @@
= Asset Usage Detector (v2.5.3) =
Documentation: https://github.com/yasirkula/UnityAssetUsageDetector
E-mail: yasirkula@gmail.com

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ac528f1751f33a647a45caeff6a9344b
timeCreated: 1520032521
licenseType: Store
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -7749,7 +7749,7 @@ GameObject:
- component: {fileID: 109379979}
- component: {fileID: 109379978}
m_Layer: 5
m_Name: Button (1)
m_Name: EnglishButton
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@ -7770,11 +7770,11 @@ RectTransform:
- {fileID: 2033966157}
m_Father: {fileID: 2079626611}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0}
m_AnchorMax: {x: 0.5, y: 0}
m_AnchoredPosition: {x: 0, y: 20.2}
m_SizeDelta: {x: 295.7585, y: 55.4547}
m_Pivot: {x: 0.5, y: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: -90}
m_SizeDelta: {x: 295.7585, y: 55.454712}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &109379978
MonoBehaviour:
m_ObjectHideFlags: 0
@ -7831,7 +7831,7 @@ MonoBehaviour:
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
- m_Target: {fileID: 1568526660}
- m_Target: {fileID: 2079626610}
m_TargetAssemblyTypeName: UnityEngine.GameObject, UnityEngine
m_MethodName: SetActive
m_Mode: 6
@ -7843,6 +7843,18 @@ MonoBehaviour:
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
- m_Target: {fileID: 401603026}
m_TargetAssemblyTypeName: LanguageManager, Assembly-CSharp
m_MethodName: SetLanguage
m_Mode: 5
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument: English
m_BoolArgument: 0
m_CallState: 2
--- !u!114 &109379979
MonoBehaviour:
m_ObjectHideFlags: 0
@ -17402,7 +17414,7 @@ GameObject:
- component: {fileID: 236729269}
- component: {fileID: 236729268}
m_Layer: 5
m_Name: Button
m_Name: ArabicButton
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@ -17423,11 +17435,11 @@ RectTransform:
- {fileID: 1029186221}
m_Father: {fileID: 2079626611}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0}
m_AnchorMax: {x: 0.5, y: 0}
m_AnchoredPosition: {x: 0, y: 107.4}
m_SizeDelta: {x: 295.7585, y: 55.4547}
m_Pivot: {x: 0.5, y: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 295.7585, y: 55.454712}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &236729268
MonoBehaviour:
m_ObjectHideFlags: 0
@ -17474,7 +17486,31 @@ MonoBehaviour:
m_Calls:
- m_Target: {fileID: 401603026}
m_TargetAssemblyTypeName: LanguageManager, Assembly-CSharp
m_MethodName: ToggleLanguage
m_MethodName: SetLanguage
m_Mode: 5
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument: Arabic
m_BoolArgument: 0
m_CallState: 2
- m_Target: {fileID: 2079626610}
m_TargetAssemblyTypeName: UnityEngine.GameObject, UnityEngine
m_MethodName: SetActive
m_Mode: 6
m_Arguments:
m_ObjectArgument: {fileID: 0}
m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
m_IntArgument: 0
m_FloatArgument: 0
m_StringArgument:
m_BoolArgument: 0
m_CallState: 2
- m_Target: {fileID: 1809986913}
m_TargetAssemblyTypeName: CharacterMovement, Assembly-CSharp
m_MethodName: AnimationStarter
m_Mode: 1
m_Arguments:
m_ObjectArgument: {fileID: 0}
@ -28154,7 +28190,7 @@ Transform:
m_GameObject: {fileID: 390376883}
serializedVersion: 2
m_LocalRotation: {x: 0.000000030434517, y: 0.7071067, z: -0.00000003795118, w: 0.7071068}
m_LocalPosition: {x: -1.5892, y: 1.4498, z: -0.0239}
m_LocalPosition: {x: -1.719, y: 1.4498, z: -0.024}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
@ -29326,6 +29362,7 @@ MonoBehaviour:
fontEnglish: {fileID: 11400000, guid: 9f7cb4b53799717478f208258f1af701, type: 2}
fontArabic: {fileID: 11400000, guid: 5d7d7d6271e9ff04b99c8a2c50c4f464, type: 2}
languageLabel: {fileID: 694310787}
languageSetBool: 0
--- !u!4 &401603027
Transform:
m_ObjectHideFlags: 0
@ -50588,7 +50625,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
m_IsActive: 0
--- !u!224 &694310786
RectTransform:
m_ObjectHideFlags: 0
@ -52215,7 +52252,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
m_IsActive: 1
--- !u!4 &718829149
Transform:
m_ObjectHideFlags: 0
@ -64478,6 +64515,140 @@ Transform:
m_CorrespondingSourceObject: {fileID: 4837329652793204, guid: 88498815dea432045b6eaa9875ec268a, type: 3}
m_PrefabInstance: {fileID: 1225554677}
m_PrefabAsset: {fileID: 0}
--- !u!1 &885259529
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 885259530}
- component: {fileID: 885259532}
- component: {fileID: 885259531}
m_Layer: 5
m_Name: Promo
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &885259530
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 885259529}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 1568526664}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 170.5492, y: 50}
m_Pivot: {x: 1, y: 0}
--- !u!114 &885259531
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 885259529}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: Powered by CyberCompanion
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 9f7cb4b53799717478f208258f1af701, type: 2}
m_sharedMaterial: {fileID: -7900421164380424285, guid: 9f7cb4b53799717478f208258f1af701, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 21.3
m_fontSizeBase: 36
m_fontWeight: 400
m_enableAutoSizing: 1
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 1
m_VerticalAlignment: 256
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_enableWordWrapping: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 1
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!222 &885259532
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 885259529}
m_CullTransparentMesh: 1
--- !u!4 &885475623 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 4156121308509644, guid: 911d4c5eaa0b3b84b83995d2433ebf94, type: 3}
@ -65126,7 +65297,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
m_IsActive: 1
--- !u!224 &893706589
RectTransform:
m_ObjectHideFlags: 0
@ -75428,6 +75599,7 @@ GameObject:
- component: {fileID: 1029186221}
- component: {fileID: 1029186223}
- component: {fileID: 1029186222}
- component: {fileID: 1029186224}
m_Layer: 5
m_Name: Text (TMP)
m_TagString: Untagged
@ -75474,10 +75646,10 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: Switch
m_text: "\u201C\u0627\u0644\u0639\u0631\u0628\u064A\u0629\u201D"
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8fb9d20fb5f90a34c9e3810c210f5f8c, type: 2}
m_sharedMaterial: {fileID: -5830505048132788728, guid: 8fb9d20fb5f90a34c9e3810c210f5f8c, type: 2}
m_fontAsset: {fileID: 11400000, guid: 5d7d7d6271e9ff04b99c8a2c50c4f464, type: 2}
m_sharedMaterial: {fileID: -4076715752635496019, guid: 5d7d7d6271e9ff04b99c8a2c50c4f464, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
@ -75501,7 +75673,7 @@ MonoBehaviour:
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 47.3
m_fontSize: 55.45
m_fontSizeBase: 24
m_fontWeight: 400
m_enableAutoSizing: 1
@ -75551,6 +75723,21 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1029186220}
m_CullTransparentMesh: 1
--- !u!114 &1029186224
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1029186220}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: cc004b44880e9b0438fdcabff26b08d8, type: 3}
m_Name:
m_EditorClassIdentifier:
rawArabicInput: "\u0627\u0644\u0639\u0631\u0628\u064A\u0629"
showTashkeel: 1
useHinduNumbers: 1
--- !u!4 &1029194909 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 4194271709752040, guid: acb8028c6738c8642b20c2f467061501, type: 3}
@ -83843,6 +84030,8 @@ GameObject:
- component: {fileID: 1165035729}
- component: {fileID: 1165035728}
- component: {fileID: 1165035727}
- component: {fileID: 1165035730}
- component: {fileID: 1165035731}
m_Layer: 0
m_Name: NewEmail
m_TagString: Untagged
@ -83962,7 +84151,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Material: {fileID: 1614071849}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
@ -83988,6 +84177,38 @@ CanvasRenderer:
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1165035725}
m_CullTransparentMesh: 1
--- !u!114 &1165035730
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1165035725}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ee158225ee1e59f4791627785501d950, type: 3}
m_Name:
m_EditorClassIdentifier:
shaderTypes: 0
normalStrength: 5
normalSmoothing: 1
computingNormal: 0
--- !u!114 &1165035731
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1165035725}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dfeee82aa0b6bf24381f7c432ba35115, type: 3}
m_Name:
m_EditorClassIdentifier:
targetMaterial: {fileID: 1614071849}
minZoom: 0.95
maxZoom: 1.25
speed: 3
--- !u!1001 &1165265475
PrefabInstance:
m_ObjectHideFlags: 0
@ -92879,7 +93100,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
m_IsActive: 0
--- !u!224 &1289299255
RectTransform:
m_ObjectHideFlags: 0
@ -95149,15 +95370,14 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: "\U0001F4CC Typos & Spelling Errors\nThe email had spelling mistakes or
odd grammar \u2014 a common phishing sign.\n\n\U0001F4CC Urgency & Pressure\nIt
used urgent language to pressure you into acting quickly.\n\n\U0001F4CC Link
Inspection\nThe link preview didn\u2019t match the real website \u2014 inspect
links before clicking.\n\n\U0001F4CC Unfamiliar or Unexpected Content\nThe email
seemed unrelated to your work \u2014 phishing often comes out of nowhere.\n\n\U0001F4CC
Generic Greetings\nIt used a generic greeting like Dear user instead of your
name.\n\n\U0001F4CC Unusual Requests\nIt asked for sensitive info like passwords
\u2014 companies don\u2019t do this by email."
m_text: "Typos & Spelling Errors\nThe email had spelling mistakes or odd grammar
\u2014 a common phishing sign.\n\nUrgency & Pressure\nIt used urgent language
to pressure you into acting quickly.\n\nLink Inspection\nThe link preview didn\u2019t
match the real website \u2014 inspect links before clicking.\n\nUnfamiliar or
Unexpected Content\nThe email seemed unrelated to your work \u2014 phishing often
comes out of nowhere.\n\nGeneric Greetings\nIt used a generic greeting like Dear
user instead of your name.\n\nUnusual Requests\nIt asked for sensitive info like
passwords \u2014 companies don\u2019t do this by email."
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 1b7bc6f8d1e1a594ea77f76cabc9a9be, type: 2}
m_sharedMaterial: {fileID: 4094059608530178792, guid: 1b7bc6f8d1e1a594ea77f76cabc9a9be, type: 2}
@ -111921,7 +112141,7 @@ GameObject:
- component: {fileID: 1568526662}
- component: {fileID: 1568526661}
m_Layer: 5
m_Name: Canvas
m_Name: ScreenCanvas
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@ -112003,6 +112223,7 @@ RectTransform:
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 2079626611}
- {fileID: 885259530}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@ -115433,6 +115654,242 @@ PrefabInstance:
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: fdfb9caedb76de945869ccf8157e4122, type: 3}
--- !u!21 &1614071849
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: AllIn1SpriteShader/AllIn1SpriteShader
m_Shader: {fileID: 4800000, guid: a36b7719ff0465b42ab1407d67672c5f, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- ZOOMUV_ON
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: 3000
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _ColorRampTex:
m_Texture: {fileID: 2800000, guid: 279657edc397ece4b8029c727adf6ddc, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ColorRampTexGradient:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ColorSwapTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DistortTex:
m_Texture: {fileID: 2800000, guid: 7aad8c583ef292e48b06af0d1f2fab97, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _FadeBurnTex:
m_Texture: {fileID: 2800000, guid: 677cca399782dea41aedc1d292ecb67d, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _FadeTex:
m_Texture: {fileID: 2800000, guid: 7aad8c583ef292e48b06af0d1f2fab97, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _GlowTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OutlineDistortTex:
m_Texture: {fileID: 2800000, guid: 7aad8c583ef292e48b06af0d1f2fab97, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OutlineTex:
m_Texture: {fileID: 2800000, guid: 74087f6d03f233e4a8a142fa01f9e5cf, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OverlayTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ShineMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _Alpha: 1
- _AlphaCutoffValue: 0.25
- _AlphaOutlineBlend: 1
- _AlphaOutlineGlow: 5
- _AlphaOutlineMinAlpha: 0
- _AlphaOutlinePower: 1
- _AlphaRoundThreshold: 0.5
- _BillboardY: 0
- _BlurHD: 0
- _BlurIntensity: 10
- _Brightness: 0
- _ChromAberrAlpha: 0.4
- _ChromAberrAmount: 1
- _ClipUvDown: 0
- _ClipUvLeft: 0
- _ClipUvRight: 0
- _ClipUvUp: 0
- _ColorChangeLuminosity: 0
- _ColorChangeTolerance: 0.25
- _ColorChangeTolerance2: 0.25
- _ColorChangeTolerance3: 0.25
- _ColorRampBlend: 1
- _ColorRampLuminosity: 0
- _ColorRampOutline: 0
- _ColorSwapBlend: 1
- _ColorSwapBlueLuminosity: 0.5
- _ColorSwapGreenLuminosity: 0.5
- _ColorSwapRedLuminosity: 0.5
- _Contrast: 1
- _CullingOption: 0
- _DistortAmount: 0.5
- _DistortTexXSpeed: 5
- _DistortTexYSpeed: 5
- _EditorDrawers: 6
- _FadeAmount: -0.1
- _FadeBurnGlow: 2
- _FadeBurnTransition: 0.075
- _FadeBurnWidth: 0.025
- _FishEyeUvAmount: 0.35
- _FlickerAlpha: 0
- _FlickerFreq: 0.2
- _FlickerPercent: 0.05
- _GhostBlend: 1
- _GhostColorBoost: 1
- _GhostTransparency: 0
- _GlitchAmount: 3
- _GlitchSize: 1
- _Glow: 10
- _GlowGlobal: 1
- _GradBlend: 1
- _GradBoostX: 1.2
- _GradBoostY: 1.2
- _GradIsRadial: 0
- _GrassManualAnim: 1
- _GrassManualToggle: 0
- _GrassRadialBend: 0.1
- _GrassSpeed: 2
- _GrassWind: 20
- _GreyscaleBlend: 1
- _GreyscaleLuminosity: 0
- _GreyscaleOutline: 0
- _HandDrawnAmount: 10
- _HandDrawnSpeed: 5
- _HitEffectBlend: 1
- _HitEffectGlow: 5
- _HologramBlend: 1
- _HologramMaxAlpha: 0.75
- _HologramMinAlpha: 0.1
- _HologramStripesAmount: 0.1
- _HologramStripesSpeed: 4.5
- _HologramUnmodAmount: 0
- _HsvBright: 1
- _HsvSaturation: 1
- _HsvShift: 180
- _InnerOutlineAlpha: 1
- _InnerOutlineGlow: 4
- _InnerOutlineThickness: 1
- _MaxXUV: 1
- _MaxYUV: 1
- _MinXUV: 0
- _MinYUV: 0
- _MotionBlurAngle: 0.1
- _MotionBlurDist: 1.25
- _MyDstMode: 10
- _MySrcMode: 5
- _NegativeAmount: 1
- _OffsetUvX: 0
- _OffsetUvY: 0
- _OnlyInnerOutline: 0
- _OnlyOutline: 0
- _OutlineAlpha: 1
- _OutlineDistortAmount: 0.5
- _OutlineDistortTexXSpeed: 5
- _OutlineDistortTexYSpeed: 5
- _OutlineGlow: 1.5
- _OutlinePixelWidth: 1
- _OutlineTexXSpeed: 10
- _OutlineTexYSpeed: 0
- _OutlineWidth: 0.004
- _OverlayBlend: 1
- _OverlayGlow: 1
- _PinchUvAmount: 0
- _PixelateSize: 32
- _PosterizeGamma: 0.75
- _PosterizeNumColors: 8
- _PosterizeOutline: 0
- _RandomSeed: 0
- _RectSize: 1
- _RotateUvAmount: 0
- _RoundWaveSpeed: 2
- _RoundWaveStrength: 0.7
- _ShadowAlpha: 0.5
- _ShadowX: 0.1
- _ShadowY: -0.05
- _ShakeUvSpeed: 2.5
- _ShakeUvX: 1.5
- _ShakeUvY: 1
- _ShineGlow: 1
- _ShineLocation: 0.5
- _ShineRotate: 0
- _ShineWidth: 0.1
- _TextureScrollXSpeed: 1
- _TextureScrollYSpeed: 0
- _TwistUvAmount: 1
- _TwistUvPosX: 0.5
- _TwistUvPosY: 0.5
- _TwistUvRadius: 0.75
- _WaveAmount: 7
- _WaveSpeed: 10
- _WaveStrength: 7.5
- _WaveX: 0
- _WaveY: 0.5
- _ZTestMode: 4
- _ZWrite: 0
- _ZoomUvAmount: 1.22
m_Colors:
- _AlphaOutlineColor: {r: 1, g: 1, b: 1, a: 1}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _ColorChangeNewCol: {r: 1, g: 1, b: 0, a: 1}
- _ColorChangeNewCol2: {r: 1, g: 1, b: 0, a: 1}
- _ColorChangeNewCol3: {r: 1, g: 1, b: 0, a: 1}
- _ColorChangeTarget: {r: 1, g: 0, b: 0, a: 1}
- _ColorChangeTarget2: {r: 1, g: 0, b: 0, a: 1}
- _ColorChangeTarget3: {r: 1, g: 0, b: 0, a: 1}
- _ColorSwapBlue: {r: 1, g: 1, b: 1, a: 1}
- _ColorSwapGreen: {r: 1, g: 1, b: 1, a: 1}
- _ColorSwapRed: {r: 1, g: 1, b: 1, a: 1}
- _FadeBurnColor: {r: 1, g: 1, b: 0, a: 1}
- _GlowColor: {r: 1, g: 1, b: 1, a: 1}
- _GradBotLeftCol: {r: 0, g: 0, b: 1, a: 1}
- _GradBotRightCol: {r: 0, g: 1, b: 0, a: 1}
- _GradTopLeftCol: {r: 1, g: 0, b: 0, a: 1}
- _GradTopRightCol: {r: 1, g: 1, b: 0, a: 1}
- _GreyscaleTintColor: {r: 1, g: 1, b: 1, a: 1}
- _HitEffectColor: {r: 1, g: 1, b: 1, a: 1}
- _HologramStripeColor: {r: 0, g: 1, b: 1, a: 1}
- _InnerOutlineColor: {r: 1, g: 0, b: 0, a: 1}
- _OutlineColor: {r: 1, g: 1, b: 1, a: 1}
- _OverlayColor: {r: 1, g: 1, b: 1, a: 1}
- _ShadowColor: {r: 0, g: 0, b: 0, a: 1}
- _ShineColor: {r: 1, g: 1, b: 1, a: 1}
m_BuildTextureStacks: []
--- !u!1001 &1614491339
PrefabInstance:
m_ObjectHideFlags: 0
@ -142747,9 +143204,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: 'Start
'
m_text: English
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 8fb9d20fb5f90a34c9e3810c210f5f8c, type: 2}
m_sharedMaterial: {fileID: -5830505048132788728, guid: 8fb9d20fb5f90a34c9e3810c210f5f8c, type: 2}
@ -146974,7 +147429,7 @@ GameObject:
- component: {fileID: 2079626613}
- component: {fileID: 2079626612}
m_Layer: 5
m_Name: BG
m_Name: LanguageUI
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
@ -147001,7 +147456,7 @@ RectTransform:
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 447.2637, y: 447.2637}
m_SizeDelta: {x: 350, y: 350}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &2079626612
MonoBehaviour:
@ -147016,14 +147471,14 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_Color: {r: 0.16078432, g: 0.18823531, b: 0.23137257, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 21300000, guid: 8464d7c9cdc4af04c9ef3874898fff0f, type: 3}
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1

View File

@ -0,0 +1,35 @@
using ArabicSupport;
using System.Text.RegularExpressions;
public static class ArabicFixerHelper
{
public static string FixPreservingTags(string rawText)
{
// Match <link=...>…</link> and preserve it
var regex = new Regex(@"<link=.*?</link>", RegexOptions.IgnoreCase);
var matches = regex.Matches(rawText);
string fixedText = rawText;
foreach (Match match in matches)
{
string tag = match.Value;
string placeholder = $"[[LINK{match.Index}]]";
fixedText = fixedText.Replace(tag, placeholder);
}
// Apply ArabicFixer to the full string with placeholders
fixedText = ArabicFixer.Fix(fixedText);
// Replace placeholders back with original tags
foreach (Match match in matches)
{
string tag = match.Value;
string placeholder = ArabicFixer.Fix($"[[LINK{match.Index}]]");
fixedText = fixedText.Replace(placeholder, tag);
}
return fixedText;
}
}

View File

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

View File

@ -1,13 +1,15 @@
using UnityEngine;
using System.Collections.Generic;
using System.Collections;
public class EmailLoader : MonoBehaviour
{
public Transform contentParent;
public GameObject emailItemPrefab;
public EmailData[] emails;
void Start()
IEnumerator Start()
{
yield return new WaitUntil(() => LanguageManager.Instance != null && LanguageManager.Instance.languageSetBool == true);
LoadEmails();
}

View File

@ -61,14 +61,26 @@ public class EmailOpenPanel : MonoBehaviour
}
}
//void LocalizeTMP(TextMeshProUGUI tmp, string english, string arabic)
//{
// if (tmp == null) return;
// bool isArabic = LanguageManager.Instance != null && LanguageManager.Instance.currentLanguage == "Arabic";
// tmp.text = isArabic ? ArabicFixer.Fix(arabic) : english;
// tmp.font = LanguageManager.Instance?.GetCurrentFont();
// //tmp.isRightToLeftText = isArabic;
// //tmp.alignment = isArabic ? TextAlignmentOptions.Right : TextAlignmentOptions.Left;
//}
void LocalizeTMP(TextMeshProUGUI tmp, string english, string arabic)
{
if (tmp == null) return;
bool isArabic = LanguageManager.Instance != null && LanguageManager.Instance.currentLanguage == "Arabic";
tmp.text = isArabic ? ArabicFixer.Fix(arabic) : english;
tmp.font = LanguageManager.Instance?.GetCurrentFont();
//tmp.isRightToLeftText = isArabic;
bool isArabic = LanguageManager.Instance.currentLanguage == "Arabic";
tmp.text = isArabic ? ArabicFixerHelper.FixPreservingTags(arabic) : english;
tmp.font = LanguageManager.Instance.GetCurrentFont();
// tmp.isRightToLeftText = isArabic;
//tmp.alignment = isArabic ? TextAlignmentOptions.Right : TextAlignmentOptions.Left;
}
}

View File

@ -20,6 +20,7 @@ public class LanguageManager : MonoBehaviour
public TMP_FontAsset fontEnglish;
public TMP_FontAsset fontArabic;
public TextMeshProUGUI languageLabel;
public bool languageSetBool = false;
public TMP_FontAsset GetCurrentFont()
{
return currentLanguage == "Arabic" ? fontArabic : fontEnglish;
@ -72,6 +73,20 @@ public class LanguageManager : MonoBehaviour
{
return languageDB.GetText(key, currentLanguage);
}
public void SetLanguage(string language)
{
languageSetBool = true;
if (language == currentLanguage) return;
if (language == "English" || language == "Arabic")
{
currentLanguage = language;
PlayerPrefs.SetString("AppLang", currentLanguage);
UpdateLanguageLabel();
UpdateAllTexts();
}
}
public void UpdateAllTexts()
{

View File

@ -1,7 +1,7 @@
using UnityEngine;
using TMPro;
using ArabicSupport;
using System.Linq;
[RequireComponent(typeof(TextMeshProUGUI))]
public class LocalizedTextComponent : MonoBehaviour
{
@ -29,6 +29,23 @@ public class LocalizedTextComponent : MonoBehaviour
}
}
//public void UpdateText()
//{
// if (LanguageManager.Instance == null)
// {
// Debug.LogWarning($"LanguageManager not available when updating text for {gameObject.name}");
// return;
// }
// bool isArabic = LanguageManager.Instance.currentLanguage == "Arabic";
// string rawText = LanguageManager.Instance.GetLocalizedText(localizationKey);
// tmp.text = isArabic ? ArabicFixer.Fix(rawText) : rawText;
// tmp.font = LanguageManager.Instance.GetCurrentFont();
// //tmp.isRightToLeftText = isArabic;
// //tmp.alignment = isArabic ? TextAlignmentOptions.Right : TextAlignmentOptions.Left;
//}
public void UpdateText()
{
if (LanguageManager.Instance == null)
@ -40,9 +57,19 @@ public class LocalizedTextComponent : MonoBehaviour
bool isArabic = LanguageManager.Instance.currentLanguage == "Arabic";
string rawText = LanguageManager.Instance.GetLocalizedText(localizationKey);
tmp.text = isArabic ? ArabicFixer.Fix(rawText) : rawText;
if (isArabic)
{
// Split by \n and fix each line
string[] lines = rawText.Split(new[] { "\\n", "\n" }, System.StringSplitOptions.None);
tmp.text = string.Join("\n", lines.Select(ArabicFixer.Fix));
}
else
{
tmp.text = rawText.Replace("\\n", "\n"); // Handle escaped newline
}
tmp.font = LanguageManager.Instance.GetCurrentFont();
//tmp.isRightToLeftText = isArabic;
//tmp.alignment = isArabic ? TextAlignmentOptions.Right : TextAlignmentOptions.Left;
tmp.ForceMeshUpdate();
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 975a612d4bf0d22479c1d2bb28530d18
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,21 @@
fileFormatVersion: 2
guid: 97826a7901e1ce9418ca23b0b6b4ec7e
TrueTypeFontImporter:
externalObjects: {}
serializedVersion: 4
fontSize: 16
forceTextureCase: -2
characterSpacing: 0
characterPadding: 1
includeFontData: 1
fontNames:
- Cairo
fallbackFontReferences: []
customCharacters:
fontRenderingMode: 0
ascentCalculationMode: 1
useLegacyBoundsCalculation: 0
shouldRoundAdvanceValue: 1
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

28
Assets/ZoomTween.cs Normal file
View File

@ -0,0 +1,28 @@
using DG.Tweening;
using UnityEngine;
public class ZoomTween : MonoBehaviour
{
[Header("Material and Property")]
public Material targetMaterial;
[Tooltip("Zoom range e.g., from 0.1 (zoomed in) to 5 (zoomed out)")]
public float minZoom = 0.1f;
public float maxZoom = 2f;
public float speed = 1f;
[Tooltip("Name of the shader property (must match _ZoomUvAmount)")]
private readonly string zoomProperty = "_ZoomUvAmount";
private float t;
void Update()
{
if (!targetMaterial) return;
t += Time.deltaTime * speed;
// Animate using ping-pong
float zoom = Mathf.Lerp(minZoom, maxZoom, Mathf.PingPong(t, 1f));
targetMaterial.SetFloat(zoomProperty, zoom);
}
}

11
Assets/ZoomTween.cs.meta Normal file
View File

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