如何获取Bevy中经过变换的Sprite的实际像素尺寸?
获取Bevy中缩放后Sprite的实时边界框
核心逻辑
Sprite的最终显示尺寸由基础尺寸(纹理或自定义尺寸)× 全局缩放系数决定,再结合全局位置和锚点,就能计算出准确的边界框。若Sprite存在父节点,必须使用GlobalTransform获取累积后的全局变换,避免局部坐标偏差。
具体实现步骤
1. 查询关键组件
需要同时获取Sprite和GlobalTransform组件:
Sprite:存储纹理原始尺寸,若设置custom_size则优先使用该值GlobalTransform:提供包含父节点变换的全局位置、缩放信息
2. 计算缩放后的实际尺寸
fn calculate_sprite_bounds( mut query: Query<(&Sprite, &GlobalTransform)>, ) { for (sprite, global_transform) in query.iter() { // 获取Sprite基础尺寸:优先用自定义尺寸,否则取纹理原始尺寸 let base_size = sprite.custom_size.unwrap_or_else(|| sprite.texture.size()); // 应用全局缩放:取x/y分量的绝对值(避免负缩放导致尺寸为负) let scale = global_transform.scale(); let scaled_size = Vec2::new( base_size.x * scale.x.abs(), base_size.y * scale.y.abs(), );
3. 结合锚点计算边界框
Sprite默认锚点为中心(Vec2(0.5, 0.5)),需根据锚点偏移调整边界框的坐标范围:
// 获取Sprite的全局中心坐标 let center_pos = global_transform.translation().truncate(); // 计算半宽高 let half_size = scaled_size / 2.0; // 根据锚点偏移计算边界框的最小/最大坐标 let anchor = sprite.anchor; let bounds_min = center_pos - half_size + (anchor * scaled_size); let bounds_max = center_pos + half_size - ((Vec2::ONE - anchor) * scaled_size); // 用Rect类型存储最终边界框 let bounds = Rect::from_corners(bounds_min, bounds_max); println!("当前Sprite边界框: {:?}", bounds); } }
4. 点击检测适配
如果要实现点击检测,可结合鼠标事件和边界框判断:
fn handle_sprite_click( mut pointer_events: EventReader<PointerInput<MouseButton>>, sprite_query: Query<(&Sprite, &GlobalTransform)>, windows: Query<&Window>, camera_query: Query<(&Camera, &GlobalTransform), With<Camera2d>>, ) { let window = windows.single(); let (camera, camera_transform) = camera_query.single(); for event in pointer_events.iter() { if event.pressed(MouseButton::Left) { // 将鼠标屏幕坐标转为世界坐标 if let Some(world_pos) = window.cursor_position() .and_then(|screen_pos| camera.viewport_to_world_2d(camera_transform, screen_pos)) { for (sprite, global_transform) in sprite_query.iter() { // 复用边界框计算逻辑 let base_size = sprite.custom_size.unwrap_or_else(|| sprite.texture.size()); let scale = global_transform.scale(); let scaled_size = Vec2::new(base_size.x * scale.x.abs(), base_size.y * scale.y.abs()); let center_pos = global_transform.translation().truncate(); let half_size = scaled_size / 2.0; let anchor = sprite.anchor; let bounds_min = center_pos - half_size + (anchor * scaled_size); let bounds_max = center_pos + half_size - ((Vec2::ONE - anchor) * scaled_size); let bounds = Rect::from_corners(bounds_min, bounds_max); // 判断点击是否在边界框内 if bounds.contains(world_pos) { println!("Sprite被点击了!"); } } } } } }
注意事项
- 若Sprite有父节点,必须使用
GlobalTransform而非Transform,否则会得到局部坐标,导致边界框位置错误。 - 处理负缩放时要取绝对值,避免计算出负尺寸。
- 若设置了
Sprite.custom_size,该值会覆盖纹理原始尺寸,需优先使用。
内容的提问来源于stack exchange,提问作者CpILL
相关产品推荐
相关产品推荐

