在Bevy中实现小行星克隆游戏的碰撞检测(基于Avian物理引擎)
在Bevy中实现小行星克隆游戏的碰撞检测(基于Avian物理引擎)
嘿,我来帮你搞定用Bevy+Avian做小行星克隆的碰撞检测问题!你已经有了Bullet和Asteroid组件,还加了Collider,接下来核心就是处理它们之间的碰撞响应,我一步步给你捋清楚:
首先先修正你代码里的小疏漏——Asteroid组件定义后面少了分号,这个得补上,不然编译会报错哦。
1. 完善项目基础配置
首先要确保你的App正确加载了Avian的物理插件,并且启用了碰撞事件,这样才能监听碰撞:
use bevy::prelude::*; use avian2d::prelude::*; #[derive(Component)] struct Bullet; #[derive(Component)] struct Asteroid; // 补充分号,修正语法错误 // 可选:给子弹加个自动销毁计时器,避免内存冗余 #[derive(Component)] struct DespawnTimer(Timer); fn main() { App::new() .add_plugins(DefaultPlugins) // 加载Avian物理核心插件 .add_plugins(PhysicsPlugins::default()) // 启用碰撞事件,这是监听碰撞的前提 .add_event::<CollisionEvent>() .add_systems(Startup, setup) // 添加处理碰撞的系统 .add_systems(Update, ( handle_bullet_asteroid_collisions, despawn_expired_bullets )) .run(); }
2. 补全实体生成逻辑
在setup函数里,你需要给每个实体搭配刚体、碰撞体和必要的组件,我给你补全了示例:
fn setup( mut commands: Commands, asset_server: Res<AssetServer>, ) { // 加载纹理(替换成你实际的资源路径) let asteroid_handle = asset_server.load("asteroid.png"); let bullet_handle = asset_server.load("bullet.png"); // 生成小行星实体示例 commands.spawn(( Asteroid, SpriteBundle { texture: asteroid_handle.clone(), transform: Transform::from_xyz(200.0, 0.0, 0.0), ..default() }, RigidBody::Dynamic, // 动态刚体,允许移动和碰撞 Collider::circle(32.0), // 圆形碰撞体,半径根据纹理大小调整 Velocity::linear(Vec2::new(-100.0, 50.0)), // 给个初始速度 )); // 生成子弹实体示例 commands.spawn(( Bullet, SpriteBundle { texture: bullet_handle, transform: Transform::from_xyz(0.0, 0.0, 0.0), ..default() }, RigidBody::Dynamic, Collider::circle(8.0), Velocity::linear(Vec2::new(300.0, 0.0)), DespawnTimer(Timer::from_seconds(2.0, TimerMode::Once)), // 2秒后自动销毁 )); }
3. 核心:处理子弹与小行星的碰撞
接下来就是最关键的碰撞处理系统,我们通过EventReader<CollisionEvent>来捕获所有碰撞事件,然后筛选出子弹和小行星的碰撞并处理:
fn handle_bullet_asteroid_collisions( mut commands: Commands, mut collision_events: EventReader<CollisionEvent>, // 查询所有子弹和小行星的实体 bullets: Query<Entity, With<Bullet>>, asteroids: Query<Entity, With<Asteroid>>, ) { for event in collision_events.read() { // 只处理碰撞开始的事件,避免重复触发响应 if let CollisionEvent::Started(entity_a, entity_b, _) = event { // 判断碰撞的两个实体是否是子弹+小行星的组合 let is_target_collision = (bullets.contains(*entity_a) && asteroids.contains(*entity_b)) || (bullets.contains(*entity_b) && asteroids.contains(*entity_a)); if is_target_collision { // 销毁碰撞的子弹 if bullets.contains(*entity_a) { commands.entity(*entity_a).despawn(); } else { commands.entity(*entity_b).despawn(); } // 销毁小行星,或者扩展成分裂成更小的小行星 if asteroids.contains(*entity_a) { // 这里可以加生成小小行星的逻辑,比如: // spawn_small_asteroids(&mut commands, &asset_server, entity_a_transform); commands.entity(*entity_a).despawn(); } else { commands.entity(*entity_b).despawn(); } // 你还可以在这里加爆炸特效、播放音效之类的逻辑 } } } }
4. 子弹自动销毁逻辑
这个小系统用来自动销毁超时的子弹,避免世界里积累大量无用实体:
fn despawn_expired_bullets( mut commands: Commands, mut query: Query<(Entity, &mut DespawnTimer)>, time: Res<Time>, ) { for (entity, mut timer) in &mut query { timer.0.tick(time.delta()); if timer.0.finished() { commands.entity(entity).despawn(); } } }
一些小提示
- 碰撞体的大小要尽量和你的Sprite匹配,不然会出现“看起来没碰到但已经触发碰撞”或者反过来的情况
- 如果要做小行星分裂,只需要在销毁大 asteroid 的位置生成几个更小的 asteroid 实体,给它们不同的速度就行
- 可以给实体加
Transform查询,在碰撞时获取位置来生成爆炸特效
备注:内容来源于stack exchange,提问作者Kaligule




