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

Bevy 0.10.1 ECS延迟生成子实体致间歇性渲染异常求助

Bevy 0.10.1 2048游戏棋盘间歇性渲染问题解决

问题核心

在使用Bevy 0.10.1开发2048时,棋盘tile的文本子实体出现间歇性不渲染/显示异常的情况。原代码通过commands.spawn创建tile后用with_children生成文本,尝试用废弃的ShouldRun延迟生成无效,推测问题源于实体重复创建、层级遮挡或ECS系统执行时机冲突。

关键解决方案

1. 避免实体堆积,批量清理旧实体

原代码每次调用display_game_board都会生成新实体,旧实体未销毁会导致渲染层级混乱、资源占用。给所有棋盘tile添加标签组件,每次生成前批量销毁旧实体:

#[derive(Component)]
struct BoardTile; // 标记棋盘tile实体

2. 调整渲染层级,避免文本被遮挡

文本子实体与tile的Z轴相同,可能被tile sprite遮挡,将文本Z轴设为更高值(如1.0),确保显示在tile上方。

3. 规范ECS资源获取,确保数据有效性

使用Res而非直接传参获取GameBoard和GameFonts,保证获取的是最新的资源状态,避免数据不一致。

4. 控制系统执行时机

将棋盘更新逻辑绑定到棋盘状态变化事件(而非每一帧运行),比如定义BoardUpdated事件,仅在棋盘数据改变时触发实体生成,减少不必要的ECS操作。

修改后的完整代码

use bevy::prelude::*;

// 自定义组件:标记棋盘tile实体
#[derive(Component)]
struct BoardTile;

// 自定义组件:存储tile的数值
#[derive(Component)]
struct TileValue(u32);

// 假设你的GameBoard和GameFonts定义如下
#[derive(Resource)]
struct GameBoard {
    size: usize,
    board: Vec<Vec<Option<u32>>>,
}

#[derive(Resource)]
struct GameFonts {
    main_font: Handle<Font>,
}

fn display_game_board(
    mut commands: Commands,
    game_board: Res<GameBoard>,
    fonts: Res<GameFonts>,
    mut tile_query: Query<Entity, With<BoardTile>>,
) {
    // 销毁所有旧的棋盘tile及其子实体
    for entity in tile_query.iter_mut() {
        commands.entity(entity).despawn_recursive();
    }

    let tile_size = 100.0;
    let gap_size = 5.0;
    let board_width = (tile_size + gap_size) * game_board.size as f32 - gap_size;
    let board_height = (tile_size + gap_size) * game_board.size as f32 - gap_size;

    // 棋盘居中偏移
    let translation = Vec3::new(
        (-board_width / 2.0) + (tile_size / 2.0),
        (-board_height / 2.0) + (tile_size / 2.0),
        0.0,
    );

    for row in 0..game_board.size {
        for col in 0..game_board.size {
            let tile_translation = translation
                + Vec3::new(
                    col as f32 * (tile_size + gap_size),
                    row as f32 * (tile_size + gap_size),
                    0.0,
                );

            // 生成空tile背景(添加BoardTile标签)
            commands.spawn((
                SpriteBundle {
                    sprite: Sprite {
                        color: Color::rgba_u8(200, 200, 200, 255), // 修正alpha值:原6几乎透明,改为255正常显示
                        custom_size: Some(Vec2::new(tile_size, tile_size)),
                        ..default()
                    },
                    transform: Transform::from_translation(tile_translation),
                    ..default()
                },
                BoardTile,
            ));

            if let Some(value) = game_board.board[row][col] {
                let text = value.to_string();
                let brightness = (value as f32 / 2048.0).min(1.0);

                // 生成有值的tile,Z轴设为0.5(在背景上方)
                let tile_entity = commands.spawn((
                    SpriteBundle {
                        sprite: Sprite {
                            color: Color::rgba(brightness, brightness, brightness, 1.0),
                            custom_size: Some(Vec2::new(tile_size, tile_size)),
                            ..default()
                        },
                        transform: Transform::from_translation(tile_translation + Vec3::Z * 0.5),
                        ..default()
                    },
                    BoardTile,
                    TileValue(value),
                )).id();

                // 文本子实体Z轴设为1.0(在tile上方)
                commands.entity(tile_entity).with_children(|parent| {
                    parent.spawn(Text2dBundle {
                        text: Text::from_section(
                            text,
                            TextStyle {
                                font: fonts.main_font.clone(),
                                font_size: 40.0,
                                color: Color::WHITE,
                            },
                        ),
                        transform: Transform::from_translation(Vec3::Z * 1.0),
                        ..default()
                    });
                });
            }
        }
    }
}

额外注意事项

  • 字体加载确认:确保GameFonts中的main_font已完全加载,可通过AssetServer的load_state或AssetEvent监听加载完成事件,再初始化GameFonts资源。
  • 事件触发逻辑:添加BoardUpdated事件,仅在玩家移动、合并tile等棋盘数据变化时发送事件,绑定display_game_board系统响应事件,避免无意义的实体重建。

内容的提问来源于stack exchange,提问作者Ruben Villalvazo Manríquez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 03:10:57