├── Assets └── EditorExtensions │ ├── Editor │ ├── CopyUtility.cs │ ├── ObjectPostprocessor.cs │ └── SettingXmlSerializer.cs │ └── Samples │ └── Editor │ ├── ObjectPostprocessorTest.cs │ ├── SettingXmlSerializerTest.cs │ └── SettingXmlSerializerTest.xml └── README.md /Assets/EditorExtensions/Editor/CopyUtility.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nora 2 | // Released under the MIT license 3 | // http://opensource.org/licenses/mit-license.php 4 | 5 | namespace EditorExtensions 6 | { 7 | 8 | public static class CopyUtility 9 | { 10 | static bool _IsAssignable( System.Type type ) 11 | { 12 | if( type != null ) { 13 | if( type.IsArray ) { 14 | return false; 15 | } 16 | 17 | if( type.IsPrimitive || type == typeof(string) ) { 18 | return true; 19 | } 20 | if( typeof(UnityEngine.Object).IsAssignableFrom( type ) ) { 21 | return true; 22 | } 23 | } 24 | 25 | return false; 26 | } 27 | 28 | public static Type DeepCopy< Type >( Type obj ) 29 | { 30 | if( obj != null ) { 31 | var objType = obj.GetType(); 32 | 33 | if( objType.IsArray ) { 34 | var objArray = (System.Array)(object)obj; 35 | var objArrayLength = objArray.Length; 36 | var newObj = (System.Array)System.Activator.CreateInstance( objType, objArrayLength ); 37 | if( newObj != null ) { 38 | if( _IsAssignable( objType.GetElementType() ) ) { 39 | for( int i = 0; i < objArrayLength; ++i ) { 40 | newObj.SetValue( objArray.GetValue( i ), i ); 41 | } 42 | } else { 43 | for( int i = 0; i < objArrayLength; ++i ) { 44 | newObj.SetValue( DeepCopy( objArray.GetValue( i ) ), i ); 45 | } 46 | } 47 | } 48 | 49 | return (Type)(object)newObj; 50 | } else { 51 | if( !_IsAssignable( objType ) ) { 52 | try { 53 | var newObj = System.Activator.CreateInstance( objType ); 54 | if( newObj != null ) { 55 | var objFields = objType.GetFields( System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public ); 56 | if( objFields != null ) { 57 | foreach( var objField in objFields ) { 58 | objField.SetValue( newObj, DeepCopy( objField.GetValue( obj ) ) ); 59 | } 60 | } 61 | 62 | return (Type)newObj; 63 | } 64 | } catch( System.Exception e ) { 65 | UnityEngine.Debug.LogError( e.ToString() ); 66 | } 67 | } 68 | return obj; 69 | } 70 | } 71 | 72 | return default(Type); 73 | } 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /Assets/EditorExtensions/Editor/ObjectPostprocessor.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nora 2 | // Released under the MIT license 3 | // http://opensource.org/licenses/mit-license.php 4 | 5 | //#define OBJECTPOSTPROCESSOR_DEBUGLOG 6 | 7 | using System.Collections.Generic; 8 | using UnityEngine; 9 | using UnityEditor; 10 | using UnityEngine.SceneManagement; 11 | using UnityEditor.SceneManagement; 12 | 13 | namespace EditorExtensions 14 | { 15 | 16 | [InitializeOnLoad] 17 | public class ObjectPostprocessor : AssetPostprocessor 18 | { 19 | public delegate void AssetAddedHandler( string assetPath ); 20 | public static event AssetAddedHandler assetAdded; 21 | 22 | public delegate void AssetMovedHandler( string assetPathFrom, string assetPathTo ); 23 | public static event AssetMovedHandler assetMoved; 24 | 25 | public delegate void AssetDeletedHandler( string assetPath ); 26 | public static event AssetDeletedHandler assetDeleted; 27 | 28 | public delegate void AssetDuplicatedHandler( string assetPathFrom, string assetPathTo ); 29 | public static event AssetDuplicatedHandler assetDuplicated; 30 | 31 | public delegate void PrefabConnectedHandler( GameObject gameObjectFrom, string assetPathTo ); 32 | public static event PrefabConnectedHandler prefabConnected; 33 | 34 | public delegate void GameObjectAddedHandler( GameObject gameObject ); 35 | public static event GameObjectAddedHandler gameObjectAdded; 36 | 37 | public delegate void GameObjectDeletedHandler( Scene scene ); 38 | public static event GameObjectDeletedHandler gameObjectDeleted; 39 | 40 | public delegate void GameObjectSceneMovedHandler( Scene sceneFrom, GameObject gameObject ); 41 | public static event GameObjectSceneMovedHandler gameObjectSceneMoved; 42 | 43 | public delegate void GameObjectDuplicatedHandler( GameObject gameObjectFrom, GameObject gameObjectTo ); 44 | public static event GameObjectDuplicatedHandler gameObjectDuplicated; 45 | 46 | public delegate void PrefabInstantiatedHandler( string assetPathFrom, GameObject gameObjectTo ); 47 | public static event PrefabInstantiatedHandler prefabInstantiated; 48 | 49 | //-------------------------------------------------------------------------------------------------------- 50 | 51 | static void _AssetAdded( string assetPath ) 52 | { 53 | DebugLog( "AssetAdded: " + assetPath ); 54 | if( assetAdded != null ) { 55 | assetAdded( assetPath ); 56 | } 57 | } 58 | 59 | static void _AssetMoved( string assetPathFrom, string assetPathTo ) 60 | { 61 | DebugLog( "AssetMoved: from: " + assetPathFrom + " to: " + assetPathTo ); 62 | if( assetMoved != null ) { 63 | assetMoved( assetPathFrom, assetPathTo ); 64 | } 65 | } 66 | 67 | static void _AssetDeleted( string assetPath ) 68 | { 69 | DebugLog( "AssetDeleted: " + assetPath ); 70 | if( assetDeleted != null ) { 71 | assetDeleted( assetPath ); 72 | } 73 | } 74 | 75 | static void _AssetDuplicated( string assetPathFrom, string assetPathTo ) 76 | { 77 | DebugLog( "AssetDuplicated: from: " + assetPathFrom + " to: " + assetPathTo ); 78 | if( assetDuplicated != null ) { 79 | assetDuplicated( assetPathFrom, assetPathTo ); 80 | } 81 | } 82 | 83 | static void _PrefabConnected( GameObject gameObjectFrom, string assetPathTo ) 84 | { 85 | DebugLog( "PrefabConnected: gameObjectFrom: " + gameObjectFrom.name + " assetPathTo: " + assetPathTo ); 86 | if( prefabConnected != null ) { 87 | prefabConnected( gameObjectFrom, assetPathTo ); 88 | } 89 | } 90 | 91 | static void _GameObjectAdded( GameObject gameObject ) 92 | { 93 | DebugLog( "GameObjectAdded: gameObject: " + gameObject.name ); 94 | if( gameObjectAdded != null ) { 95 | gameObjectAdded( gameObject ); 96 | } 97 | } 98 | 99 | static void _GameObjectDeleted( Scene scene ) 100 | { 101 | DebugLog( "GameObjectDeleted: scene: " + scene.name ); 102 | if( gameObjectDeleted != null ) { 103 | gameObjectDeleted( scene ); 104 | } 105 | } 106 | 107 | static void _GameObjectSceneMoved( Scene sceneFrom, GameObject gameObject ) 108 | { 109 | DebugLog( "GameObjectSceneMoved: sceneFrom: " + sceneFrom.name + " gameObject: " + gameObject.name ); 110 | if( gameObjectSceneMoved != null ) { 111 | gameObjectSceneMoved( sceneFrom, gameObject ); 112 | } 113 | } 114 | 115 | static void _GameObjectDuplicated( GameObject gameObjectFrom, GameObject gameObjectTo ) 116 | { 117 | DebugLog( "GameObjectDuplicated: gameObjectFrom: " + gameObjectFrom.name + " gameObjectTo: " + gameObjectTo.name ); 118 | if( gameObjectDuplicated != null ) { 119 | gameObjectDuplicated( gameObjectFrom, gameObjectTo ); 120 | } 121 | } 122 | 123 | static void _PrefabInstantiated( string assetPathFrom, GameObject gameObjectTo ) 124 | { 125 | DebugLog( "PrefabInstantiated: assetPathFrom: " + assetPathFrom + " gameObjectTo: " + gameObjectTo.name ); 126 | if( prefabInstantiated != null ) { 127 | prefabInstantiated( assetPathFrom, gameObjectTo ); 128 | } 129 | } 130 | 131 | //-------------------------------------------------------------------------------------------------------- 132 | 133 | [System.Diagnostics.Conditional("OBJECTPOSTPROCESSOR_DEBUGLOG")] 134 | static void DebugLog( string text ) 135 | { 136 | Debug.Log( text ); 137 | } 138 | 139 | //-------------------------------------------------------------------------------------------------------- 140 | 141 | static bool _isPlaying; 142 | 143 | static HashSet _assetPaths; 144 | static HashSet _inSelection_importedAssetPaths = new HashSet(); 145 | static string[] _selectionPaths; 146 | 147 | static _GameObjectsInScenes _gameObjectsInScenes; 148 | 149 | static GameObject _prev_selectionGameObject; 150 | static GameObject _selectionGameObject; 151 | static GameObject[] _prev_selectionGameObjects; 152 | static GameObject[] _selectionGameObjects; 153 | static _GameObjectHierarchy _prev_selectionGameObjectHierarchy; 154 | static _GameObjectHierarchy _selectionGameObjectHierarchy; 155 | static _GameObjectSiblings[] _prev_selectionGameObjectSiblings; 156 | static _GameObjectSiblings[] _selectionGameObjectSiblings; 157 | 158 | //-------------------------------------------------------------------------------------------------------- 159 | 160 | static ObjectPostprocessor() 161 | { 162 | _assetPaths = _GetAssetPaths(); 163 | 164 | _SetIsPlaying( EditorApplication.isPlayingOrWillChangePlaymode || EditorApplication.isPlaying ); 165 | 166 | EditorApplication.playmodeStateChanged += PlaymodeStateChanged; 167 | Selection.selectionChanged += SelectionChanged; 168 | } 169 | 170 | static HashSet _GetAssetPaths() 171 | { 172 | var assetPaths = new HashSet(); 173 | var allAssetPaths = AssetDatabase.GetAllAssetPaths(); 174 | if( allAssetPaths != null ) { 175 | foreach( var assetPath in allAssetPaths ) { 176 | if( _IsTargetAssetPath( assetPath ) ) { 177 | assetPaths.Add( assetPath ); 178 | } 179 | } 180 | } 181 | 182 | return assetPaths; 183 | } 184 | 185 | static void _SetIsPlaying( bool isPlaying ) 186 | { 187 | _isPlaying = isPlaying; 188 | if( _isPlaying ) { 189 | _gameObjectsInScenes = null; 190 | 191 | _UpdateSelection(); 192 | _UpdatePrevSelection(); 193 | 194 | EditorApplication.hierarchyWindowChanged -= HierarchyWindowChanged; 195 | } else { 196 | _gameObjectsInScenes = new _GameObjectsInScenes(); 197 | 198 | _UpdateSelection(); 199 | _UpdatePrevSelection(); 200 | 201 | EditorApplication.hierarchyWindowChanged += HierarchyWindowChanged; 202 | } 203 | } 204 | 205 | static void PlaymodeStateChanged() 206 | { 207 | bool isPlaying = (EditorApplication.isPlayingOrWillChangePlaymode || EditorApplication.isPlaying); 208 | if( _isPlaying != isPlaying ) { 209 | _SetIsPlaying( isPlaying ); 210 | } 211 | } 212 | 213 | #if false 214 | static GameObject CreateGameObjectInScene( UnityEngine.SceneManagement.Scene scene, string name, HideFlags hideFlags ) 215 | { 216 | var gameObject = UnityEditor.EditorUtility.CreateGameObjectWithHideFlags( name, hideFlags ); 217 | UnityEditor.SceneManagement.EditorSceneManager.MoveGameObjectToScene( gameObject, scene ); 218 | return gameObject; 219 | } 220 | #endif 221 | 222 | static void OnPostprocessAllAssets( string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths ) 223 | { 224 | if( deletedAssets != null ) { 225 | for( int i = 0; i < deletedAssets.Length; ++i ) { 226 | if( _IsTargetAssetPath( deletedAssets[i] ) ) { 227 | _assetPaths.Remove( deletedAssets[i] ); 228 | _inSelection_importedAssetPaths.Remove( deletedAssets[i] ); 229 | _StringsSetNull( _selectionPaths, deletedAssets[i] ); 230 | 231 | _AssetDeleted( deletedAssets[i] ); 232 | } 233 | } 234 | } 235 | 236 | if( movedAssets != null ) { 237 | for( int i = 0; i < movedAssets.Length; ++i ) { 238 | if( _IsTargetAssetPath( movedAssets[i] ) ) { 239 | if( _assetPaths.Remove( movedFromAssetPaths[i] ) ) { 240 | _assetPaths.Add( movedAssets[i] ); 241 | } 242 | if( _inSelection_importedAssetPaths.Remove( movedFromAssetPaths[i] ) ) { 243 | _inSelection_importedAssetPaths.Add( movedAssets[i] ); 244 | } 245 | _StringsExchange( _selectionPaths, movedFromAssetPaths[i], movedAssets[i] ); 246 | 247 | _AssetMoved( movedFromAssetPaths[i], movedAssets[i] ); 248 | } 249 | } 250 | } 251 | 252 | if( importedAssets != null ) { 253 | if( _IsNewAssetContains( importedAssets ) ) { 254 | string[] duplicatedAssetPaths = _GetDuplicatedAssetPaths( importedAssets ); 255 | 256 | for( int i = 0; i < importedAssets.Length; ++i ) { 257 | if( _IsTargetAssetPath( importedAssets[i] ) && !_assetPaths.Contains( importedAssets[i] ) ) { 258 | _assetPaths.Add( importedAssets[i] ); 259 | _inSelection_importedAssetPaths.Add( importedAssets[i] ); // Check for PrefabConnected 260 | if( duplicatedAssetPaths != null && duplicatedAssetPaths[i] != null ) { 261 | _AssetDuplicated( duplicatedAssetPaths[i], importedAssets[i] ); 262 | } else { 263 | _AssetAdded( importedAssets[i] ); 264 | } 265 | } 266 | } 267 | } 268 | } 269 | } 270 | 271 | static void _StringsSetNull( string[] strings, string str ) 272 | { 273 | if( strings != null ) { 274 | for( int i = 0; i < strings.Length; ++i ) { 275 | if( strings[i] == str ) { 276 | strings[i] = null; 277 | } 278 | } 279 | } 280 | } 281 | 282 | static void _StringsExchange( string[] strings, string strFrom, string strTo ) 283 | { 284 | if( strings != null ) { 285 | for( int i = 0; i < strings.Length; ++i ) { 286 | if( strings[i] == strFrom ) { 287 | strings[i] = strTo; 288 | } 289 | } 290 | } 291 | } 292 | 293 | static bool _IsTargetAssetPath( string assetPath ) 294 | { 295 | return assetPath != null && assetPath.StartsWith("Assets/") && !assetPath.StartsWith("Assets/__DELETED_GUID_Trash/"); 296 | } 297 | 298 | static bool _IsNewAssetContains( string[] importedAssets ) 299 | { 300 | if( importedAssets != null ) { 301 | for( int i = 0; i < importedAssets.Length; ++i ) { 302 | if( _IsTargetAssetPath( importedAssets[i] ) && !_assetPaths.Contains( importedAssets[i] ) ) { 303 | return true; 304 | } 305 | } 306 | } 307 | 308 | return false; 309 | } 310 | 311 | static void HierarchyWindowChanged() 312 | { 313 | _CheckSceneChanged(); 314 | _CheckSelectionObjects_GameObjectMoved(); 315 | } 316 | 317 | static void _CheckSceneChanged() 318 | { 319 | if( _gameObjectsInScenes.CheckSceneChanged() ) { 320 | _prev_selectionGameObjectHierarchy.CheckSceneChanged(); 321 | _selectionGameObjectHierarchy.CheckSceneChanged(); 322 | _CheckSceneChanged( ref _prev_selectionGameObject, _prev_selectionGameObjects, _prev_selectionGameObjectSiblings ); 323 | _CheckSceneChanged( ref _selectionGameObject, _selectionGameObjects, _selectionGameObjectSiblings ); 324 | } 325 | } 326 | 327 | static void _CheckSceneChanged( ref GameObject gameObject, GameObject[] gameObjects, _GameObjectSiblings[] siblings ) 328 | { 329 | if( gameObjects != null && siblings != null ) { 330 | for( int i = 0; i < siblings.Length; ++i ) { 331 | if( siblings[i] != null && !siblings[i].scene.isLoaded ) { 332 | if( gameObject == gameObjects[i] ) { 333 | gameObject = null; 334 | } 335 | gameObjects[i] = null; 336 | siblings[i] = null; 337 | } 338 | } 339 | } 340 | } 341 | 342 | static void _CheckSelectionObjects_GameObjectMoved() 343 | { 344 | if( _selectionGameObjectHierarchy == null || 345 | _selectionGameObjectSiblings == null || 346 | _selectionGameObjects == null ) { 347 | return; 348 | } 349 | 350 | for( int i = 0; i < _selectionGameObjects.Length; ++i ) { 351 | var siblings = _selectionGameObjectSiblings[i]; 352 | var gameObject = _selectionGameObjects[i]; 353 | if( siblings != null && gameObject != null ) { 354 | if( siblings.scene != gameObject.scene ) { 355 | _selectionGameObjectSiblings[i] = _selectionGameObjectHierarchy.Move( siblings, gameObject ); 356 | _gameObjectsInScenes.Remove( gameObject ); 357 | _gameObjectsInScenes.Add( gameObject ); 358 | _GameObjectSceneMoved( siblings.scene, gameObject ); 359 | } 360 | } 361 | } 362 | } 363 | 364 | static void _UpdatePrevSelection() 365 | { 366 | _prev_selectionGameObject = _selectionGameObject; 367 | _prev_selectionGameObjects = _selectionGameObjects; 368 | _prev_selectionGameObjectSiblings = _selectionGameObjectSiblings; 369 | _prev_selectionGameObjectHierarchy = _selectionGameObjectHierarchy; 370 | } 371 | 372 | static void _UpdateSelection() 373 | { 374 | var selectionObjects = Selection.objects; 375 | _selectionPaths = _CollectSelectionPaths( selectionObjects ); 376 | 377 | _selectionGameObject = null; 378 | _selectionGameObjects = null; 379 | _selectionGameObjectHierarchy = null; 380 | _selectionGameObjectSiblings = null; 381 | 382 | if( _isPlaying ) { 383 | return; 384 | } 385 | 386 | if( selectionObjects != null && selectionObjects.Length > 0 ) { 387 | if( _selectionPaths == null || !_StringsIsFully( _selectionPaths ) ) { 388 | var selectionObject = Selection.activeObject; 389 | _selectionGameObjects = new GameObject[selectionObjects.Length]; 390 | for( int i = 0; i < selectionObjects.Length; ++i ) { 391 | if( selectionObjects[i] != null ) { 392 | if( _selectionPaths == null || string.IsNullOrEmpty( _selectionPaths[i] ) ) { 393 | _selectionGameObjects[i] = selectionObjects[i] as GameObject; 394 | if( selectionObjects[i] == selectionObject ) { 395 | _selectionGameObject = _selectionGameObjects[i]; 396 | } 397 | } 398 | } 399 | } 400 | } 401 | } 402 | 403 | if( _selectionGameObjects != null && _selectionGameObjects.Length > 0 ) { 404 | _selectionGameObjectHierarchy = new _GameObjectHierarchy( _selectionGameObjects ); 405 | _selectionGameObjectSiblings = new _GameObjectSiblings[_selectionGameObjects.Length]; 406 | for( int i = 0; i < _selectionGameObjects.Length; ++i ) { 407 | _selectionGameObjectSiblings[i] = _selectionGameObjectHierarchy.GetSiblings( _selectionGameObjects[i] ); 408 | } 409 | } else { 410 | _selectionGameObjectHierarchy = new _GameObjectHierarchy( null ); // Note: Collection Root only.(for "GameObject/Creat Empty") 411 | } 412 | } 413 | 414 | static void SelectionChanged() 415 | { 416 | _UpdatePrevSelection(); 417 | _UpdateSelection(); 418 | 419 | _CheckSelectionObjects(); 420 | } 421 | 422 | static string[] _CollectSelectionPaths( UnityEngine.Object[] selectionObjects ) 423 | { 424 | if( selectionObjects != null ) { 425 | string[] selectionPaths = null; 426 | for( int i = 0; i < selectionObjects.Length; ++i ) { 427 | var assetPath = AssetDatabase.GetAssetPath( selectionObjects[i] ); 428 | if( !string.IsNullOrEmpty( assetPath ) ) { 429 | if( selectionPaths == null ) { 430 | selectionPaths = new string[selectionObjects.Length]; 431 | } 432 | selectionPaths[i] = assetPath; 433 | } 434 | } 435 | 436 | return selectionPaths; 437 | } 438 | 439 | return null; 440 | } 441 | 442 | /* 443 | Note: Increment Patterns for Asset Names. 444 | 445 | AssetName 446 | AssetName 1 447 | ... 448 | 449 | AssetName 0 450 | AssetName 1 451 | ... 452 | 453 | AssetName -1 454 | AssetName -2 455 | ... 456 | */ 457 | 458 | struct _AssetPathTemp 459 | { 460 | public string assetPath; 461 | public string assetPathBase; // Without number and extention. 462 | public string extension; 463 | 464 | public ulong assetNum; 465 | 466 | public _AssetPathTemp( string assetPath ) 467 | { 468 | this.assetPath = assetPath; 469 | this.assetPathBase = assetPath; 470 | this.extension = null; 471 | this.assetNum = 0; 472 | 473 | if( assetPath == null ) { 474 | return; 475 | } 476 | 477 | int length = assetPath.Length; 478 | if( length == 0 ) { 479 | return; 480 | } 481 | 482 | int extensionPos; 483 | int i = _SkipExtention( assetPath, out extensionPos ); 484 | if( extensionPos >= 0 ) { 485 | this.extension = assetPath.Substring( extensionPos ); 486 | } 487 | 488 | ulong num = 0; 489 | int numPos = -1; 490 | unchecked { 491 | for( ulong scl = 1; i >= 0; --i, scl *= 10 ) { 492 | char c = assetPath[i]; 493 | ulong t = (ulong)(c - '0'); 494 | if( t <= 9 ) { 495 | numPos = i; 496 | num += t * scl; 497 | } else { 498 | break; 499 | } 500 | } 501 | } 502 | 503 | this.assetNum = num; 504 | 505 | if( numPos >= 0 ) { 506 | this.assetPathBase = this.assetPath.Substring( 0, numPos ); 507 | } else if( extensionPos >= 0 ) { 508 | this.assetPathBase = this.assetPath.Substring( 0, extensionPos ) + " "; 509 | } 510 | } 511 | 512 | public void Increment() 513 | { 514 | ++this.assetNum; 515 | 516 | if( this.assetPathBase != null ) { 517 | this.assetPath = this.assetPathBase + this.assetNum.ToString(); 518 | if( this.extension != null ) { 519 | this.assetPath += this.extension; 520 | } 521 | } 522 | } 523 | 524 | static int _SkipExtention( string objectName, out int extensionPos ) 525 | { 526 | extensionPos = -1; 527 | 528 | if( objectName == null ) { 529 | return 0; 530 | } 531 | 532 | int length = objectName.Length; 533 | if( length > 0 ) { 534 | for( int i = length - 1; i >= 0; --i ) { 535 | char c = objectName[i]; 536 | if( c == '.' ) { 537 | if( i > 0 ) { 538 | extensionPos = i; // Has extention. 539 | return i - 1; 540 | } else { 541 | return length - 1; // No extention. 542 | } 543 | } else if( c == '/' || c == '\\' ) { 544 | return length - 1; // No extention. 545 | } 546 | } 547 | 548 | return length - 1; 549 | } 550 | 551 | return 0; 552 | } 553 | } 554 | 555 | // Note: Must be updated when naming rules are changed. 556 | static string[] _GetDuplicatedAssetPaths( string[] importedAssets ) 557 | { 558 | if( _assetPaths == null || _selectionPaths == null || importedAssets == null ) { 559 | return null; 560 | } 561 | 562 | string[] sourceAssetPaths = null; 563 | HashSet reservedAssetPaths = null; 564 | 565 | for( int i = 0; i < _selectionPaths.Length; ++i ) { 566 | string assetPath = _selectionPaths[i]; 567 | if( string.IsNullOrEmpty( assetPath ) ) { 568 | continue; 569 | } 570 | 571 | _AssetPathTemp destAssetPathTemp = new _AssetPathTemp( assetPath ); 572 | 573 | for(;;) { 574 | destAssetPathTemp.Increment(); 575 | if( _assetPaths.Contains( destAssetPathTemp.assetPath ) ) { 576 | continue; 577 | } 578 | if( reservedAssetPaths != null && reservedAssetPaths.Contains( destAssetPathTemp.assetPath ) ) { 579 | continue; 580 | } 581 | 582 | break; 583 | } 584 | 585 | int index = _StringsIndexOf( importedAssets, destAssetPathTemp.assetPath ); 586 | if( index >= 0 ) { 587 | if( sourceAssetPaths == null ) { 588 | sourceAssetPaths = new string[importedAssets.Length]; 589 | reservedAssetPaths = new HashSet( _assetPaths ); 590 | } 591 | sourceAssetPaths[index] = assetPath; 592 | reservedAssetPaths.Add( destAssetPathTemp.assetPath ); 593 | } 594 | } 595 | 596 | return sourceAssetPaths; 597 | } 598 | 599 | static bool _StringsIsFully( string[] strings ) 600 | { 601 | if( strings != null ) { 602 | foreach( var str in strings ) { 603 | if( string.IsNullOrEmpty( str ) ) { 604 | return false; 605 | } 606 | } 607 | 608 | return true; 609 | } else { 610 | return false; 611 | } 612 | } 613 | 614 | static bool _StringsIsEmpty( string[] strings ) 615 | { 616 | if( strings != null ) { 617 | foreach( var str in strings ) { 618 | if( !string.IsNullOrEmpty( str ) ) { 619 | return false; 620 | } 621 | } 622 | 623 | return true; 624 | } else { 625 | return true; 626 | } 627 | } 628 | 629 | static int _StringsIndexOf( string[] strings, string str ) 630 | { 631 | if( strings != null && str != null ) { 632 | for( int i = 0; i < strings.Length; ++i ) { 633 | if( strings[i] != null && strings[i] == str ) { 634 | return i; 635 | } 636 | } 637 | } 638 | 639 | return -1; 640 | } 641 | 642 | /* 643 | Note: Increment Patterns for GameObject Names. 644 | 645 | GameObject 646 | GameObject (1) 647 | ... 648 | 649 | GameObject (0) 650 | GameObject (1) 651 | ... 652 | 653 | GameObject (-1) 654 | GameObject (-1) (1) 655 | GameObject (-1) (2) 656 | ... 657 | 658 | GameObject -(0) 659 | GameObject - (1) 660 | GameObject - (2) 661 | ... 662 | 663 | GameObject -(1) 664 | GameObject - (1) 665 | GameObject - (2) 666 | ... 667 | 668 | GameObject -(2) 669 | GameObject - (2) 670 | GameObject - (3) 671 | ... 672 | 673 | (0) 674 | (1) 675 | ... 676 | 677 | (1) 678 | (1) 679 | ... 680 | */ 681 | 682 | static bool _CheckNewInstanceAvailable() 683 | { 684 | if( _prev_selectionGameObjectHierarchy == null || 685 | _selectionGameObjects == null || 686 | _selectionGameObjects.Length == 0 ) { 687 | return false; 688 | } 689 | 690 | bool isNewInstanceAvailable = false; 691 | for( int i = 0; i < _selectionGameObjects.Length; ++i ) { 692 | GameObject destGameObject = _selectionGameObjects[i]; 693 | if( destGameObject == null ) { 694 | continue; // Asset. 695 | } 696 | 697 | if( _gameObjectsInScenes.Contains( destGameObject ) ) { 698 | continue; // Not new instance. 699 | } 700 | 701 | isNewInstanceAvailable = true; 702 | 703 | var destSiblings = _prev_selectionGameObjectHierarchy.GetSiblings( destGameObject ); 704 | if( destSiblings != null && !destSiblings.Contains( destGameObject ) ) { 705 | destSiblings.markNewInstanceAvailable = true; // Note: Edit _prev_selectionGameObjectHierarchy directlly. 706 | } 707 | } 708 | 709 | return isNewInstanceAvailable; 710 | } 711 | 712 | // Note: Must be updated when naming rules are changed. 713 | static GameObject[] _GetDuplicatedGameObjects() 714 | { 715 | if( _prev_selectionGameObjectHierarchy == null || 716 | _prev_selectionGameObjects == null || 717 | _prev_selectionGameObjects.Length == 0 || 718 | _selectionGameObjects == null || 719 | _selectionGameObjects.Length == 0 ) { 720 | return null; 721 | } 722 | 723 | Scene activeScene = new Scene(); 724 | if( _prev_selectionGameObject != null ) { 725 | activeScene = _prev_selectionGameObject.scene; 726 | } 727 | 728 | GameObject[] duplicatedGameObjects = null; 729 | 730 | for( int i = 0; i < _prev_selectionGameObjects.Length; ++i ) { 731 | var sourceGameObject = _prev_selectionGameObjects[i]; 732 | if( sourceGameObject == null ) { 733 | continue; // Skip asset. 734 | } 735 | 736 | var index = _GetDuplicatedGameObjectIndex( sourceGameObject, activeScene ); 737 | if( index >= 0 ) { 738 | if( duplicatedGameObjects == null ) { 739 | duplicatedGameObjects = new GameObject[_selectionGameObjects.Length]; 740 | } 741 | duplicatedGameObjects[index] = sourceGameObject; 742 | } 743 | } 744 | 745 | return duplicatedGameObjects; 746 | } 747 | 748 | static int _GetDuplicatedGameObjectIndex( GameObject sourceGameObject, Scene activeScene ) 749 | { 750 | var sourceSiblings = _prev_selectionGameObjectHierarchy.GetSiblings( sourceGameObject ); 751 | if( sourceSiblings == null || !sourceSiblings.markNewInstanceAvailable ) { 752 | return -1; 753 | } 754 | 755 | string gameObjectNewName = sourceGameObject.name; 756 | if( sourceGameObject.scene == activeScene ) { // Note: This rule is for active scene only. (Might be bug.) 757 | // Generate duplicated name. 758 | var gameObjectNameTemp = new _GameObjectNameTemp( sourceGameObject.name ); 759 | for(;;) { 760 | gameObjectNameTemp.Increment(); 761 | if( !sourceSiblings.Contains( gameObjectNameTemp.gameObjectName ) ) { 762 | gameObjectNewName = gameObjectNameTemp.gameObjectName; 763 | break; 764 | } 765 | } 766 | } 767 | 768 | for( int i = 0; i < _selectionGameObjects.Length; ++i ) { 769 | GameObject destGameObject = _selectionGameObjects[i]; 770 | if( destGameObject == null ) { 771 | continue; // Asset or same instance. 772 | } 773 | if( _gameObjectsInScenes.Contains( destGameObject ) ) { 774 | continue; // Not new instance. 775 | } 776 | 777 | var destSiblings = _prev_selectionGameObjectHierarchy.GetSiblings( destGameObject ); 778 | if( destSiblings == null ) { 779 | continue; // No siblings. (New selected.) 780 | } 781 | 782 | if( destSiblings.Contains( destGameObject ) ) { 783 | continue; // Failsafe. 784 | } 785 | 786 | if( destSiblings != sourceSiblings ) { 787 | continue; // Different hierarchy. 788 | } 789 | 790 | if( destGameObject.name == gameObjectNewName ) { 791 | sourceSiblings.Add( destGameObject ); // Note: Edit _prev_selectionGameObjectHierarchy directlly. 792 | return i; 793 | } 794 | } 795 | 796 | return -1; 797 | } 798 | 799 | struct _GameObjectNameTemp 800 | { 801 | public string gameObjectName; 802 | public string gameObjectNameBase; // Without number and extention. 803 | 804 | public ulong gameObjectNum; 805 | 806 | public _GameObjectNameTemp( string gameObjectName ) 807 | { 808 | this.gameObjectName = gameObjectName; 809 | this.gameObjectNameBase = gameObjectName; 810 | this.gameObjectNum = 0; 811 | 812 | if( gameObjectName == null ) { 813 | return; 814 | } 815 | 816 | this.gameObjectNameBase = gameObjectName + " "; 817 | 818 | int length = gameObjectName.Length; 819 | if( length < 3 ) { 820 | return; 821 | } 822 | if( gameObjectName[length - 1] != ')' ) { 823 | return; 824 | } 825 | 826 | int i = length - 2; 827 | ulong num = 0; 828 | int numPos = -1; 829 | bool bracketFound = false; 830 | unchecked { 831 | for( ulong scl = 1; i >= 0; --i, scl *= 10 ) { 832 | char c = gameObjectName[i]; 833 | ulong t = (ulong)(c - '0'); 834 | if( t <= 9 ) { 835 | numPos = i; 836 | num += t * scl; 837 | } else { 838 | if( c != '(' ) { 839 | return; 840 | } 841 | 842 | bracketFound = true; 843 | break; 844 | } 845 | } 846 | } 847 | 848 | if( !bracketFound || numPos <= 0 ) { 849 | return; 850 | } 851 | 852 | this.gameObjectNum = num; 853 | this.gameObjectNameBase = this.gameObjectName.Substring( 0, numPos - 1 ); 854 | if( string.IsNullOrEmpty( this.gameObjectNameBase ) ) { 855 | this.gameObjectNameBase = " "; 856 | } 857 | } 858 | 859 | public void Increment() 860 | { 861 | ++this.gameObjectNum; 862 | 863 | if( this.gameObjectNameBase != null ) { 864 | this.gameObjectName = this.gameObjectNameBase + '(' + this.gameObjectNum.ToString() + ')'; 865 | } 866 | } 867 | } 868 | 869 | static void _CheckSelectionObjects() 870 | { 871 | var dragAndDropObjects = DragAndDrop.objectReferences; 872 | foreach( var importedAssetPath in _inSelection_importedAssetPaths ) { 873 | _CheckSelectionObjects_PrefabConnected( dragAndDropObjects, importedAssetPath ); 874 | } 875 | _inSelection_importedAssetPaths.Clear(); 876 | 877 | if( !_isPlaying && _gameObjectsInScenes != null ) { 878 | _gameObjectsInScenes.CheckDeleted(); 879 | 880 | if( _CheckNewInstanceAvailable() ) { 881 | var duplicatedGameObjects = _GetDuplicatedGameObjects(); 882 | if( _selectionGameObjects != null ) { 883 | for( int i = 0; i < _selectionGameObjects.Length; ++i ) { 884 | if( _selectionGameObjects[i] != null ) { 885 | if( !_gameObjectsInScenes.Contains( _selectionGameObjects[i] ) ) { 886 | _gameObjectsInScenes.Add( _selectionGameObjects[i] ); 887 | _CheckSelectionObjects_GameObject( dragAndDropObjects, _selectionGameObjects[i], 888 | (duplicatedGameObjects != null) ? duplicatedGameObjects[i] : null ); 889 | } 890 | } 891 | } 892 | } 893 | } 894 | } 895 | } 896 | 897 | static bool _CheckSelectionObjects_PrefabConnected( UnityEngine.Object[] dragAndDropObjects, string assetPath ) 898 | { 899 | if( dragAndDropObjects != null ) { 900 | foreach( var obj in dragAndDropObjects ) { 901 | var gameObject = obj as GameObject; 902 | if( gameObject != null ) { 903 | var prefabParent = PrefabUtility.GetPrefabParent( gameObject ); 904 | if( prefabParent != null && AssetDatabase.GetAssetPath( prefabParent ) == assetPath ) { 905 | _PrefabConnected( gameObject, assetPath ); 906 | return true; 907 | } 908 | } 909 | } 910 | } 911 | 912 | return false; 913 | } 914 | 915 | static void _CheckSelectionObjects_GameObject( UnityEngine.Object[] dragAndDropObjects, GameObject gameObject, GameObject duplicatedGameObject ) 916 | { 917 | if( _CheckSelectionObjects_PrefabInstantiated( dragAndDropObjects, gameObject ) ) { 918 | return; 919 | } 920 | 921 | if( duplicatedGameObject != null ) { 922 | _GameObjectDuplicated( duplicatedGameObject, gameObject ); 923 | return; 924 | } 925 | 926 | // Finally, this gameObject will be added 927 | _GameObjectAdded( gameObject ); 928 | } 929 | 930 | static bool _CheckSelectionObjects_PrefabInstantiated( UnityEngine.Object[] dragAndDropObjects, GameObject gameObject ) 931 | { 932 | if( dragAndDropObjects != null ) { 933 | var prefabParent = PrefabUtility.GetPrefabParent( gameObject ); 934 | if( prefabParent != null && _Contains( dragAndDropObjects, prefabParent ) ) { 935 | _PrefabInstantiated( AssetDatabase.GetAssetPath( prefabParent ), gameObject ); 936 | return true; 937 | } 938 | } 939 | 940 | return false; 941 | } 942 | 943 | static bool _Contains< Type >( Type[] values, Type value ) 944 | where Type : class 945 | { 946 | if( values != null ) { 947 | foreach( var v in values ) { 948 | if( v == value ) { 949 | return true; 950 | } 951 | } 952 | } 953 | 954 | return false; 955 | } 956 | 957 | //-------------------------------------------------------------------------------------------------------------------- 958 | 959 | class _GameObjectsInScenes 960 | { 961 | static Dictionary> _gameObjectsInScenes; 962 | 963 | public _GameObjectsInScenes() 964 | { 965 | _gameObjectsInScenes = _GetGameObjectsInScenes(); 966 | } 967 | 968 | Dictionary> _GetGameObjectsInScenes() 969 | { 970 | var sceneCount = EditorSceneManager.sceneCount; 971 | var gameObjectsInScenes = new Dictionary>(); 972 | for( int i = 0; i < sceneCount; ++i ) { 973 | var scene = EditorSceneManager.GetSceneAt( i ); 974 | if( scene.isLoaded ) { 975 | gameObjectsInScenes.Add( scene, _GetGameObjectsInScene( scene ) ); 976 | } 977 | } 978 | 979 | return gameObjectsInScenes; 980 | } 981 | 982 | HashSet _GetGameObjectsInScene( Scene scene ) 983 | { 984 | var gameObjects = new HashSet(); 985 | var rootGameObjects = scene.GetRootGameObjects(); 986 | foreach( var rootGameObject in rootGameObjects ) { 987 | if( rootGameObject != null ) { 988 | _GetGameObjectsInScenes( gameObjects, rootGameObject.transform ); 989 | } 990 | } 991 | 992 | return gameObjects; 993 | } 994 | 995 | void _GetGameObjectsInScenes( HashSet gameObjects, Transform transform ) 996 | { 997 | if( transform != null ) { 998 | gameObjects.Add( transform.gameObject ); 999 | 1000 | var childCount = transform.childCount; 1001 | for( int i = 0; i < childCount; ++i ) { 1002 | _GetGameObjectsInScenes( gameObjects, transform.GetChild( i ) ); 1003 | } 1004 | } 1005 | } 1006 | 1007 | public void Add( GameObject gameObject ) 1008 | { 1009 | if( gameObject != null ) { 1010 | HashSet gameObjects; 1011 | if( !_gameObjectsInScenes.TryGetValue( gameObject.scene, out gameObjects ) ) { 1012 | gameObjects = new HashSet(); 1013 | _gameObjectsInScenes.Add( gameObject.scene, gameObjects ); 1014 | } 1015 | 1016 | _GetGameObjectsInScenes( gameObjects, gameObject.transform ); 1017 | } 1018 | } 1019 | 1020 | public void Remove( GameObject gameObject ) 1021 | { 1022 | if( gameObject != null ) { 1023 | foreach( var gameObjectInScene in _gameObjectsInScenes ) { 1024 | if( gameObjectInScene.Value.Remove( gameObject ) ) { 1025 | gameObjectInScene.Value.RemoveWhere( g => g.transform.IsChildOf( gameObject.transform ) ); 1026 | return; 1027 | } 1028 | } 1029 | } 1030 | } 1031 | 1032 | public bool Contains( GameObject gameObject ) 1033 | { 1034 | Scene tempScene; 1035 | return _TryGetSceneFromGameObject( gameObject, out tempScene ); 1036 | } 1037 | 1038 | static bool _TryGetSceneFromGameObject( GameObject gameObject, out Scene scene ) 1039 | { 1040 | if( gameObject != null ) { 1041 | foreach( var gameObjectInScenes in _gameObjectsInScenes ) { 1042 | if( gameObjectInScenes.Value.Contains( gameObject ) ) { 1043 | scene = gameObjectInScenes.Key; 1044 | return true; 1045 | } 1046 | } 1047 | } 1048 | 1049 | scene = new Scene(); 1050 | return false; 1051 | } 1052 | 1053 | public void CheckDeleted() 1054 | { 1055 | foreach( var gameObjectInScene in _gameObjectsInScenes ) { 1056 | if( gameObjectInScene.Value.RemoveWhere( g => g == null ) > 0 ) { 1057 | _GameObjectDeleted( gameObjectInScene.Key ); 1058 | } 1059 | } 1060 | } 1061 | 1062 | public bool CheckSceneChanged() 1063 | { 1064 | bool changedAnything = false; 1065 | 1066 | for(;;) { 1067 | bool removedAnything = false; 1068 | foreach( var gameObjectInScene in _gameObjectsInScenes ) { 1069 | if( !gameObjectInScene.Key.isLoaded ) { 1070 | _gameObjectsInScenes.Remove( gameObjectInScene.Key ); 1071 | changedAnything = true; 1072 | removedAnything = true; 1073 | break; 1074 | } 1075 | } 1076 | if( !removedAnything ) { 1077 | break; 1078 | } 1079 | } 1080 | 1081 | var sceneCount = SceneManager.sceneCount; 1082 | for( int i = 0; i < sceneCount; ++i ) { 1083 | var scene = SceneManager.GetSceneAt( i ); 1084 | if( scene.isLoaded && !_gameObjectsInScenes.ContainsKey( scene ) ) { 1085 | _gameObjectsInScenes.Add( scene, _GetGameObjectsInScene( scene ) ); 1086 | changedAnything = true; 1087 | } 1088 | } 1089 | 1090 | return changedAnything; 1091 | } 1092 | } 1093 | 1094 | class _GameObjectSiblings 1095 | { 1096 | List gameObjects = new List(); 1097 | HashSet names = new HashSet(); 1098 | 1099 | public Scene scene; 1100 | public bool markNewInstanceAvailable = false; 1101 | 1102 | public _GameObjectSiblings( Scene scene ) 1103 | { 1104 | this.scene = scene; 1105 | } 1106 | 1107 | public _GameObjectSiblings( Scene scene, GameObject[] gameObjects ) 1108 | { 1109 | this.scene = scene; 1110 | Add( gameObjects ); 1111 | } 1112 | 1113 | public void Add( GameObject gameObject ) 1114 | { 1115 | if( gameObject != null ) { 1116 | this.gameObjects.Add( gameObject ); 1117 | this.names.Add( gameObject.name ); 1118 | } 1119 | } 1120 | 1121 | public void Add( GameObject[] gameObjects ) 1122 | { 1123 | if( gameObjects != null ) { 1124 | foreach( var gameObject in gameObjects ) { 1125 | Add( gameObject ); 1126 | } 1127 | } 1128 | } 1129 | 1130 | public bool Contains( GameObject gameObject ) 1131 | { 1132 | if( gameObject != null ) { 1133 | return this.gameObjects.Contains( gameObject ); 1134 | } 1135 | 1136 | return false; 1137 | } 1138 | 1139 | public bool Contains( string name ) 1140 | { 1141 | if( name != null ) { 1142 | return this.names.Contains( name ); 1143 | } 1144 | 1145 | return false; 1146 | } 1147 | 1148 | public void Remove( GameObject gameObject ) 1149 | { 1150 | if( gameObject != null ) { 1151 | this.gameObjects.Remove( gameObject ); 1152 | this.names.Remove( gameObject.name ); 1153 | } 1154 | } 1155 | } 1156 | 1157 | class _GameObjectHierarchy 1158 | { 1159 | Dictionary _rootSiblings = new Dictionary(); 1160 | Dictionary _siblings = new Dictionary(); 1161 | 1162 | public _GameObjectHierarchy( GameObject[] selectionGameObjects ) 1163 | { 1164 | // Note: Collect minimum siblings. 1165 | 1166 | _CollectSiblingsInScenes(); // for "GameObject/Create Empty" 1167 | 1168 | if( selectionGameObjects != null ) { 1169 | foreach( var gameObject in selectionGameObjects ) { 1170 | if( gameObject != null ) { 1171 | _CollectSiblings( gameObject.transform ); // for "GameObject/Create Empty Child" 1172 | _CollectSiblings( gameObject.transform.parent ); // for "Edit/Duplicate" 1173 | } 1174 | } 1175 | } 1176 | } 1177 | 1178 | public _GameObjectSiblings GetSiblings( GameObject gameObject ) 1179 | { 1180 | _GameObjectSiblings siblings; 1181 | if( gameObject != null ) { 1182 | var parent = gameObject.transform.parent; 1183 | if( parent == null ) { 1184 | if( _rootSiblings.TryGetValue( gameObject.scene, out siblings ) ) { 1185 | return siblings; 1186 | } 1187 | } else { 1188 | if( _siblings.TryGetValue( parent, out siblings ) ) { 1189 | return siblings; 1190 | } 1191 | } 1192 | } 1193 | 1194 | return null; 1195 | } 1196 | 1197 | public _GameObjectSiblings Move( _GameObjectSiblings siblingsFrom, GameObject gameObject ) 1198 | { 1199 | if( siblingsFrom != null && gameObject != null ) { 1200 | siblingsFrom.Remove( gameObject ); 1201 | 1202 | var siblingsTo = GetSiblings( gameObject ); 1203 | if( siblingsTo != null ) { 1204 | siblingsTo.Add( gameObject ); 1205 | return siblingsTo; 1206 | } 1207 | 1208 | if( gameObject.transform.parent != null ) { 1209 | return _CollectSiblings( gameObject.transform.parent ); 1210 | } else { 1211 | return _CollectSiblingsInScene( gameObject.scene ); 1212 | } 1213 | } 1214 | 1215 | return null; 1216 | } 1217 | 1218 | void _CollectSiblingsInScenes() 1219 | { 1220 | var sceneCount = SceneManager.sceneCount; 1221 | for( int i = 0; i < sceneCount; ++i ) { 1222 | _CollectSiblingsInScene( SceneManager.GetSceneAt( i ) ); 1223 | } 1224 | } 1225 | 1226 | _GameObjectSiblings _CollectSiblingsInScene( Scene scene ) 1227 | { 1228 | if( scene.isLoaded ) { 1229 | var siblings = new _GameObjectSiblings( scene, scene.GetRootGameObjects() ); 1230 | _rootSiblings.Add( scene, siblings ); 1231 | return siblings; 1232 | } else { 1233 | return null; 1234 | } 1235 | } 1236 | 1237 | _GameObjectSiblings _CollectSiblings( Transform parent ) 1238 | { 1239 | if( parent == null ) { 1240 | return null; 1241 | } 1242 | 1243 | _GameObjectSiblings siblings; 1244 | if( _siblings.TryGetValue( parent, out siblings ) ) { 1245 | return siblings; 1246 | } 1247 | 1248 | siblings = new _GameObjectSiblings( parent.gameObject.scene ); 1249 | for( int i = 0; i < parent.childCount; ++i ) { 1250 | siblings.Add( parent.GetChild( i ).gameObject ); 1251 | } 1252 | _siblings.Add( parent, siblings ); 1253 | return siblings; 1254 | } 1255 | 1256 | public void CheckSceneChanged() 1257 | { 1258 | for(;;) { 1259 | bool removedAnything = false; 1260 | foreach( var rootSiblings in _rootSiblings ) { 1261 | if( !rootSiblings.Key.isLoaded ) { 1262 | _rootSiblings.Remove( rootSiblings.Key ); 1263 | removedAnything = true; 1264 | break; 1265 | } 1266 | } 1267 | if( !removedAnything ) { 1268 | break; 1269 | } 1270 | } 1271 | 1272 | for(;;) { 1273 | bool removedAnything = false; 1274 | foreach( var siblings in _siblings ) { 1275 | if( !siblings.Value.scene.isLoaded ) { 1276 | _siblings.Remove( siblings.Key ); 1277 | removedAnything = true; 1278 | break; 1279 | } 1280 | } 1281 | if( !removedAnything ) { 1282 | break; 1283 | } 1284 | } 1285 | 1286 | var sceneCount = SceneManager.sceneCount; 1287 | for( int i = 0; i < sceneCount; ++i ) { 1288 | var scene = SceneManager.GetSceneAt( i ); 1289 | if( scene.isLoaded && !_rootSiblings.ContainsKey( scene ) ) { 1290 | _CollectSiblingsInScene( scene ); 1291 | } 1292 | } 1293 | } 1294 | } 1295 | } 1296 | 1297 | } 1298 | -------------------------------------------------------------------------------- /Assets/EditorExtensions/Editor/SettingXmlSerializer.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nora 2 | // Released under the MIT license 3 | // http://opensource.org/licenses/mit-license.php 4 | 5 | using UnityEngine; 6 | using System.Xml; 7 | using System.Reflection; 8 | using System.Collections.Generic; 9 | 10 | namespace EditorExtensions 11 | { 12 | 13 | public static class SettingXmlSerializer 14 | { 15 | public class ExtraData 16 | { 17 | public string rootDirectory; // root directory for the serialization target. 18 | public Transform rootTransform; // root transform for the serialization target. 19 | public List objects; // If this value is null, all meta:extraDataObjectIndex are removed in xml. 20 | } 21 | 22 | static class AssetPathHelper 23 | { 24 | static bool _IsSeparater( char c ) 25 | { 26 | return c == '/' || c == '\\'; 27 | } 28 | 29 | static int _GetDirectoryDepth( string directory ) 30 | { 31 | if( string.IsNullOrEmpty( directory ) ) { 32 | return 0; 33 | } 34 | 35 | bool elem = false; 36 | int depth = 0; 37 | for( int i = 0; i < directory.Length; ++i ) { 38 | if( !_IsSeparater( directory[i] ) ) { 39 | if( !elem ) { 40 | elem = true; 41 | ++depth; 42 | } 43 | } else { 44 | elem = false; 45 | } 46 | } 47 | 48 | return depth; 49 | } 50 | 51 | static string _RemoveSeparatorOnHead( string path ) 52 | { 53 | if( path != null && path.Length > 0 && _IsSeparater( path[0] ) ) { 54 | return path.Substring( 1 ); 55 | } else { 56 | return path; 57 | } 58 | } 59 | 60 | public static string GenerateRelativeAssetPath( string baseDirectory, string targetPath ) 61 | { 62 | if( string.IsNullOrEmpty( baseDirectory ) || string.IsNullOrEmpty( targetPath ) ) { 63 | return targetPath; 64 | } 65 | 66 | int minLength = (baseDirectory.Length < targetPath.Length) ? baseDirectory.Length : targetPath.Length; 67 | int matchLength = 0; 68 | for( int i = 0; i < minLength; ++i ) { 69 | if( _IsSeparater( baseDirectory[i] ) && _IsSeparater( targetPath[i] ) ) { 70 | matchLength = i + 1; 71 | } else { 72 | if( baseDirectory[i] != targetPath[i] ) { 73 | break; 74 | } 75 | if( i + 1 == minLength ) { 76 | if( targetPath.Length > minLength && _IsSeparater( targetPath[minLength] ) ) { 77 | matchLength = i + 1; 78 | } 79 | } 80 | } 81 | } 82 | 83 | if( matchLength > 0 ) { 84 | baseDirectory = baseDirectory.Substring( matchLength ); 85 | targetPath = targetPath.Substring( matchLength ); 86 | } 87 | 88 | targetPath = _RemoveSeparatorOnHead( targetPath ); 89 | 90 | int baseDepth = _GetDirectoryDepth( baseDirectory ); 91 | if( baseDepth > 0 ) { 92 | var str = new System.Text.StringBuilder(); 93 | for( int i = 0; i < baseDepth; ++i ) { 94 | str.Append("../"); 95 | } 96 | return str.ToString() + targetPath; 97 | } else { 98 | return targetPath; 99 | } 100 | } 101 | 102 | public static string GenerateAssetPath( string rootDirectory, string relativeAssetPath ) 103 | { 104 | if( string.IsNullOrEmpty( rootDirectory ) || string.IsNullOrEmpty( relativeAssetPath ) ) { 105 | return relativeAssetPath; 106 | } 107 | 108 | while( relativeAssetPath.StartsWith("../") || relativeAssetPath.StartsWith("..\\") ) { 109 | relativeAssetPath = relativeAssetPath.Substring( 3 ); 110 | rootDirectory = System.IO.Path.GetDirectoryName( rootDirectory ); 111 | if( rootDirectory == null ) { 112 | return ""; 113 | } 114 | } 115 | 116 | return rootDirectory + '/' + relativeAssetPath; 117 | } 118 | } 119 | 120 | public class ObjectMeta 121 | { 122 | public string name; 123 | public string typeName; 124 | public int extraDataObjectIndex; // for extraData.objects only. 125 | public string assetPath; 126 | public string guid; 127 | public string relativeAssetPath; 128 | public string transformPath; // for Transform / GameObject 129 | 130 | public static ObjectMeta GetAt( UnityEngine.Object obj, ExtraData extraData ) 131 | { 132 | ObjectMeta r = new ObjectMeta(); 133 | if( obj != null ) { 134 | r.name = obj.name; 135 | r.typeName = obj.GetType().FullName; 136 | r.extraDataObjectIndex = -1; 137 | r.assetPath = UnityEditor.AssetDatabase.GetAssetPath( obj ); 138 | if( !string.IsNullOrEmpty(r.assetPath) ) { 139 | r.guid = UnityEditor.AssetDatabase.AssetPathToGUID( r.assetPath ); 140 | 141 | if( !string.IsNullOrEmpty( extraData.rootDirectory ) ) { 142 | r.relativeAssetPath = AssetPathHelper.GenerateRelativeAssetPath( extraData.rootDirectory, r.assetPath ); 143 | } 144 | } 145 | 146 | r.transformPath = ""; 147 | if( extraData != null && extraData.rootTransform != null ) { 148 | Transform transform = null; 149 | if( obj is GameObject ) { 150 | transform = ((GameObject)obj).transform; 151 | } else if( obj is Transform ) { 152 | transform = (Transform)obj; 153 | } 154 | 155 | if( transform != null && transform.IsChildOf( extraData.rootTransform ) ) { 156 | r.transformPath = _GetTransformPath( extraData.rootTransform, transform ); 157 | } 158 | } 159 | } 160 | 161 | return r; 162 | } 163 | 164 | static string _GetTransformPath( Transform parent, Transform child ) 165 | { 166 | if( parent != null && child != null && parent != child ) { 167 | bool isAdded = false; 168 | System.Text.StringBuilder transformPath = new System.Text.StringBuilder(); 169 | while( child != null ) { 170 | if( isAdded ) { 171 | transformPath.Insert( 0, "/" ); 172 | } else { 173 | isAdded = true; 174 | } 175 | transformPath.Insert( 0, child.name ); 176 | child = child.parent; 177 | if( child == parent ) { 178 | return transformPath.ToString(); 179 | } 180 | } 181 | } 182 | 183 | return ""; 184 | } 185 | 186 | public UnityEngine.Object Find( ExtraData extraData ) 187 | { 188 | if( this.extraDataObjectIndex >= 0 && extraData != null && extraData.objects != null ) { 189 | if( this.extraDataObjectIndex < extraData.objects.Count ) { 190 | var obj = extraData.objects[this.extraDataObjectIndex]; 191 | if( obj != null ) { 192 | return obj; 193 | } 194 | } 195 | } 196 | 197 | if( !string.IsNullOrEmpty( transformPath ) && extraData.rootTransform != null ) { 198 | var transform = extraData.rootTransform.FindChild( transformPath ); 199 | if( transform != null ) { 200 | if( this.typeName == "UnityEngine.Transform" ) { 201 | return transform; 202 | } else if( this.typeName == "UnityEngine.GameObject" ) { 203 | return transform.gameObject; 204 | } 205 | } 206 | } 207 | 208 | string modifiedAssetPath = this.assetPath; 209 | if( !string.IsNullOrEmpty( this.relativeAssetPath ) && !string.IsNullOrEmpty( extraData.rootDirectory ) ) { 210 | modifiedAssetPath = AssetPathHelper.GenerateAssetPath( extraData.rootDirectory, this.relativeAssetPath ); 211 | } 212 | 213 | // GUID & assetPath(relativeAssetPath) 214 | if( !string.IsNullOrEmpty( this.guid ) ) { 215 | string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath( this.guid ); 216 | if( !string.IsNullOrEmpty( assetPath ) ) { 217 | if( assetPath == modifiedAssetPath || assetPath == this.assetPath ) { 218 | var obj = UnityEditor.AssetDatabase.LoadMainAssetAtPath( assetPath ); 219 | if( obj != null && obj.GetType().FullName == this.typeName ) { 220 | return obj; 221 | } 222 | } 223 | } 224 | } 225 | 226 | // Relative assetPath 227 | if( !string.IsNullOrEmpty( modifiedAssetPath ) ) { 228 | var obj = UnityEditor.AssetDatabase.LoadMainAssetAtPath( modifiedAssetPath ); 229 | if( obj != null && obj.GetType().FullName == this.typeName ) { 230 | return obj; 231 | } 232 | } 233 | 234 | // Abusolute assetPath 235 | if( !string.IsNullOrEmpty( this.assetPath ) ) { 236 | var obj = UnityEditor.AssetDatabase.LoadMainAssetAtPath( this.assetPath ); 237 | if( obj != null && obj.GetType().FullName == this.typeName ) { 238 | return obj; 239 | } 240 | } 241 | 242 | // Finally, find by guid only. 243 | if( !string.IsNullOrEmpty( this.guid ) ) { 244 | string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath( this.guid ); 245 | if( !string.IsNullOrEmpty( assetPath ) ) { 246 | var obj = UnityEditor.AssetDatabase.LoadMainAssetAtPath( assetPath ); 247 | if( obj != null && obj.GetType().FullName == this.typeName ) { 248 | return obj; 249 | } 250 | } 251 | } 252 | 253 | return null; 254 | } 255 | 256 | public void WriteToXml( XmlWriter writer, ExtraData extraData = null ) 257 | { 258 | writer.WriteAttributeString( "name", null, this.name ); 259 | writer.WriteAttributeString( "typeName", null, this.typeName ); 260 | if( extraData != null && this.extraDataObjectIndex >= 0 ) { 261 | writer.WriteAttributeString( "extraDataObjectIndex", null, this.extraDataObjectIndex.ToString() ); 262 | } 263 | if( !string.IsNullOrEmpty(this.assetPath) ) { 264 | writer.WriteAttributeString( "assetPath", null, this.assetPath ); 265 | } 266 | if( !string.IsNullOrEmpty(this.relativeAssetPath) ) { 267 | writer.WriteAttributeString( "relativeAssetPath", null, this.relativeAssetPath ); 268 | } 269 | if( !string.IsNullOrEmpty(this.guid) ) { 270 | writer.WriteAttributeString( "guid", null, this.guid ); 271 | } 272 | if( !string.IsNullOrEmpty(this.transformPath) ) { 273 | writer.WriteAttributeString( "transformPath", null, this.transformPath ); 274 | } 275 | } 276 | 277 | public void ReadFromXml( XmlReader reader ) 278 | { 279 | this.name = reader.GetAttribute("name"); 280 | this.typeName = reader.GetAttribute("typeName"); 281 | if( !int.TryParse( reader.GetAttribute("extraDataObjectIndex"), out this.extraDataObjectIndex ) ) { 282 | this.extraDataObjectIndex = -1; 283 | } 284 | this.guid = reader.GetAttribute("guid"); 285 | this.transformPath = reader.GetAttribute("transformPath"); 286 | this.assetPath = reader.GetAttribute("assetPath"); 287 | this.relativeAssetPath = reader.GetAttribute("relativeAssetPath"); 288 | } 289 | } 290 | 291 | public static void WriteXml( XmlWriter writer, object obj, ExtraData extraData = null ) 292 | { 293 | writer.WriteStartDocument(); 294 | if( obj != null ) { 295 | writer.WriteStartElement( _GetXmlLocalName(obj.GetType()) ); 296 | writer.WriteAttributeString( "xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance" ); 297 | writer.WriteAttributeString( "xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema" ); 298 | _WriteXmlFields( writer, obj, extraData ); 299 | writer.WriteEndElement(); 300 | } 301 | writer.WriteEndDocument(); 302 | } 303 | 304 | static void _WriteXmlFields( XmlWriter writer, object obj, ExtraData extraData ) 305 | { 306 | if( obj == null ) { 307 | return; 308 | } 309 | 310 | foreach( var field in obj.GetType().GetFields( BindingFlags.Public | BindingFlags.Instance ) ) { 311 | var value = field.GetValue( obj ); 312 | writer.WriteStartElement(field.Name); 313 | if( value != null ) { 314 | if( field.FieldType.IsArray ) { 315 | var array = (System.Array)value; 316 | var valueType = field.FieldType.GetElementType(); 317 | var xmlElementType = _GetXmlLocalName( valueType ); 318 | for( int i = 0; i < array.Length; ++i ) { 319 | writer.WriteStartElement( xmlElementType ); 320 | _WriteXmlValue( writer, array.GetValue( i ), extraData ); 321 | writer.WriteEndElement(); 322 | } 323 | } else { 324 | _WriteXmlValue( writer, value, extraData ); 325 | } 326 | } 327 | writer.WriteEndElement(); 328 | } 329 | } 330 | 331 | static void _WriteXmlValue( XmlWriter writer, object value, ExtraData extraData ) 332 | { 333 | if( value == null ) { 334 | return; 335 | } 336 | 337 | if( typeof(UnityEngine.Object).IsAssignableFrom( value.GetType() ) ) { 338 | _WriteXmlValue_UnityEngine_Object( writer, value, extraData ); 339 | } else { 340 | if( value.GetType().IsPrimitive ) { 341 | writer.WriteString( _ToString( value ) ); 342 | } else if( value.GetType() == typeof(string) ) { 343 | writer.WriteString( (string)value ); 344 | } else { 345 | _WriteXmlFields( writer, value, extraData ); 346 | } 347 | } 348 | } 349 | 350 | static void _WriteXmlValue_UnityEngine_Object( XmlWriter writer, object value, ExtraData extraData ) 351 | { 352 | var meta = ObjectMeta.GetAt( (UnityEngine.Object)value, extraData ); 353 | if( extraData != null && extraData.objects != null && value != null ) { 354 | meta.extraDataObjectIndex = extraData.objects.IndexOf( (UnityEngine.Object)value ); 355 | if( meta.extraDataObjectIndex < 0 ) { 356 | meta.extraDataObjectIndex = extraData.objects.Count; 357 | extraData.objects.Add( (UnityEngine.Object)value ); 358 | } 359 | } 360 | 361 | meta.WriteToXml( writer, extraData ); 362 | } 363 | 364 | static string _ToString( object value ) 365 | { 366 | return value.ToString(); 367 | } 368 | 369 | static string _GetXmlLocalName( System.Type type ) 370 | { 371 | if( type.IsPrimitive ) { 372 | if( type == typeof(bool) ) { 373 | return "boolean"; 374 | } else if( type == typeof(System.SByte) ) { 375 | return "byte"; 376 | } else if( type == typeof(System.Int16) ) { 377 | return "short"; 378 | } else if( type == typeof(System.Int32) ) { 379 | return "int"; 380 | } else if( type == typeof(System.Int64) ) { 381 | return "long"; 382 | } else if( type == typeof(System.Byte) ) { 383 | return "unsignedByte"; 384 | } else if( type == typeof(System.UInt16) ) { 385 | return "unsignedShort"; 386 | } else if( type == typeof(System.UInt32) ) { 387 | return "unsignedInt"; 388 | } else if( type == typeof(System.UInt64) ) { 389 | return "unsignedLong"; 390 | } else if( type == typeof(float) ) { 391 | return "float"; 392 | } else if( type == typeof(double) ) { 393 | return "double"; 394 | } else { 395 | return "string"; 396 | } 397 | } else if( type == typeof(string) ){ 398 | return "string"; 399 | } else { 400 | return type.Name; 401 | } 402 | } 403 | 404 | public static System.Object ReadXml( XmlReader reader, System.Type type, ExtraData extraData = null ) 405 | { 406 | while( reader.Read() ) { 407 | //Debug.Log( "NodeType: " + reader.NodeType + " Name:" + reader.Name + " Value:" + reader.Value + " ValueType:" + reader.ValueType ); 408 | switch( reader.NodeType ) { 409 | case XmlNodeType.Element: 410 | return _ReadXmlElement( reader, type, extraData ); 411 | } 412 | } 413 | 414 | Debug.LogWarning( "Element is not terminated." ); 415 | return null; 416 | } 417 | 418 | static void _SkipXmlElement( XmlReader reader ) 419 | { 420 | while( reader.Read() ) { 421 | //Debug.Log( "NodeType: " + reader.NodeType + " Name:" + reader.Name + " Value:" + reader.Value + " ValueType:" + reader.ValueType ); 422 | switch( reader.NodeType ) { 423 | case XmlNodeType.Element: 424 | _SkipXmlElement( reader ); 425 | break; 426 | case XmlNodeType.EndElement: 427 | return; 428 | } 429 | } 430 | 431 | Debug.LogWarning( "Element is not terminated." ); 432 | } 433 | 434 | static object _ReadXmlElement( XmlReader reader, System.Type elementType, ExtraData extraData ) 435 | { 436 | System.Object obj = null; 437 | 438 | System.Collections.ArrayList arrayList = null; 439 | System.Type arrayElementType = null; 440 | if( elementType.IsArray ) { 441 | arrayList = new System.Collections.ArrayList(); 442 | arrayElementType = elementType.GetElementType(); 443 | } 444 | 445 | while( reader.Read() ) { 446 | //Debug.Log( "NodeType: " + reader.NodeType + " Name:" + reader.Name + " Value:" + reader.Value + " ValueType:" + reader.ValueType ); 447 | switch( reader.NodeType ) { 448 | case XmlNodeType.Element: 449 | if( elementType.IsArray ) { 450 | arrayList.Add( _ReadXmlElement( reader, arrayElementType, extraData ) ); 451 | } else { 452 | var field = elementType.GetField( reader.Name, BindingFlags.Public | BindingFlags.Instance ); 453 | if( field != null ) { 454 | if( obj == null ) { 455 | try { 456 | obj = System.Activator.CreateInstance( elementType ); 457 | } catch( System.Exception ) { 458 | Debug.LogWarning( "Instanciate failed: " + elementType.ToString() ); 459 | } 460 | } 461 | 462 | if( obj != null ) { // Failsafe. 463 | if( typeof(UnityEngine.Object).IsAssignableFrom( field.FieldType ) ) { 464 | var element = _ReadXmlElement_UnityEngine_Object( reader, field.FieldType, extraData ); 465 | if( element != null ) { 466 | try { 467 | field.SetValue( obj, element ); 468 | } catch( System.Exception e ) { // Type mismatch. 469 | Debug.LogWarning( e.ToString() ); 470 | } 471 | } 472 | } else { 473 | var element = _ReadXmlElement( reader, field.FieldType, extraData ); 474 | if( element != null ) { 475 | try { 476 | field.SetValue( obj, element ); 477 | } catch( System.Exception e ) { // Type mismatch. 478 | Debug.LogWarning( e.ToString() ); 479 | } 480 | } 481 | } 482 | } else { 483 | _SkipXmlElement( reader ); 484 | } 485 | } else { 486 | _SkipXmlElement( reader ); 487 | } 488 | } 489 | break; 490 | case XmlNodeType.Text: 491 | if( !elementType.IsArray ) { // Failsafe. 492 | if( typeof(UnityEngine.Object).IsAssignableFrom( elementType ) ) { 493 | Debug.LogWarning( "Unsupported type: " + elementType.ToString() ); 494 | } else { 495 | if( elementType == typeof(string) ) { 496 | obj = reader.Value; 497 | } else { 498 | var parse = elementType.GetMethod("Parse", 499 | BindingFlags.Public | BindingFlags.Static, null, new [] { typeof(string) }, null); 500 | if( parse != null ) { 501 | try { 502 | obj = parse.Invoke(null, new object[] { reader.Value }); 503 | } catch( System.Exception ) { 504 | Debug.LogWarning( "Parse failed: " + elementType.ToString() + " Value: " + reader.Value ); 505 | } 506 | } else { 507 | Debug.LogWarning( "Unsupported type: " + elementType.ToString() ); 508 | } 509 | } 510 | } 511 | } else { 512 | Debug.LogWarning( "Unknown flow." ); 513 | } 514 | break; 515 | case XmlNodeType.EndElement: 516 | if( elementType.IsArray ) { 517 | obj = System.Array.CreateInstance( arrayElementType, arrayList.Count ); 518 | for( int i = 0; i < arrayList.Count; ++i ) { 519 | ((System.Array)obj).SetValue( arrayList[i], i ); 520 | } 521 | } 522 | 523 | return obj; 524 | } 525 | } 526 | 527 | Debug.LogWarning( "Element is not terminated." ); 528 | return null; 529 | } 530 | 531 | static UnityEngine.Object _ReadXmlElement_UnityEngine_Object( XmlReader reader, System.Type elementType, ExtraData extraData = null ) 532 | { 533 | var meta = new ObjectMeta(); 534 | meta.ReadFromXml( reader ); 535 | return meta.Find( extraData ); 536 | } 537 | 538 | public static string SerializeXmlToString( object settings, ExtraData extraData ) 539 | { 540 | try { 541 | using( var stringWriter = new System.IO.StringWriter() ) { 542 | var xmlWriterSettings = new XmlWriterSettings { 543 | Indent = true, 544 | }; 545 | using( XmlWriter xmlWriter = XmlWriter.Create( stringWriter, xmlWriterSettings ) ) { 546 | SettingXmlSerializer.WriteXml( xmlWriter, settings, extraData ); 547 | } 548 | return stringWriter.ToString(); 549 | } 550 | } catch( System.Exception e ) { 551 | Debug.LogError( e.ToString() ); 552 | return null; 553 | } 554 | } 555 | 556 | public static byte[] SerializeXmlToBytes( object settings, ExtraData extraData ) 557 | { 558 | try { 559 | using( var memoryStream = new System.IO.MemoryStream() ) { 560 | var xmlWriterSettings = new XmlWriterSettings { 561 | Indent = true, 562 | Encoding = new System.Text.UTF8Encoding(true), 563 | }; 564 | using( XmlWriter xmlWriter = XmlWriter.Create( memoryStream, xmlWriterSettings ) ) { 565 | WriteXml( xmlWriter, settings, extraData ); 566 | } 567 | return memoryStream.ToArray(); 568 | } 569 | } catch( System.Exception e ) { 570 | Debug.LogError( e.ToString() ); 571 | return null; 572 | } 573 | } 574 | 575 | public static Type DeserializeXml< Type >( string serializedString, ExtraData extraData ) 576 | { 577 | try { 578 | using( var stringReader = new System.IO.StringReader( serializedString ) ) { 579 | using( XmlReader reader = XmlReader.Create( stringReader ) ) { 580 | var r = (Type)ReadXml(reader, typeof(Type), extraData); 581 | return r; 582 | } 583 | } 584 | } catch( System.Exception e ) { 585 | Debug.LogError( e.ToString() ); 586 | return default(Type); 587 | } 588 | } 589 | 590 | public static Type DeserializeXml< Type >( byte[] bytes, ExtraData extraData ) 591 | { 592 | try { 593 | using( var memoryStream = new System.IO.MemoryStream( bytes ) ) { 594 | using( XmlReader reader = XmlReader.Create( memoryStream ) ) { 595 | var r = (Type)ReadXml(reader, typeof(Type), extraData); 596 | return r; 597 | } 598 | } 599 | } catch( System.Exception e ) { 600 | Debug.LogError( e.ToString() ); 601 | return default(Type); 602 | } 603 | } 604 | } 605 | 606 | } 607 | -------------------------------------------------------------------------------- /Assets/EditorExtensions/Samples/Editor/ObjectPostprocessorTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nora 2 | // Released under the MIT license 3 | // http://opensource.org/licenses/mit-license.php 4 | 5 | using UnityEngine; 6 | using UnityEditor; 7 | using UnityEngine.SceneManagement; 8 | 9 | namespace EditorExtensions 10 | { 11 | 12 | [InitializeOnLoad] 13 | public class ObjectPostprocessorTest 14 | { 15 | static ObjectPostprocessorTest() 16 | { 17 | ObjectPostprocessor.assetAdded += AssetAdded; 18 | ObjectPostprocessor.assetMoved += AssetMoved; 19 | ObjectPostprocessor.assetDeleted += AssetDeleted; 20 | ObjectPostprocessor.assetDuplicated += AssetDuplicated; 21 | ObjectPostprocessor.prefabConnected += PrefabConnected; 22 | 23 | ObjectPostprocessor.gameObjectAdded += GameObjectAdded; 24 | ObjectPostprocessor.gameObjectSceneMoved += GameObjectSceneMoved; 25 | ObjectPostprocessor.gameObjectDeleted += GameObjectDeleted; 26 | ObjectPostprocessor.gameObjectDuplicated += GameObjectDuplicated; 27 | ObjectPostprocessor.prefabInstantiated += PrefabInstantiated; 28 | } 29 | 30 | public static void AssetAdded( string assetPath ) 31 | { 32 | Debug.Log( "AssetAdded: " + assetPath ); 33 | } 34 | 35 | public static void AssetMoved( string assetPathFrom, string assetPathTo ) 36 | { 37 | Debug.Log( "AssetMoved: from: " + assetPathFrom + " to: " + assetPathTo ); 38 | } 39 | 40 | public static void AssetDeleted( string assetPath ) 41 | { 42 | Debug.Log( "AssetDeleted: " + assetPath ); 43 | } 44 | 45 | public static void AssetDuplicated( string assetPathFrom, string assetPathTo ) 46 | { 47 | Debug.Log( "AssetDuplicated: from: " + assetPathFrom + " to: " + assetPathTo ); 48 | } 49 | 50 | public static void PrefabConnected( GameObject gameObjectFrom, string assetPathTo ) 51 | { 52 | Debug.Log( "PrefabConnected: from: " + gameObjectFrom + " to: " + assetPathTo ); 53 | } 54 | 55 | public static void GameObjectAdded( GameObject gameObject ) 56 | { 57 | Debug.Log( "GameObjectAdded: " + gameObject ); 58 | } 59 | 60 | public static void GameObjectDeleted( Scene scene ) 61 | { 62 | Debug.Log( "GameObjectDeleted: scene: " + scene.name ); 63 | } 64 | 65 | public static void GameObjectSceneMoved( Scene sceneFrom, GameObject gameObject ) 66 | { 67 | Debug.Log( "GameObjectSceneMoved: sceneFrom: " + sceneFrom + " to: " + gameObject ); 68 | } 69 | 70 | public static void GameObjectDuplicated( GameObject gameObjectFrom, GameObject gameObjectTo ) 71 | { 72 | Debug.Log( "GameObjectDuplicated: from: " + gameObjectFrom + " to: " + gameObjectTo ); 73 | } 74 | 75 | public static void PrefabInstantiated( string assetPathFrom, GameObject gameObjectTo ) 76 | { 77 | Debug.Log( "PrefabInstantiated: from: " + assetPathFrom + " to: " + gameObjectTo ); 78 | } 79 | 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /Assets/EditorExtensions/Samples/Editor/SettingXmlSerializerTest.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Nora 2 | // Released under the MIT license 3 | // http://opensource.org/licenses/mit-license.php 4 | 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using UnityEngine; 8 | using UnityEditor; 9 | 10 | namespace EditorExtensions 11 | { 12 | 13 | [InitializeOnLoad] 14 | public class SettingXmlSerializerTest 15 | { 16 | struct XmlTestClassA 17 | { 18 | public int a; 19 | public float b; 20 | } 21 | 22 | struct XmlTestClassB 23 | { 24 | public Vector3 v; 25 | public Quaternion q; 26 | public Matrix4x4 mat; 27 | } 28 | 29 | struct XmlTestRoot 30 | { 31 | public XmlTestClassA classA; 32 | public XmlTestClassB[] classB; 33 | public UnityEngine.Object objectC; 34 | } 35 | 36 | static SettingXmlSerializerTest() 37 | { 38 | var root = new XmlTestRoot(); 39 | 40 | root.classA = new XmlTestClassA() { 41 | a = 1234, 42 | b = 5678.0f, 43 | }; 44 | 45 | root.classB = new XmlTestClassB[] { 46 | new XmlTestClassB() { 47 | v = new Vector3(0.1f, 0.2f, 0.3f), 48 | q = Quaternion.Euler( 10.0f, 20.0f, 30.0f ), 49 | mat = Matrix4x4.identity, 50 | } 51 | }; 52 | 53 | root.objectC = AssetDatabase.LoadMainAssetAtPath( "Assets/EditorExtensions/Samples/Editor/SettingXmlSerializerTest.cs" ); 54 | 55 | SettingXmlSerializer.ExtraData extraData = new SettingXmlSerializer.ExtraData(); 56 | extraData.rootDirectory = "Assets/EditorExtensions/Samples/Editor"; 57 | var bytes = SettingXmlSerializer.SerializeXmlToBytes( root, extraData ); 58 | System.IO.File.WriteAllBytes( "Assets/EditorExtensions/Samples/Editor/SettingXmlSerializerTest.xml", bytes ); 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /Assets/EditorExtensions/Samples/Editor/SettingXmlSerializerTest.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 1234 5 | 5678 6 | 7 | 8 | 9 | 10 | 0.1 11 | 0.2 12 | 0.3 13 | 14 | 15 | 0.1276794 16 | 0.1448781 17 | 0.2392983 18 | 0.9515485 19 | 20 | 21 | 1 22 | 0 23 | 0 24 | 0 25 | 0 26 | 1 27 | 0 28 | 0 29 | 0 30 | 0 31 | 1 32 | 0 33 | 0 34 | 0 35 | 0 36 | 1 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Editor Extensions for Unity Editor 2 | 3 | ## What's this 4 | 5 | This is toolset for Unity Editor. 6 | 7 | This repository contains below classes.. 8 | 9 | --- 10 | 11 | ### ObjectPostprocessor 12 | 13 | - This is called after changing of any number of assets / gameObjects. 14 | 15 | - Performable events are Added, Moved, Deleted, Duplicated, and Instantiated. 16 | 17 | --- 18 | 19 | ### SettingXmlSerializer 20 | 21 | - This is xml serializer supporting with UnityEngine.Object serialization. 22 | 23 | - UnityEngine.Object is serialized to assetPath, relativeAssetPath, GUID. 24 | 25 | - UnityEngine.Transform is serialized to relative transform path. (BTW, "root/parent/transform") 26 | 27 | --- 28 | 29 | ## License 30 | 31 | MIT License 32 | --------------------------------------------------------------------------------