You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在Bevy中实现小行星克隆游戏的碰撞检测(基于Avian物理引擎)

在Bevy中实现小行星克隆游戏的碰撞检测(基于Avian物理引擎)

嘿,我来帮你搞定用Bevy+Avian做小行星克隆的碰撞检测问题!你已经有了BulletAsteroid组件,还加了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

火山引擎 最新活动