Closed13

作業メモ:bevy 0.9 → 0.10

hyoihyoi

メインウィンドウの表示

Bevy 0.9
let window = WindowDescriptor
{   title    : APP_TITLE.to_string(),
    width    : SCREEN_PIXELS_WIDTH,
    height   : SCREEN_PIXELS_HEIGHT,
    resizable: false,
    ..default()
};
app.add_plugins( DefaultPlugins.set( WindowPlugin { window, ..default() } ) );
Bevy 0.10
let window = Window
{   title     : APP_TITLE.to_string(),
    resolution: ( SCREEN_PIXELS_WIDTH, SCREEN_PIXELS_HEIGHT ).into(),
    resizable : false,
    ..default()
};
let primary_window = Some( window );
app.add_plugins( DefaultPlugins.set( WindowPlugin { primary_window, ..default() } ) );
  1. WindowDescriptorWindowに改名され、その構造体のwidthheightresolutionにまとめられた。resolutionの型は意識する必要はなく(f32,f32).into()を使って導出できる。
  2. WindowPlugin構造体のwindowprimary_windowに改名され、Optionでラップされた。
hyoihyoi

アンチエイリアスの設定

Bevy 0.9
app.insert_resource( Msaa { samples: 4 } );
Bevy 0.10
app.insert_resource( Msaa::Sample4 );
hyoihyoi

Stateのderiveと初期化

Bevy 0.9
#[derive( Clone, Copy, Eq, PartialEq, Hash, Debug )]
enum GameState
{   InitApp,
    TitleDemo, DemoLoop,
    GameStart, StageStart, MainLoop, StageClear, GameOver,
    Pause, Debug,
}
app.add_state( GameState::InitApp );
Bevy 0.10
#[derive( Clone, Copy, Eq, PartialEq, Hash, Debug, Default, States )]
enum GameState
{   #[default] InitApp,
    TitleDemo, DemoLoop,
    GameStart, StageStart, MainLoop, StageClear, GameOver,
    Pause, Debug,
}
app.add_state::<GameState>();
  1. #[derive(…)]DefaultStatesが増えた。#[default]によって初期値を指定する。
  2. .add_state()の書き方が変わった。
hyoihyoi

SystemLabeのderive

Bevy 0.9
#[derive( SystemLabel, Clone )]
Bevy 0.10
#[derive( SystemSet, Clone, Debug, PartialEq, Eq, Hash )]

以下の指示に従って変更。

SystemLabel derives should be replaced with SystemSet. You will also need to add the Debug, PartialEq, Eq, and Hash traits to satisfy the new trait bounds.

hyoihyoi

フルスクリーンとウィンドウモードを切り替える関数

Bevy 0.9
use bevy::window::WindowMode::*;
fn toggle_window_mode( mut windows: ResMut<Windows> )
{   if let Some( window ) = windows.get_primary_mut()
    {   let mode = if window.mode() == Windowed { SizedFullscreen } else { Windowed };
        window.set_mode( mode );
    }
}
Bevy 0.10
use bevy::window::WindowMode::*;
fn toggle_window_mode( mut windows: Query<&mut Window> )
{   if let Ok( mut window ) = windows.get_single_mut()
    {   let mode = if window.mode == Windowed { SizedFullscreen } else { Windowed };
        window.mode = mode;
    }
}
  1. 関数の引数をResMut<Windows>からQuery<&mut Window>へ変更した(WindowResourceからComponetへクラスチェンジした影響)。それに合わせてwindows.get_primary_mut()windows.get_single_mut()へ変更した。
  2. ゲッタ・セッタがなくなった(?)らしく、Window構造体modeへの直接アクセスへ変更。
hyoihyoi

ECS Schedule v3 (1)

.add_system_set() SystemSet ::on_enter() ::on_update() ::on_exit()

