feat(ecs): add query3 and query4 for multi-component queries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-25 18:19:50 +09:00
parent 803df19305
commit 1707728094

View File

@@ -114,6 +114,119 @@ impl World {
}
result
}
pub fn query3<A: 'static, B: 'static, C: 'static>(&self) -> Vec<(Entity, &A, &B, &C)> {
let a_storage = match self.storage::<A>() {
Some(s) => s,
None => return Vec::new(),
};
let b_storage = match self.storage::<B>() {
Some(s) => s,
None => return Vec::new(),
};
let c_storage = match self.storage::<C>() {
Some(s) => s,
None => return Vec::new(),
};
// Find the smallest storage to iterate
let a_len = a_storage.len();
let b_len = b_storage.len();
let c_len = c_storage.len();
let mut result = Vec::new();
if a_len <= b_len && a_len <= c_len {
for (entity, a) in a_storage.iter() {
if let (Some(b), Some(c)) = (b_storage.get(entity), c_storage.get(entity)) {
result.push((entity, a, b, c));
}
}
} else if b_len <= a_len && b_len <= c_len {
for (entity, b) in b_storage.iter() {
if let (Some(a), Some(c)) = (a_storage.get(entity), c_storage.get(entity)) {
result.push((entity, a, b, c));
}
}
} else {
for (entity, c) in c_storage.iter() {
if let (Some(a), Some(b)) = (a_storage.get(entity), b_storage.get(entity)) {
result.push((entity, a, b, c));
}
}
}
result
}
pub fn query4<A: 'static, B: 'static, C: 'static, D: 'static>(
&self,
) -> Vec<(Entity, &A, &B, &C, &D)> {
let a_storage = match self.storage::<A>() {
Some(s) => s,
None => return Vec::new(),
};
let b_storage = match self.storage::<B>() {
Some(s) => s,
None => return Vec::new(),
};
let c_storage = match self.storage::<C>() {
Some(s) => s,
None => return Vec::new(),
};
let d_storage = match self.storage::<D>() {
Some(s) => s,
None => return Vec::new(),
};
// Find the smallest storage to iterate
let a_len = a_storage.len();
let b_len = b_storage.len();
let c_len = c_storage.len();
let d_len = d_storage.len();
let mut result = Vec::new();
if a_len <= b_len && a_len <= c_len && a_len <= d_len {
for (entity, a) in a_storage.iter() {
if let (Some(b), Some(c), Some(d)) = (
b_storage.get(entity),
c_storage.get(entity),
d_storage.get(entity),
) {
result.push((entity, a, b, c, d));
}
}
} else if b_len <= a_len && b_len <= c_len && b_len <= d_len {
for (entity, b) in b_storage.iter() {
if let (Some(a), Some(c), Some(d)) = (
a_storage.get(entity),
c_storage.get(entity),
d_storage.get(entity),
) {
result.push((entity, a, b, c, d));
}
}
} else if c_len <= a_len && c_len <= b_len && c_len <= d_len {
for (entity, c) in c_storage.iter() {
if let (Some(a), Some(b), Some(d)) = (
a_storage.get(entity),
b_storage.get(entity),
d_storage.get(entity),
) {
result.push((entity, a, b, c, d));
}
}
} else {
for (entity, d) in d_storage.iter() {
if let (Some(a), Some(b), Some(c)) = (
a_storage.get(entity),
b_storage.get(entity),
c_storage.get(entity),
) {
result.push((entity, a, b, c, d));
}
}
}
result
}
}
impl Default for World {
@@ -222,6 +335,59 @@ mod tests {
assert!(!entities.contains(&e2));
}
#[test]
fn test_query3() {
#[derive(Debug, PartialEq)]
struct Health(i32);
let mut world = World::new();
let e0 = world.spawn();
world.add(e0, Position { x: 1.0, y: 0.0 });
world.add(e0, Velocity { dx: 1.0, dy: 0.0 });
world.add(e0, Health(100));
let e1 = world.spawn();
world.add(e1, Position { x: 2.0, y: 0.0 });
world.add(e1, Velocity { dx: 2.0, dy: 0.0 });
// e1 has no Health
let results = world.query3::<Position, Velocity, Health>();
assert_eq!(results.len(), 1);
assert_eq!(results[0].0, e0);
}
#[test]
fn test_query4() {
#[derive(Debug, PartialEq)]
struct Health(i32);
#[derive(Debug, PartialEq)]
struct Tag(u8);
let mut world = World::new();
let e0 = world.spawn();
world.add(e0, Position { x: 1.0, y: 0.0 });
world.add(e0, Velocity { dx: 1.0, dy: 0.0 });
world.add(e0, Health(100));
world.add(e0, Tag(1));
let e1 = world.spawn();
world.add(e1, Position { x: 2.0, y: 0.0 });
world.add(e1, Velocity { dx: 2.0, dy: 0.0 });
world.add(e1, Health(50));
// e1 has no Tag
let e2 = world.spawn();
world.add(e2, Position { x: 3.0, y: 0.0 });
world.add(e2, Velocity { dx: 3.0, dy: 0.0 });
world.add(e2, Tag(2));
// e2 has no Health
let results = world.query4::<Position, Velocity, Health, Tag>();
assert_eq!(results.len(), 1);
assert_eq!(results[0].0, e0);
}
#[test]
fn test_entity_count() {
let mut world = World::new();