1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//! Grouping without bookkeeping. Tags live in the engine's entity registry,
//! survive scene round trips, and clean up on despawn, so a game never has to
//! hand maintain a `Vec<Entity>` of its coins.
use nightshade::prelude::*;
/// Tags the entity with a label. An entity can carry any number of tags.
pub fn tag(world: &mut World, entity: Entity, label: &str) {
let tags = world.resources.entities.tags.entry(entity).or_default();
if !tags.iter().any(|existing| existing == label) {
tags.push(label.to_string());
}
}
/// Removes a label from the entity.
pub fn untag(world: &mut World, entity: Entity, label: &str) {
if let Some(tags) = world.resources.entities.tags.get_mut(&entity) {
tags.retain(|existing| existing != label);
}
}
/// True when the entity carries the label.
pub fn has_tag(world: &World, entity: Entity, label: &str) -> bool {
world
.resources
.entities
.tags
.get(&entity)
.is_some_and(|tags| tags.iter().any(|existing| existing == label))
}
/// Every entity carrying the label, in no particular order.
pub fn tagged(world: &World, label: &str) -> Vec<Entity> {
world
.resources
.entities
.tags
.iter()
.filter(|(_, tags)| tags.iter().any(|existing| existing == label))
.map(|(&entity, _)| entity)
.collect()
}
/// Runs `action` for every entity carrying the label, with full world access,
/// so despawning, recoloring, or moving members from inside the closure is
/// fine.
///
/// ```ignore
/// for_each_tagged(world, "coin", |world, coin| {
/// let step = delta_time(world);
/// rotate(world, coin, Vec3::y(), step * 2.0);
/// });
/// ```
pub fn for_each_tagged(world: &mut World, label: &str, mut action: impl FnMut(&mut World, Entity)) {
let entities = tagged(world, label);
for entity in entities {
action(world, entity);
}
}
/// Visits every entity that has a transform, handing the closure its world
/// space position straight from the table, the same loop an ECS query
/// compiles to.
pub fn for_each_with_position(world: &World, mut action: impl FnMut(Entity, Vec3)) {
world
.core
.query()
.with(LOCAL_TRANSFORM | GLOBAL_TRANSFORM)
.iter(|entity, table, index| {
action(entity, table.global_transform[index].translation());
});
}