🦀
chip2. "Hello, world!"を動かす
はじめに
2023/07/29時点の内容です。
- rustc 1.71.0
- bevy 0.11.0
bevyは開発初期段階のOSSで、まだまだ破壊的なアップデートが入ります。
でも、面白いですよ。
前回
chip1. RustとBevyで"Hello, world!"
Transform
で移動、回転、拡縮
bevyでモノを動かす場合しょっちゅうお世話になるのがTransform
です。
見てのとおり、移動、回転、拡縮ができます。
Transform
pub struct Transform {
pub translation: Vec3,
pub rotation: Quat,
pub scale: Vec3,
}
"Hello, world!"を動かす
前回のソースコードを改造して、3つ表示した"Hello, world!"をそれぞれ移動・回転・拡縮させます。
動くと楽しい
chip2
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]キーで終了
move_helloworld, //移動
rotate_helloworld, //回転
scale_helloworld, //拡縮
)
)
.run();
}
//カメラを作る
fn spawn_camera2d( mut cmds: Commands )
{ cmds.spawn( Camera2dBundle::default() );
}
//マーカーの準備
#[derive( Component )] struct HelloWorld1;
#[derive( Component )] struct HelloWorld2;
#[derive( Component )] struct HelloWorld3;
//2Dテキストを作る
fn spawn_text2d_helloworld( mut cmds: Commands )
{ let func = | x: i32 |
{ let textstyle = TextStyle { font_size: 50.0, ..default() };
let text = Text::from_section( "Hello, world!", textstyle );
let transform = Transform::from_xyz( x as f32, 0.0, 0.0 );
Text2dBundle { text, transform, ..default() }
};
cmds.spawn( ( func( -400 ), HelloWorld1 ) ); //マーカー1付きspawn
cmds.spawn( ( func( 0 ), HelloWorld2 ) ); //マーカー2付きspawn
cmds.spawn( ( func( 400 ), HelloWorld3 ) ); //マーカー3付きspawn
}
//移動
fn move_helloworld
( mut q_transform: Query<&mut Transform, With<HelloWorld1>>, //マーカー1で検索
time: Res<Time>,
mut angle: Local<f32>, //ローカル変数
)
{ let Ok ( mut transform ) = q_transform.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 };
//楕円軌道の移動
let x = angle.to_radians().cos() * 400.0; //横軸は半径400
let y = angle.to_radians().sin() * 200.0; //縦軸は半径200
transform.translation = Vec3::new( x, y, 0.0 );
}
//回転
fn rotate_helloworld
( mut q_transform: Query<&mut Transform, With<HelloWorld2>>, //マーカー2で検索
time: Res<Time>,
)
{ let Ok ( mut transform ) = q_transform.get_single_mut() else { return };
let time_delta = time.delta().as_secs_f32(); //前回の実行からの経過時間
let angle = 360.0 * time_delta;
let quat = Quat::from_rotation_z( angle.to_radians() );
//回転(四元数Quatは掛け算で回る)
transform.rotation *= quat;
}
//拡縮
fn scale_helloworld
( mut q_transform: Query<&mut Transform, With<HelloWorld3>>, //マーカー3で検索
time: Res<Time>,
mut angle: Local<f32>, //ローカル変数
)
{ let Ok ( mut transform ) = q_transform.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 };
//拡縮(sin()がマイナスになると表示が反転する)
transform.scale = Vec3::ONE * angle.to_radians().sin();
}
Componet
とQuery
//マーカーの準備
の箇所で#[derive( Component )]
を付けて型を3つ宣言しています。それぞれの型は"Hello, world!"をspawnする箇所と、移動・回転・拡縮の各関数の引数に登場します。
関数の引数にQuery
を使うと、その内側に記述した型に合致する値を取得することができます。
chip2のソースコードでは移動・回転・拡縮の各関数がそれぞれTransform
を必要としていますが、マーカーとセットでQuery
することによって、どのTransform
を取得するか指定しているわけです。
Local
変数
移動と拡縮の関数の引数にLoacl
が登場しています。
Loacl
の変数は実行時に一度Defaultで初期化され、以降値を保持し続けます。その関数の中だけで参照・更新でき、関数が終了しても次回関数実行時に値を復元できるので、大変便利です。
Discussion