Configuração: Um clone de asteroides com bando e aves
Quero criar um clone de asteroides em Rust com o motor de jogo Bevy e o motor de física Avian. Já tenho componentes para Bullets e Astroids. Quando eles são gerados, eles também recebem um Collider.
use bevy::prelude::*;
use avian2d::prelude::*;
#[derive(Component)]
struct Bullet;
#[derive(Component)]
struct Asteroid
fn setup(
) {
asteroid_handle = asset_server.load("asteroid.png");
bullet_handle = asset_server.load("bullet.png");
commands.spawn(
(
Asteroid,
Sprite::from_image(asteroid_handle),
Collider::circle(50.),
)
);
commands.spawn(
(
Bullet,
Sprite::from_image(bullet_handle),
Collider::circle(5.),
)
);
}
(É claro que há algum código para que eles se movam, virem, etc., mas isso não é relevante para a questão)
O Problema: Encontrar colisões entre balas e asteroides
As interações entre entidades são onde estou tendo dificuldades: quero detectar quando um asteroide é atingido por uma bala (para que eu possa fazer a bala desaparecer e destruir o asteroide, ainda não sei como). Detectar colisões é muito fácil, posso apenas ouvir os eventos de colisão.
fn print_collisions(mut collision_event_reader: EventReader<Collision>) {
for Collision(contacts) in collision_event_reader.read() {
println!(
"Entities {} and {} are colliding",
contacts.entity1,
contacts.entity2,
);
}
}
Isso imprime as colisões conforme o esperado. Mas inclui colisões entre a nave e a bala, a nave e os asteroides... Aqui, tudo o que obtenho são duas entidades e nenhuma informação adicional. Como posso testar se elas têm os componentes que desejo?
O que eu quero é obter apenas aquelas colisões entre uma bala e um asteroide.
O que a IA sugeriu
Perguntei à IA e ela sugeriu algo assim:
fn print_collisions(
mut collision_event_reader: EventReader<Collision>,
query: Query<(Entity, Option<&Bullet>, Option<&Asteroid>)>,
) {
for Collision(contacts) in collision_event_reader.read() {
let (entity1, bullet1, asteroid1) = query.get(contacts.entity1).unwrap_or((contacts.entity1, None, None));
let (entity2, bullet2, asteroid2) = query.get(contacts.entity2).unwrap_or((contacts.entity2, None, None));
// Check if one entity is a Bullet and the other is an Asteroid
if (bullet1.is_some() && asteroid2.is_some()) || (bullet2.is_some() && asteroid1.is_some()) {
println!(
"Bullet {} collided with Asteroid {}",
if bullet1.is_some() { entity1 } else { entity2 },
if asteroid1.is_some() { entity1 } else { entity2 },
);
}
}
}
Acho que funcionaria, mas me parece muito ineficiente. Já temos as entidades no evento, então não deveria haver necessidade de consultar todas as balas e asteroides novamente para ver se estão na consulta.
O que estou procurando: uma solução simples
Sou iniciante tanto em Rust quanto em Bevy. Tenho experiência em programação (principalmente em Python) e outras engines de jogos (como Godot), e todo o código Bevy que escrevi até agora parece elegante e modular. Isso realmente me impressiona. Por isso, estou procurando uma solução simples e elegante.