Bevy 0.9
app
.add_system_set
(   SystemSet::on_enter( GameState::InitApp )    //<ENTER>
    .with_system( start_fetching_assets )        //Assetのロード開始
    .with_system( spawn_sprite_now_loading )     //アニメ用スプライトの生成
)
.add_system_set
(   SystemSet::on_update( GameState::InitApp )   //<UPDATE>
    .with_system( change_state_after_loading )   //ロード完了か判定しState変更
    .with_system( move_sprite_now_loading )      //ローディングアニメ
)
.add_system_set
(   SystemSet::on_exit( GameState::InitApp )     //<EXIT>
    .with_system( despawn_entity::<SpriteTile> ) //アニメ用スプライトの削除
    .with_system( spawn_game_frame )             //ゲームの枠の表示
);
Bevy 0.10 .add_system()を使う場合
app
.add_system( start_fetching_assets        .in_schedule( OnEnter( GameState::InitApp ) ) )
.add_system( spawn_sprite_now_loading     .in_schedule( OnEnter( GameState::InitApp ) ) )

.add_system( change_state_after_loading   .in_set( OnUpdate( GameState::InitApp ) ) )
.add_system( move_sprite_now_loading      .in_set( OnUpdate( GameState::InitApp ) ) )

.add_system( despawn_entity::<SpriteTile> .in_schedule( OnExit( GameState::InitApp ) ) )
.add_system( spawn_game_frame             .in_schedule( OnExit( GameState::InitApp ) ) );
Bevy 0.10 .add_systems()を使う場合 (1)
app
.add_systems
(   (   start_fetching_assets        .in_schedule( OnEnter( GameState::InitApp ) ),
        spawn_sprite_now_loading     .in_schedule( OnEnter( GameState::InitApp ) ),

        change_state_after_loading   .in_set( OnUpdate( GameState::InitApp ) ),
        move_sprite_now_loading      .in_set( OnUpdate( GameState::InitApp ) ),

        despawn_entity::<SpriteTile> .in_schedule( OnExit( GameState::InitApp ) ),
        spawn_game_frame             .in_schedule( OnExit( GameState::InitApp ) ),
    ),
);
Bevy 0.10 .add_systems()を使う場合 (2)
app
.add_systems
(   (   start_fetching_assets,
        spawn_sprite_now_loading, ).in_schedule( OnEnter( GameState::InitApp ) ),
)
.add_systems
(   (   change_state_after_loading,
        move_sprite_now_loading, ).in_set( OnUpdate( GameState::InitApp ) ),
)
.add_systems
(   (   despawn_entity::<SpriteTile>,
        spawn_game_frame, ).in_schedule( OnExit( GameState::InitApp ) ),
);
  1. .add_system_set()ではなく.add_system().add_systems()を使って関数を登録する。
  2. SystemSetというグルーピングの手段がなくなった代わりに、.add_systems()はタプルによって関数を一度に複数登録できる。MAXはいくつなんだろう?
  3. .in_schedule().in_set()といったメソッドは関数単体や(関数, 関数, …)というタプルに生やせる。
  4. ::on_enter() ::on_update() ::on_exit()の置き換えは下記の表のとおり。updateだけメソッドが違う理由は何だろう?
0.9 0.10
::on_enter(…) .in_schedule( OnEnter(…) )
::on_update(…) .in_set( OnUpdate(…) )
::on_exit(…) .in_schedule( OnExit(…) )
hyoihyoi

ECS Schedule v3 (2)

Stateの操作

Bevy 0.9
fn change_state( mut state: ResMut<State<GameState>> )
{   let _ = state.overwrite_set( GameState::Next );
}
Bevy 0.10
fn change_state( mut next_state: ResMut<NextState<GameState>> )
{   next_state.set( GameState::Next );
}
  1. Stateを変更する場合、ResMut<State<GameState>>を操作せず、ResMut<NextState<GameState>>にセッタを使う。
