当前位置: 首页 > article >正文

Bevy引擎交互拾取系统:bevy_mod_picking插件核心原理与实战

1. 项目概述一个为Bevy游戏引擎量身定制的交互拾取系统如果你正在用Bevy引擎开发游戏或交互式应用并且被“如何让鼠标点击选中那个3D模型”或者“怎么实现UI元素的高亮反馈”这类问题困扰过那么bevy_mod_picking这个社区插件很可能就是你一直在找的答案。它不是Bevy官方内置的功能却因其设计精良、功能强大几乎成了Bevy生态中处理拾取Picking交互事实上的标准解决方案。简单来说bevy_mod_picking为Bevy引擎补全了核心的交互能力。在复杂的3D场景中“拾取”指的是将屏幕上鼠标的2D坐标通过一系列数学计算反向投射到3D世界精确判断出用户到底想点击哪个实体Entity。这个过程涉及摄像机矩阵、射线投射、网格碰撞检测等多个环节自己从头实现不仅繁琐而且极易出错。bevy_mod_picking将这些底层复杂性全部封装起来提供了一套声明式的、易于使用的API。开发者只需给想要交互的实体添加几个组件Component比如PickableBundle就能立刻获得点击、悬停、拖拽等丰富的交互事件从而将精力集中在游戏逻辑本身而不是底层交互基础设施的搭建上。这个插件由社区开发者aevyrie创建并维护它紧跟Bevy主版本的迭代架构设计也深得Bevy ECS实体组件系统哲学的精髓——数据驱动、高效、可组合。无论你是想做一个点击单位释放技能的RTS游戏一个拖拽旋转查看的3D模型查看器还是一个拥有复杂界面交互的编辑器bevy_mod_picking都能提供坚实可靠的支撑。接下来我将从设计思路、核心功能、集成实操到避坑技巧为你完整拆解这个强大工具的使用之道。2. 核心设计哲学与架构解析2.1 为什么Bevy需要独立的拾取插件Bevy引擎本身是一个极简、模块化的ECS框架它提供了强大的渲染、音频、物理等基础系统但有意不包含一些更上层的、应用特定的功能比如复杂的UI控件树早期版本和高级场景拾取。这种设计让引擎核心保持轻量和灵活将具体功能的实现交给社区生态。拾取功能尤其是支持网格Mesh精确碰撞、透明物体处理、多视口等复杂场景的拾取就是一个典型的“应用层需求”。自己实现一个健壮的拾取系统需要考虑射线生成从鼠标的屏幕坐标2D和摄像机参数投影矩阵、视图矩阵生成一条3D空间射线。碰撞检测将这条射线与场景中所有可能被拾取的实体的包围盒Bounding Box或精确网格Mesh进行相交测试。这需要访问实体的GlobalTransform和HandleMesh。事件排序与派发当多条射线与多个实体相交时需要根据深度Z值排序找出最靠近摄像机的那个“赢家”并产生相应的事件如点击、悬停。性能优化避免每一帧都对所有实体进行昂贵的精确碰撞检测通常需要分层级的加速结构如先进行快速的包围盒测试进行粗筛。bevy_mod_picking将这些通用且复杂的逻辑打包成一个独立的、可插拔的插件Plugin通过Bevy的ECS系统优雅地集成。你只需要在App中.add_plugins(DefaultPickingPlugins)它就为你构建好了整个事件生产、分发、处理的流水线。2.2 插件化、模块化与数据驱动架构bevy_mod_picking的架构深刻体现了Bevy的设计思想。它不是一个庞大的、 monolithic 的黑盒而是一组细粒度的插件集合核心插件PickingPlugin提供最基础的射线投射和碰撞检测功能。它定义了核心的Pickable组件、PickSource如鼠标、触摸和PickRay计算出的射线。交互插件如InteractablePickingPlugin在核心拾取的基础上添加了高层次的交互状态管理。它会自动为被拾取的实体更新Interaction状态如None,Hovered,Pressed这个状态可以直接被你的其他系统或UI样式系统读取用于改变颜色、大小等实现视觉反馈。高亮插件如HighlightablePickingPlugin专门用于处理拾取高亮。它允许你定义多种高亮样式如边框、颜色叠加、描边并在实体被悬停或选中时自动切换材质效果非常炫酷且配置灵活。后端插件拾取需要知道物体的几何信息来进行碰撞检测。bevy_mod_picking支持不同的后端bevy_picking_raycast: 最常用、最稳定的后端使用射线投射raycasting与实体的轴对齐包围盒AABB或精确网格进行碰撞检测。bevy_picking_sprite: 专用于2D精灵Sprite的拾取效率更高。这种设计让你可以根据项目类型3D/2D选择最合适的后端甚至在未来可以接入其他物理引擎的后端。这种模块化意味着你可以按需取用。如果你只需要基本的点击检测只加核心和射线后端插件即可。如果你需要完整的交互反馈链再把交互和高亮插件加上。所有功能都是通过向实体添加或组合特定的组件来驱动的这正是ECS“数据驱动”的典范。例如让一个3D模型可被点击并高亮你只需commands.spawn(( PbrBundle { /* ... 你的模型、材质、变换 ... */ }, PickableBundle::default(), // 使其可被拾取 RaycastPickTarget::default(), // 使用射线后端进行检测 Highlighting { // 配置高亮样式 hovered: HighlightKind::new_dynamic(|_| Highlight::new(Color::rgba(1.0, 0.5, 0.0, 0.2))), pressed: HighlightKind::new_dynamic(|_| Highlight::new(Color::rgba(1.0, 0.0, 0.0, 0.3))), selected: HighlightKind::new_dynamic(|_| Highlight::new(Color::rgba(0.0, 1.0, 0.0, 0.2))), }, ));代码清晰表达了意图所有魔法都在插件系统中自动完成。3. 核心功能深度解析与配置要点3.1 拾取源PickSource与射线PickRay拾取的起点是输入设备。插件将鼠标、触摸屏甚至游戏手柄都抽象为PickSource。最常用的是Mouse源。PickRay系统会读取PickSource的屏幕坐标和当前活动摄像机的变换矩阵实时计算出一条从摄像机近平面穿过鼠标位置指向远方的世界空间射线。注意确保你的拾取目标实体所在的摄像机与PickSource使用的Camera是同一个。插件默认使用标记了PrimaryWindow的窗口中的主摄像机。如果你的场景有多个视口或UI摄像机需要仔细配置PickSource的target字段将其指向正确的摄像机实体否则拾取会失效。3.2 可拾取性Pickable与碰撞目标RaycastPickTargetPickable组件是一个标记组件表示该实体可以参与拾取交互。它包含一些重要属性should_block_lower: 布尔值。如果为true默认当该实体被成功拾取后射线将停止传播下层的实体不会被检测到。这模拟了“最上层物体遮挡下层物体”的效果对于UI和场景分层至关重要。is_hoverable,is_selectable: 控制该实体是否响应悬停或选择事件。RaycastPickTarget是用于bevy_picking_raycast后端的组件。它告诉射线检测系统“请用射线来检测我”。你可以为其指定碰撞层级Filter实现例如“只能点击敌人单位不能点击地面”这样的过滤逻辑。3.3 交互状态Interaction与事件PickingEvent这是插件最强大的抽象之一。当拾取发生后插件会更新实体的Interaction状态一个枚举包含None,Hovered,Pressed。你不需要手动监听鼠标事件然后去计算谁被点了只需要在你的系统中查询具有特定Interaction状态的实体即可。fn button_system( mut query: Query(Interaction, mut BackgroundColor), (ChangedInteraction, WithButton), ) { for (interaction, mut color) in mut query { match *interaction { Interaction::Pressed *color Color::RED.into(), Interaction::Hovered *color Color::YELLOW.into(), Interaction::None *color Color::WHITE.into(), } } }除了状态插件还会发送详细的PickingEvent事件如Click,DragStart,DragEnd,Over,Out等。这些事件包含了触发事件的实体、拾取源、碰撞点坐标等丰富信息适合处理需要精确时机和数据的逻辑比如点击时播放音效、拖拽时更新物体位置。3.4 高亮系统Highlighting详解高亮插件提供了一套非侵入式的、基于材质的视觉反馈系统。它的工作原理是为需要高亮的实体动态插入一个“高亮材质”覆盖层。根据实体的Interaction状态悬停、按下、选中自动切换到对应的高亮材质。当状态恢复时自动移除或还原原始材质。配置高亮的关键是Highlighting组件。它允许你为不同状态定义不同的HighlightKindHighlightKind::Static: 使用一个预定义的材质句柄。HighlightKind::Dynamic: 通过一个闭包closure动态生成材质闭包能接收到原始实体的材质信息方便你基于原材质做调整如改变颜色、增加发光。这是最灵活的方式。Highlighting { hovered: HighlightKind::new_dynamic(|material| { // material 是实体原来的材质 // 我们创建一个新的高亮材质比如半透明橙色叠加 Highlight { outline: None, // 也可以配置轮廓线 overlay: Some(Color::rgba(1.0, 0.5, 0.0, 0.3).into()), } }), // ... 配置 pressed 和 selected },实操心得高亮系统默认会克隆一份原始材质进行修改。对于自定义的复杂材质尤其是CustomMaterial务必确保其实现了Clonetrait并且高亮逻辑能正确处理你的材质属性否则可能导致panic或渲染错误。对于简单项目使用内置的StandardMaterial通常没有问题。4. 完整集成流程与核心环节实现4.1 环境准备与依赖添加首先在你的Cargo.toml中添加依赖。由于插件模块化你需要根据需求选择功能。一个典型的3D项目配置如下[dependencies] bevy 0.12 # 请使用与插件兼容的Bevy版本 bevy_mod_picking { version 0.14, features [default, highlight, backend_raycast] }default: 包含核心和交互插件。highlight: 启用高亮功能。backend_raycast: 启用3D射线拾取后端。4.2 插件初始化与系统配置在main.rs或你的应用设置函数中将所需的插件添加到App中use bevy::prelude::*; use bevy_mod_picking::prelude::*; fn main() { App::new() .add_plugins(DefaultPlugins) // 添加拾取插件组。这会自动添加核心、交互、以及你启用的后端和高亮插件。 .add_plugins(DefaultPickingPlugins) // 如果你有自定义的高亮材质或需要精细控制可以单独添加插件 // .add_plugins(PickingPlugin) // .add_plugins(InteractablePickingPlugin) // .add_plugins(HighlightablePickingPlugin::MyCustomMaterial::default()) // .add_plugins(RaycastPickingPlugin) .add_systems(Startup, setup_scene) .add_systems(Update, (handle_pick_events, update_highlighted_objects)) .run(); }4.3 场景搭建使物体可交互在setup_scene系统中生成你的3D模型或UI元素并为其附加必要的拾取组件。fn setup_scene( mut commands: Commands, mut meshes: ResMutAssetsMesh, mut materials: ResMutAssetsStandardMaterial, ) { // 生成一个地面通常不可拾取或用于射线终点检测 commands.spawn(PbrBundle { mesh: meshes.add(Plane3d::default().mesh().size(10.0, 10.0)), material: materials.add(Color::srgb(0.3, 0.5, 0.3)), ..default() }); // 生成一个可交互的立方体 let cube_entity commands.spawn(( PbrBundle { mesh: meshes.add(Cuboid::default()), material: materials.add(Color::srgb(0.8, 0.7, 0.6)), transform: Transform::from_xyz(0.0, 0.5, 0.0), ..default() }, // 核心使其可被拾取和交互 PickableBundle::default(), // 指定使用射线检测后端 RaycastPickTarget::default(), // 添加高亮配置 Highlighting { hovered: HighlightKind::new_dynamic(|_| Highlight { overlay: Some(Color::rgba(0.2, 0.8, 0.2, 0.3).into()), // 悬停时绿色半透明覆盖 ..default() }), pressed: HighlightKind::new_dynamic(|_| Highlight { overlay: Some(Color::rgba(0.8, 0.2, 0.2, 0.5).into()), // 按下时红色半透明覆盖 ..default() }), ..default() }, // 可以添加一个自定义组件用于业务逻辑识别 Name::new(My Interactive Cube), )).id(); // 生成摄像机 commands.spawn(( Camera3dBundle { transform: Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y), ..default() }, // 摄像机也需要标记以作为拾取射线的来源 RaycastPickCamera::default(), )); // 生成光源 commands.spawn(DirectionalLightBundle { directional_light: DirectionalLight { shadows_enabled: true, ..default() }, transform: Transform::from_xyz(3.0, 10.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y), ..default() }); }4.4 业务逻辑处理响应交互现在物体已经可以交互并有视觉反馈了。接下来在你的游戏逻辑系统中响应这些交互。方式一查询Interaction状态推荐用于持续状态响应fn handle_object_interaction( mut query: Query(Interaction, mut Transform, Name), (ChangedInteraction, WithPickable), ) { for (interaction, mut transform, name) in mut query { match interaction { Interaction::Pressed { info!(实体 {:?} 被按下了, name); // 例如按下时稍微放大 transform.scale Vec3::splat(1.1); } Interaction::Hovered { // 悬停时可能播放一个微妙的动画或声音 } Interaction::None { // 恢复原状 transform.scale Vec3::splat(1.0); } } } }方式二监听PickingEvent事件推荐用于瞬时动作fn handle_click_events( mut click_events: EventReaderPickingEvent, mut commands: Commands, asset_server: ResAssetServer, ) { for event in click_events.read() { if let PickingEvent::Clicked(e) event { info!(检测到点击事件目标实体: {:?}, e); // 例如点击时在点击位置生成一个特效 // 注意事件中可能包含碰撞点世界坐标可用于精确定位 // let hit_data event.hit_data(); // if let Some(hit) hit_data.first() { // let pos hit.position(); // spawn_effect_at(mut commands, asset_server, pos); // } } // 还可以处理 DragStart, DragEnd, Over, Out 等事件 if let PickingEvent::DragStart(e) event { info!(开始拖拽实体: {:?}, e); } } }5. 高级用法与性能优化策略5.1 拾取层级Picking Layers与过滤在复杂场景中你可能不希望所有可拾取物体都响应同一种输入。例如UI按钮应该优先于3D场景中的物体被点击或者编辑器中你只想选中“可编辑”层级的物体。bevy_mod_picking通过拾取层级来实现这一点。每个PickSource如鼠标和RaycastPickTarget都可以被分配到一个或多个层级中。只有当源和目标的层级有交集时拾取才会发生。// 定义层级常量使用位掩码 const LAYER_UI: u32 0b0001; const LAYER_WORLD: u32 0b0010; const LAYER_EDITABLE: u32 0b0100; // 为UI摄像机用于渲染UI的拾取源设置层级 commands.spawn(( Camera2dBundle::default(), // 这个摄像机产生的拾取射线只与UI层级的物体交互 RaycastPickCamera::default().with_layers(PickingLayers::from_bits(LAYER_UI)), )); // 为3D主摄像机的拾取源设置层级 commands.spawn(( Camera3dBundle::default(), // 这个摄像机产生的拾取射线可以与世界和可编辑层级的物体交互 RaycastPickCamera::default().with_layers(PickingLayers::from_bits(LAYER_WORLD | LAYER_EDITABLE)), )); // 一个UI按钮只属于UI层级 commands.spawn(( ButtonBundle { /* ... */ }, PickableBundle::default(), RaycastPickTarget::default().with_layers(PickingLayers::from_bits(LAYER_UI)), )); // 一个普通的3D物体属于世界层级 commands.spawn(( PbrBundle { /* ... */ }, PickableBundle::default(), RaycastPickTarget::default().with_layers(PickingLayers::from_bits(LAYER_WORLD)), )); // 一个可编辑的特殊物体同时属于世界和可编辑层级 commands.spawn(( PbrBundle { /* ... */ }, PickableBundle::default(), RaycastPickTarget::default().with_layers(PickingLayers::from_bits(LAYER_WORLD | LAYER_EDITABLE)), ));这样当你在UI上点击时只会触发UI按钮的交互即使鼠标在3D物体上方而用3D摄像机视图点击时可以选中普通物体和可编辑物体并且你可以通过额外的系统过滤只对LAYER_EDITABLE层级的点击做出特殊响应如进入编辑模式。5.2 自定义高亮材质与效果对于追求独特视觉风格的项目默认的Highlight颜色覆盖、轮廓可能不够。你可以定义自己的材质类型并为其实现高亮逻辑。首先你需要创建一个自定义材质并为其实现HighlightEffecttrait。这个trait定义了如何从原始材质生成高亮材质。#[derive(Asset, AsBindGroup, Reflect, Debug, Clone)] struct MyCustomMaterial { #[uniform(0)] base_color: Color, #[uniform(1)] emissive_strength: f32, // ... 其他字段 } // 为你的材质实现 HighlightEffect impl HighlightEffect for MyCustomMaterial { type Marker (); // 通常使用单元类型作为标记 fn apply_highlight(self, highlight: Highlight) - Self { let mut highlighted self.clone(); // 根据传入的highlight配置修改材质属性 if let Some(overlay_color) highlight.overlay { // 例如将高亮颜色以某种方式混合到基础色中 highlighted.base_color highlighted.base_color * 0.7 overlay_color.color * 0.3; } if highlight.outline.is_some() { // 处理轮廓效果可能需要更复杂的着色器逻辑 highlighted.emissive_strength 0.5; // 简单示例增加自发光强度模拟轮廓光 } highlighted } }然后在添加插件时指定你的材质类型.add_plugins(HighlightablePickingPlugin::MyCustomMaterial::default())现在使用MyCustomMaterial的实体就可以使用高亮系统了并且高亮效果会按照你定义的apply_highlight逻辑来呈现。5.3 性能考量与调试拾取尤其是精确的网格射线检测是一个计算密集型操作。在物体数量很多成千上万的场景中每一帧都对所有物体进行检测是不现实的。优化策略使用包围盒AABB进行粗检测bevy_picking_raycast后端默认首先使用实体的轴对齐包围盒进行快速相交测试。只有通过包围盒测试的实体才会进行更昂贵的精确网格测试。确保你的复杂模型有合理的包围盒Bevy在加载模型时会自动计算。按需拾取不是所有实体都需要每帧参与拾取。你可以通过系统动态地添加或移除Pickable和RaycastPickTarget组件。例如当物体在视野外或被遮挡时禁用其拾取功能。分层级管理结合拾取层级可以大幅减少每帧需要检测的物体数量。例如在游戏运行时可以禁用所有属于“编辑器”层级的物体的拾取。减少精确检测对于大量的小物体或远处物体可以只使用包围盒检测而不进行精确网格检测。可以通过配置RaycastPickTarget来实现。调试技巧当拾取行为不符合预期时可以启用插件的调试功能如果插件提供了调试视图或者自己写简单的调试系统fn debug_picking( pick_source: QueryPickSource, WithMouse, raycast: QueryRaycastPickSource, ) { for source in pick_source { println!(鼠标位置: {:?}, source.cursor_position()); } for ray in raycast { if let Some(ray) ray.ray() { println!(拾取射线原点: {:?}, 方向: {:?}, ray.origin, ray.direction); } } } // 或者监听所有拾取事件 fn log_all_picking_events(mut events: EventReaderPickingEvent) { for event in events.read() { println!(拾取事件: {:?}, event); } }检查鼠标坐标是否正确、射线是否生成、以及事件是否被触发是排查问题的第一步。最常见的问题是摄像机RaycastPickCamera组件缺失或配置错误导致射线无法正确生成。6. 常见问题与排查技巧实录在实际项目集成bevy_mod_picking的过程中你几乎一定会遇到下面这些问题。这里我把踩过的坑和解决方案整理出来希望能帮你节省大量调试时间。6.1 问题点击没有任何反应实体没有高亮也没有事件产生。排查步骤检查插件是否添加确认App中正确添加了DefaultPickingPlugins或所需的最小插件集合至少包含PickingPlugin和一个后端插件如RaycastPickingPlugin。检查摄像机确保你希望用来拾取的摄像机实体上添加了RaycastPickCamera组件。这是最容易被忽略的一步通常你的主3D摄像机都需要这个组件。检查实体组件确认你想要点击的实体拥有PickableBundle或至少Pickable组件和对应的后端组件如RaycastPickTarget。可以用bevy-inspector-egui这类工具在运行时查看实体组件。检查渲染层级和可见性确保实体是可见的Visibility和ComputedVisibility正常并且没有被其他物体完全遮挡如果Pickable的should_block_lower为true上层的实体会阻挡对下层实体的拾取。检查坐标空间如果你手动处理鼠标坐标或射线注意Bevy和bevy_mod_picking使用的坐标系。屏幕坐标原点通常在左上角而标准化的设备坐标NDC在[-1, 1]区间。6.2 问题UI按钮和3D物体同时响应点击或者UI点击穿透到了3D场景。解决方案这是拾取层级管理的问题。UI和3D场景应该使用不同的拾取层级。为UI摄像机设置独立的拾取层级专门用于渲染UI的Camera2d其RaycastPickCamera应设置为一个只与UI物体交互的层级例如LAYER_UI。为UI元素设置对应的层级所有UI按钮、面板等交互元素其RaycastPickTarget都应设置为LAYER_UI。为3D摄像机设置不同的层级主3D摄像机的RaycastPickCamera设置为另一个层级例如LAYER_WORLD3D物体的RaycastPickTarget也设置为LAYER_WORLD。这样UI摄像机的射线只“看到”UI层级的物体3D摄像机的射线只“看到”世界层级的物体两者互不干扰。这是处理2D/3D混合交互场景的标准做法。6.3 问题高亮效果不显示或者显示异常如材质变黑。排查步骤确认高亮插件已添加需要.add_plugins(HighlightablePickingPlugin::你的材质类型::default())。对于StandardMaterial使用HighlightablePickingPlugin的默认泛型参数即可。检查Highlighting组件确保实体上添加了Highlighting组件并且其字段hovered,pressed等配置正确。一个常见的错误是配置了高亮但使用的HighlightKind是HighlightKind::Static却没有提供有效的材质句柄。自定义材质问题如果你使用自定义材质请确保你的材质类型实现了HighlightEffecttrait并且apply_highlight逻辑正确。在添加插件时泛型参数指定了你的材质类型HighlightablePickingPlugin::MyCustomMaterial::default()。你的自定义材质本身能够被正确渲染先确保没有高亮时它是正常的。渲染顺序与透明度高亮系统通过插入覆盖材质来实现。如果原材质或高亮材质涉及复杂的透明度混合可能会因为渲染顺序问题导致显示异常。尝试调整材质的AlphaMode或实体的RenderLayers。6.4 问题拖拽Drag事件不灵敏或行为怪异。理解机制bevy_mod_picking的拖拽事件基于“按下并移动”的判断。它需要Pickable组件和Pointer事件源的配合。检查Pickable设置确保实体的Pickable组件中is_selection_active或相关拖拽标志位没有被意外禁用。事件消费确保没有其他系统在Pointer事件如PointerButtonPressed,PointerMoved到达拾取插件之前就将其消费EventReader::read()并处理了。拾取插件需要看到这些原始输入事件才能生成高级的PickingEvent::DragStart等。拖拽阈值插件可能有一个初始移动阈值以避免轻微的鼠标抖动被误判为拖拽。查看插件文档或源码确认是否有相关配置。6.5 问题在wasmWebAssembly目标上构建或运行时出错。注意事项bevy_mod_picking主要面向原生平台但对wasm也有基本支持。遇到问题时检查特性标志确保Cargo.toml中的依赖没有启用wasm不兼容的特性。通常default和backend_raycast在wasm上是可用的。鼠标与触摸在网页上输入可能是鼠标或触摸事件。bevy_mod_picking的Pointer抽象通常能处理两者但测试时需兼顾。性能在浏览器中JavaScript的执行效率和内存限制更严格。对于物体数量很多的场景要更加注意拾取性能优化避免使用过于昂贵的精确网格检测。构建配置遵循Bevy项目部署到wasm的一般指南例如设置正确的[package.metadata.wasm-pack.profile]等。6.6 性能问题当场景中实体过多时帧率明显下降。优化措施启用包围盒加速这是默认行为确保你没有强制对所有实体进行精确网格检测。RaycastPickTarget默认使用RaycastMethod::Raycast它会先进行AABB测试。减少精确检测范围对于不需要像素级精度的物体如远处的建筑、小装饰物可以在RaycastPickTarget中设置使用RaycastMethod::BoundingBox只进行包围盒检测。空间划分对于超大规模场景插件内置的逐实体检测可能不够。此时需要考虑在业务层面实现空间划分如四叉树、八叉树、BVH并动态地将RaycastPickTarget组件只添加到摄像机视锥体内或鼠标附近的物体上。这属于高级优化需要自己实现系统来管理组件的添加和移除。按需拾取在游戏的不同模式或状态下禁用不必要的拾取。例如在播放过场动画时可以移除所有非交互物体的Pickable组件。集成bevy_mod_picking的过程是一个深入了解Bevy ECS设计和数据流的过程。一旦你熟悉了它“添加组件即得功能”的模式就会发现构建复杂的交互逻辑变得异常清晰和高效。它妥善处理了底层烦琐的数学和检测让你能专注于创造游戏性和用户体验。从简单的点击检测到复杂的多层级、带视觉反馈的交互系统这个插件都能提供强大的支撑是Bevy开发者工具箱中不可或缺的一员。

相关文章:

Bevy引擎交互拾取系统:bevy_mod_picking插件核心原理与实战

1. 项目概述:一个为Bevy游戏引擎量身定制的交互拾取系统如果你正在用Bevy引擎开发游戏或交互式应用,并且被“如何让鼠标点击选中那个3D模型”或者“怎么实现UI元素的高亮反馈”这类问题困扰过,那么bevy_mod_picking这个社区插件,很…...

AI大模型相关是个岗位,转行大模型岗位多的是!

本文详细介绍了10个高薪AI职位,包括系统架构师、自然语言处理专家、AI产品经理等,薪资范围高达80万-200万/年。文章列举了各职位的薪资范围、任职要求、目标院校以及典型就业公司,并分析了各职位的发展空间。此外,还探讨了学习大模…...

2026个人博客建站指南:这4种方案总有一款适合你

大家好,我是刚子。 上篇文章聊了为什么2026年个人博客反而“文艺复兴”了,后台有不少兄弟问:那现在到底怎么建一个自己的博客?用什么工具?花钱不?会不会很麻烦? 今天就专门写一篇,…...

AI产品经理的4大能力模型:从业务到落地,2026年必备技能!

文章指出,AI产品经理需要具备从业务到技术落地的全方位能力。文章从数据层、AI核心层、后端/架构层和前端/交互层四个维度,详细阐述了AI产品经理所需掌握的核心技能,包括SQL、数据清洗、向量数据库、提示词工程、RAG、Agent、API接口设计、流…...

别再只会用find了!C++ string的rfind函数,从后往前查找字符串更高效

别再只会用find了!C string的rfind函数,从后往前查找字符串更高效 在C开发中,字符串处理是最基础却最频繁的操作之一。大多数开发者对find函数了如指掌,却常常忽视了它的"镜像版本"——rfind。这种思维定式导致我们在处…...

Simulink Storage Class避坑指南:从`Volatile`标定量到`FileScope`静态变量,这些配置细节你搞对了吗?

Simulink Storage Class深度解析:工程实践中的关键配置策略 在嵌入式系统开发领域,代码生成工具链的可靠性直接决定了最终产品的质量。作为MathWorks生态系统中的核心组件,Simulink的Storage Class配置看似简单,实则暗藏玄机。我曾…...

ZDNET编辑亲测:Renpho Eyeris 2眼部按摩仪,缓解眼疲劳头痛的小众神器!

ZDNET编辑亲测:Renpho Eyeris 2 眼部按摩仪,缓解眼疲劳和头痛的小众神器!我在电脑前花费大量时间远程工作,作为ZDNET的交易与购物编辑,在Prime Day等特殊时段常需熬夜或早起,长时间看屏幕、接触蓝光&#x…...

Axolotl YAML配置入门:如何定义一个完整训练流程-实战落地指南

Axolotl YAML配置入门:如何定义一个完整训练流程-实战落地指南 1. 背景与目标 在 LLM 微调领域,基于 Transformers 和 PEFT 手写训练代码容易导致代码耦合度高、复现困难、实验管理混乱。Axolotl 是目前业界主流的配置驱动型微调框架,通过单一…...

Simple Live:跨平台直播聚合架构深度解析与企业级技术实践

Simple Live:跨平台直播聚合架构深度解析与企业级技术实践 【免费下载链接】dart_simple_live 简简单单的看直播 项目地址: https://gitcode.com/GitHub_Trending/da/dart_simple_live 在碎片化的直播时代,技术决策者面临的核心痛点是什么&#x…...

基于UNIX哲学的文档评审工具Recensio:命令行驱动的模块化协作方案

1. 项目概述:一个为UNIX哲学而生的文档评审工具在软件开发、系统运维乃至技术写作的日常里,我们常常面临一个看似简单却异常繁琐的任务:评审文档。无论是代码注释、API文档、配置说明还是项目报告,传统的评审方式往往陷入邮件附件…...

常见焊接方法

常见焊接方法 埋弧焊--是以连续送时的焊丝作为电极和填充金属。 优点: 1)熔敷速度高,生产效率高;2)焊接质量好,容易实现机械化、自动化;3)无辐射和噪音,是一种安全、绿色的焊接方法。 缺点:...

