@@ -530,6 +530,16 @@ public List<string> GetComponentValues(string name, string typename)
530
530
/// </summary>
531
531
Dictionary < string , List < System . Type > > m_componentsToDestroy ;
532
532
533
+ struct ComponentValue {
534
+ public System . Type t ;
535
+ public string jsonValue ;
536
+
537
+ public ComponentValue ( System . Type t , string jsonValue ) {
538
+ this . t = t ;
539
+ this . jsonValue = jsonValue ;
540
+ }
541
+ }
542
+
533
543
/// <summary>
534
544
/// List of components to update or create in steps 4b and 4c.
535
545
/// The string is the name in the prefab.
@@ -538,7 +548,7 @@ public List<string> GetComponentValues(string name, string typename)
538
548
/// If the component exists in the prefab, we update the first
539
549
/// match (without repetition).
540
550
/// </summary>
541
- Dictionary < string , List < Component > > m_componentsToUpdate ;
551
+ Dictionary < string , List < ComponentValue > > m_componentsToUpdate ;
542
552
543
553
void ClassifyDestroyCreateNodes ( )
544
554
{
@@ -617,119 +627,84 @@ void ClassifyReparenting()
617
627
}
618
628
}
619
629
620
- void ClassifyComponents ( Transform newFbx )
630
+ void ClassifyComponents ( Transform newFbx , Transform prefab )
621
631
{
622
632
Initialize ( ref m_componentsToDestroy ) ;
623
633
Initialize ( ref m_componentsToUpdate ) ;
624
634
625
- // Flatten the list of components in the transform hierarchy so we can remember what to copy.
626
- var components = new Dictionary < string , Dictionary < string , List < Component > > > ( ) ;
627
- var builder = new System . Text . StringBuilder ( ) ;
635
+ // Figure out how to map from type names to System.Type values,
636
+ // without going through reflection APIs. This allows us to handle
637
+ // components from various assemblies.
638
+ //
639
+ // We're going to be adding from the newFbx, and deleting from the prefab,
640
+ // so we don't need to know about all the types that oldFbx might have.
641
+ var componentTypes = new Dictionary < string , System . Type > ( ) ;
628
642
foreach ( var component in newFbx . GetComponentsInChildren < Component > ( ) ) {
629
- string name ;
630
- if ( component . transform == newFbx ) {
631
- name = "" ;
632
- } else {
633
- name = component . name ;
634
- }
635
- var typename = component . GetType ( ) . ToString ( ) ;
636
- builder . AppendFormat ( "\t {0}:{1}\n " , name , typename ) ;
637
- Append ( ref components , name , typename , component ) ;
643
+ var componentType = component . GetType ( ) ;
644
+ componentTypes [ componentType . ToString ( ) ] = componentType ;
645
+ }
646
+ foreach ( var component in prefab . GetComponentsInChildren < Component > ( ) ) {
647
+ var componentType = component . GetType ( ) ;
648
+ componentTypes [ componentType . ToString ( ) ] = componentType ;
638
649
}
639
650
640
- // What's the logic?
641
- // First check if a component is present or absent. It's
642
- // present if the node exists and has that component; it's
643
- // absent if the node doesn't exist, or the node exists but
644
- // doesn't have that component:
645
- // old new prefab
646
- // x x x => ignore
647
- // x x y => ignore
648
- // x y x => create a new component, copy from new
649
- // x y y => ignore
650
- // y x x => ignore
651
- // y x y => destroy the component
652
- // y y x => ignore
653
- // y y y => check if we need to update
651
+ // For each node in the prefab (after adding any new nodes):
652
+ // 1. If a component is in the old but not the new, remove it from
653
+ // the prefab.
654
+ // 2. If it's in the new but not the old, add it to the prefab.
655
+ // 3. If it's in both the old and new, but with different values,
656
+ // update the prefab values.
657
+ // If a component type is repeated (e.g. two BoxCollider on the
658
+ // same node), we line up the components in the order they
659
+ // appear. This never happens in stock Unity, someone must have
660
+ // added an AssetPostprocessor for it to occur.
661
+ // TODO: do something smarter.
654
662
//
655
- // In that last case, check whether we need to update the values:
656
- // a a a => ignore
657
- // a a b => ignore
658
- // a a c => ignore (indistinguishable from aab case)
659
- // a b a => update to b
660
- // a b b => ignore (already matches)
661
- // a b c => conflict, update to b
662
- //
663
- // Big question: how do we handle multiplicity? I'll skip it for today...
664
-
665
- // We only care about nodes in the prefab after creating/destroying.
663
+ // If the node isn't going to be in the prefab, we don't care
664
+ // about what components might be on it.
666
665
foreach ( var name in m_nodesInUpdatedPrefab )
667
666
{
668
667
if ( ! m_new . HasNode ( name ) ) {
669
668
// It's not in the FBX, so clearly we're not updating any components.
669
+ // We don't need to check if it's in m_prefab because
670
+ // we're only iterating over those.
670
671
continue ;
671
672
}
672
673
var allTypes = m_old . GetComponentTypes ( name ) . Union (
673
- m_new . GetComponentTypes ( name ) . Union (
674
- m_prefab . GetComponentTypes ( name ) ) ) ;
675
-
676
- List < string > typesToDestroy = null ;
677
- List < string > typesToUpdate = null ;
674
+ m_new . GetComponentTypes ( name ) ) ;
678
675
679
676
foreach ( var typename in allTypes ) {
680
677
var oldValues = m_old . GetComponentValues ( name , typename ) ;
681
678
var newValues = m_new . GetComponentValues ( name , typename ) ;
682
- var prefabValues = m_prefab . GetComponentValues ( name , typename ) ;
683
-
684
- // TODO: handle multiplicity! The algorithm is eluding me right now...
685
- // We'll need to do some kind of 3-way matching.
686
- if ( oldValues . Count > 1 ) { Debug . LogError ( "TODO: handle multiplicity " + oldValues . Count ) ; }
687
- if ( newValues . Count > 1 ) { Debug . LogError ( "TODO: handle multiplicity " + newValues . Count ) ; }
688
- if ( prefabValues . Count > 1 ) { Debug . LogError ( "TODO: handle multiplicity " + prefabValues . Count ) ; }
689
-
690
- if ( oldValues . Count == 0 && newValues . Count != 0 && prefabValues . Count == 0 ) {
691
- Append ( ref typesToUpdate , typename ) ;
692
- }
693
- else if ( oldValues . Count != 0 && newValues . Count == 0 && prefabValues . Count != 0 ) {
694
- Append ( ref typesToDestroy , typename ) ;
695
- }
696
- else if ( oldValues . Count != 0 && newValues . Count != 0 && prefabValues . Count != 0 ) {
697
- // Check whether we need to update.
698
- var oldValue = oldValues [ 0 ] ;
699
- var newValue = newValues [ 0 ] ;
700
- var prefabValue = prefabValues [ 0 ] ;
701
-
702
- if ( oldValue != newValue ) {
703
- // if oldValue == prefabValue, update.
704
- // if oldValue != prefabValue, conflict =>
705
- // resolve in favor of Chris, so update
706
- // anyway.
707
- Append ( ref typesToUpdate , typename ) ;
708
- }
709
- }
710
- }
711
-
712
- // Figure out the types so we can destroy them.
713
- if ( typesToDestroy != null ) {
714
- // TODO: handle monobehaviour in other assemblies
715
- // Sample use: using custom attributes in fbx to drive
716
- // unity behaviour by adding a monobehaviour in an
717
- // assetpostprocessor.
718
- var unityEngine = typeof ( Component ) . Assembly ;
719
- foreach ( var typename in typesToDestroy ) {
720
- var thetype = unityEngine . GetType ( typename ) ;
721
- Append ( ref m_componentsToDestroy , name , thetype ) ;
722
- }
723
- }
724
-
725
- // Find the actual components in the new fbx so we can copy them.
726
- if ( typesToUpdate != null ) {
727
- foreach ( var typename in typesToUpdate ) {
728
- if ( components [ name ] [ typename ] . Count > 1 ) {
729
- Debug . LogError ( string . Format ( "todo: multiplicity {0} on {1}:{2}" ,
730
- components [ name ] [ typename ] . Count , name , typename ) ) ;
679
+ List < string > prefabValues = null ; // get them only if we need them.
680
+
681
+ // If we have multiple identical-type components, match them up by index.
682
+ // TODO: match them up to minimize the diff instead.
683
+ int oldN = oldValues . Count ;
684
+ int newN = newValues . Count ;
685
+ for ( int i = 0 , n = System . Math . Max ( oldN , newN ) ; i < n ; ++ i ) {
686
+ if ( /* isNew */ i < newN ) {
687
+ var newValue = newValues [ i ] ;
688
+ if ( /* isOld */ i < oldN && oldValues [ i ] == newValue ) {
689
+ // No change from the old => skip.
690
+ continue ;
691
+ }
692
+ if ( prefabValues == null ) { prefabValues = m_prefab . GetComponentValues ( name , typename ) ; }
693
+ if ( i < prefabValues . Count && prefabValues [ i ] == newValue ) {
694
+ // Already updated => skip.
695
+ continue ;
696
+ }
697
+ Append ( m_componentsToUpdate , name ,
698
+ new ComponentValue ( componentTypes [ typename ] , newValue ) ) ;
699
+ } else {
700
+ // Not in the new, but is in the old, so delete
701
+ // it if it's not already deleted from the
702
+ // prefab.
703
+ if ( prefabValues == null ) { prefabValues = m_prefab . GetComponentValues ( name , typename ) ; }
704
+ if ( i < prefabValues . Count ) {
705
+ Append ( m_componentsToDestroy , name , componentTypes [ typename ] ) ;
706
+ }
731
707
}
732
- Append ( ref m_componentsToUpdate , name , components [ name ] [ typename ] [ 0 ] ) ;
733
708
}
734
709
}
735
710
}
@@ -757,7 +732,7 @@ public UpdateList(
757
732
758
733
ClassifyDestroyCreateNodes ( ) ;
759
734
ClassifyReparenting ( ) ;
760
- ClassifyComponents ( newFbx ) ;
735
+ ClassifyComponents ( newFbx , prefab . transform ) ;
761
736
}
762
737
763
738
public bool NeedsUpdates ( ) {
@@ -842,35 +817,30 @@ public void ImplementUpdates(FbxPrefab prefabInstance)
842
817
}
843
818
844
819
// Create or update the new components.
845
- foreach ( var kvp in prefabNodes ) {
846
- var name = kvp . Key ;
847
- var prefabXfo = kvp . Value ;
848
- List < Component > fbxComponents ;
849
- if ( m_componentsToUpdate . TryGetValue ( name , out fbxComponents ) ) {
850
- // Copy the components once so we can match them up even if there's multiple fbxComponents.
851
- List < Component > prefabComponents = new List < Component > ( prefabXfo . GetComponents < Component > ( ) ) ;
852
-
853
- foreach ( var fbxComponent in fbxComponents ) {
854
- int index = prefabComponents . FindIndex ( x => x . GetType ( ) == fbxComponent . GetType ( ) ) ;
855
- if ( index >= 0 ) {
856
- // Don't match this index again.
857
- Component prefabComponent = prefabComponents [ index ] ;
858
- prefabComponents . RemoveAt ( index ) ;
859
-
860
- // Now update it.
861
- if ( UnityEditorInternal . ComponentUtility . CopyComponent ( fbxComponent ) ) {
862
- UnityEditorInternal . ComponentUtility . PasteComponentValues ( prefabComponent ) ;
863
- Log ( "updated component {0}:{1}" , name , fbxComponent . GetType ( ) ) ;
864
-
865
- }
866
- } else {
867
- // We didn't find a match, so create the component as new.
868
- if ( UnityEditorInternal . ComponentUtility . CopyComponent ( fbxComponent ) ) {
869
- UnityEditorInternal . ComponentUtility . PasteComponentAsNew ( prefabXfo . gameObject ) ;
870
- Log ( "added new component {0}:{1}" , name , fbxComponent . GetType ( ) ) ;
871
- }
872
- }
820
+ foreach ( var kvp in m_componentsToUpdate ) {
821
+ var nodeName = kvp . Key ;
822
+ var fbxComponents = kvp . Value ;
823
+ var prefabXfo = prefabNodes [ nodeName ] ;
824
+
825
+ // Copy the components once so we can match them up even if there's multiple fbxComponents.
826
+ List < Component > prefabComponents = new List < Component > ( prefabXfo . GetComponents < Component > ( ) ) ;
827
+
828
+ foreach ( var fbxComponent in fbxComponents ) {
829
+ // Find or create the component to update.
830
+ int index = prefabComponents . FindIndex ( x => x . GetType ( ) == fbxComponent . t ) ;
831
+ Component prefabComponent ;
832
+ if ( index >= 0 ) {
833
+ // Don't match this index again.
834
+ prefabComponent = prefabComponents [ index ] ;
835
+ prefabComponents . RemoveAt ( index ) ;
836
+ Log ( "updated component {0}:{1}" , nodeName , fbxComponent . t ) ;
837
+ } else {
838
+ prefabComponent = prefabXfo . gameObject . AddComponent ( fbxComponent . t ) ;
839
+ Log ( "created component {0}:{1}" , nodeName , fbxComponent . t ) ;
873
840
}
841
+
842
+ // Now set the values.
843
+ UnityEditor . EditorJsonUtility . FromJsonOverwrite ( fbxComponent . jsonValue , prefabComponent ) ;
874
844
}
875
845
}
876
846
}
0 commit comments