Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ecs/src/system/commands/entity_command.rs
6849 views
1
//! Contains the definition of the [`EntityCommand`] trait,
2
//! as well as the blanket implementation of the trait for closures.
3
//!
4
//! It also contains functions that return closures for use with
5
//! [`EntityCommands`](crate::system::EntityCommands).
6
7
use alloc::vec::Vec;
8
use log::info;
9
10
use crate::{
11
bundle::{Bundle, InsertMode},
12
change_detection::MaybeLocation,
13
component::{Component, ComponentId, ComponentInfo},
14
entity::{Entity, EntityClonerBuilder, OptIn, OptOut},
15
event::EntityEvent,
16
relationship::RelationshipHookMode,
17
system::IntoObserverSystem,
18
world::{error::EntityMutableFetchError, EntityWorldMut, FromWorld},
19
};
20
use bevy_ptr::{move_as_ptr, OwningPtr};
21
22
/// A command which gets executed for a given [`Entity`].
23
///
24
/// Should be used with [`EntityCommands::queue`](crate::system::EntityCommands::queue).
25
///
26
/// The `Out` generic parameter is the returned "output" of the command.
27
///
28
/// # Examples
29
///
30
/// ```
31
/// # use std::collections::HashSet;
32
/// # use bevy_ecs::prelude::*;
33
/// use bevy_ecs::system::EntityCommand;
34
/// #
35
/// # #[derive(Component, PartialEq)]
36
/// # struct Name(String);
37
/// # impl Name {
38
/// # fn new(s: String) -> Self { Name(s) }
39
/// # fn as_str(&self) -> &str { &self.0 }
40
/// # }
41
///
42
/// #[derive(Resource, Default)]
43
/// struct Counter(i64);
44
///
45
/// /// A `Command` which names an entity based on a global counter.
46
/// fn count_name(mut entity: EntityWorldMut) {
47
/// // Get the current value of the counter, and increment it for next time.
48
/// let i = {
49
/// let mut counter = entity.resource_mut::<Counter>();
50
/// let i = counter.0;
51
/// counter.0 += 1;
52
/// i
53
/// };
54
/// // Name the entity after the value of the counter.
55
/// entity.insert(Name::new(format!("Entity #{i}")));
56
/// }
57
///
58
/// // App creation boilerplate omitted...
59
/// # let mut world = World::new();
60
/// # world.init_resource::<Counter>();
61
/// #
62
/// # let mut setup_schedule = Schedule::default();
63
/// # setup_schedule.add_systems(setup);
64
/// # let mut assert_schedule = Schedule::default();
65
/// # assert_schedule.add_systems(assert_names);
66
/// #
67
/// # setup_schedule.run(&mut world);
68
/// # assert_schedule.run(&mut world);
69
///
70
/// fn setup(mut commands: Commands) {
71
/// commands.spawn_empty().queue(count_name);
72
/// commands.spawn_empty().queue(count_name);
73
/// }
74
///
75
/// fn assert_names(named: Query<&Name>) {
76
/// // We use a HashSet because we do not care about the order.
77
/// let names: HashSet<_> = named.iter().map(Name::as_str).collect();
78
/// assert_eq!(names, HashSet::from_iter(["Entity #0", "Entity #1"]));
79
/// }
80
/// ```
81
pub trait EntityCommand<Out = ()>: Send + 'static {
82
/// Executes this command for the given [`Entity`].
83
fn apply(self, entity: EntityWorldMut) -> Out;
84
}
85
86
/// An error that occurs when running an [`EntityCommand`] on a specific entity.
87
#[derive(thiserror::Error, Debug)]
88
pub enum EntityCommandError<E> {
89
/// The entity this [`EntityCommand`] tried to run on could not be fetched.
90
#[error(transparent)]
91
EntityFetchError(#[from] EntityMutableFetchError),
92
/// An error that occurred while running the [`EntityCommand`].
93
#[error("{0}")]
94
CommandFailed(E),
95
}
96
97
impl<Out, F> EntityCommand<Out> for F
98
where
99
F: FnOnce(EntityWorldMut) -> Out + Send + 'static,
100
{
101
fn apply(self, entity: EntityWorldMut) -> Out {
102
self(entity)
103
}
104
}
105
106
/// An [`EntityCommand`] that adds the components in a [`Bundle`] to an entity.
107
#[track_caller]
108
pub fn insert(bundle: impl Bundle, mode: InsertMode) -> impl EntityCommand {
109
let caller = MaybeLocation::caller();
110
move |mut entity: EntityWorldMut| {
111
move_as_ptr!(bundle);
112
entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);
113
}
114
}
115
116
/// An [`EntityCommand`] that adds a dynamic component to an entity.
117
///
118
/// # Safety
119
///
120
/// - [`ComponentId`] must be from the same world as the target entity.
121
/// - `T` must have the same layout as the one passed during `component_id` creation.
122
#[track_caller]
123
pub unsafe fn insert_by_id<T: Send + 'static>(
124
component_id: ComponentId,
125
value: T,
126
mode: InsertMode,
127
) -> impl EntityCommand {
128
let caller = MaybeLocation::caller();
129
move |mut entity: EntityWorldMut| {
130
// SAFETY:
131
// - `component_id` safety is ensured by the caller
132
// - `ptr` is valid within the `make` block
133
OwningPtr::make(value, |ptr| unsafe {
134
entity.insert_by_id_with_caller(
135
component_id,
136
ptr,
137
mode,
138
caller,
139
RelationshipHookMode::Run,
140
);
141
});
142
}
143
}
144
145
/// An [`EntityCommand`] that adds a component to an entity using
146
/// the component's [`FromWorld`] implementation.
147
///
148
/// `T::from_world` will only be invoked if the component will actually be inserted.
149
/// In other words, `T::from_world` will *not* be invoked if `mode` is [`InsertMode::Keep`]
150
/// and the entity already has the component.
151
#[track_caller]
152
pub fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityCommand {
153
let caller = MaybeLocation::caller();
154
move |mut entity: EntityWorldMut| {
155
if !(mode == InsertMode::Keep && entity.contains::<T>()) {
156
let value = entity.world_scope(|world| T::from_world(world));
157
move_as_ptr!(value);
158
entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);
159
}
160
}
161
}
162
163
/// An [`EntityCommand`] that adds a component to an entity using
164
/// some function that returns the component.
165
///
166
/// The function will only be invoked if the component will actually be inserted.
167
/// In other words, the function will *not* be invoked if `mode` is [`InsertMode::Keep`]
168
/// and the entity already has the component.
169
#[track_caller]
170
pub fn insert_with<T: Component, F>(component_fn: F, mode: InsertMode) -> impl EntityCommand
171
where
172
F: FnOnce() -> T + Send + 'static,
173
{
174
let caller = MaybeLocation::caller();
175
move |mut entity: EntityWorldMut| {
176
if !(mode == InsertMode::Keep && entity.contains::<T>()) {
177
let bundle = component_fn();
178
move_as_ptr!(bundle);
179
entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);
180
}
181
}
182
}
183
184
/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity.
185
#[track_caller]
186
pub fn remove<T: Bundle>() -> impl EntityCommand {
187
let caller = MaybeLocation::caller();
188
move |mut entity: EntityWorldMut| {
189
entity.remove_with_caller::<T>(caller);
190
}
191
}
192
193
/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity,
194
/// as well as the required components for each component removed.
195
#[track_caller]
196
pub fn remove_with_requires<T: Bundle>() -> impl EntityCommand {
197
let caller = MaybeLocation::caller();
198
move |mut entity: EntityWorldMut| {
199
entity.remove_with_requires_with_caller::<T>(caller);
200
}
201
}
202
203
/// An [`EntityCommand`] that removes a dynamic component from an entity.
204
#[track_caller]
205
pub fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {
206
let caller = MaybeLocation::caller();
207
move |mut entity: EntityWorldMut| {
208
entity.remove_by_id_with_caller(component_id, caller);
209
}
210
}
211
212
/// An [`EntityCommand`] that removes all components from an entity.
213
#[track_caller]
214
pub fn clear() -> impl EntityCommand {
215
let caller = MaybeLocation::caller();
216
move |mut entity: EntityWorldMut| {
217
entity.clear_with_caller(caller);
218
}
219
}
220
221
/// An [`EntityCommand`] that removes all components from an entity,
222
/// except for those in the given [`Bundle`].
223
#[track_caller]
224
pub fn retain<T: Bundle>() -> impl EntityCommand {
225
let caller = MaybeLocation::caller();
226
move |mut entity: EntityWorldMut| {
227
entity.retain_with_caller::<T>(caller);
228
}
229
}
230
231
/// An [`EntityCommand`] that despawns an entity.
232
///
233
/// # Note
234
///
235
/// This will also despawn the entities in any [`RelationshipTarget`](crate::relationship::RelationshipTarget)
236
/// that is configured to despawn descendants.
237
///
238
/// For example, this will recursively despawn [`Children`](crate::hierarchy::Children).
239
#[track_caller]
240
pub fn despawn() -> impl EntityCommand {
241
let caller = MaybeLocation::caller();
242
move |entity: EntityWorldMut| {
243
entity.despawn_with_caller(caller);
244
}
245
}
246
247
/// An [`EntityCommand`] that creates an [`Observer`](crate::observer::Observer)
248
/// watching for an [`EntityEvent`] of type `E` whose [`EntityEvent::event_target`]
249
/// targets this entity.
250
#[track_caller]
251
pub fn observe<E: EntityEvent, B: Bundle, M>(
252
observer: impl IntoObserverSystem<E, B, M>,
253
) -> impl EntityCommand {
254
let caller = MaybeLocation::caller();
255
move |mut entity: EntityWorldMut| {
256
entity.observe_with_caller(observer, caller);
257
}
258
}
259
260
/// An [`EntityCommand`] that clones parts of an entity onto another entity,
261
/// configured through [`EntityClonerBuilder`].
262
///
263
/// This builder tries to clone every component from the source entity except
264
/// for components that were explicitly denied, for example by using the
265
/// [`deny`](EntityClonerBuilder<OptOut>::deny) method.
266
///
267
/// Required components are not considered by denied components and must be
268
/// explicitly denied as well if desired.
269
pub fn clone_with_opt_out(
270
target: Entity,
271
config: impl FnOnce(&mut EntityClonerBuilder<OptOut>) + Send + Sync + 'static,
272
) -> impl EntityCommand {
273
move |mut entity: EntityWorldMut| {
274
entity.clone_with_opt_out(target, config);
275
}
276
}
277
278
/// An [`EntityCommand`] that clones parts of an entity onto another entity,
279
/// configured through [`EntityClonerBuilder`].
280
///
281
/// This builder tries to clone every component that was explicitly allowed
282
/// from the source entity, for example by using the
283
/// [`allow`](EntityClonerBuilder<OptIn>::allow) method.
284
///
285
/// Required components are also cloned when the target entity does not contain them.
286
pub fn clone_with_opt_in(
287
target: Entity,
288
config: impl FnOnce(&mut EntityClonerBuilder<OptIn>) + Send + Sync + 'static,
289
) -> impl EntityCommand {
290
move |mut entity: EntityWorldMut| {
291
entity.clone_with_opt_in(target, config);
292
}
293
}
294
295
/// An [`EntityCommand`] that clones the specified components of an entity
296
/// and inserts them into another entity.
297
pub fn clone_components<B: Bundle>(target: Entity) -> impl EntityCommand {
298
move |mut entity: EntityWorldMut| {
299
entity.clone_components::<B>(target);
300
}
301
}
302
303
/// An [`EntityCommand`] moves the specified components of this entity into another entity.
304
///
305
/// Components with [`Ignore`] clone behavior will not be moved, while components that
306
/// have a [`Custom`] clone behavior will be cloned using it and then removed from the source entity.
307
/// All other components will be moved without any other special handling.
308
///
309
/// Note that this will trigger `on_remove` hooks/observers on this entity and `on_insert`/`on_add` hooks/observers on the target entity.
310
///
311
/// # Panics
312
///
313
/// The command will panic when applied if the target entity does not exist.
314
///
315
/// [`Ignore`]: crate::component::ComponentCloneBehavior::Ignore
316
/// [`Custom`]: crate::component::ComponentCloneBehavior::Custom
317
pub fn move_components<B: Bundle>(target: Entity) -> impl EntityCommand {
318
move |mut entity: EntityWorldMut| {
319
entity.move_components::<B>(target);
320
}
321
}
322
323
/// An [`EntityCommand`] that logs the components of an entity.
324
pub fn log_components() -> impl EntityCommand {
325
move |entity: EntityWorldMut| {
326
let debug_infos: Vec<_> = entity
327
.world()
328
.inspect_entity(entity.id())
329
.expect("Entity existence is verified before an EntityCommand is executed")
330
.map(ComponentInfo::name)
331
.collect();
332
info!("Entity {}: {debug_infos:?}", entity.id());
333
}
334
}
335
336