SuperAPI AI 工具集

bevy-inventory-system

编程与开发

查看项目

更新于 10-16 06:08

技能介绍

用途

本技能用于在 Bevy 引擎的 ECS 架构下实现背包/物品栏系统,核心场景是物品堆叠(stack)在容器(container)之间移动。当堆叠需要在容器间转移时加载本技能。UI 网格布局仍由 bevy-ui-layout 负责,经济价格仍由 bevy-economy-system 负责。

主要能力

  • 定义物品 ID 与堆叠数量组件。
  • 以实体(entity)表示拥有堆叠的容器。
  • 通过消息(MoveStack)发起移动请求,使单个系统能够同时可变借用两个背包。
  • 提供装备(Equip)标记组件;属性变化交由 bevy-rpg-stats 处理。
  • 提供添加堆叠、转移堆叠、装备首个物品、生成背包等示例代码。

ECS 结构

  • 组件(Components):由使用者定义的物品 ID 与堆叠数量。
  • 资源(Resources):运行期全局表存放在资源中(bevy-data-resources)。
  • 系统(Systems):在 Update 或固定时间步(fixed timestep)上修改这些组件;当伤害必须先于死亡结算时使用 .chain()。
  • 消息(Messages):一次性事实(命中、拾取、任务推进)通过 bevy-events-messaging 传递;本文件不新增事件类型名称。

所有权划分

  • 堆叠(Stack):组件上的物品 ID 加数量,或资源背包内部。
  • 容器(Container):拥有堆叠的实体。
  • 装备(Equip):一个标记组件;属性变化走 bevy-rpg-stats。

范围

物品 ID、堆叠数量与容器都是组件。移动请求是消息,因此一个系统即可写入两个背包。槽位控件留在 bevy-ui-layout,价格留在 bevy-economy-system。

使用方式(示例代码)

依赖声明:

[package]
name = "inventory"
version = "0.1.0"
edition = "2021"

[dependencies]
bevy = "0.20"

堆叠存放在容器实体上,移动通过消息实现,使单个查询能同时可变借用两个背包:

#[derive(Clone, Copy, PartialEq, Eq)]
struct ItemId(u32);

#[derive(Clone, Copy)]
struct Stack {
    item: ItemId,
    count: u32,
}

#[derive(Component, Default)]
struct Inventory {
    slots: Vec<Stack>,
}

#[derive(Message)]
struct MoveStack {
    from: Entity,
    to: Entity,
    slot: usize,
    count: u32,
}

fn add_stack(inv: &mut Inventory, item: ItemId, mut count: u32) {
    for stack in &mut inv.slots {
        if stack.item == item {
            stack.count += count;
            return;
        }
    }
    if count > 0 {
        inv.slots.push(Stack { item, count });
    }
}

fn transfer(mut requests: MessageReader<MoveStack>, mut bags: Query<&mut Inventory>) {
    for req in requests.read() {
        if req.from == req.to || req.count == 0 {
            continue;
        }
        let Ok([mut from, mut to]) = bags.get_many_mut([req.from, req.to]) else {
            continue;
        };
        let Some(stack) = from.slots.get_mut(req.slot) else {
            continue;
        };
        if stack.count < req.count {
            continue;
        }
        let item = stack.item;
        stack.count -= req.count;
        let moved = req.count;
        if stack.count == 0 {
            from.slots.remove(req.slot);
        }
        add_stack(&mut to, item, moved);
    }
}

pub struct InventoryPlugin;

impl Plugin for InventoryPlugin {
    fn build(&self, app: &mut App) {
        app.add_message::<MoveStack>()
            .add_systems(Update, transfer);
    }
}

装备会将堆叠复制到标记上,剩余部分留在背包中;属性变化仍由 bevy-rpg-stats 负责:

#[derive(Clone, Copy, PartialEq, Eq)]
struct ItemId(u32);

