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

Bevy中2D网格纹理分配方法及六边形Tile创建的Vertex_Normal缺失错误排查

搞定Bevy六边形Tile:解决错误、纹理适配与交互实现指南

我来帮你搞定Bevy里这个六边形Tile的问题,你遇到的错误其实很好解释,咱们一步步拆解解决:


一、解决「Vertex_Normal缺失」的核心错误

你自定义的六边形网格只添加了顶点位置,但Bevy的ColorMaterial对应的2D渲染管线要求网格必须包含法线属性——默认的Quad网格创建时自动帮你加了这些必要的属性,而你手动拼的网格没加,所以才会抛出那个错误。

对于2D平面来说,所有顶点的法线都可以统一设为[0.0, 0.0, 1.0](指向相机方向),直接补上就行。另外,要是想让纹理正常显示,还得加UV坐标属性,不然纹理会乱拉伸,这也是默认Quad自带的,咱们得手动补上。


二、修改后的完整可运行代码

这里我帮你补上了法线和UV,还调整了顶点索引让渲染更合理:

const TILE_SPRITE: &str = "tile.png";

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
    asset_server: Res<AssetServer>,
) {
    // 六边形顶点坐标(中心在原点)
    let vertices = vec![
        [-0.8660, 0.5000],  // 左上
        [0.8660, 0.5000],   // 右上
        [1.0000, 0.0000],   // 右中
        [0.8660, -0.5000],  // 右下
        [-0.8660, -0.5000], // 左下
        [-1.0000, 0.0000],  // 左中
    ];

    // 三角形索引(确保面朝向相机)
    let indices = Indices::U16(vec![
        0, 1, 2,
        0, 2, 5,
        2, 3, 4,
        2, 4, 5,
    ]);

    // 2D平面统一法线:指向Z轴正方向(相机方向)
    let normals = vec![[0.0, 0.0, 1.0]; vertices.len()];

    // UV坐标:适配六边形顶点的纹理映射
    let uvs = vec![
        [0.0, 1.0], // 左上
        [1.0, 1.0], // 右上
        [1.0, 0.5], // 右中
        [1.0, 0.0], // 右下
        [0.0, 0.0], // 左下
        [0.0, 0.5], // 左中
    ];

    let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);
    mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, vertices);
    mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, normals); // 补上缺失的法线
    mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs);      // 补上纹理UV
    mesh.set_indices(Some(indices));

    // 2D相机
    commands.spawn_bundle(OrthographicCameraBundle::new_2d());

    // 生成可显示纹理的六边形Tile
    commands.spawn_bundle(MaterialMesh2dBundle {
        mesh: meshes.add(mesh).into(),
        transform: Transform::default().with_scale(Vec3::splat(128.)),
        material: materials.add(asset_server.load(TILE_SPRITE).into()),
        ..default()
    });
}

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_startup_system(setup)
        .run();
}

三、实现Tile点击操作

要让Tile支持点击,最省心的方式是用bevy_rapier2d物理引擎添加碰撞体,步骤如下:

  1. 在Cargo.toml添加依赖:bevy_rapier2d = "0.21"(版本要和你的Bevy版本匹配)
  2. 给App添加物理插件:.add_plugin(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.0))
  3. 给Tile添加碰撞体(在生成MaterialMesh2dBundle的代码后追加):
use bevy_rapier2d::prelude::*;

// 给Tile添加六边形碰撞体
.collider(Collider::convex_polyline(
    vertices.into_iter().map(|v| Vec2::new(v[0], v[1])).collect()
)?)
  1. 编写点击检测系统:
fn tile_click_system(
    mut mouse_events: EventReader<MouseButtonInput>,
    rapier_context: Res<RapierContext>,
    camera: Query<(&Camera, &GlobalTransform)>,
    windows: Res<Windows>,
) {
    for event in mouse_events.iter() {
        if event.button == MouseButton::Left && event.state == ElementState::Pressed {
            let (camera, camera_transform) = camera.single();
            let window = windows.get_primary().unwrap();
            // 把鼠标屏幕坐标转成世界坐标
            if let Some(world_pos) = window.cursor_position()
                .and_then(|pos| camera.viewport_to_world(camera_transform, pos))
                .map(|ray| ray.origin.truncate())
            {
                // 检测鼠标是否点击到Tile
                rapier_context.intersections_with_point(world_pos, |entity| {
                    println!("Clicked Tile: {:?}", entity);
                    // 这里可以加你的点击逻辑,比如高亮Tile、打开菜单等
                    true
                });
            }
        }
    }
}

最后把这个系统加到App里:.add_system(tile_click_system)


四、实现物品拖放交互

拖放功能需要跟踪拖拽状态和目标Tile,核心思路:

  1. 给可拖拽物品加Draggable标记组件,给Tile加Tile标记组件
  2. 编写三个事件处理系统:
    • 拖拽开始:监听PointerDragStart,记录当前拖拽的物品实体,隐藏原位置的物品
    • 拖拽过程:监听PointerDrag,更新物品的位置跟随鼠标移动
    • 拖拽结束:监听PointerDragEnd,检测鼠标位置是否在Tile上,执行物品进出逻辑(比如把物品移到Tile,或者从Tile取出)

你可以复用前面的点击检测逻辑,在拖拽结束时判断目标Tile,再处理对应的业务逻辑。

内容的提问来源于stack exchange,提问作者Lyndon Alcock

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 21:07:45