Ajouter / supprimer une jonction de dossier (lien symbolique) dans Unity

Ce devlog montre comment avoir un dossier externe dans votre projet Unity. Il y a deux grandes méthodes :
- les liens symboliques (ils ne peuvent pas être poussés sur git),
- les jonctions (elles peuvent être poussées sur git).
Nous allons voir comment créer des jonctions. Le script ajoute aussi une façon astucieuse et optimisée de les visualiser.
Visualiser les jonctions

Vous voyez dans votre projet quels fichiers et dossiers appartiennent à une jonction grâce aux petits indicateurs <=> et * : soyez donc prudent en les modifiant.

Vous voyez même dans la hiérarchie si un GameObject a un lien avec un dossier lié :
- le prefab principal,
- n’importe quel composant,
- n’importe quel champ ou propriété.
Pour la sécurité de vos projets et fichiers, quand vous travaillez avec des jonctions et des liens symboliques, gardez toujours une sauvegarde, ou tout sur un dépôt git.
Ajouter une jonction depuis Unity

Vous voyez ici à quel point il est simple de créer une jonction depuis Unity, en deux étapes :
- Clic droit sur un dossier dans la vue Project, puis SymLink/Add SymLink.
- Choisissez le dossier externe à ajouter dans le sélecteur de dossiers.
Et c’est tout ! Les jonctions sont signalées par le petit indicateur <=> dans la vue Project.
Impossible d’avoir une jonction dans une autre jonction.
Supprimer une jonction depuis Unity en gardant les fichiers locaux

Vous pouvez aussi supprimer le lien et garder les données locales, en deux étapes :
- Clic droit sur un dossier avec une jonction dans la vue Project, puis SymLink/Remove SymLink.
- Puis choisissez entre deux options :
- supprimer seulement le lien (garder les fichiers locaux),
- supprimer le dossier (comme avec la touche Suppr).
Pas d’inquiétude : quand vous supprimez une jonction depuis Unity, le vrai dossier externe n’est pas supprimé, seulement celui dans Unity.
Restaurer une jonction

