We want to show the values from the Points
component on each tile, so we are going to start with showing any text at all on each tile.
We’ll create a unit struct, that is: a struct with no fields, to tag the text so we can find it later.
#[derive(Component)]
struct TileText;
We’ll put .with_children
in between spawning our new tiles and inserting the Points
component.
The tile sprite children will be a Text2dBundle
to display the value of each tile. Text
uses sections to define updateable areas of content, so we use from_section
. We initialize the displayed string to "2"
because that will always be the starting value, although later on this value will update so fast we won’t even notice the initial value.
We also insert the TileText
label component on the entity we got from spawning the Text2dBundle
so we can find it later for all tiles.
.with_children(|child_builder| {
child_builder
.spawn(Text2dBundle {
text: Text::from_section(
"2",
TextStyle {
font_size: 40.0,
color: Color::BLACK,
..default()
},
)
.with_alignment(
TextAlignment::Center,
),
transform: Transform::from_xyz(
0.0, 0.0, 1.0,
),
..default()
})
.insert(TileText);
})
The Text for the point values on the tiles is set up, but it isn't showing in the tiles yet. This is because we haven't loaded a font using the asset server.
We'll create a new struct to load a font and keep it around for us.
Create a new struct called FontSpec
with a field named family
that will hold a Handle
to a Bevy Font
.
#[derive(Resource)]
struct FontSpec {
family: Handle<Font>,
}
We still need to instantiate this struct and insert it into the Asset system to use as a Resource. To do this we can use the init_resource
function.
Here, we're using the FontSpec
struct as the type argument to init_resource
using the turbofish syntax. In Rust sometimes functions can handle many different types and we need to tell it which one we want. For example in this case, where we could be instantiating any type of resource.
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "2048".to_string(),
..default()
}),
..default()
}))
.init_resource::<FontSpec>()
init_resource
tries a couple of different approaches to instantiating the type we're asking for. One of those approaches is calling the FromWorld
implementation for the type.
The implementation of the FromWorld
trait for FontSpec
is how we'll initialize our font loading.
The FromWorld
trait requires that we implement the from_world
function which accepts an argument of &mut World
and returns Self
, or FontSpec
to be more specific.
Remember that the World
is where everything in our game is, so having exclusive access to our world is how we know we can insert new resources into it.
We need access to the AssetServer
which controls loading files from the filesystem and we can use get_resource_mut
on World
to get it.
Finally, we can use the asset_server.load()
to load a font file.
impl FromWorld for FontSpec {
fn from_world(world: &mut World) -> Self {
let asset_server = world
.get_resource_mut::<AssetServer>()
.unwrap();
FontSpec {
family: asset_server
.load("fonts/FiraSans-Bold.ttf"),
}
}
}
To make it easy, we'll follow what Bevy does in their own examples and use the Fira Sans font family. We can grab the .ttf from the bevy repo and put it in the assets/fonts/
folder. The assets
folder sits at the root of our project and the AssetServer
looks here for the fonts folder.
The spawn_tiles
system will need to request the FontSpec
resource to access it after it is loaded.
fn spawn_tiles(
mut commands: Commands,
query_board: Query<&Board>,
font_spec: Res<FontSpec>,
) {...}
at which point we can use it by cloning the FontSpec.family
Handle
. We can also remove the ..default()
here since we’ve specified all of the fields.
TextStyle {
font: font_spec
.family
.clone(),
font_size: 40.0,
color: Color::BLACK,
},
The will display 2 in each box for us.