@@ -2,23 +2,38 @@ use crate::components::{GlobalTransform, Transform};
2
2
use bevy_ecs:: {
3
3
change_detection:: Ref ,
4
4
prelude:: { Changed , DetectChanges , Entity , Query , With , Without } ,
5
+ removal_detection:: RemovedComponents ,
6
+ system:: ParamSet ,
5
7
} ;
6
8
use bevy_hierarchy:: { Children , Parent } ;
7
9
8
10
/// Update [`GlobalTransform`] component of entities that aren't in the hierarchy
9
11
///
10
12
/// Third party plugins should ensure that this is used in concert with [`propagate_transforms`].
11
13
pub fn sync_simple_transforms (
12
- mut query : Query <
13
- ( & Transform , & mut GlobalTransform ) ,
14
- ( Changed < Transform > , Without < Parent > , Without < Children > ) ,
15
- > ,
14
+ mut query : ParamSet < (
15
+ Query <
16
+ ( & Transform , & mut GlobalTransform ) ,
17
+ ( Changed < Transform > , Without < Parent > , Without < Children > ) ,
18
+ > ,
19
+ Query < ( Ref < Transform > , & mut GlobalTransform ) , Without < Children > > ,
20
+ ) > ,
21
+ mut orphaned : RemovedComponents < Parent > ,
16
22
) {
17
23
query
24
+ . p0 ( )
18
25
. par_iter_mut ( )
19
26
. for_each_mut ( |( transform, mut global_transform) | {
20
27
* global_transform = GlobalTransform :: from ( * transform) ;
21
28
} ) ;
29
+ // updated orphaned entities
30
+ let mut query = query. p1 ( ) ;
31
+ let mut iter = query. iter_many_mut ( orphaned. iter ( ) ) ;
32
+ while let Some ( ( transform, mut global_transform) ) = iter. fetch_next ( ) {
33
+ if !transform. is_changed ( ) {
34
+ * global_transform = GlobalTransform :: from ( * transform) ;
35
+ }
36
+ }
22
37
}
23
38
24
39
/// Update [`GlobalTransform`] component of entities based on entity hierarchy and
@@ -30,12 +45,15 @@ pub fn propagate_transforms(
30
45
( Entity , & Children , Ref < Transform > , & mut GlobalTransform ) ,
31
46
Without < Parent > ,
32
47
> ,
48
+ mut orphaned : RemovedComponents < Parent > ,
33
49
transform_query : Query < ( Ref < Transform > , & mut GlobalTransform , Option < & Children > ) , With < Parent > > ,
34
50
parent_query : Query < ( Entity , Ref < Parent > ) > ,
35
51
) {
52
+ let mut orphaned = orphaned. iter ( ) . collect :: < Vec < _ > > ( ) ;
53
+ orphaned. sort_unstable ( ) ;
36
54
root_query. par_iter_mut ( ) . for_each_mut (
37
55
|( entity, children, transform, mut global_transform) | {
38
- let changed = transform. is_changed ( ) ;
56
+ let changed = transform. is_changed ( ) || orphaned . binary_search ( & entity ) . is_ok ( ) ;
39
57
if changed {
40
58
* global_transform = GlobalTransform :: from ( * transform) ;
41
59
}
@@ -161,10 +179,62 @@ mod test {
161
179
use bevy_tasks:: { ComputeTaskPool , TaskPool } ;
162
180
163
181
use crate :: components:: { GlobalTransform , Transform } ;
164
- use crate :: systems:: * ;
165
- use crate :: TransformBundle ;
182
+ use crate :: { propagate_transforms, sync_simple_transforms, TransformBundle } ;
166
183
use bevy_hierarchy:: { BuildChildren , BuildWorldChildren , Children , Parent } ;
167
184
185
+ #[ test]
186
+ fn correct_parent_removed ( ) {
187
+ ComputeTaskPool :: init ( TaskPool :: default) ;
188
+ let mut world = World :: default ( ) ;
189
+ let offset_global_transform =
190
+ |offset| GlobalTransform :: from ( Transform :: from_xyz ( offset, offset, offset) ) ;
191
+ let offset_transform =
192
+ |offset| TransformBundle :: from_transform ( Transform :: from_xyz ( offset, offset, offset) ) ;
193
+
194
+ let mut schedule = Schedule :: new ( ) ;
195
+ schedule. add_system ( sync_simple_transforms) ;
196
+ schedule. add_system ( propagate_transforms) ;
197
+
198
+ let mut command_queue = CommandQueue :: default ( ) ;
199
+ let mut commands = Commands :: new ( & mut command_queue, & world) ;
200
+ let root = commands. spawn ( offset_transform ( 3.3 ) ) . id ( ) ;
201
+ let parent = commands. spawn ( offset_transform ( 4.4 ) ) . id ( ) ;
202
+ let child = commands. spawn ( offset_transform ( 5.5 ) ) . id ( ) ;
203
+ commands. entity ( parent) . set_parent ( root) ;
204
+ commands. entity ( child) . set_parent ( parent) ;
205
+ command_queue. apply ( & mut world) ;
206
+ schedule. run ( & mut world) ;
207
+
208
+ assert_ne ! (
209
+ world. get:: <GlobalTransform >( parent) . unwrap( ) ,
210
+ & GlobalTransform :: from( Transform :: IDENTITY )
211
+ ) ;
212
+
213
+ // Remove parent of `parent`
214
+ let mut command_queue = CommandQueue :: default ( ) ;
215
+ let mut commands = Commands :: new ( & mut command_queue, & world) ;
216
+ commands. entity ( parent) . remove_parent ( ) ;
217
+ command_queue. apply ( & mut world) ;
218
+ schedule. run ( & mut world) ;
219
+
220
+ assert_eq ! (
221
+ world. get:: <GlobalTransform >( parent) . unwrap( ) ,
222
+ & offset_global_transform( 4.4 )
223
+ ) ;
224
+
225
+ // Remove parent of `child`
226
+ let mut command_queue = CommandQueue :: default ( ) ;
227
+ let mut commands = Commands :: new ( & mut command_queue, & world) ;
228
+ commands. entity ( child) . remove_parent ( ) ;
229
+ command_queue. apply ( & mut world) ;
230
+ schedule. run ( & mut world) ;
231
+
232
+ assert_eq ! (
233
+ world. get:: <GlobalTransform >( child) . unwrap( ) ,
234
+ & offset_global_transform( 5.5 )
235
+ ) ;
236
+ }
237
+
168
238
#[ test]
169
239
fn did_propagate ( ) {
170
240
ComputeTaskPool :: init ( TaskPool :: default) ;
@@ -375,8 +445,8 @@ mod test {
375
445
let mut temp = World :: new ( ) ;
376
446
let mut app = App :: new ( ) ;
377
447
378
- app. add_system ( propagate_transforms )
379
- . add_system ( sync_simple_transforms ) ;
448
+ app. add_system ( sync_simple_transforms ) ;
449
+ app . add_system ( propagate_transforms ) ;
380
450
381
451
fn setup_world ( world : & mut World ) -> ( Entity , Entity ) {
382
452
let mut grandchild = Entity :: from_raw ( 0 ) ;
0 commit comments