Enfin, vous pouvez restaurer le lien entre un dossier local et un dossier externe :
- Clic droit sur un dossier sans jonction dans la vue Project, puis SymLink/Restore SymLink.
- Choisissez le dossier externe à lier dans le sélecteur de dossiers.
Attention : cette option écrase les fichiers présents dans les deux dossiers. Regardez bien les deux options ci-dessous, et assurez-vous que vos fichiers sont sauvegardés sur git avant cette procédure.
En restaurant un lien depuis un dossier local, le programme fusionne les deux dossiers. Si deux fichiers ont le même nom, deux choix :
- garder les fichiers locaux (plus sûr pour votre projet !),
- les écraser avec le fichier externe (les liens sont perdus).
Si vous choisissez d’écraser avec les fichiers externes, vous perdrez les références à ces fichiers dans Unity (vous savez, les fameuses « missing references » dues à des GUID perdus ou incorrects dans les fichiers meta…). Vous voudrez sans doute choisir l’option « garder les fichiers locaux ».
Scripts
Placez simplement ces scripts dans un dossier Editor/, et c’est tout !
ExtSymLinks.cs
using UnityEditor;
using UnityEngine;
using System.IO;
using hedCommon.extension.editor;
using System.Collections.Generic;
using hedCommon.extension.runtime;
using extUnityComponents.transform;
using extUnityComponents;
using System;
using System.Reflection;
using hedCommon.time;
namespace hedCommon.symlinks
{
/// <summary>
/// An editor utility for easily creating symlinks in your project.
///
/// Adds a Menu item under `Assets/Create/Folder(Symlink)`, and
/// draws a small indicator in the Project view for folders that are
/// symlinks.
/// </summary>
public static class ExtSymLinks
{
private static GUIStyle _symlinkMarkerStyle = null;
private static GUIContent _guiContent = null;
private static GUIStyle SymlinkMarkerStyle
{
get
{
if (_symlinkMarkerStyle == null)
{
_symlinkMarkerStyle = new GUIStyle(EditorStyles.label);
_symlinkMarkerStyle.normal.textColor = Color.blue;
_symlinkMarkerStyle.alignment = TextAnchor.MiddleRight;
}
return _symlinkMarkerStyle;
}
}
public static void DisplayBigMarker(Rect r, string toolTip)
{
_guiContent = new GUIContent("<=>", toolTip);
GUI.Label(r, _guiContent, ExtSymLinks.SymlinkMarkerStyle);
}
public static void DisplayTinyMarker(Rect r, string toolTip)
{
_guiContent = new GUIContent("* ", toolTip);
GUI.Label(r, _guiContent, ExtSymLinks.SymlinkMarkerStyle);
}
public static void ResetSymLinksDatas()
{
SymLinksOnProjectWindowItemGUI.ResetSymLinksDatas();
SymLinksOnHierarchyItemGUI.ResetSymLinksDatas();
}
}
}
SymLinksCreation.cs
using UnityEditor;
using UnityEngine;
using System.IO;
using hedCommon.extension.editor;
using System.Collections.Generic;
using hedCommon.extension.runtime;
using extUnityComponents.transform;
using extUnityComponents;
using System;
using System.Reflection;
using hedCommon.time;
namespace hedCommon.symlinks
{
/// <summary>
/// An editor utility for easily creating symlinks in your project.
///
/// Adds a Menu item under `Assets/Create/Folder(Symlink)`, and
/// draws a small indicator in the Project view for folders that are
/// symlinks.
/// </summary>
[InitializeOnLoad]
public static class SymLinksCreation
{
private const FileAttributes FOLDER_SYMLINK_ATTRIBS = FileAttributes.Directory | FileAttributes.ReparsePoint;
/// <summary>
/// add a folder link at the current selection in the project view
/// </summary>
[MenuItem("Assets/SymLink/Add Folder Link", false, 20)]
static void DoTheSymlink()
{
string targetPath = GetSelectedPathOrFallback();
FileAttributes attribs = File.GetAttributes(targetPath);
if ((attribs & FileAttributes.Directory) != FileAttributes.Directory)
targetPath = Path.GetDirectoryName(targetPath);
if (IsSymLinkFolder(targetPath))
{
throw new System.Exception("directory is already a symLink");
}
if (IsFolderHasParentSymLink(targetPath, out string pathLinkFound))
{
throw new System.Exception("parent " + pathLinkFound + " is a symLink, doen't allow recursive symlinks");
}
ExtWindowComands.OpenFolderPanel(out string sourceFolderPath, out string sourceFolderName);
targetPath = targetPath + "/" + sourceFolderName;
if (Directory.Exists(targetPath))
{
UnityEngine.Debug.LogWarning(string.Format("A folder already exists at this location, aborting link.\n{0} -> {1}", sourceFolderPath, targetPath));
return;
}
string commandeLine = "/C mklink /J \"" + targetPath + "\" \"" + sourceFolderPath + "\"";
ExtWindowComands.Execute(commandeLine);
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
ExtSymLinks.ResetSymLinksDatas();
}
/// <summary>
/// Restore a lost link
/// WARNING: it will override identical files
/// </summary>
[MenuItem("Assets/SymLink/Restore Folder Link", false, 20)]
private static void RestoreSymLink()
{
string targetPath = GetSelectedPathOrFallback();
string directoryName = Path.GetFileName(targetPath);
FileAttributes attribs = File.GetAttributes(targetPath);
if ((attribs & FileAttributes.Directory) != FileAttributes.Directory)
{
targetPath = Path.GetDirectoryName(targetPath);
}
if (IsSymLinkFolder(targetPath))
{
throw new System.Exception("directory " + directoryName + " is already a symLinkFolder");
}
ExtWindowComands.OpenFolderPanel(out string sourceFolderPath, out string sourceFolderName);
if (string.IsNullOrEmpty(sourceFolderName))
{
return;
}
if (directoryName != sourceFolderName)
{
throw new System.Exception("source and target have different names");
}
int choice = EditorUtility.DisplayDialogComplex("Restore SymLink", "If 2 files have the same names," +
"which one do you want to keep ?", "Keep Local ones /!\\", "cancel procedure", "Override with new ones /!\\");
if (choice == 1)
{
return;
}
try
{
//Place the Asset Database in a state where
//importing is suspended for most APIs
AssetDatabase.StartAssetEditing();
ExtFileEditor.DuplicateDirectory(targetPath, out string newPathCreated);
ExtFileEditor.DeleteDirectory(targetPath);
string commandeLine = "/C mklink /J \"" + targetPath + "\" \"" + sourceFolderPath + "\"";
ExtWindowComands.Execute(commandeLine);
ExtFileEditor.CopyAll(new DirectoryInfo(newPathCreated), new DirectoryInfo(targetPath), choice == 2);
ExtFileEditor.DeleteDirectory(newPathCreated);
}
finally
{
//By adding a call to StopAssetEditing inside
//a "finally" block, we ensure the AssetDatabase
//state will be reset when leaving this function
AssetDatabase.StopAssetEditing();
}
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
ExtSymLinks.ResetSymLinksDatas();
}
[MenuItem("Assets/SymLink/Remove Folder Link", false, 20)]
private static void RemoveSymLink()
{
string targetPath = GetSelectedPathOrFallback();
string directoryName = Path.GetFileName(targetPath);
FileAttributes attribs = File.GetAttributes(targetPath);
if ((attribs & FileAttributes.Directory) != FileAttributes.Directory)
{
targetPath = Path.GetDirectoryName(targetPath);
}
if (!IsSymLinkFolder(targetPath))
{
throw new System.Exception("directory " + directoryName + " is not a symLinkFolder");
}
int choice = EditorUtility.DisplayDialogComplex("Remove SymLink", "Do you want to Remove the link only ?", "Remove Link Only", "Cancel", "Remove Link and Directory /!\\");
if (choice == 1)
{
return;
}
string commandeLine = "/C rmdir \"" + ReformatPathForWindow(targetPath) + "\"";
if (choice == 2)
{
ExtWindowComands.Execute(commandeLine);
}
else
{
try
{
//Place the Asset Database in a state where
//importing is suspended for most APIs
AssetDatabase.StartAssetEditing();
ExtFileEditor.DuplicateDirectory(targetPath, out string newPathCreated);
ExtWindowComands.Execute(commandeLine);
ExtFileEditor.RenameDirectory(newPathCreated, directoryName, false);
}
finally
{
//By adding a call to StopAssetEditing inside
//a "finally" block, we ensure the AssetDatabase
//state will be reset when leaving this function
AssetDatabase.StopAssetEditing();
}
}
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
ExtSymLinks.ResetSymLinksDatas();
}
#region UseFull Function
public static bool IsFolderHasParentSymLink(string pathFolder, out string pathSymLinkFound)
{
pathSymLinkFound = "";
while (!string.IsNullOrEmpty(pathFolder))
{
string directoryName = Path.GetDirectoryName(pathFolder);
if (IsSymLinkFolder(directoryName))
{
pathSymLinkFound = directoryName;
return (true);
}
pathFolder = directoryName;
}
return (false);
}
public static bool IsSymLinkFolder(string targetPath)
{
if (string.IsNullOrEmpty(targetPath))
{
return (false);
}
FileAttributes attribs = File.GetAttributes(targetPath);
if ((attribs & FOLDER_SYMLINK_ATTRIBS) != FOLDER_SYMLINK_ATTRIBS)
{
return (false);
}
else
{
return (true);
}
}
/// <summary>
/// return the path of the current selected folder (or the current folder of the asset selected)
/// </summary>
/// <returns></returns>
public static string GetSelectedPathOrFallback()
{
string path = "Assets";
foreach (UnityEngine.Object obj in Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets))
{
path = AssetDatabase.GetAssetPath(obj);
if (!string.IsNullOrEmpty(path) && File.Exists(path))
{
path = Path.GetDirectoryName(path);
break;
}
}
return ReformatPathForUnity(path);
}
/// <summary>
/// change a path from
/// Assets\path\of\file
/// to
/// Assets/path/of/file
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static string ReformatPathForUnity(string path, char characterReplacer = '-')
{
string formattedPath = path.Replace('\\', '/');
formattedPath = formattedPath.Replace('|', characterReplacer);
return (formattedPath);
}
/// <summary>
/// change a path from
/// Assets/path/of/file
/// to
/// Assets\path\of\file
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public static string ReformatPathForWindow(string path)
{
string formattedPath = path.Replace('/', '\\');
formattedPath = formattedPath.Replace('|', '-');
return (formattedPath);
}
#endregion
}
}
SymLinksOnHierarchyItemGUI.cs
using UnityEditor;
using UnityEngine;
using System.IO;
using hedCommon.extension.editor;
using System.Collections.Generic;
using hedCommon.extension.runtime;
using extUnityComponents.transform;
using extUnityComponents;
using System;
using System.Reflection;
using hedCommon.time;
namespace hedCommon.symlinks
{
/// <summary>
/// An editor utility for easily creating symlinks in your project.
///
/// Adds a Menu item under `Assets/Create/Folder(Symlink)`, and
/// draws a small indicator in the Project view for folders that are
/// symlinks.
/// </summary>
[InitializeOnLoad]
public static class SymLinksOnHierarchyItemGUI
{
private const string SYMLINK_TOOLTIP = "In Symlink: ";
private const int MAX_SETUP_IN_ONE_FRAME = 10;
private const float TIME_BETWEEN_2_CHUNK_SETUP = 0.1f;
//Don't use dictionnary ! it crash !!
private static List<int> _gameObjectsId = new List<int>(300);
private static List<UnityEngine.Object> _gameObjectsList = new List<UnityEngine.Object>(300);
private static List<bool> _gameObjectsHasBeenSetup = new List<bool>(300);
private static List<bool> _gameObjectsIsInFrameWork = new List<bool>(300);
private static List<string> _toolTipInfo = new List<string>(300);
private static bool _needToSetup = false;
private static int _settupedGameObject = 0;
private static EditorChronoWithNoTimeEditor _timerBetween2ChunkSetup = new EditorChronoWithNoTimeEditor();
/// <summary>
/// Static constructor subscribes to projectWindowItemOnGUI delegate.
/// </summary>
static SymLinksOnHierarchyItemGUI()
{
EditorApplication.hierarchyWindowItemOnGUI -= OnHierarchyItemGUI;
EditorApplication.hierarchyWindowItemOnGUI += OnHierarchyItemGUI;
EditorApplication.hierarchyChanged -= OnHierarchyWindowChanged;
EditorApplication.hierarchyChanged += OnHierarchyWindowChanged;
_needToSetup = false;
_settupedGameObject = 0;
}
public static void ResetSymLinksDatas()
{
_gameObjectsId.Clear();
_gameObjectsList.Clear();
_toolTipInfo.Clear();
_gameObjectsHasBeenSetup.Clear();
_gameObjectsIsInFrameWork.Clear();
_settupedGameObject = 0;
}
[UnityEditor.Callbacks.DidReloadScripts]
private static void OnScriptsReloaded()
{
_needToSetup = true;
}
private static void OnHierarchyWindowChanged()
{
_needToSetup = true;
}
/// <summary>
/// called at every frameGUI for every gaemObjects inside hierarchy
/// </summary>
/// <param name="instanceId"></param>
/// <param name="selectionRect"></param>
private static void OnHierarchyItemGUI(int instanceId, Rect selectionRect)
{
try
{
if (_needToSetup)
{
ResetHasBeenSetupOfGameObjects();
_needToSetup = false;
}
bool succes = _gameObjectsId.ContainIndex(instanceId, out int index);
if (!succes)
{
UnityEngine.Object targetGameObject = EditorUtility.InstanceIDToObject(instanceId);
if (targetGameObject == null)
{
return;
}
_gameObjectsId.Add(instanceId);
_gameObjectsList.Add(targetGameObject);
_toolTipInfo.Add(SYMLINK_TOOLTIP);
_gameObjectsHasBeenSetup.Add(false);
_gameObjectsIsInFrameWork.Add(false);
index = _gameObjectsId.Count - 1;
SetupGameObject(index);
}
if (!_gameObjectsHasBeenSetup[index])
{
SetupGameObject(index);
}
if (_gameObjectsIsInFrameWork[index])
{
DisplayMarker(selectionRect, _toolTipInfo[index]);
}
}
catch (Exception e) { Debug.Log(e); }
}
private static void ResetHasBeenSetupOfGameObjects()
{
_settupedGameObject = 0;
for (int i = 0; i < _gameObjectsHasBeenSetup.Count; i++)
{
_gameObjectsHasBeenSetup[i] = false;
_toolTipInfo[i] = SYMLINK_TOOLTIP;
}
}
/// <summary>
/// From an instance id, setup the info:
/// HasBennSetup at true
/// determine if gameObject is inside framework or not !
/// </summary>
/// <param name="instanceId"></param>
/// <param name="info"></param>
private static void SetupGameObject(int index)
{
if (_settupedGameObject > MAX_SETUP_IN_ONE_FRAME)
{
_timerBetween2ChunkSetup.StartChrono(TIME_BETWEEN_2_CHUNK_SETUP);
_settupedGameObject = 0;
return;
}
if (_timerBetween2ChunkSetup.IsRunning())
{
return;
}
string toolTip = _toolTipInfo[index];
bool prefab = DetermineIfGameObjectIsInSymLink.IsPrefabsAndInSymLink(_gameObjectsList[index] as GameObject, ref SymLinksOnProjectWindowItemGUI.AllSymLinksAssetPathSaved, ref toolTip);
bool component = DetermineIfGameObjectIsInSymLink.HasComponentInSymLink(_gameObjectsList[index] as GameObject, ref SymLinksOnProjectWindowItemGUI.AllSymLinksAssetPathSaved, ref toolTip);
bool assets = DetermineIfGameObjectIsInSymLink.HasSymLinkAssetInsideComponent(_gameObjectsList[index] as GameObject, ref SymLinksOnProjectWindowItemGUI.AllSymLinksAssetPathSaved, ref toolTip);
bool isSomethingInsideFrameWork = prefab || component || assets;
_toolTipInfo[index] = toolTip;
_gameObjectsHasBeenSetup[index] = true;
_gameObjectsIsInFrameWork[index] = isSomethingInsideFrameWork;
_settupedGameObject++;
}
/// <summary>
/// display the marker at the given position
/// </summary>
/// <param name="selectionRect"></param>
private static void DisplayMarker(Rect selectionRect, string toolTip)
{
Rect r = new Rect(selectionRect);
r.x = r.width - 20;
r.width = 18;
ExtSymLinks.DisplayTinyMarker(selectionRect, toolTip);
}
}
}
SymLinksOnProjectWindowItemGUI.cs
using UnityEditor;
using UnityEngine;
using System.IO;
using hedCommon.extension.editor;
using System.Collections.Generic;
using hedCommon.extension.runtime;
using extUnityComponents.transform;
using extUnityComponents;
using System;
using System.Reflection;
using hedCommon.time;
namespace hedCommon.symlinks
{
/// <summary>
/// An editor utility for easily creating symlinks in your project.
///
/// Adds a Menu item under `Assets/Create/Folder(Symlink)`, and
/// draws a small indicator in the Project view for folders that are
/// symlinks.
/// </summary>
[InitializeOnLoad]
public static class SymLinksOnProjectWindowItemGUI
{
public static List<string> AllSymLinksAssetPathSaved = new List<string>(300);
public static void AddPathOfSymLinkAsset(string path)
{
AllSymLinksAssetPathSaved.AddIfNotContain(path);
}
/// <summary>
/// Static constructor subscribes to projectWindowItemOnGUI delegate.
/// </summary>
static SymLinksOnProjectWindowItemGUI()
{
EditorApplication.projectWindowItemOnGUI -= OnProjectWindowItemGUI;
EditorApplication.projectWindowItemOnGUI += OnProjectWindowItemGUI;
}
public static void ResetSymLinksDatas()
{
AllSymLinksAssetPathSaved.Clear();
}
/// <summary>
/// Draw a little indicator if folder is a symlink
/// </summary>
/// <param name="guid"></param>
/// <param name="r"></param>
private static void OnProjectWindowItemGUI(string guid, Rect r)
{
try
{
string path = AssetDatabase.GUIDToAssetPath(guid);
if (!string.IsNullOrEmpty(path))
{
FileAttributes attribs = File.GetAttributes(path);
DetermineIfAssetIsOrIsInSymLink.UpdateSymLinksParent(path, ref AllSymLinksAssetPathSaved);
if (DetermineIfAssetIsOrIsInSymLink.IsAttributeASymLink(attribs))
{
ExtSymLinks.DisplayBigMarker(r, "this folder is a symlink");
}
else if (DetermineIfAssetIsOrIsInSymLink.IsAttributeAFileInsideASymLink(path, attribs, ref AllSymLinksAssetPathSaved))
{
ExtSymLinks.DisplayTinyMarker(r, "this object is inside a symlink folder");
}
}
}
catch {}
}
}
}
DetermineIfAssetIsOrIsInSymLink.cs
using extUnityComponents;
using extUnityComponents.transform;
using hedCommon.extension.runtime;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace hedCommon.symlinks
{
public static class DetermineIfAssetIsOrIsInSymLink
{
private const FileAttributes FOLDER_SYMLINK_ATTRIBS = FileAttributes.Directory | FileAttributes.ReparsePoint;
private const FileAttributes FILE_SYMLINK_ATTRIBS = FileAttributes.Directory & FileAttributes.Archive;
/// <summary>
/// is this
/// </summary>
/// <param name="path"></param>
/// <param name="attribs"></param>
/// <returns></returns>
public static bool IsAttributeAFileInsideASymLink(string path, FileAttributes attribs, ref List<string> allSymLinksAssetPathSaved)
{
return (attribs & FILE_SYMLINK_ATTRIBS) == FILE_SYMLINK_ATTRIBS && path.ContainIsPaths(allSymLinksAssetPathSaved);
}
public static bool IsAttributeASymLink(FileAttributes attribs)
{
return (attribs & FOLDER_SYMLINK_ATTRIBS) == FOLDER_SYMLINK_ATTRIBS;
}
public static void UpdateSymLinksParent(string path, ref List<string> allSymLinksAssetPathSaved)
{
while (!string.IsNullOrEmpty(path))
{
FileAttributes attribs = File.GetAttributes(path);
if (IsAttributeASymLink(attribs))
{
allSymLinksAssetPathSaved.AddIfNotContain(path);
return;
}
path = ExtPaths.GetDirectoryFromCompletPath(path);
}
}
//end of class
}
}
DetermineIfGameObjectIsInSymLink.cs
using extUnityComponents;
using extUnityComponents.transform;
using hedCommon.extension.runtime;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEngine;
namespace hedCommon.symlinks
{
public static class DetermineIfGameObjectIsInSymLink
{
private const BindingFlags _flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic;
private static List<System.Type> _allNonPersistentTypeComponents = new List<System.Type>()
{
typeof(Transform),
typeof(GameObject),
typeof(TransformHiddedTools),
typeof(AnimatorHiddedTools),
typeof(MeshFilterHiddedTools),
typeof(RectTransformHiddedTools),
typeof(RectTransformHiddedTools),
typeof(RigidBodyAdditionalMonobehaviourSettings),
};
private static List<string> _allForbiddenPropertyName = new List<string>()
{
"material",
"materials",
"mesh"
};
private static List<System.Type> _allForbiddenPropertyType = new List<Type>()
{
typeof(Transform),
typeof(GameObject)
};
/// <summary>
/// Determine if a gameObject is a prefab, if it is, determine if it's inside a symLink
/// </summary>
/// <param name="g">possible prefabs to test</param>
/// <returns>true if gameObject IS a prefabs, AND inside a symLink folder</returns>
public static bool IsPrefabsAndInSymLink(GameObject g, ref List<string> allSymLinksAssetPathSaved, ref string toolTipInfo)
{
bool isPrefab = ExtPrefabsEditor.IsPrefab(g, out GameObject prefab);
if (!isPrefab)
{
return (false);
}
UnityEngine.Object parentObject = PrefabUtility.GetCorrespondingObjectFromSource(prefab);
string path = AssetDatabase.GetAssetPath(parentObject);
DetermineIfAssetIsOrIsInSymLink.UpdateSymLinksParent(path, ref allSymLinksAssetPathSaved);
FileAttributes attribs = File.GetAttributes(path);
bool isInPrefab = DetermineIfAssetIsOrIsInSymLink.IsAttributeAFileInsideASymLink(path, attribs, ref allSymLinksAssetPathSaved);
if (isInPrefab)
{
toolTipInfo += "\n - Prefab root";
}
return (isInPrefab);
}
/// <summary>
/// return true if this gameObject has at least one component inside a symLink
/// </summary>
/// <param name="gameObject"></param>
/// <returns></returns>
public static bool HasComponentInSymLink(GameObject g, ref List<string> allSymLinksAssetPathSaved, ref string toolTip)
{
bool hasComponent = false;
MonoBehaviour[] monoBehaviours = g.GetComponents<MonoBehaviour>();
for (int i = 0; i < monoBehaviours.Length; i++)
{
MonoBehaviour mono = monoBehaviours[i];
if (mono != null && mono.hideFlags == HideFlags.None && !_allNonPersistentTypeComponents.Contains(mono.GetType()))
{
MonoScript script = MonoScript.FromMonoBehaviour(mono); // gets script as an asset
string path = script.GetPath();
DetermineIfAssetIsOrIsInSymLink.UpdateSymLinksParent(path, ref allSymLinksAssetPathSaved);
FileAttributes attribs = File.GetAttributes(path);
if (DetermineIfAssetIsOrIsInSymLink.IsAttributeAFileInsideASymLink(path, attribs, ref allSymLinksAssetPathSaved))
{
toolTip += "\n - " + script.name + " c#";
hasComponent = true;
}
}
}
return (hasComponent);
}
public static bool HasSymLinkAssetInsideComponent(GameObject g, ref List<string> allSymLinksAssetPathSaved, ref string toolTip)
{
bool hasAsset = false;
Component[] components = g.GetComponents<Component>();
if (components == null || components.Length == 0)
{
return (false);
}
for (int i = 0; i < components.Length; i++)
{
Component component = components[i];
if (component == null)
{
continue;
}
Type componentType = component.GetType();
if (_allNonPersistentTypeComponents.Contains(componentType))
{
continue;
}
try
{
PropertyInfo[] properties = componentType.GetProperties(_flags);
foreach (PropertyInfo property in properties)
{
if (_allForbiddenPropertyName.Contains(property.Name))
{
continue;
}
if (_allForbiddenPropertyType.Contains(property.PropertyType))
{
continue;
}
if (property.IsDefined(typeof(ObsoleteAttribute), true))
{
continue;
}
UnityEngine.Object propertyValue = property.GetValue(component, null) as UnityEngine.Object;
if (propertyValue is UnityEngine.Object && !propertyValue.IsTruelyNull())
{
string path = propertyValue.GetPath();
DetermineIfAssetIsOrIsInSymLink.UpdateSymLinksParent(path, ref allSymLinksAssetPathSaved);
FileAttributes attribs = File.GetAttributes(path);
if (DetermineIfAssetIsOrIsInSymLink.IsAttributeAFileInsideASymLink(path, attribs, ref allSymLinksAssetPathSaved))
{
toolTip += "\n - " + propertyValue.name + " in " + ShortType(component.GetType().ToString());
hasAsset = true;
}
}
}
FieldInfo[] fields = componentType.GetFields(_flags);
foreach (FieldInfo field in fields)
{
if (field.IsDefined(typeof(ObsoleteAttribute), true))
{
continue;
}
UnityEngine.Object fieldValue = field.GetValue(component) as UnityEngine.Object;
if (fieldValue is UnityEngine.Object && !fieldValue.IsTruelyNull())
{
string path = fieldValue.GetPath();
DetermineIfAssetIsOrIsInSymLink.UpdateSymLinksParent(path, ref allSymLinksAssetPathSaved);
FileAttributes attribs = File.GetAttributes(path);
if (DetermineIfAssetIsOrIsInSymLink.IsAttributeAFileInsideASymLink(path, attribs, ref allSymLinksAssetPathSaved))
{
toolTip += "\n - " + fieldValue.name + " in " + ShortType(component.GetType().ToString());
hasAsset = true;
}
}
}
}
catch { }
}
return (hasAsset);
}
private static string ShortType(string fullType)
{
return (Path.GetExtension(fullType).Replace(".", ""));
}
//end of class
}
}
Fichiers source
- Télécharger ExtSymLinks.cs
- Télécharger SymLinksCreation.cs
- Télécharger SymLinksOnHierarchyItemGUI.cs
- Télécharger SymLinksOnProjectWindowItemGUI.cs
- Télécharger DetermineIfAssetIsOrIsInSymLink.cs
- Télécharger DetermineIfGameObjectIsInSymLink.cs
Voir aussi
- Extension de l’Animator : utiliser la réflexion avancée pas à pas : lire automatiquement la première animation d’un Animator depuis la Scene view, sans script supplémentaire sur les GameObjects.
- Changer la projection de la caméra : passer la caméra de la perspective à l’orthographique par interpolation linéaire couplée à une fonction d’easing.



