SuperAPI AI 工具集

bevy-game-loop-collection

编程与开发

查看项目

更新于 10-16 06:08

技能介绍

用途

本技能用于在 Bevy 0.20 中实现“收集循环”玩法:玩家或收集者拾取场景中的物体,累计分数,达到配额后触发状态切换(例如进入胜利状态)。当一次运行的核心是“拾取物体并达到某个数量”时,加载本技能。

主要能力

  • 可收集物:以标记组件(Component)表示,附带分值。
  • 收集者:以标记组件表示。
  • 配额:以资源(Resource)保存全局分数与目标配额。
  • 拾取检测:通过距离判断(在物理技能提供接触检测之前,重叠检测由使用者自行实现)。
  • 每轮每实体只计分一次:由于 Commands::despawn 是延迟执行的,同一轮中第二个收集者可能重复计分,因此使用集合记录本轮已拾取的实体。
  • 达到配额后写入 NextState,由状态调度在本系统返回后应用状态转换。

使用方式

依赖:

[dependencies]
bevy = "0.20"

核心代码结构(节选自资料):

#[derive(Component)]
struct Collectible {
    points: u32,
}

#[derive(Component)]
struct Collector;

#[derive(Resource)]
struct CollectionScore {
    points: u32,
    quota: u32,
}

#[derive(States, Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
enum RunState {
    #[default]
    Playing,
    Won,
}

fn collect_by_distance(
    mut commands: Commands,
    mut score: ResMut<CollectionScore>,
    collectors: Query<&Transform, With<Collector>>,
    pickups: Query<(Entity, &Transform, &Collectible)>,
) {
    let mut taken = std::collections::HashSet::new();
    for collector in &collectors {
        for (entity, transform, item) in &pickups {
            if taken.contains(&entity) {
                continue;
            }
            let distance = collector.translation.distance(transform.translation);
            if distance <= 1.0 {
                taken.insert(entity);
                score.points += item.points;
                commands.entity(entity).despawn();
            }
        }
    }
}

fn reach_quota(score: Res<CollectionScore>, mut next: ResMut<NextState<RunState>>) {
    if score.points >= score.quota {
        next.set(RunState::Won);
    }
}

fn spawn_pickups(mut commands: Commands) {
    commands.insert_resource(CollectionScore {
        points: 0,
        quota: 3,
    });
    commands.spawn((Collector, Transform::default()));
    for index in 0..3 {
        commands.spawn((
            Collectible { points: 1 },
            Transform::from_xyz(index as f32, 0.0, 0.0),
        ));
    }
}

pub struct CollectionPlugin;

impl Plugin for CollectionPlugin {
    fn build(&self, app: &mut App) {
        app.init_state::<RunState>()
            .add_systems(Startup, spawn_pickups)
            .add_systems(Update, (collect_by_distance, reach_quota).chain());
    }
}

资料还给出了另一种计数写法:

#[derive(Component)]
struct Remaining(u32);

#[derive(Resource, Default)]
struct Quota {
    collected: u32,
    need: u32,
}

fn tally(mut quota: ResMut<Quota>, mut counters: Query<&mut Remaining>) {
    for mut remaining in &mut counters {
        if remaining.0 > 0 {
            remaining.0 -= 1;
            quota.collected += 1;
        }
    }
}

fn quota_met(quota: Res<Quota>) -> bool {
    quota.collected >= quota.need && quota.need > 0
}

适用场景

  • 以拾取物品并达到数量为目标的运行(run)。
  • 需要全局配额资源与状态切换(如 Playing → Won)的收集类玩法。

限制与注意事项

  • 目标版本为 Bevy 0.20+。
  • 不要在两个未排序的系统中可变查询同一组件;调度默认并行运行系统,除非用 .chain() 排序。
  • 不要在同一系统中把 NextState::set 视为已生效;状态调度在本系统返回后才执行转换。
  • 不要在同一轮中让两个收集者为同一拾取物计分;despawn 是延迟的,实体仍在查询中。
  • 不要在本技能中发明物理、导航、网络或 XR crate;本技能只命名有文档的 Bevy crate 与模块。
  • 不要在 Bevy 代码中使用 Godot 的节点、资源或信号类型。
  • 不要在文档 URL 中固定 /0.20.0/;链接保持指向最新版本,0.20 目标只在正文中说明一次。
  • 消息与观察者属于 bevy-events-messaging 的职责范围,本文件不新增事件类型名。

相关技能

前置:bevy-master、bevy-ecs-composition、bevy-state-machines、bevy-data-resources、bevy-events-messaging。 互补:bevy-state-machines、godot-game-loop-collection(等效的 Godot 技能)。 主路由:bevy-master。