首页
/ Bevy 0.20 迁移指南:cursor 光标模块从 bevy_feathers 迁入 bevy_picking

Bevy 0.20 迁移指南:cursor 光标模块从 bevy_feathers 迁入 bevy_picking

2026-09-05 16:49:41作者:盛欣凯Ernestine

在 Bevy 当前仓库(版本 0.20.0-dev)中,bevy_featherscursor 模块已被整体迁移到 bevy_picking 之下,custom_cursor 特性也随之转移。阅读本文后,你将掌握这次迁移涉及的类型、导入路径与特性开关的具体变化,能够正确修改自己的 use 语句与 Cargo.toml,并理解 EntityCursorDefaultCursorOverrideCursorCursorIconPlugin 在新位置的实现原理:即如何根据指针悬停实体自动切换窗口鼠标光标,以及 custom_cursor 特性如何逐层打通 bevy_windowbevy_winit

迁移内容总览

根据迁移说明文档 _release-content/migration-guides/cursor_module_to_bevy_picking.md,本次迁移(对应上游 PR #25294)包含两部分变化:

  1. bevy_feathers 中的 cursor 模块——包含 EntityCursorDefaultCursorOverrideCursorCursorIconPlugin 四个公开类型——从 bevy_feathers::cursor 移动到 bevy_picking::cursor
  2. custom_cursor 特性从 bevy_feathers 迁移到 bevy_picking

最直接的改动就是导入路径:

// Before(0.20 之前)
use bevy_feathers::cursor::{CursorIconPlugin, DefaultCursor, EntityCursor, OverrideCursor};
// After(当前仓库版本)
use bevy_picking::cursor::{CursorIconPlugin, DefaultCursor, EntityCursor, OverrideCursor};

模块本身在 crates/bevy_picking/src/lib.rs 中以 pub mod cursor; 的形式公开(见第 160 行),实现代码位于 crates/bevy_picking/src/cursor.rs

四个类型的职责与实现

DefaultCursor:无悬停时的回退光标

DefaultCursor 是一个资源(Resource),指定当鼠标没有悬停在任何带光标设置的实体上时,窗口使用的默认光标图标。其实现是一个对 EntityCursor 的透明包装:

/// A resource that specifies the cursor icon to be used when the mouse is not hovering over
/// any other entity.
#[derive(Deref, Resource, Debug, Clone, Default, Reflect)]
#[reflect(Resource, Debug, Default)]
pub struct DefaultCursor(pub EntityCursor);

它支持 Deref,因此 Res<DefaultCursor> 可以直接按 EntityCursor 使用;同时派生了 Reflect,可参与 ECS 反射体系。

EntityCursor:悬停实体的光标形状

EntityCursor 是组件(Component),插入到实体上后,当指针悬停该实体时,窗口光标会被设置为该值。它是一个枚举:

#[derive(Component, Debug, Clone, Reflect, PartialEq, Eq, FromTemplate)]
#[reflect(Component, Debug, Default, PartialEq, Clone)]
pub enum EntityCursor {
    #[cfg(feature = "custom_cursor")]
    /// Custom cursor image.
    Custom(CustomCursor),
    #[default]
    /// System provided cursor icon.
    System(SystemCursorIcon),
}
  • EntityCursor::Custom(CustomCursor):仅在 custom_cursor 特性开启时存在,允许使用自定义光标图片(CustomCursor 来自 bevy_window);
  • EntityCursor::System(SystemCursorIcon):使用系统提供的标准光标(箭头、等待、文本框等),且是该枚举的 #[default]

该类型还提供两个转换方法(见 cursor.rs 第 54-75 行):

  • to_cursor_icon():把 EntityCursor 转为 bevy_window::CursorIcon,以便插入窗口实体;
  • eq_cursor_icon():比较当前值与窗口已有的 CursorIcon 是否一致,用于避免每帧重复写入窗口组件。源码中的注释特别解释了它的实现动机:当 bevy_feathers 未启用 custom_cursor 时,无法静态判断 bevy_window 侧是否启用了该特性,因此借助 cursor_icon.as_system() 包装函数,让 bevy_window 自行按自身特性决定比较逻辑,从而在所有特性组合下都能编译通过且无不可达分支。

OverrideCursor:全局覆盖

OverrideCursor 是另一个资源,内部为 Option<EntityCursor>

/// A resource used to override any [`EntityCursor`] cursor changes.
/// This is meant for cases like loading where you don't want the cursor to imply
/// you can interact with something.
#[derive(Deref, Resource, Debug, Clone, Default, Reflect)]
pub struct OverrideCursor(pub Option<EntityCursor>);

它的用途是全局压过任何实体级的 EntityCursor——典型场景是加载期间强制显示等待光标,避免误导用户以为可以交互。

CursorIconPlugin 与 update_cursor 系统

CursorIconPlugin 是入口插件,其 build 方法做了两件事(见 cursor.rs 第 124-131 行):

  1. 若资源尚未初始化,则 init_resource::<DefaultCursor>()init_resource::<OverrideCursor>()
  2. update_cursor 系统注册到 PreUpdate 调度,并放入 PickingSystems::Last 系统集。

update_cursor 系统的决策逻辑是理解整条链路的钥匙:

let cursor = r_override_cursor.0.as_ref().unwrap_or_else(|| {
    hover_map
        .and_then(|hover_map| match hover_map.get(&PointerId::Mouse))
        ...
        .unwrap_or(&r_default_cursor)
});

优先级为:

  1. OverrideCursor 有值时,直接使用它;
  2. 否则查询 HoverMap 中鼠标(PointerId::Mouse)悬停的实体集合,逐一查找带 EntityCursor 组件的实体(排除 Window 实体),并沿 ChildOf 父链向上回溯(parent_query.iter_ancestors),子实体未设置时继承祖先的光标;
  3. 都找不到时回落到 DefaultCursor 资源。

确定目标光标后,系统遍历所有带 Window 组件的实体,若窗口当前的 CursorIcon 与新值不相等(eq_cursor_icon 判断),才执行 commands.entity(entity).insert(cursor.to_cursor_icon()),天然支持多窗口场景并避免无谓写入。

值得注意的运行时机:系统挂在 PickingSystems::Last,即 PreUpdate 中所有 picking 系统集(ProcessInputBackendHoverPostHoverLast,定义于 lib.rs 第 259-277 行)之后——此时 HoverMap 已由本帧的悬停计算更新完毕,光标切换始终基于最新悬停状态。

custom_cursor 特性的迁移路径

特性迁移在 Cargo 层面的证据链如下:

custom_cursor = [
  "bevy_window/custom_cursor",
  "bevy_winit/custom_cursor",
  "bevy_picking/custom_cursor",
]
  • crates/bevy_feathers/Cargo.tomlbevy_feathersbevy_picking 的依赖已固定启用 custom_cursor 特性(第 26-28 行),因此使用 feathers 的 EntityCursor::Custom 变体无需用户再额外声明特性。

从源码结构看,这条特性链最终落到 crates/bevy_winit/src/cursor/mod.rsbevy_winitcustom_cursor 开启时才会编译自定义光标模块、维护 WinitCustomCursorCache 光标缓存,并在渲染循环中通过 event_loop.create_custom_cursor(cursor) 创建 winit 层的自定义光标。也就是说,光标的“决策”(Bevy 侧)与“落地”(winit 侧)被特性开关严格对齐。

bevy_feathers 自身也已完成内部切换:crates/bevy_feathers/src/lib.rs 第 30 行改为 use bevy_picking::cursor::{CursorIconPlugin, DefaultCursor, EntityCursor};FeathersCorePlugin::build 中直接注册 CursorIconPlugin(第 79 行),并插入默认值 DefaultCursor(EntityCursor::System(SystemCursorIcon::Default))(第 98-100 行)。由于旧路径 bevy_feathers::cursor 已删除,第三方 crate 若仍按旧路径导入将直接编译失败,必须按前文 “After” 代码更新导入。

实际用例验证

仓库内的示例 examples/ui/widgets/feathers_gallery.rs 展示了迁移后 API 的典型用法:通过 picking::cursor::{EntityCursor, OverrideCursor} 导入类型,并在加载中把覆盖光标设为系统等待光标:

Some(EntityCursor::System(SystemCursorIcon::Wait))

配合 OverrideCursor 资源即可实现“加载时禁用交互暗示”的效果,与源码文档注释中的设计意图一致。

迁移检查清单

针对升级 0.20.0-dev 的项目,建议按以下顺序核对:

  1. 全局替换导入路径:把 bevy_feathers::cursor::{...} 替换为 bevy_picking::cursor::{...},涉及 EntityCursorDefaultCursorOverrideCursorCursorIconPlugin 四个类型;
  2. 更新特性声明:如果 Cargo.toml 中对 bevybevy_feathers 启用了 custom_cursor,确认改为/补充启用 bevy_picking/custom_cursor(或直接依赖统一的 bevy 入口特性,由 bevy_internal 转发);
  3. 依赖可达性:直接使用 bevy_picking::cursor 需要项目依赖 bevy_picking crate(或经由 bevy 入口与 bevy_picking 特性启用,见 crates/bevy_internal/Cargo.toml 第 352 行 bevy_picking = ["dep:bevy_picking"]);
  4. 行为不变确认:迁移只改变了模块归属与特性位置,update_cursor 的优先级逻辑(Override → 悬停实体/祖先链 → Default)、多窗口写入与变化检测行为均保持原样。

适用前提说明:以上结论均基于当前仓库 0.20.0-dev 版本源码与迁移文档;EntityCursor::Custom 变体仅在 custom_cursor 特性开启时存在,未启用该特性的代码应只使用 EntityCursor::System 分支。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384