游戏状态

拆分一下单独的游戏状态,

#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)]  
pub enum GameState {  
    #[default]  
    Idle,  
    Setup,  
    Generating,  
    Play,  
    Pause,  
    Finish,  
}

生成拼图块

一块块生成,并且计数显示在UI上,所有块生成完后, 进入Play状态

app.add_systems(  
    PostUpdate,  
    (handle_tasks, count_spawned_piece).run_if(in_state(GameState::Generating)),  
);
 
fn count_spawned_piece(  
    mut text: Single<&mut Text, With<PieceCount>>,  
    generator: Res<JigsawPuzzleGenerator>,  
    mut game_state: ResMut<NextState<GameState>>,  
    q_pieces: Query<Entity, With<ColorImage>>,  
) {  
    let loaded_pieces = q_pieces.iter().count();  
    text.0 = format!("{}/{}", loaded_pieces, generator.pieces_count());  
    if loaded_pieces == generator.pieces_count() {  
        game_state.set(GameState::Play)  
    }  
}

游戏计时

先定义一个秒表和它的显示方式

#[derive(Resource, Deref, DerefMut, Debug)]  
pub struct GameTimer(pub Stopwatch);  
  
impl std::fmt::Display for GameTimer {  
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {  
        let elapsed = self.elapsed();  
        let seconds = elapsed.as_secs();  
        let minutes = seconds / 60;  
        let hours = minutes / 60;  
        write!(  
            f,  
            "{}",  
            format!("{:02}:{:02}:{:02}", hours, minutes % 60, seconds % 60)  
        )  
    }  
}

秒表(Stopwatch)需要用tick来驱动, 当我们在Play状态时, 调用system来计时和显示。在其他状态时,自动停止计时。

.add_systems(  
    Update,  
    (  
        update_game_time
    )  
        .run_if(in_state(GameState::Play)),  
)
 
fn update_game_time(  
    mut game_timer: ResMut<GameTimer>,  
    time: Res<Time>,  
    mut text: Single<&mut Text, With<TimerText>>,  
) {  
    game_timer.tick(time.delta());  
    text.0 = game_timer.to_string();  
}

游戏暂停

暂停时,显示暂停界面UI, 点击界面和按ESC都可以返回Play状态

// pause logic  
app.add_systems(OnEnter(GameState::Pause), setup_pause_ui)  
    .add_systems(OnExit(GameState::Pause), despawn_screen::<OnPauseScreen>)  
    .add_systems(Update, back_to_game.run_if(in_state(GameState::Pause)));

游戏结算

同样, 拼图完成/放弃时,显示结算页面,显示计时成绩,可以返回菜单页,或者重新开始一局。

// finish  
app.add_systems(  
    OnEnter(GameState::Finish),  
    (despawn_screen::<OnPlayScreen>, setup_finish_ui),  
)  
.add_systems(OnExit(GameState::Finish), despawn_screen::<OnFinishScreen>);

游戏页面