Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_app/src/propagate.rs
9353 views
1
use alloc::vec::Vec;
2
use core::marker::PhantomData;
3
4
use crate::{App, Plugin};
5
#[cfg(feature = "bevy_reflect")]
6
use bevy_ecs::reflect::ReflectComponent;
7
use bevy_ecs::{
8
change_detection::DetectChangesMut,
9
component::Component,
10
entity::Entity,
11
hierarchy::ChildOf,
12
intern::Interned,
13
lifecycle::RemovedComponents,
14
query::{Changed, Or, QueryFilter, With, Without},
15
relationship::{Relationship, RelationshipTarget},
16
schedule::{IntoScheduleConfigs, ScheduleLabel, SystemSet},
17
system::{Commands, Local, Query},
18
};
19
#[cfg(feature = "bevy_reflect")]
20
use bevy_reflect::Reflect;
21
22
/// Plugin to automatically propagate a component value to all direct and transient relationship
23
/// targets (e.g. [`bevy_ecs::hierarchy::Children`]) of entities with a [`Propagate`] component.
24
///
25
/// The plugin Will maintain the target component over hierarchy changes, adding or removing
26
/// `C` when a relationship `R` (e.g. [`ChildOf`]) is added to or removed from a
27
/// relationship tree with a [`Propagate<C>`] source, or if the [`Propagate<C>`] component
28
/// is added, changed or removed.
29
///
30
/// Optionally you can include a query filter `F` to restrict the entities that are updated.
31
/// Note that the filter is not rechecked dynamically: changes to the filter state will not be
32
/// picked up until the [`Propagate`] component is touched, or the hierarchy is changed.
33
/// All members of the tree between source and target must match the filter for propagation
34
/// to reach a given target.
35
/// Individual entities can be skipped or terminate the propagation with the [`PropagateOver`]
36
/// and [`PropagateStop`] components.
37
///
38
/// The schedule can be configured via [`HierarchyPropagatePlugin::new`].
39
/// You should be sure to schedule your logic relative to this set: making changes
40
/// that modify component values before this logic, and reading the propagated
41
/// values after it.
42
pub struct HierarchyPropagatePlugin<
43
C: Component + Clone + PartialEq,
44
F: QueryFilter = (),
45
R: Relationship = ChildOf,
46
> {
47
schedule: Interned<dyn ScheduleLabel>,
48
_marker: PhantomData<fn() -> (C, F, R)>,
49
}
50
51
impl<C: Component + Clone + PartialEq, F: QueryFilter, R: Relationship>
52
HierarchyPropagatePlugin<C, F, R>
53
{
54
/// Construct the plugin. The propagation systems will be placed in the specified schedule.
55
pub fn new(schedule: impl ScheduleLabel) -> Self {
56
Self {
57
schedule: schedule.intern(),
58
_marker: PhantomData,
59
}
60
}
61
}
62
63
/// Causes the inner component to be added to this entity and all direct and transient relationship
64
/// targets. A target with a [`Propagate<C>`] component of its own will override propagation from
65
/// that point in the tree.
66
#[derive(Component, Clone, PartialEq)]
67
#[cfg_attr(
68
feature = "bevy_reflect",
69
derive(Reflect),
70
reflect(Component, Clone, PartialEq)
71
)]
72
pub struct Propagate<C: Component + Clone + PartialEq>(pub C);
73
74
/// Stops the output component being added to this entity.
75
/// Relationship targets will still inherit the component from this entity or its parents.
76
#[derive(Component)]
77
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component))]
78
pub struct PropagateOver<C>(PhantomData<fn() -> C>);
79
80
/// Stops the propagation at this entity. Children will not inherit the component.
81
#[derive(Component)]
82
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component))]
83
pub struct PropagateStop<C>(PhantomData<fn() -> C>);
84
85
/// The set in which propagation systems are added. You can schedule your logic relative to this set.
86
#[derive(SystemSet, Clone, PartialEq, PartialOrd, Ord)]
87
pub struct PropagateSet<C: Component + Clone + PartialEq> {
88
_p: PhantomData<fn() -> C>,
89
}
90
91
/// Internal struct for managing propagation
92
#[derive(Component, Clone, PartialEq, Debug)]
93
#[cfg_attr(
94
feature = "bevy_reflect",
95
derive(Reflect),
96
reflect(Component, Clone, PartialEq)
97
)]
98
pub struct Inherited<C: Component + Clone + PartialEq>(pub C);
99
100
impl<C> Default for PropagateOver<C> {
101
fn default() -> Self {
102
Self(Default::default())
103
}
104
}
105
106
impl<C> Default for PropagateStop<C> {
107
fn default() -> Self {
108
Self(Default::default())
109
}
110
}
111
112
impl<C: Component + Clone + PartialEq> core::fmt::Debug for PropagateSet<C> {
113
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
114
f.debug_struct("PropagateSet")
115
.field("_p", &self._p)
116
.finish()
117
}
118
}
119
120
impl<C: Component + Clone + PartialEq> Eq for PropagateSet<C> {}
121
122
impl<C: Component + Clone + PartialEq> core::hash::Hash for PropagateSet<C> {
123
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
124
self._p.hash(state);
125
}
126
}
127
128
impl<C: Component + Clone + PartialEq> Default for PropagateSet<C> {
129
fn default() -> Self {
130
Self {
131
_p: Default::default(),
132
}
133
}
134
}
135
136
impl<C: Component + Clone + PartialEq, F: QueryFilter + 'static, R: Relationship> Plugin
137
for HierarchyPropagatePlugin<C, F, R>
138
{
139
fn build(&self, app: &mut App) {
140
app.add_systems(
141
self.schedule,
142
(
143
update_source::<C, F, R>,
144
update_reparented::<C, F, R>,
145
update_removed_limit::<C, F, R>,
146
propagate_inherited::<C, F, R>,
147
propagate_output::<C, F>,
148
)
149
.chain()
150
.in_set(PropagateSet::<C>::default()),
151
);
152
}
153
}
154
155
/// add/remove `Inherited::<C>` for entities with a direct `Propagate::<C>`
156
pub fn update_source<C: Component + Clone + PartialEq, F: QueryFilter, R: Relationship>(
157
mut commands: Commands,
158
changed: Query<(Entity, &Propagate<C>), (Or<(Changed<Propagate<C>>, Without<Inherited<C>>)>,)>,
159
mut removed: RemovedComponents<Propagate<C>>,
160
relationship: Query<&R>,
161
relations: Query<&Inherited<C>, Without<PropagateStop<C>>>,
162
) {
163
for (entity, source) in &changed {
164
commands
165
.entity(entity)
166
.try_insert(Inherited(source.0.clone()));
167
}
168
169
// set `Inherited::<C>` based on ancestry when `Propagate::<C>` is removed
170
for removed in removed.read() {
171
if let Ok(mut commands) = commands.get_entity(removed) {
172
if let Some(inherited) = relationship
173
.get(removed)
174
.ok()
175
.and_then(|r| relations.get(r.get()).ok())
176
{
177
commands.insert(inherited.clone());
178
} else {
179
commands.try_remove::<Inherited<C>>();
180
}
181
}
182
}
183
}
184
185
/// add/remove `Inherited::<C>` for entities which have changed relationship
186
pub fn update_reparented<C: Component + Clone + PartialEq, F: QueryFilter, R: Relationship>(
187
mut commands: Commands,
188
moved: Query<(Entity, &R, Option<&Inherited<C>>), (Changed<R>, Without<Propagate<C>>, F)>,
189
relations: Query<&Inherited<C>, Without<PropagateStop<C>>>,
190
orphaned: Query<Entity, (With<Inherited<C>>, Without<Propagate<C>>, Without<R>, F)>,
191
) {
192
for (entity, relation, maybe_inherited) in &moved {
193
if let Ok(inherited) = relations.get(relation.get()) {
194
commands.entity(entity).try_insert(inherited.clone());
195
} else if maybe_inherited.is_some() {
196
commands.entity(entity).try_remove::<Inherited<C>>();
197
}
198
}
199
200
for orphan in &orphaned {
201
commands.entity(orphan).try_remove::<Inherited<C>>();
202
}
203
}
204
205
/// When `PropagateOver` or `PropagateStop` is removed, update the `Inherited::<C>` to trigger propagation
206
pub fn update_removed_limit<C: Component + Clone + PartialEq, F: QueryFilter, R: Relationship>(
207
mut inherited: Query<&mut Inherited<C>>,
208
mut removed_skip: RemovedComponents<PropagateOver<C>>,
209
mut removed_stop: RemovedComponents<PropagateStop<C>>,
210
) {
211
for entity in removed_skip.read() {
212
if let Ok(mut inherited) = inherited.get_mut(entity) {
213
inherited.set_changed();
214
}
215
}
216
for entity in removed_stop.read() {
217
if let Ok(mut inherited) = inherited.get_mut(entity) {
218
inherited.set_changed();
219
}
220
}
221
}
222
223
/// add/remove `Inherited::<C>` for targets of entities with modified `Inherited::<C>`
224
pub fn propagate_inherited<C: Component + Clone + PartialEq, F: QueryFilter, R: Relationship>(
225
mut commands: Commands,
226
changed: Query<
227
(&Inherited<C>, &R::RelationshipTarget),
228
(Changed<Inherited<C>>, Without<PropagateStop<C>>, F),
229
>,
230
recurse: Query<
231
(
232
Option<&R::RelationshipTarget>,
233
Option<&Inherited<C>>,
234
Option<&PropagateStop<C>>,
235
),
236
(Without<Propagate<C>>, F),
237
>,
238
mut removed: RemovedComponents<Inherited<C>>,
239
mut to_process: Local<Vec<(Entity, Option<Inherited<C>>)>>,
240
) {
241
// gather changed
242
for (inherited, targets) in &changed {
243
to_process.extend(
244
targets
245
.iter()
246
.map(|target| (target, Some(inherited.clone()))),
247
);
248
}
249
250
// and removed
251
for entity in removed.read() {
252
if let Ok((Some(targets), _, _)) = recurse.get(entity) {
253
to_process.extend(targets.iter().map(|target| (target, None)));
254
}
255
}
256
257
// propagate
258
while let Some((entity, maybe_inherited)) = (*to_process).pop() {
259
let Ok((maybe_targets, maybe_current, maybe_stop)) = recurse.get(entity) else {
260
continue;
261
};
262
263
if maybe_current == maybe_inherited.as_ref() {
264
continue;
265
}
266
267
// update children if required
268
if maybe_stop.is_none()
269
&& let Some(targets) = maybe_targets
270
{
271
to_process.extend(
272
targets
273
.iter()
274
.map(|target| (target, maybe_inherited.clone())),
275
);
276
}
277
278
// update this node's `Inherited<C>`
279
if let Some(inherited) = maybe_inherited {
280
commands.entity(entity).try_insert(inherited);
281
} else {
282
commands.entity(entity).try_remove::<Inherited<C>>();
283
}
284
}
285
}
286
287
/// add/remove `C` on entities with `Inherited::<C>`
288
pub fn propagate_output<C: Component + Clone + PartialEq, F: QueryFilter>(
289
mut commands: Commands,
290
changed: Query<
291
(Entity, &Inherited<C>, Option<&C>),
292
(Changed<Inherited<C>>, Without<PropagateOver<C>>, F),
293
>,
294
mut removed: RemovedComponents<Inherited<C>>,
295
skip: Query<(), With<PropagateOver<C>>>,
296
) {
297
for (entity, inherited, maybe_current) in &changed {
298
if maybe_current.is_some_and(|c| &inherited.0 == c) {
299
continue;
300
}
301
302
commands.entity(entity).try_insert(inherited.0.clone());
303
}
304
305
for removed in removed.read() {
306
if skip.get(removed).is_err() {
307
commands.entity(removed).try_remove::<C>();
308
}
309
}
310
}
311
312
#[cfg(test)]
313
mod tests {
314
use bevy_ecs::schedule::Schedule;
315
316
use crate::{App, Update};
317
318
use super::*;
319
320
#[derive(Component, Clone, PartialEq, Debug)]
321
struct TestValue(u32);
322
323
#[test]
324
fn test_simple_propagate() {
325
let mut app = App::new();
326
app.add_schedule(Schedule::new(Update));
327
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
328
329
let mut query = app.world_mut().query::<&TestValue>();
330
331
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
332
let intermediate = app
333
.world_mut()
334
.spawn_empty()
335
.insert(ChildOf(propagator))
336
.id();
337
let propagatee = app
338
.world_mut()
339
.spawn_empty()
340
.insert(ChildOf(intermediate))
341
.id();
342
343
app.update();
344
345
assert_eq!(
346
query.get_many(app.world(), [propagator, intermediate, propagatee]),
347
Ok([&TestValue(1), &TestValue(1), &TestValue(1)])
348
);
349
}
350
351
#[test]
352
fn test_remove_propagate() {
353
let mut app = App::new();
354
app.add_schedule(Schedule::new(Update));
355
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
356
357
let mut query = app.world_mut().query::<&TestValue>();
358
359
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
360
let propagatee = app
361
.world_mut()
362
.spawn_empty()
363
.insert(ChildOf(propagator))
364
.id();
365
366
app.update();
367
368
assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(1)));
369
370
app.world_mut()
371
.commands()
372
.entity(propagator)
373
.remove::<Propagate<TestValue>>();
374
app.update();
375
376
assert!(query.get(app.world(), propagator).is_err());
377
assert!(query.get(app.world(), propagatee).is_err());
378
}
379
380
#[test]
381
fn test_remove_orphan() {
382
let mut app = App::new();
383
app.add_schedule(Schedule::new(Update));
384
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
385
386
let mut query = app.world_mut().query::<&TestValue>();
387
388
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
389
let propagatee = app
390
.world_mut()
391
.spawn_empty()
392
.insert(ChildOf(propagator))
393
.id();
394
395
app.update();
396
397
assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(1)));
398
399
app.world_mut()
400
.commands()
401
.entity(propagatee)
402
.remove::<ChildOf>();
403
app.update();
404
405
assert!(query.get(app.world(), propagatee).is_err());
406
}
407
408
#[test]
409
fn test_reparented() {
410
let mut app = App::new();
411
app.add_schedule(Schedule::new(Update));
412
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
413
414
let mut query = app.world_mut().query::<&TestValue>();
415
416
let propagator_one = app.world_mut().spawn(Propagate(TestValue(1))).id();
417
let other_parent = app.world_mut().spawn_empty().id();
418
let propagatee = app
419
.world_mut()
420
.spawn_empty()
421
.insert(ChildOf(propagator_one))
422
.id();
423
424
app.update();
425
426
assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(1)));
427
428
app.world_mut()
429
.commands()
430
.entity(propagatee)
431
.insert(ChildOf(other_parent));
432
433
app.update();
434
435
assert!(query.get(app.world(), propagatee).is_err());
436
}
437
438
#[test]
439
fn test_reparented_with_prior() {
440
let mut app = App::new();
441
app.add_schedule(Schedule::new(Update));
442
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
443
444
let mut query = app.world_mut().query::<&TestValue>();
445
446
let propagator_a = app.world_mut().spawn(Propagate(TestValue(1))).id();
447
let propagator_b = app.world_mut().spawn(Propagate(TestValue(2))).id();
448
let propagatee = app
449
.world_mut()
450
.spawn_empty()
451
.insert(ChildOf(propagator_a))
452
.id();
453
454
app.update();
455
456
assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(1)));
457
458
app.world_mut()
459
.commands()
460
.entity(propagatee)
461
.insert(ChildOf(propagator_b));
462
app.update();
463
464
assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(2)));
465
}
466
467
#[test]
468
fn test_propagate_over() {
469
let mut app = App::new();
470
app.add_schedule(Schedule::new(Update));
471
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
472
473
let mut query = app.world_mut().query::<&TestValue>();
474
475
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
476
let propagate_over = app
477
.world_mut()
478
.spawn(TestValue(2))
479
.insert((PropagateOver::<TestValue>::default(), ChildOf(propagator)))
480
.id();
481
let propagatee = app
482
.world_mut()
483
.spawn_empty()
484
.insert(ChildOf(propagate_over))
485
.id();
486
487
app.update();
488
489
assert_eq!(query.get(app.world(), propagate_over), Ok(&TestValue(2)));
490
assert_eq!(query.get(app.world(), propagatee), Ok(&TestValue(1)));
491
}
492
493
#[test]
494
fn test_remove_propagate_over() {
495
let mut app = App::new();
496
app.add_schedule(Schedule::new(Update));
497
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
498
499
let mut query = app.world_mut().query::<&TestValue>();
500
501
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
502
let propagate_over = app
503
.world_mut()
504
.spawn(TestValue(2))
505
.insert((PropagateOver::<TestValue>::default(), ChildOf(propagator)))
506
.id();
507
let propagatee = app
508
.world_mut()
509
.spawn_empty()
510
.insert(ChildOf(propagate_over))
511
.id();
512
513
app.update();
514
assert_eq!(
515
app.world_mut()
516
.query::<&Inherited<TestValue>>()
517
.get(app.world(), propagate_over),
518
Ok(&Inherited(TestValue(1)))
519
);
520
assert_eq!(
521
app.world_mut()
522
.query::<&Inherited<TestValue>>()
523
.get(app.world(), propagatee),
524
Ok(&Inherited(TestValue(1)))
525
);
526
assert_eq!(
527
query.get_many(app.world(), [propagate_over, propagatee]),
528
Ok([&TestValue(2), &TestValue(1)])
529
);
530
531
app.world_mut()
532
.commands()
533
.entity(propagate_over)
534
.remove::<PropagateOver<TestValue>>();
535
app.update();
536
537
assert_eq!(
538
query.get_many(app.world(), [propagate_over, propagatee]),
539
Ok([&TestValue(1), &TestValue(1)])
540
);
541
}
542
543
#[test]
544
fn test_propagate_over_parent_removed() {
545
let mut app = App::new();
546
app.add_schedule(Schedule::new(Update));
547
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
548
549
let mut query = app.world_mut().query::<&TestValue>();
550
551
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
552
let propagate_over = app
553
.world_mut()
554
.spawn(TestValue(2))
555
.insert((PropagateOver::<TestValue>::default(), ChildOf(propagator)))
556
.id();
557
558
app.update();
559
560
assert_eq!(
561
query.get_many(app.world(), [propagator, propagate_over]),
562
Ok([&TestValue(1), &TestValue(2)])
563
);
564
565
app.world_mut()
566
.commands()
567
.entity(propagator)
568
.remove::<Propagate<TestValue>>();
569
app.update();
570
571
assert!(query.get(app.world(), propagator).is_err(),);
572
assert_eq!(query.get(app.world(), propagate_over), Ok(&TestValue(2)));
573
}
574
575
#[test]
576
fn test_orphaned_propagate_over() {
577
let mut app = App::new();
578
app.add_schedule(Schedule::new(Update));
579
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
580
581
let mut query = app.world_mut().query::<&TestValue>();
582
583
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
584
let propagate_over = app
585
.world_mut()
586
.spawn(TestValue(2))
587
.insert((PropagateOver::<TestValue>::default(), ChildOf(propagator)))
588
.id();
589
let propagatee = app
590
.world_mut()
591
.spawn_empty()
592
.insert(ChildOf(propagate_over))
593
.id();
594
595
app.update();
596
597
assert_eq!(
598
query.get_many(app.world(), [propagate_over, propagatee]),
599
Ok([&TestValue(2), &TestValue(1)])
600
);
601
602
app.world_mut()
603
.commands()
604
.entity(propagate_over)
605
.remove::<ChildOf>();
606
app.update();
607
608
assert_eq!(query.get(app.world(), propagate_over), Ok(&TestValue(2)));
609
assert!(query.get(app.world(), propagatee).is_err());
610
}
611
612
#[test]
613
fn test_propagate_stop() {
614
let mut app = App::new();
615
app.add_schedule(Schedule::new(Update));
616
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
617
618
let mut query = app.world_mut().query::<&TestValue>();
619
620
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
621
let propagate_stop = app
622
.world_mut()
623
.spawn(PropagateStop::<TestValue>::default())
624
.insert(ChildOf(propagator))
625
.id();
626
let no_propagatee = app
627
.world_mut()
628
.spawn_empty()
629
.insert(ChildOf(propagate_stop))
630
.id();
631
632
app.update();
633
634
assert_eq!(query.get(app.world(), propagate_stop), Ok(&TestValue(1)));
635
assert!(query.get(app.world(), no_propagatee).is_err());
636
}
637
638
#[test]
639
fn test_remove_propagate_stop() {
640
let mut app = App::new();
641
app.add_schedule(Schedule::new(Update));
642
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
643
644
let mut query = app.world_mut().query::<&TestValue>();
645
646
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
647
let propagate_stop = app
648
.world_mut()
649
.spawn(PropagateStop::<TestValue>::default())
650
.insert(ChildOf(propagator))
651
.id();
652
let no_propagatee = app
653
.world_mut()
654
.spawn_empty()
655
.insert(ChildOf(propagate_stop))
656
.id();
657
658
app.update();
659
660
assert_eq!(query.get(app.world(), propagate_stop), Ok(&TestValue(1)));
661
assert!(query.get(app.world(), no_propagatee).is_err());
662
663
app.world_mut()
664
.commands()
665
.entity(propagate_stop)
666
.remove::<PropagateStop<TestValue>>();
667
app.update();
668
669
assert_eq!(
670
query.get_many(app.world(), [propagate_stop, no_propagatee]),
671
Ok([&TestValue(1), &TestValue(1)])
672
);
673
}
674
675
#[test]
676
fn test_intermediate_override() {
677
let mut app = App::new();
678
app.add_schedule(Schedule::new(Update));
679
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
680
681
let mut query = app.world_mut().query::<&TestValue>();
682
683
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
684
let intermediate = app
685
.world_mut()
686
.spawn_empty()
687
.insert(ChildOf(propagator))
688
.id();
689
let propagatee = app
690
.world_mut()
691
.spawn_empty()
692
.insert(ChildOf(intermediate))
693
.id();
694
695
app.update();
696
697
assert_eq!(
698
query.get_many(app.world(), [propagator, intermediate, propagatee]),
699
Ok([&TestValue(1), &TestValue(1), &TestValue(1)])
700
);
701
702
app.world_mut()
703
.entity_mut(intermediate)
704
.insert(Propagate(TestValue(2)));
705
app.update();
706
707
assert_eq!(
708
app.world_mut()
709
.query::<&TestValue>()
710
.get_many(app.world(), [propagator, intermediate, propagatee]),
711
Ok([&TestValue(1), &TestValue(2), &TestValue(2)])
712
);
713
}
714
715
#[test]
716
fn test_filter() {
717
#[derive(Component)]
718
struct Marker;
719
720
let mut app = App::new();
721
app.add_schedule(Schedule::new(Update));
722
app.add_plugins(HierarchyPropagatePlugin::<TestValue, With<Marker>>::new(
723
Update,
724
));
725
726
let mut query = app.world_mut().query::<&TestValue>();
727
728
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
729
let propagatee = app
730
.world_mut()
731
.spawn_empty()
732
.insert(ChildOf(propagator))
733
.id();
734
735
app.update();
736
737
assert!(query.get(app.world(), propagator).is_err());
738
assert!(query.get(app.world(), propagatee).is_err());
739
740
// NOTE: changes to the filter condition are not rechecked
741
app.world_mut().entity_mut(propagator).insert(Marker);
742
app.update();
743
744
assert!(query.get(app.world(), propagator).is_err());
745
assert!(query.get(app.world(), propagatee).is_err());
746
747
app.world_mut()
748
.entity_mut(propagator)
749
.insert(Propagate(TestValue(1)));
750
app.update();
751
752
assert_eq!(query.get(app.world(), propagator), Ok(&TestValue(1)));
753
assert!(query.get(app.world(), propagatee).is_err());
754
755
app.world_mut().entity_mut(propagatee).insert(Marker);
756
app.update();
757
758
assert_eq!(query.get(app.world(), propagator), Ok(&TestValue(1)));
759
assert!(query.get(app.world(), propagatee).is_err());
760
761
app.world_mut()
762
.entity_mut(propagator)
763
.insert(Propagate(TestValue(1)));
764
app.update();
765
766
assert_eq!(
767
app.world_mut()
768
.query::<&TestValue>()
769
.get_many(app.world(), [propagator, propagatee]),
770
Ok([&TestValue(1), &TestValue(1)])
771
);
772
}
773
774
#[test]
775
fn test_removed_propagate_still_inherits() {
776
let mut app = App::new();
777
app.add_schedule(Schedule::new(Update));
778
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
779
780
let mut query = app.world_mut().query::<&TestValue>();
781
782
let propagator = app.world_mut().spawn(Propagate(TestValue(1))).id();
783
let propagatee = app
784
.world_mut()
785
.spawn(Propagate(TestValue(2)))
786
.insert(ChildOf(propagator))
787
.id();
788
789
app.update();
790
791
assert_eq!(
792
query.get_many(app.world(), [propagator, propagatee]),
793
Ok([&TestValue(1), &TestValue(2)])
794
);
795
796
app.world_mut()
797
.commands()
798
.entity(propagatee)
799
.remove::<Propagate<TestValue>>();
800
app.update();
801
802
assert_eq!(
803
query.get_many(app.world(), [propagator, propagatee]),
804
Ok([&TestValue(1), &TestValue(1)])
805
);
806
}
807
808
#[test]
809
fn test_reparent_respects_stop() {
810
let mut app = App::new();
811
app.add_schedule(Schedule::new(Update));
812
app.add_plugins(HierarchyPropagatePlugin::<TestValue>::new(Update));
813
814
let mut query = app.world_mut().query::<&TestValue>();
815
816
let propagator = app
817
.world_mut()
818
.spawn((
819
Propagate(TestValue(1)),
820
PropagateStop::<TestValue>::default(),
821
))
822
.id();
823
let propagatee = app.world_mut().spawn(TestValue(2)).id();
824
825
app.update();
826
827
assert_eq!(
828
query.get_many(app.world(), [propagator, propagatee]),
829
Ok([&TestValue(1), &TestValue(2)])
830
);
831
832
app.world_mut()
833
.commands()
834
.entity(propagatee)
835
.insert(ChildOf(propagator));
836
app.update();
837
838
assert_eq!(
839
query.get_many(app.world(), [propagator, propagatee]),
840
Ok([&TestValue(1), &TestValue(2)])
841
);
842
}
843
}
844
845