#[derive(Component)]
struct Equipped {
    item: ItemId,
}

#[derive(Component, Default)]
struct Inventory {
    slots: Vec<(ItemId, u32)>,
}

fn equip_first(mut commands: Commands, actors: Query<(Entity, &Inventory), Without<Equipped>>) {
    for (entity, bag) in &actors {
        let Some((item, count)) = bag.slots.first().copied() else {
            continue;
        };
        if count == 0 {
            continue;
        }
        commands.entity(entity).insert(Equipped { item });
    }
}

生成一个箱子和一个带有初始堆叠的玩家背包:

#[derive(Clone, Copy, PartialEq, Eq)]
struct ItemId(u32);

#[derive(Clone, Copy)]
struct Stack {
    item: ItemId,
    count: u32,
}

#[derive(Component)]
struct Inventory {
    slots: Vec<Stack>,
}

fn spawn_bags(mut commands: Commands) {
    commands.spawn(Inventory {
        slots: vec![Stack {
            item: ItemId(1),
            count: 8,
        }],
    });
    commands.spawn(Inventory { slots: Vec::new() });
}

专家知识要点

目标版本:Bevy 0.20+。

核心 ECS 类型:Component、Resource、Query、Commands、Startup、Update、Fixed / FixedMain(bevy::time)、.chain()、Plugin、DefaultPlugins、MinimalPlugins。消息与观察者属于所有权槽位,其 API 参见 bevy-events-messaging。除非用 .chain() 对冲突系统排序,否则系统并行运行。

状态所有权:

  • 单个实体上的数据:组件,由单个系统通过 Query 修改,运行于 Update 或 FixedMain。
  • 整个应用的一个值:资源,由单个系统修改,Startup 插入,随后由读取它的调度使用。
  • 生成:Commands,由生成系统负责;初始世界用 Startup,运行时生成用后续调度。
  • 不可竞态的顺序:.chain(),仅作用于被链式连接的系统,同一调度内。
  • 插件边界:Plugin,build 注册系统与资源,在应用运行前。

借用规则:两个都可变查询同一组件的系统必须排序(.chain())或拆分,使只有一个写入。无关系统保持并行。

决策表:

  • 需要逐实体数据:用组件 + Query,不要用组件之外的字段。
  • 需要应用级配置或时钟:用资源,不要复制到每个实体。
  • 需要稳定模拟步长:用 FixedMain 与 Fixed 时钟,不要把模拟绑定到原始帧 Update。
  • 需要启动生成:在 Startup 中用 Commands,不要从渲染系统生成。
  • 需要非共享状态的跨系统通知:用 bevy-events-messaging 中的消息或观察者槽位,不要在本文件中新造事件类型。
  • 需要堆叠与移动:用 Inventory 加 MoveStack 消息,不要让两个系统各写一个背包。

限制与禁止事项

  • 不要在文档 URL 中固定 /0.20.0/;链接保持指向最新版,0.20 目标只在正文中说明一次。
  • 不要从两个未排序的系统可变查询同一组件,因为除非用 .chain() 排序,调度会并行运行系统。
  • 不要在此发明物理、导航、网络或 XR crate,本技能只命名有文档的 Bevy crate 与模块。
  • 不要在 Bevy 代码中使用其他引擎的类型。
  • 不要发明 API 或把未命名的第三方 crate 作为选定求解器。

适用场景

适用于 Bevy 0.20+ 游戏中需要在容器之间转移物品堆叠、管理背包槽位、实现装备标记的场景。

相关技能

前置:bevy-master(枢纽与调度)、bevy-ecs-composition(组件与系统)、bevy-rpg-stats(装备加成)、bevy-ui-layout(槽位布局)、bevy-data-resources(表与定义)、bevy-events-messaging(一次性事实)。

互补:bevy-state-machines(模式标志)。

下游/消费者:暂无。

主技能:bevy-master(Bevy 路由)。