bevy_extract/
extract_component.rs1use crate::{
2 sync_component::{SyncComponent, SyncComponentPlugin},
3 sync_world::SubEntity,
4 Extract, ExtractSchedule,
5};
6use bevy_app::{App, AppLabel, Plugin};
7use bevy_camera::visibility::ViewVisibility;
8use bevy_ecs::{
9 bundle::NoBundleEffect,
10 prelude::*,
11 query::{QueryFilter, QueryItem, ReadOnlyQueryData},
12};
13use core::marker::PhantomData;
14
15pub use bevy_extract_macros::ExtractComponent;
16
17pub trait ExtractComponent<L: AppLabel, F = ()>: SyncComponent<L, F> {
29 type QueryData: ReadOnlyQueryData;
31 type QueryFilter: QueryFilter;
33 type Out: Bundle<Effect: NoBundleEffect>;
38 fn extract_component(item: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out>;
46}
47
48pub struct ExtractComponentPlugin<C, L: AppLabel, F = ()> {
59 only_extract_visible: bool,
60 marker: PhantomData<fn() -> (C, L, F)>,
61}
62
63impl<C, L: AppLabel, F> Default for ExtractComponentPlugin<C, L, F> {
64 fn default() -> Self {
65 Self {
66 only_extract_visible: false,
67 marker: PhantomData,
68 }
69 }
70}
71
72impl<C, L: AppLabel, F> ExtractComponentPlugin<C, L, F> {
73 pub fn extract_visible() -> Self {
74 Self {
75 only_extract_visible: true,
76 marker: PhantomData,
77 }
78 }
79}
80
81impl<
82 C: ExtractComponent<L, F>,
83 L: AppLabel + Default + Clone + Copy + Eq,
84 F: 'static + Send + Sync,
85 > Plugin for ExtractComponentPlugin<C, L, F>
86{
87 fn build(&self, app: &mut App) {
88 app.add_plugins(SyncComponentPlugin::<C, L, F>::default());
89
90 if let Some(sub_app) = app.get_sub_app_mut(L::default()) {
91 if self.only_extract_visible {
92 sub_app.add_systems(ExtractSchedule, extract_visible_components::<C, L, F>);
93 } else {
94 sub_app.add_systems(ExtractSchedule, extract_components::<C, L, F>);
95 }
96 }
97 }
98}
99
100fn extract_components<C: ExtractComponent<L, F>, L: AppLabel + Clone + Copy + Eq, F>(
102 mut commands: Commands,
103 mut previous_len: Local<usize>,
104 query: Extract<Query<(SubEntity<L>, C::QueryData), C::QueryFilter>>,
105) {
106 let mut values = Vec::with_capacity(*previous_len);
107 for (entity, query_item) in &query {
108 if let Some(component) = C::extract_component(query_item) {
109 values.push((entity, component));
110 } else {
111 commands.entity(entity).remove::<C::Target>();
112 }
113 }
114 *previous_len = values.len();
115 commands.try_insert_batch(values);
116}
117
118fn extract_visible_components<C: ExtractComponent<L, F>, L: AppLabel + Clone + Copy + Eq, F>(
120 mut commands: Commands,
121 mut previous_len: Local<usize>,
122 query: Extract<Query<(SubEntity<L>, &ViewVisibility, C::QueryData), C::QueryFilter>>,
123) {
124 let mut values = Vec::with_capacity(*previous_len);
125 for (entity, view_visibility, query_item) in &query {
126 if view_visibility.get() {
127 if let Some(component) = C::extract_component(query_item) {
128 values.push((entity, component));
129 } else {
130 commands.entity(entity).remove::<C::Target>();
131 }
132 }
133 }
134 *previous_len = values.len();
135 commands.try_insert_batch(values);
136}