@@ -30,6 +30,7 @@ public class RubberInspector : MainInspector<RubberData, RubberComponent>
3030 {
3131 private SerializedProperty _thicknessProperty ;
3232 private SerializedProperty _restLengthProperty ;
33+ private bool _bindingChangeScheduled ;
3334
3435 protected override void OnEnable ( )
3536 {
@@ -86,13 +87,14 @@ private void DrawPathSection()
8687 EditorGUILayout . LabelField ( "Path" , EditorStyles . boldLabel ) ;
8788 var source = ( RubberPathSource ) EditorGUILayout . EnumPopup ( "Source" , MainComponent . PathSource ) ;
8889 if ( source != MainComponent . PathSource ) {
89- Undo . RegisterFullObjectHierarchyUndo ( MainComponent . gameObject , "Change Rubber Path Source" ) ;
9090 if ( source == RubberPathSource . Guides ) {
91- MainComponent . SetGuideBindings ( MainComponent . GuideBindings ) ;
91+ EditorUtility . DisplayDialog ( "Guide Bindings Required" ,
92+ "Select the rubber and its guides, then use GameObject > Pinball > Rubber > Bind Selected Guides." ,
93+ "OK" ) ;
9294 } else {
93- MainComponent . DetachFromGuides ( ) ;
95+ ScheduleBindingsChange ( MainComponent . GuideBindings . ToArray ( ) ,
96+ Array . Empty < RubberGuideBinding > ( ) , "Detach Rubber From Guides" ) ;
9497 }
95- EditorUtility . SetDirty ( MainComponent ) ;
9698 }
9799
98100 if ( MainComponent . PathSource != RubberPathSource . Guides ) {
@@ -101,15 +103,22 @@ private void DrawPathSection()
101103 return ;
102104 }
103105
104- var bindings = MainComponent . GuideBindings . ToArray ( ) ;
106+ var expectedBindings = MainComponent . GuideBindings . ToArray ( ) ;
107+ var bindings = expectedBindings . ToArray ( ) ;
105108 for ( var i = 0 ; i < bindings . Length ; i ++ ) {
106109 EditorGUILayout . BeginVertical ( EditorStyles . helpBox ) ;
107110 var guide = ( RubberGuideComponent ) EditorGUILayout . ObjectField ( $ "Guide { i + 1 } ",
108111 bindings [ i ] . Guide , typeof ( RubberGuideComponent ) , true ) ;
109112 if ( guide != bindings [ i ] . Guide ) {
110- bindings [ i ] . Guide = guide ;
111- bindings [ i ] . SlotId = default ;
112- ApplyBindings ( bindings , "Change Rubber Guide Binding" ) ;
113+ if ( ! guide ) {
114+ ScheduleBindingsChange ( expectedBindings ,
115+ bindings . Where ( ( _ , index ) => index != i ) . ToArray ( ) ,
116+ bindings . Length == 1 ? "Detach Rubber From Guides"
117+ : "Remove Rubber Guide Binding" ) ;
118+ } else {
119+ ScheduleGuideReplacement ( i , expectedBindings , guide ) ;
120+ }
121+ guide = bindings [ i ] . Guide ;
113122 }
114123 if ( guide && guide . Slots . Length > 0 ) {
115124 var selectedSlot = Array . FindIndex ( guide . Slots , slot => slot . Id == bindings [ i ] . SlotId ) ;
@@ -122,24 +131,24 @@ private void DrawPathSection()
122131 var nextSlot = EditorGUILayout . Popup ( "Slot" , 0 , labels ) ;
123132 if ( nextSlot > 0 ) {
124133 bindings [ i ] . SlotId = guide . Slots [ nextSlot - 1 ] . Id ;
125- ApplyBindings ( bindings , "Repair Rubber Guide Slot" ) ;
134+ ScheduleBindingsChange ( expectedBindings , bindings , "Repair Rubber Guide Slot" ) ;
126135 }
127136 } else {
128137 var nextSlot = EditorGUILayout . Popup ( "Slot" , selectedSlot , slotLabels ) ;
129138 if ( nextSlot != selectedSlot && nextSlot >= 0
130139 && nextSlot < guide . Slots . Length ) {
131140 bindings [ i ] . SlotId = guide . Slots [ nextSlot ] . Id ;
132- ApplyBindings ( bindings , "Change Rubber Guide Slot" ) ;
141+ ScheduleBindingsChange ( expectedBindings , bindings , "Change Rubber Guide Slot" ) ;
133142 }
134143 }
135144 } else if ( guide ) {
136145 EditorGUILayout . HelpBox ( "This guide has no slots." , MessageType . Error ) ;
137146 }
138- if ( GUILayout . Button ( "Remove Binding" ) ) {
139- ApplyBindings ( bindings . Where ( ( _ , index ) => index != i ) . ToArray ( ) ,
140- "Remove Rubber Guide Binding" ) ;
141- EditorGUILayout . EndVertical ( ) ;
142- break ;
147+ if ( GUILayout . Button ( bindings . Length == 1 ? "Detach From Guides" : "Remove Binding" ) ) {
148+ ScheduleBindingsChange ( expectedBindings ,
149+ bindings . Where ( ( _ , index ) => index != i ) . ToArray ( ) ,
150+ bindings . Length == 1 ? "Detach Rubber From Guides"
151+ : "Remove Rubber Guide Binding" ) ;
143152 }
144153 EditorGUILayout . EndVertical ( ) ;
145154 }
@@ -159,19 +168,109 @@ private void DrawPathSection()
159168 }
160169 }
161170 if ( GUILayout . Button ( "Detach From Guides" ) ) {
162- Undo . RegisterFullObjectHierarchyUndo ( MainComponent . gameObject , "Detach Rubber From Guides" ) ;
163- MainComponent . DetachFromGuides ( ) ;
164- MainComponent . RebuildMeshes ( ) ;
165- EditorUtility . SetDirty ( MainComponent ) ;
171+ ScheduleBindingsChange ( expectedBindings , Array . Empty < RubberGuideBinding > ( ) ,
172+ "Detach Rubber From Guides" ) ;
166173 }
167174 EditorGUILayout . EndHorizontal ( ) ;
168175 }
169176
170- private void ApplyBindings ( RubberGuideBinding [ ] bindings , string undoName )
177+ private void ScheduleBindingsChange ( RubberGuideBinding [ ] expectedBindings ,
178+ RubberGuideBinding [ ] bindings , string undoName )
179+ {
180+ if ( _bindingChangeScheduled ) {
181+ return ;
182+ }
183+ expectedBindings = expectedBindings . ToArray ( ) ;
184+ bindings = bindings . ToArray ( ) ;
185+ _bindingChangeScheduled = true ;
186+ var rubber = MainComponent ;
187+ EditorApplication . delayCall += ( ) => {
188+ if ( ! this ) {
189+ return ;
190+ }
191+ _bindingChangeScheduled = false ;
192+ if ( rubber && BindingsMatch ( rubber , expectedBindings ) ) {
193+ ApplyBindings ( rubber , bindings , undoName ) ;
194+ }
195+ Repaint ( ) ;
196+ } ;
197+ }
198+
199+ private void ScheduleGuideReplacement ( int bindingIndex , RubberGuideBinding [ ] expectedBindings ,
200+ RubberGuideComponent guide )
201+ {
202+ if ( _bindingChangeScheduled ) {
203+ return ;
204+ }
205+ expectedBindings = expectedBindings . ToArray ( ) ;
206+ _bindingChangeScheduled = true ;
207+ var rubber = MainComponent ;
208+ EditorApplication . delayCall += ( ) => {
209+ if ( ! this ) {
210+ return ;
211+ }
212+ _bindingChangeScheduled = false ;
213+ if ( ! rubber || ! guide || bindingIndex < 0
214+ || bindingIndex >= rubber . GuideBindings . Count ) {
215+ Repaint ( ) ;
216+ return ;
217+ }
218+ if ( ! BindingsMatch ( rubber , expectedBindings ) ) {
219+ Repaint ( ) ;
220+ return ;
221+ }
222+ if ( RubberGuideSlotPickerWindow . TryPick ( new [ ] { guide } , out var selectedBindings )
223+ && BindingsMatch ( rubber , expectedBindings ) ) {
224+ var bindings = rubber . GuideBindings . ToArray ( ) ;
225+ bindings [ bindingIndex ] = selectedBindings [ 0 ] ;
226+ ApplyBindings ( rubber , bindings , "Change Rubber Guide Binding" ) ;
227+ }
228+ Repaint ( ) ;
229+ } ;
230+ }
231+
232+ private static bool BindingsMatch ( RubberComponent rubber , RubberGuideBinding [ ] expectedBindings )
233+ {
234+ if ( rubber . PathSource != RubberPathSource . Guides
235+ || rubber . GuideBindings . Count != expectedBindings . Length ) {
236+ return false ;
237+ }
238+ for ( var i = 0 ; i < expectedBindings . Length ; i ++ ) {
239+ var current = rubber . GuideBindings [ i ] ;
240+ var expected = expectedBindings [ i ] ;
241+ if ( current . Guide != expected . Guide || current . SlotId != expected . SlotId ) {
242+ return false ;
243+ }
244+ }
245+ return true ;
246+ }
247+
248+ private static void ApplyBindings ( RubberComponent rubber , RubberGuideBinding [ ] bindings , string undoName )
171249 {
172- Undo . RegisterFullObjectHierarchyUndo ( MainComponent . gameObject , undoName ) ;
173- MainComponent . SetGuideBindings ( bindings ) ;
174- EditorUtility . SetDirty ( MainComponent ) ;
250+ Undo . IncrementCurrentGroup ( ) ;
251+ Undo . RegisterFullObjectHierarchyUndo ( rubber . gameObject , undoName ) ;
252+ var detaching = bindings . Length == 0 ;
253+ if ( detaching ) {
254+ rubber . DetachFromGuides ( ) ;
255+ rubber . RebuildMeshes ( ) ;
256+ } else if ( ! RubberAutofit . TryReplaceGuideBindings ( rubber , bindings ,
257+ out _ , out var error ) ) {
258+ Undo . RevertAllInCurrentGroup ( ) ;
259+ EditorUtility . DisplayDialog ( "Rubber Binding Change Failed" ,
260+ $ "The previous bindings and sampled path were preserved.\n \n { error } ", "OK" ) ;
261+ return ;
262+ }
263+ EditorUtility . SetDirty ( rubber ) ;
264+ PrefabUtility . RecordPrefabInstancePropertyModifications ( rubber ) ;
265+ if ( detaching ) {
266+ var collider = rubber . GetComponent < RubberColliderComponent > ( ) ;
267+ if ( collider ) {
268+ EditorUtility . SetDirty ( collider ) ;
269+ PrefabUtility . RecordPrefabInstancePropertyModifications ( collider ) ;
270+ }
271+ }
272+ RubberGuideDependencyTracker . RebuildSoon ( ) ;
273+ SceneView . RepaintAll ( ) ;
175274 }
176275
177276 private void OnSceneGUI ( )
0 commit comments