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
6609 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::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
entity.insert_with_caller(bundle, mode, caller, RelationshipHookMode::Run);
112
}
113
}
114
115
/// An [`EntityCommand`] that adds a dynamic component to an entity.
116
///
117
/// # Safety
118
///
119
/// - [`ComponentId`] must be from the same world as the target entity.
120
/// - `T` must have the same layout as the one passed during `component_id` creation.
121
#[track_caller]
122
pub unsafe fn insert_by_id<T: Send + 'static>(
123
component_id: ComponentId,
124
value: T,
125
mode: InsertMode,
126
) -> impl EntityCommand {
127
let caller = MaybeLocation::caller();
128
move |mut entity: EntityWorldMut| {
129
// SAFETY:
130
// - `component_id` safety is ensured by the caller
131
// - `ptr` is valid within the `make` block
132
OwningPtr::make(value, |ptr| unsafe {
133
entity.insert_by_id_with_caller(
134
component_id,
135
ptr,
136
mode,
137
caller,
138
RelationshipHookMode::Run,
139
);
140
});
141
}
142
}
143
144
/// An [`EntityCommand`] that adds a component to an entity using
145
/// the component's [`FromWorld`] implementation.
146
///
147
/// `T::from_world` will only be invoked if the component will actually be inserted.
148
/// In other words, `T::from_world` will *not* be invoked if `mode` is [`InsertMode::Keep`]
149
/// and the entity already has the component.
150
#[track_caller]
151
pub fn insert_from_world<T: Component + FromWorld>(mode: InsertMode) -> impl EntityCommand {
152
let caller = MaybeLocation::caller();
153
move |mut entity: EntityWorldMut| {
154
if !(mode == InsertMode::Keep && entity.contains::<T>()) {
155
let value = entity.world_scope(|world| T::from_world(world));
156
entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);
157
}
158
}
159
}
160
161
/// An [`EntityCommand`] that adds a component to an entity using
162
/// some function that returns the component.
163
///
164
/// The function will only be invoked if the component will actually be inserted.
165
/// In other words, the function will *not* be invoked if `mode` is [`InsertMode::Keep`]
166
/// and the entity already has the component.
167
#[track_caller]
168
pub fn insert_with<T: Component, F>(component_fn: F, mode: InsertMode) -> impl EntityCommand
169
where
170
F: FnOnce() -> T + Send + 'static,
171
{
172
let caller = MaybeLocation::caller();
173
move |mut entity: EntityWorldMut| {
174
if !(mode == InsertMode::Keep && entity.contains::<T>()) {
175
let value = component_fn();
176
entity.insert_with_caller(value, mode, caller, RelationshipHookMode::Run);
177
}
178
}
179
}
180
181
/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity.
182
#[track_caller]
183
pub fn remove<T: Bundle>() -> impl EntityCommand {
184
let caller = MaybeLocation::caller();
185
move |mut entity: EntityWorldMut| {
186
entity.remove_with_caller::<T>(caller);
187
}
188
}
189
190
/// An [`EntityCommand`] that removes the components in a [`Bundle`] from an entity,
191
/// as well as the required components for each component removed.
192
#[track_caller]
193
pub fn remove_with_requires<T: Bundle>() -> impl EntityCommand {
194
let caller = MaybeLocation::caller();
195
move |mut entity: EntityWorldMut| {
196
entity.remove_with_requires_with_caller::<T>(caller);
197
}
198
}
199
200
/// An [`EntityCommand`] that removes a dynamic component from an entity.
201
#[track_caller]
202
pub fn remove_by_id(component_id: ComponentId) -> impl EntityCommand {
203
let caller = MaybeLocation::caller();
204
move |mut entity: EntityWorldMut| {
205
entity.remove_by_id_with_caller(component_id, caller);
206
}
207
}
208
209
/// An [`EntityCommand`] that removes all components from an entity.
210
#[track_caller]
211
pub fn clear() -> impl EntityCommand {
212
let caller = MaybeLocation::caller();
213
move |mut entity: EntityWorldMut| {
214
entity.clear_with_caller(caller);
215
}
216
}
217
218
/// An [`EntityCommand`] that removes all components from an entity,
219
/// except for those in the given [`Bundle`].
220
#[track_caller]
221
pub fn retain<T: Bundle>() -> impl EntityCommand {
222
let caller = MaybeLocation::caller();
223
move |mut entity: EntityWorldMut| {
224
entity.retain_with_caller::<T>(caller);
225
}
226
}
227
228
/// An [`EntityCommand`] that despawns an entity.
229
///
230
/// # Note
231
///
232
/// This will also despawn the entities in any [`RelationshipTarget`](crate::relationship::RelationshipTarget)
233
/// that is configured to despawn descendants.
234
///
235
/// For example, this will recursively despawn [`Children`](crate::hierarchy::Children).
236
#[track_caller]
237
pub fn despawn() -> impl EntityCommand {
238
let caller = MaybeLocation::caller();
239
move |entity: EntityWorldMut| {
240
entity.despawn_with_caller(caller);
241
}
242
}
243
244
/// An [`EntityCommand`] that creates an [`Observer`](crate::observer::Observer)
245
/// watching for an [`EntityEvent`] of type `E` whose [`EntityEvent::event_target`]
246
/// targets this entity.
247
#[track_caller]
248
pub fn observe<E: EntityEvent, B: Bundle, M>(
249
observer: impl IntoObserverSystem<E, B, M>,
250
) -> impl EntityCommand {
251
let caller = MaybeLocation::caller();
252
move |mut entity: EntityWorldMut| {
253
entity.observe_with_caller(observer, caller);
254
}
255
}
256
257
/// An [`EntityCommand`] that clones parts of an entity onto another entity,
258
/// configured through [`EntityClonerBuilder`].
259
///
260
/// This builder tries to clone every component from the source entity except
261
/// for components that were explicitly denied, for example by using the
262
/// [`deny`](EntityClonerBuilder<OptOut>::deny) method.
263
///
264
/// Required components are not considered by denied components and must be
265
/// explicitly denied as well if desired.
266
pub fn clone_with_opt_out(
267
target: Entity,
268
config: impl FnOnce(&mut EntityClonerBuilder<OptOut>) + Send + Sync + 'static,
269
) -> impl EntityCommand {
270
move |mut entity: EntityWorldMut| {
271
entity.clone_with_opt_out(target, config);
272
}
273
}
274
275
/// An [`EntityCommand`] that clones parts of an entity onto another entity,
276
/// configured through [`EntityClonerBuilder`].
277
///
278
/// This builder tries to clone every component that was explicitly allowed
279
/// from the source entity, for example by using the
280
/// [`allow`](EntityClonerBuilder<OptIn>::allow) method.
281
///
282
/// Required components are also cloned when the target entity does not contain them.
283
pub fn clone_with_opt_in(
284
target: Entity,
285
config: impl FnOnce(&mut EntityClonerBuilder<OptIn>) + Send + Sync + 'static,
286
) -> impl EntityCommand {
287
move |mut entity: EntityWorldMut| {
288
entity.clone_with_opt_in(target, config);
289
}
290
}
291
292
/// An [`EntityCommand`] that clones the specified components of an entity
293
/// and inserts them into another entity.
294
pub fn clone_components<B: Bundle>(target: Entity) -> impl EntityCommand {
295
move |mut entity: EntityWorldMut| {
296
entity.clone_components::<B>(target);
297
}
298
}
299
300
/// An [`EntityCommand`] moves the specified components of this entity into another entity.
301
///
302
/// Components with [`Ignore`] clone behavior will not be moved, while components that
303
/// have a [`Custom`] clone behavior will be cloned using it and then removed from the source entity.
304
/// All other components will be moved without any other special handling.
305
///
306
/// Note that this will trigger `on_remove` hooks/observers on this entity and `on_insert`/`on_add` hooks/observers on the target entity.
307
///
308
/// # Panics
309
///
310
/// The command will panic when applied if the target entity does not exist.
311
///
312
/// [`Ignore`]: crate::component::ComponentCloneBehavior::Ignore
313
/// [`Custom`]: crate::component::ComponentCloneBehavior::Custom
314
pub fn move_components<B: Bundle>(target: Entity) -> impl EntityCommand {
315
move |mut entity: EntityWorldMut| {
316
entity.move_components::<B>(target);
317
}
318
}
319
320
/// An [`EntityCommand`] that logs the components of an entity.
321
pub fn log_components() -> impl EntityCommand {
322
move |entity: EntityWorldMut| {
323
let debug_infos: Vec<_> = entity
324
.world()
325
.inspect_entity(entity.id())
326
.expect("Entity existence is verified before an EntityCommand is executed")
327
.map(ComponentInfo::name)
328
.collect();
329
info!("Entity {}: {debug_infos:?}", entity.id());
330
}
331
}
332
333