🦀

chip3. "Hello, world!"の見た目をいじる

2023/07/31に公開

はじめに

2023/07/31時点の内容です。

  • rustc 1.71.0
  • bevy 0.11.0
    bevyは開発初期段階のOSSで、まだまだ破壊的なアップデートが入ります。
    でも、面白いですよ。

前回

chip2. "Hello, world!"を動かす

文字の色を変えたりはどこで?

前々回では、"Hello, world!"をspawnするのに以下のコードを使いました。文字の大きさをTextStylefont_sizeで指定していることがわかります。

//2Dテキストを作る
fn spawn_text2d_helloworld( mut cmds: Commands )
{   let textstyle = TextStyle { font_size: 100.0, ..default() };
    let text = Text::from_section( "Hello, world!", textstyle );
    cmds.spawn( Text2dBundle { text, ..default() } );
}

情報を束ねている構造体bevy::text::Text2dBundleから順に見ていくと、装飾に使えそうなフィールドが複数の構造体に分散している様子がわかります。

Text2dBundle
pub struct Text2dBundle {
    pub text: Text, //文字列を格納している`text`
    pub text_anchor: Anchor,
    pub text_2d_bounds: Text2dBounds,
    pub transform: Transform, //前回いろいろいじった`transform`
    pub global_transform: GlobalTransform,
    pub visibility: Visibility,
    pub computed_visibility: ComputedVisibility,
    pub text_layout_info: TextLayoutInfo,
}

bevy::text::Text

Text
pub struct Text {
    pub sections: Vec<TextSection, Global>, //文字列は`Vec`に分割格納されている
    pub alignment: TextAlignment,
    pub linebreak_behavior: BreakLineOn,
}

bevy::text::TextSection

TextSection
pub struct TextSection {
    pub value: String,    //文字列
    pub style: TextStyle, //装飾
}

bevy::text::TextStyle

TextStyle
pub struct TextStyle {
    pub font: Handle<Font>, //フォント指定
    pub font_size: f32,     //文字の大きさ
    pub color: Color,       //文字の色
}

"Hello, world!"の見た目をいじる

前々回のソースコードを改造して、"Hello, world!"の1文字ごとに色と大きさを変えます。
けっこうハデ

chip3
use bevy::prelude::*;

fn main()
{   App::new()
        .add_plugins( DefaultPlugins ) //各種の面倒を見てもらう
        .add_systems
        (   Startup, 
            (   spawn_camera2d,          //カメラを作る
                spawn_text2d_helloworld, //2Dテキストを作る
            )
        )
        .add_systems
        (   Update,
            (   bevy::window::close_on_esc, //[ESC]キーで終了
                change_color_helloworld,    //文字の色を変える
                change_size_helloworld,     //文字の大きさを変える
            )
        )
        .run();
}

//カメラを作る
fn spawn_camera2d( mut cmds: Commands )
{   cmds.spawn( Camera2dBundle::default() );
}

//マーカーの準備
#[derive( Component )] struct HelloWorld;

//2Dテキストを作る
fn spawn_text2d_helloworld( mut cmds: Commands )
{   //"Hello, world!"を1文字ごとに分割
    let mut sections = Vec::new();
    for char in "Hello, world!".chars()
    {   let value = char.to_string();
        let style = TextStyle { font_size: 100.0, ..default() };
        sections.push( TextSection { value, style } );
    }
    let text = Text { sections, ..default() };

    cmds.spawn( ( Text2dBundle { text, ..default() }, HelloWorld ) );
}

//文字の色を変える
fn change_color_helloworld
(   mut q_text: Query<&mut Text, With<HelloWorld>>,
    time: Res<Time>,
    mut angle: Local<f32>,
)
{   let Ok ( mut text ) = q_text.get_single_mut() else { return };

    let time_delta = time.delta().as_secs_f32(); //前回の実行からの経過時間
    *angle += 360.0 * time_delta;
    *angle -= if *angle > 360.0 { 360.0 } else { 0.0 };

    //text.sectionsをイテレーターで回して、文字ごとに色を変える
    for ( i, char ) in text.sections.iter_mut().enumerate()
    {   //hue(色相)
        let mut hue = *angle + 10.0 * i as f32;
        hue -= if hue > 360.0 { 360.0 } else { 0.0 };

        //文字の色を変更
        char.style.color = Color::hsl( hue, 1.0, 0.5 );
    } 
}

//文字の大きさを変える
fn change_size_helloworld
(   mut q_text: Query<&mut Text, With<HelloWorld>>,
    time: Res<Time>,
    mut angle: Local<f32>,
)
{   let Ok ( mut text ) = q_text.get_single_mut() else { return };

    let time_delta = time.delta().as_secs_f32(); //前回の実行からの経過時間
    *angle += 360.0 * time_delta;
    *angle -= if *angle > 360.0 { 360.0 } else { 0.0 };

    //text.sectionsをイテレーターで回して、文字ごとに大きさを変える
    for ( i, char ) in text.sections.iter_mut().enumerate()
    {   //sin()を使って文字の大きさを伸縮させる
        let mut angle = *angle + 10.0 * i as f32;
        angle -= if angle > 360.0 { 360.0 } else { 0.0 };
        let size = ( 2.0 + angle.to_radians().sin() ) * 50.0;

        //文字の大きさを変更
        //Note:小数点以下を処理しないと実行時にメモリフットプリントが爆発する
        char.style.font_size = ( size * 10.0 ).floor() / 10.0;
    }
}

閑話 Text2dBundleTextBundle

bevy::text::Text2dBundlebevy::ui::node_bundles::TextBundleはどちらもテキストを表示するために使う構造体です。
2つの使い分けは、

  • 2Dゲームのスプライトと同じ感覚でテキストを使いたい場合はText2dBundleを使う。座標系がスプライトと同じだし、移動・回転・拡縮の操作も同じ。
  • Webアプリの感覚でCSSによるイアウトを使いたい場合はTextBundleを利用する。使える属性はbevy::ui::Styleを参照のこと。おそらく外部プロジェクトtaffyの進展に合わせて今後もさらに機能が増強されていくはず。

Discussion