You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 05:53:10