Bevy 0.9
fn xxx( state: Res<State<GameState>> )
{   let current = state.current();
    if *current == GameState::GameOver {}
}
Bevy 0.10
fn xxx( state: Res<State<GameState>> )
{   let current = state.0;
    if current == GameState::GameOver {}
}
  1. 現在のStateを知るには.0で値を取り出す。(.current()はなくなった?)
hyoihyoi

UI (1)

TextAlignment VerticalAlign HorizontalAlign Textの定義の変更

Bevy 0.9
pub struct TextAlignment {
    pub vertical: VerticalAlign,
    pub horizontal: HorizontalAlign,
}
pub enum VerticalAlign {
    Top,
    Center,
    Bottom,
}
pub enum HorizontalAlign {
    Left,
    Center,
    Right,
}
Bevy 0.10
pub enum TextAlignment {
    Left,
    Center,
    Right,
}
  1. VerticalAlignにあたる要素がなくなり、シンプルになった。
Bevy 0.9
pub struct Text {
    pub sections: Vec<TextSection, Global>,
    pub alignment: TextAlignment,
}
Bevy 0.10
pub struct Text {
    pub sections: Vec<TextSection>,
    pub alignment: TextAlignment,
    pub linebreak_behaviour: BreakLineOn,
}
  1. 構造体にlinebreak_behaviourが増えた。とりあえずなら..default()を使えば良いと思う。
hyoihyoi

UI (2)

Visibilityの定義の変更

Bevy 0.9
pub struct Visibility {
    pub is_visible: bool,
}
Bevy 0.10
pub enum Visibility {
    #[default]
    Inherited,
    Hidden,
    Visible,
}
  1. XXX.visibility.is_visibletrue falseをセットするのではなく(is_visibleはもう無い)、XXX.visibilityに直接Visibility::Inherited Visibility::Hiddenをセットする。
  2. InheritedVisibleの違いは、親子関係の継承の有無。

An entity with Visibility::Inherited will inherit the Visibility of its [Parent].
Note that an entity with Visibility::Visible will be visible regardless of whether the [Parent] entity is hidden.

hyoihyoi

ECS Schedule v3 (3)

.before() .label() .after()による実行順の調整

Bevy 0.9
app
.add_system_set
(   SystemSet::on_update( GameState::MainLoop )
    .before( Mark::DetectCollisions )               //<before>
    .with_system( player::scoring_and_clear_stage ) //スコアリング&クリア判定⇒StageClear
)
.add_system_set
(   SystemSet::on_update( GameState::MainLoop )
    .label( Mark::DetectCollisions )                //<label>
    .with_system( chasers::detect_collisions )      //衝突判定⇒GameOver
)
.add_system_set
(   SystemSet::on_update( GameState::MainLoop )
    .after( Mark::DetectCollisions )                //<after>
    .with_system( player::move_sprite )             //スプライト移動
    .with_system( chasers::move_sprite )            //スプライト移動
)
;
Bevy 0.10
app
.add_system
(   player::scoring_and_clear_stage            //スコアリング&クリア判定⇒StageClear
    .before( Mark::DetectCollisions )          //<before>
    .in_set( OnUpdate( GameState::MainLoop ) )
)
.add_system
(   chasers::detect_collisions                 //衝突判定⇒GameOver
    .in_set( Mark::DetectCollisions )          //<label>
    .in_set( OnUpdate( GameState::MainLoop ) )
)
.add_systems
(   (   player::move_sprite,                   //スプライト移動
        chasers::move_sprite,                  //スプライト移動
    )
    .after( Mark::DetectCollisions )           //<after>
    .in_set( OnUpdate( GameState::MainLoop ) )
)
;
  1. .label().in_set()に改名された。ソースコードが読みにくくなった気が…?
hyoihyoi

ECS Schedule v3 (4)

PAUSE処理にStateを利用していた場合の注意点

