Bevy 0.20 迁移指南:cursor 光标模块从 bevy_feathers 迁入 bevy_picking
2026/9/6 18:43:53 网站建设 项目流程

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

【免费下载链接】bevyA refreshingly simple>项目地址: https://gitcode.com/GitHub_Trending/be/bevy

在 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 层面的证据链如下:

  • crates/bevy_picking/Cargo.toml:custom_cursor = ["bevy_window/custom_cursor"](第 13 行),即bevy_picking的该特性现在直接转发给bevy_window
  • crates/bevy_internal/Cargo.toml:统一入口bevycustom_cursor特性(第 404-407 行)展开为三个依赖项特性:
custom_cursor = [ "bevy_window/custom_cursor", "bevy_winit/custom_cursor", "bevy_picking/custom_cursor", ]
  • crates/bevy_feathers/Cargo.toml:bevy_feathersbevy_picking的依赖已固定启用custom_cursor特性(第 26-28 行),因此使用 feathers 的EntityCursor::Custom变体无需用户再额外声明特性。

从源码结构看,这条特性链最终落到 crates/bevy_winit/src/cursor/mod.rs:bevy_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_pickingcrate(或经由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分支。

【免费下载链接】bevyA refreshingly simple>项目地址: https://gitcode.com/GitHub_Trending/be/bevy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询