设置:带有群和鸟类的小行星克隆
我想用游戏引擎 Bevy 和物理引擎 Avian 在 Rust 中制作一个小行星克隆。我已经有了子弹和小行星的组件。它们生成时也会获得一个碰撞器。
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.),
)
);
}
(当然有一些代码,所以它们可以移动、转动等,但这与问题无关)
问题:寻找子弹和小行星之间的碰撞
实体之间的相互作用是我遇到困难的地方:我想检测小行星何时被子弹击中(这样我就可以让子弹消失并摧毁小行星,但我还不知道如何做)检测碰撞实际上非常容易,我只需监听碰撞事件即可。
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,
);
}
}
这确实按预期打印出了碰撞。但它包含了飞船与子弹、飞船与小行星之间的碰撞……这里我只得到了两个实体,没有更多信息。我该如何测试它们是否包含我想要的组件?
我想要的只是子弹和小行星之间的碰撞。
人工智能的建议
我询问了人工智能,它给出了如下建议:
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 },
);
}
}
}
我想这应该可行,但在我看来效率很低。我们已经有了事件中的实体,应该没必要再查询所有子弹和小行星来确认它们是否在查询范围内。
我正在寻找:一个简单的解决方案
我是 Rust 和 Bevy 的初学者。我有一些编程经验(主要是 Python)以及其他游戏引擎(例如 Godot),而且到目前为止,我编写的所有 Bevy 代码看起来都很优雅且模块化。这真的让我印象深刻。所以我正在寻找一个简单而优雅的解决方案。
你的方向是正确的。在Bevy中, a
Query
是在实体上查询组件的预期方式。在 avian2 文档中,它显示了一种稍微不同的方式来确定哪些实体是“无敌的”:
它使用
With
过滤器而不是实际检索Invulnerable
组件来实现此目的,如果您不需要组件数据本身,则这种方法效率会更高一些,但效果是一样的 - 您需要Query
确定组件。您会在多个地方发现这种模式,因为通用参与者(例如事件,触发器,观察者)通常只会给您一个,
Entity
并且再次需要一个Query
才能用它做更多的事情。这不是
Query
它的作用;如果你要迭代查询,它会按需执行(也就是延迟执行),没有前期成本。在这种情况下,你不是在迭代,而是使用.get()
调用来查询特定的实体。在这种情况下,它也不会加载所有匹配的实体,而是更像地图查找(即你只需为该特定实体付费)。