Latest commit fbab01a

use bevy::app::AppExit;
use bevy::ecs::schedule::ShouldRun;
use bevy::prelude::*;

/// 演示`SystemSet`如何搭配运行准则来使用

/// [`SystemLabel`]可以作为一个标签应用于系统和系统集,然后可以从其他系统中引用
/// TODO:This is useful in case a user wants to e.g. run _before_ or _after_ some label
/// 派生[`SystemLabel`]时,需要同时派生`Clone`、`Hash`、`Debug`、`PartialEq`、`Eq`
#[derive(Clone, Hash, Debug, PartialEq, Eq, SystemLabel)]
struct Physics;
#[derive(Clone, Hash, Debug, PartialEq, Eq, SystemLabel)]
struct PostPhysics;

// 用这个资源来停止此示例
#[derive(Default)]
struct Done(bool);

/// 这个用来说明:在[`SystemSet']中,单个系统也可以被标记,允许进一步微调运行顺序
#[derive(Clone, Hash, Debug, PartialEq, Eq, SystemLabel)]
enum PhysicsSystem {
    UpdateVelocity,
    Movement,
}

/// 这个例子实现了以下方案:
///
/// ```none
/// Physics                     (Criteria: App has run < 1.0 seconds)
///     \--> update_velocity        (via label PhysicsSystem::UpdateVelocity)
///     \--> movement               (via label PhysicsSystem::Movement)
/// PostPhysics                 (Criteria: Resource `done` is false)
///     \--> collision || sfx
/// Exit                        (Criteria: Resource `done` is true)
///     \--> exit
/// ```
///
/// `Physics`是一个包含两个系统的[`SystemSet`],
/// `Physics`的运行准则是“经过2秒后停止”,
/// `Physics`中的两个系统(`update_velocity`、`movement`)按指定顺序运行
///
/// `PostPhysics`的运行准则是“`Physics`完成后才开始运行”,
/// `PostPhysics`的条件是由“Done”资源确定的,在“false”时运行,“true”时停止
/// `PostPhysics`中的两个系统(`collision`、`sfx`)没有指定运行顺序
///
/// 最后,根据“Done”资源来确认要退出应用程序
fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .init_resource::<Done>()
        // 请注意,本例中添加的系统集明确设置了运行准则
        // TODO:See the `ecs/state.rs` example for a pattern where run criteria are set implicitly for common use cases- typically state transitions.
        // 一个系统集最多有一个运行准则,这意味着:
        // 执行`SystemSet::on_update(...)`之后再执行`.with_run_criteria(...)`,会覆盖状态过度准则
        .add_system_set(
            SystemSet::new()
                // 系统集内所有系统都会添加这个标签
                // 这个标签可以在其他所有地方引用,包括其他系统集
                .label(Physics)
                // 这个准则的作用:仅当"运行一秒"系统的输出为`ShouldRun::Yes`时,当前系统集才会运行
                .with_run_criteria(运行一秒)
                .with_system(
                    更新速度
                        // 这个标签只添加给`更新速度`系统
                        .label(PhysicsSystem::UpdateVelocity),
                )
                .with_system(
                    移动
                        // 只添加给`移动`系统
                        .label(PhysicsSystem::Movement)
                        // 明确地指定执行顺序
                        .after(PhysicsSystem::UpdateVelocity),
                ),
        )
        .add_system_set(
            SystemSet::new()
                .label(PostPhysics)
                // 这个系统集在`Physics`后面运行
                // 与`.after(..)`类似的有`.before(..)`
                .after(Physics)
                // 可以修改现有的运行准则结果
                // 这一步的目的是:TODO:Here we create a _not done_ criteria by piping the output of the `is_done` system and inverting the output.
                // 字符串也可以作为标签使用
                .with_run_criteria(RunCriteria::pipe("已完成", 反转.system()))
                // `collision`和`sfx`没有明确指定顺序,因此运行时两个系统的顺序不确定
                .with_system(碰撞)
                .with_system(sfx),
        )
        .add_system(
            退出.after(PostPhysics)
                .with_run_criteria(已完成.label("已完成")),
        )
        .run();
}

/// 运行准则的示例
/// 仅仅运行1秒,然后停止
fn 运行一秒(
    time: Res<Time>,
    mut done: ResMut<Done>,
) -> ShouldRun {
    let elapsed = time.seconds_since_startup();
    if elapsed < 1.0 {
        info!(
            "应该再次运行。经过时间/剩余时间:{:.2}秒/{:.2}秒",
            elapsed,
            1.0 - elapsed,
        );
        ShouldRun::Yes
    } else {
        done.0 = true;
        ShouldRun::No
    }
}

/// 另一个运行准则,只用了一个资源
fn 已完成(done: Res<Done>) -> ShouldRun {
    if done.0 {
        ShouldRun::Yes
    } else {
        ShouldRun::No
    }
}

/// 与[`RunCriteria::pipe`]一起使用,把传递过去的系统反转
fn 反转(input: In<ShouldRun>) -> ShouldRun {
    match input.0 {
        ShouldRun::No => ShouldRun::Yes,
        ShouldRun::Yes => ShouldRun::No,
        _ => unreachable!(),
    }
}

fn 更新速度() {
    info!("正在更新速度");
}

fn 移动() {
    info!("正在更新移动");
}

fn 碰撞() {
    info!("完成“Physics”,正在检查碰撞");
}

fn sfx() {
    info!("Physics已完成,播放一些sfx");
}

fn 退出(mut app_exit_events: EventWriter<AppExit>) {
    info!("正在退出...");
    app_exit_events.send(AppExit);
}