Bevy 0.9
pub fn pause_with_key
(   mut state: ResMut<State<GameState>>,
    mut inkey: ResMut<Input<KeyCode>>,
)
{   //入力がないなら関数脱出
    if ! inkey.just_pressed( KEY_PAUSE ) { return }

    //PAUSEのトグル処理
    if state.current().is_pause()
    {   let _ = state.pop(); //PAUSEから復帰
    }
    else
    {   let _ = state.push( GameState::Pause ); //PAUSEする
    }

    //NOTE: https://bevy-cheatbook.github.io/programming/states.html#with-input
    inkey.reset( KEY_PAUSE );
}
Bevy 0.10
pub fn pause_with_key
(   mut state: ResMut<State<GameState>>,
    mut old_state: Local<GameState>,
    mut inkey: ResMut<Input<KeyCode>>,
)
{    //入力がないなら関数脱出
    if ! inkey.just_pressed( KEY_PAUSE ) { return }

    //PAUSEのトグル処理
    if state.0.is_pause()
    {  state.0 = *old_state; //PAUSEから復帰
    }
    else
    {  *old_state = state.0;
        state.0 = GameState::Pause; //PAUSEする
    }

    //NOTE: https://bevy-cheatbook.github.io/programming/states.html#with-input
    inkey.reset( KEY_PAUSE );
}
  1. .push() .pop()は無くなった。(Stateの実装が簡素化されキュー処理しなくなった)
  2. ResMut<NextState<GameState>>を使わない。理由はPAUSEからの復帰時に.in_schedule( OnEnter( … ) )の処理が実行されて都合が悪いため。(元から同処理が無いなら気にならないかも)
  3. ResMut<State<GameState>>の設定値を直接操作する(state.0 = GameState::Pause;のように)。
  4. PAUSEからの復帰元を保存するにはLocal<GameState>が便利。
hyoihyoi

外部crateをコメントアウトした状態で、bevyのmaster branchを使い自作ピコゲーのコンパイルを通した。
コメントアウトしたcrateは音関係だからピコ音が鳴らないけど、WASMでも動いたし一段落。
後はbevyのv0.10の正式リリース(3/4?)を待って更新しよう。
https://github.com/hyoi/tigtag/tree/dev-migration_0.10

hyoihyoi

0.10のリリース前だけど一点。

ECS Schedule v3 (5)

.chain()の利用

Bevy 0.10 .before()、.after()を使った場合
app
.add_system
(   init_demoplay_record //demoでのrecordの初期化
    .before( Mark::MakeMapNewData )
    .in_schedule( OnEnter( GameState::TitleDemo ) )
)
.add_system
(   map::make_new_data //新マップのデータ作成
    .in_set( Mark::MakeMapNewData )
    .in_schedule( OnEnter( GameState::TitleDemo ) )
)
.add_systems
(   (   map::spawn_sprite,     //スプライトをspawnする
        player::spawn_sprite,  //スプライトをspawnする
        chasers::spawn_sprite, //スプライトをspawnする
    )
    .after( Mark::MakeMapNewData )
    .in_schedule( OnEnter( GameState::TitleDemo ) )
);
Bevy 0.10 .chain()を使った場合
app
.add_systems
(   (   init_demoplay_record,  //demoでのrecordの初期化
        map::make_new_data.in_set( Mark::MakeMapNewData ), //新マップのデータ作成
        map::spawn_sprite,     //スプライトをspawnする
        player::spawn_sprite,  //スプライトをspawnする
        chasers::spawn_sprite, //スプライトをspawnする
    )
    .chain()
    .in_schedule( OnEnter( GameState::TitleDemo ) )
);
  1. 単純に.before()→ラベル(.in_set())→.after()の順番をまとめるなら、.chain()が便利。
  2. 上の例で.chain()利用時に.in_set()を残したが、ソースコードの他の場所でこのラベルを目印に関数を追加するような使い方をしないのであれば、削除してよい。
このスクラップは2023/03/21にクローズされました