๐Ÿ“ฆ bevyengine / bevy

๐Ÿ“„ font_atlas_debug.rs ยท 111 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111//! This example illustrates how `FontAtlas`'s are populated.
//! Bevy uses `FontAtlas`'s under the hood to optimize text rendering.

use bevy::{color::palettes::basic::YELLOW, prelude::*, text::FontAtlasSet};
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha8Rng;

fn main() {
    App::new()
        .init_resource::<State>()
        .insert_resource(ClearColor(Color::BLACK))
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_systems(Update, (text_update_system, atlas_render_system))
        .run();
}

#[derive(Resource)]
struct State {
    atlas_count: u32,
    handle: Handle<Font>,
    timer: Timer,
}

impl Default for State {
    fn default() -> Self {
        Self {
            atlas_count: 0,
            handle: Handle::default(),
            timer: Timer::from_seconds(0.05, TimerMode::Repeating),
        }
    }
}

#[derive(Resource, Deref, DerefMut)]
struct SeededRng(ChaCha8Rng);

fn atlas_render_system(
    mut commands: Commands,
    mut state: ResMut<State>,
    font_atlas_set: Res<FontAtlasSet>,
    images: Res<Assets<Image>>,
) {
    if let Some(font_atlases) = font_atlas_set.values().next() {
        let x_offset = state.atlas_count as f32;
        if state.atlas_count == font_atlases.len() as u32 {
            return;
        }
        let font_atlas = &font_atlases[state.atlas_count as usize];
        let image = images.get(&font_atlas.texture).unwrap();
        state.atlas_count += 1;
        commands.spawn((
            ImageNode::new(font_atlas.texture.clone()),
            Node {
                position_type: PositionType::Absolute,
                top: Val::ZERO,
                left: px(image.width() as f32 * x_offset),
                ..default()
            },
        ));
    }
}

fn text_update_system(
    mut state: ResMut<State>,
    time: Res<Time>,
    mut query: Query<&mut Text>,
    mut seeded_rng: ResMut<SeededRng>,
) {
    if !state.timer.tick(time.delta()).just_finished() {
        return;
    }

    for mut text in &mut query {
        let c = seeded_rng.random::<u8>() as char;
        let string = &mut **text;
        if !string.contains(c) {
            string.push(c);
        }
    }
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut state: ResMut<State>) {
    state.handle = asset_server.load("fonts/FiraSans-Bold.ttf");
    let font = FontSource::from(state.handle.clone());
    commands.spawn(Camera2d);
    commands
        .spawn((
            Node {
                position_type: PositionType::Absolute,
                bottom: Val::ZERO,
                ..default()
            },
            BackgroundColor(Color::NONE),
        ))
        .with_children(|parent| {
            parent.spawn((
                Text::new("a"),
                TextFont {
                    font,
                    font_size: 50.0,
                    ..default()
                },
                TextColor(YELLOW.into()),
            ));
        });
    // We're seeding the PRNG here to make this example deterministic for testing purposes.
    // This isn't strictly required in practical use unless you need your app to be deterministic.
    commands.insert_resource(SeededRng(ChaCha8Rng::seed_from_u64(19878367467713)));
}