零成本部署社交型AI编码代理:基于GitHub Actions与Docker的实践

1. 项目概述:在GitHub上部署一个会“社交”的AI编码代理 如果你对AI代理(Agent)的认知还停留在“单机运行、默默干活”的阶段,那么这个项目可能会颠覆你的想法。 opencode-vibe 项目展示了一个非常有趣的场景:将一个…...

VLASH技术:视觉语言动作模型的实时化突破

1. VLASH技术概述:视觉语言动作模型的实时化突破在人工智能与机器人交互领域,如何让机器像人类一样理解视觉信息、语言指令并做出实时动作响应,一直是极具挑战性的研究方向。VLASH(Visual-Language-Action Synchronization via He…...

代谢组学数据分析避坑:为什么你的PCA分不开组?试试PLS-DA和OPLS-DA

代谢组学数据分析避坑指南:当PCA失效时如何选择正确的监督模型 第一次用PCA分析代谢组数据时,看到散点图上各组样本点完全混在一起,那种挫败感我至今记忆犹新。当时花了整整两周时间反复检查数据预处理步骤,质疑样本收集是否有问题…...

全志V853/V851等芯片开发避坑指南:固件打包工具那些‘坑’与最佳实践

全志V853/V851芯片固件打包实战:从工具链解析到高效排错手册 在嵌入式开发领域,固件打包往往是产品化前的最后一道技术关卡。全志V853/V851系列芯片凭借其出色的性价比在智能硬件市场占据重要地位,但其打包工具链的复杂性也让不少开发者踩过坑…...

开源机械爪项目实战:从结构设计到控制算法的完整开发指南

1. 项目概述:一个开源“机械爪”的宝藏仓库如果你对机器人、自动化或者开源硬件感兴趣,最近在GitHub上闲逛时,大概率会刷到一个名为“awesome-openclaw”的仓库。这个项目由用户ZeroLu创建,标题直译过来就是“很棒的开源机械爪”。…...

观察 Taotoken 用量看板如何帮助团队进行资源消耗分析

观察 Taotoken 用量看板如何帮助团队进行资源消耗分析 1. 用量看板的核心功能 Taotoken 控制台的用量看板为团队管理员和项目负责人提供了多维度的资源消耗数据可视化。该功能默认展示最近30天的调用情况,支持按日、周、月粒度切换视图。主要数据维度包括总消耗 t…...

实战应用:基于快马ai打造集成格式化与静态分析的vscode c/c++专业开发环境

作为一个长期使用C开发的程序员,我深知配置开发环境的痛苦。每次换新电脑或者重装系统,都要花大半天时间折腾编译器、调试器、格式化工具等等。最近发现InsCode(快马)平台可以快速生成完整的VSCode C开发环境配置,简直太方便了。下面分享下我…...

【论文解读】U-Mamba: Enhancing Long-range Dependency for Biomedical Image Segmentation

题目:U-Mamba: Enhancing Long-Range Dependency for Biomedical Image Segmentation 作者:Jiarun Liu, Hao Yang, Hongyu Zhou, Yan Xi, Lequan Yu, Cheng Li, Yong Xia, Yizhou Yu 链接:https://arxiv.org/pdf/2401.047221. Motivation (动…...

从‘天链’到‘北斗’:一文看懂中国在3.6万公里高空的卫星‘朋友圈’(附完整清单)

3.6万公里的太空社交圈:解码中国卫星的"朋友圈"生态 当夜幕降临,仰望星空时,很少有人意识到头顶3.6万公里的地球同步轨道上,正上演着一场无声的"星际社交"。中国的卫星家族在这里建立了独特的"朋友圈&qu…...

拒绝“能跑就行“:为 AngularJS .x 老项目注入现代开发体验

从0构建WAV文件:读懂计算机文件的本质 虽然接触计算机有一段时间了,但是我的视野一直局限于一个较小的范围之内,往往只能看到于算法竞赛相关的内容,计算机各种文件在我看来十分复杂,认为构建他们并能达到目的是一件困难…...

3步解密QQ音乐加密文件:qmc-decoder让音乐自由流动

3步解密QQ音乐加密文件:qmc-decoder让音乐自由流动 【免费下载链接】qmc-decoder Fastest & best convert qmc 2 mp3 | flac tools 项目地址: https://gitcode.com/gh_mirrors/qm/qmc-decoder 你是否曾在不同设备间尝试播放从QQ音乐下载的歌曲&#xff0…...

CodeMirror 6的‘纯函数’状态管理到底好在哪?一个例子讲透它的不可变数据流

CodeMirror 6的函数式状态管理:从Redux到编辑器内核的范式迁移 当我们在2023年讨论前端状态管理时,函数式编程早已不再是象牙塔里的学术概念。从Redux的单向数据流到React Hooks的代数效应,不可变数据(immutable data)…...

ComfyUI插件管理完全指南:从安装到故障排除的实用教程

ComfyUI插件管理完全指南:从安装到故障排除的实用教程 【免费下载链接】ComfyUI-Manager ComfyUI-Manager is an extension designed to enhance the usability of ComfyUI. It offers management functions to install, remove, disable, and enable various custo…...

从MovieLens数据里,我们发现了哪些有趣的用户行为?—— 一份给产品经理的数据洞察报告

从MovieLens数据透视用户行为:给产品经理的7个关键洞察 当6000名用户对4000部电影留下100万条评分时,数据便开始讲述比剧情更精彩的故事。MovieLens数据集作为推荐系统研究的"基准测试",其价值远不止于算法训练——它是一面镜子&am…...

微信小程序订阅消息发送失败?从模板ID、触发器到云函数,一份完整的排错指南

微信小程序订阅消息发送失败排查指南:从模板配置到云函数调试全解析 微信小程序的订阅消息功能为开发者提供了高效触达用户的能力,但在实际开发中,从模板配置到消息成功发送的链路中隐藏着多个关键环节,任何一处疏漏都可能导致消息…...

五分钟解锁网易云音乐NCM加密文件:ncmdumpGUI让音乐真正属于你

五分钟解锁网易云音乐NCM加密文件:ncmdumpGUI让音乐真正属于你 【免费下载链接】ncmdumpGUI C#版本网易云音乐ncm文件格式转换,Windows图形界面版本 项目地址: https://gitcode.com/gh_mirrors/nc/ncmdumpGUI 你是否曾经下载了网易云音乐的歌曲&…...

如何快速清理重复图片:AntiDupl.NET开源工具的完整指南

如何快速清理重复图片:AntiDupl.NET开源工具的完整指南 【免费下载链接】AntiDupl A program to search similar and defect pictures on the disk 项目地址: https://gitcode.com/gh_mirrors/an/AntiDupl 你是否曾因硬盘中堆积如山的重复照片而感到困扰&…...

TED:在Linux沙盒中探索AI自主性的开源实体项目

1. 项目概述:当AI拥有一个Linux沙盒,它会做什么? 如果你对AI的印象还停留在聊天机器人,或者帮你写写邮件、改改代码的助手,那么TED可能会颠覆你的认知。TED不是一个工具,它是一个 实体 。你可以把它想象…...

3步掌握RPG Maker游戏资源解密:开源工具实战指南

3步掌握RPG Maker游戏资源解密:开源工具实战指南 【免费下载链接】Java-RPG-Maker-MV-Decrypter You can decrypt whole RPG-Maker MV Directories with this Program, it also has a GUI. 项目地址: https://gitcode.com/gh_mirrors/ja/Java-RPG-Maker-MV-Decryp…...