bevy 一个游戏引擎

code

git clone https://github.com/bevyengine/bevy
cargo run --example breakout

state

主要的数据结构:


#![allow(unused)]
fn main() {
// 用于操作游戏state, 比如 设置/替换/退出/进入 state
enum ScheduledOperation<T: Component + Clone + Eq> {
    Set(T),
    Replace(T),
    Pop,
    Push(T),
}

// 状态切换 如果是 PreStartup 则会进入 Startup
// 后面几个是 (leaving, entering), 表示从 leaving状态 切换到 entering状态
enum StateTransition<T: Component + Clone + Eq> {
    PreStartup,
    Startup,
    // The parameter order is always (leaving, entering)
    ExitingToResume(T, T),
    ExitingFull(T, T),
    Entering(T, T),
    Resuming(T, T),
    Pausing(T, T),
}
}

初始化状态, 并添加特定状态下要执行的系统集:


#![allow(unused)]
fn main() {
// 添加一个 state, 即此处的GameState, 设置初始状态为 Playing
// 并添加一个systemset, 设置 run_criteria 为 state_cleaner
App::build()
    .add_state(GameState::Playing)
    .add_system_set(SystemSet::on_enter(GameState::Playing).with_system(setup.system()))

// 这个函数主要用于处理 state.scheduled 和 state.transition
fn state_cleaner<T: Component + Clone + Eq>(
    mut state: ResMut<State<T>>,
    mut prep_exit: Local<bool>,
)
{
    match state.scheduled.take() {

        Some(ScheduledOperation::Set(next)) => {
            state.transition = Some(StateTransition::ExitingFull(
                state.stack.last().unwrap().clone(),
                next,
            ));
        }
        Some(ScheduledOperation::Replace(next)) => { ... }
        Some(ScheduledOperation::Push(next)) => { ... }
        Some(ScheduledOperation::Pop) => { ... }
        None => match state.transition.take() {
            Some(StateTransition::ExitingFull(p, n)) => { ... }
            Some(StateTransition::Pausing(p, n)) => { ... }
            Some(StateTransition::ExitingToResume(p, n)) => { ... }
            Some(StateTransition::PreStartup) => { ... }
        }
}

// 当执行 state.set(GameState::GameOver), 会设置 scheduled 的值, 就会改变 state.transition 的值, 后面特定的system_set就启动
pub fn set(&mut self, state: T) -> Result<(), StateError> {
    if self.stack.last().unwrap() == &state {
        return Err(StateError::AlreadyInState);
    }

    if self.scheduled.is_some() {
        return Err(StateError::StateAlreadyQueued);
    }

    self.scheduled = Some(ScheduledOperation::Set(state));
    Ok(())
}

}

这会添加两个systemset, 其中的system会按照添加的顺序添加到, 在stage中运行时, 这些systems会自上而下运行

.add_state(GameState::Playing) 这会初始化状态并放入全局组件中, 会添加一个 systemset, 并带有 state_cleaner 的 run_criteria, 用于处理 判断状态是否满足预期, 是否应该运行.

当执行 SystemSet::on_enter, SystemSet::on_exit, SystemSet::on_update时, 会添加不同的run_criteria, 用于判断特定的状态发生时是否应该执行. 比如:


#![allow(unused)]
fn main() {
pub fn on_update(s: T) -> RunCriteriaDescriptor {
    (|state: Res<State<T>>, pred: Local<Option<T>>| {
        // 当判断当前的state是预期的状态时, 同时事务还未开始时, return true, 表示执行
        state.stack.last().unwrap() == pred.as_ref().unwrap() && state.transition.is_none()
    })
    .system()
    .config(|(_, pred)| *pred = Some(Some(s.clone()))) // 这会把传入的状态即期望的状态保存到这个 system 的第二个参数, 即pred
    .chain(should_run_adapter::<T>.system())
    .after(DriverLabel::of::<T>())
    .label_discard_if_duplicate(StateCallback::Update.into_label(s))
}
}

#![allow(unused)]
fn main() {
// 初始化状态
Self {
    stack: vec![initial],
    transition: Some(StateTransition::PreStartup),
    scheduled: None,
    end_next_loop: false,
}

}

当 systemset 第一次运行时, stage.scheduled 为 None, 则设置 state.transition = Some(StateTransition::Startup), 表示执行Startup事务. 并 设置 prep_exit 为 true, 表示第一次运行完成.

先有 scheduled 再根据 scheduled 判断 transaction

当执行 state.set(GameState::GameOver) 时, 会验证: 1.当前的 stage.stack 的最后一项不能是要设置的状态 2.stage.scheduled 需要是空的, 然后更新状态: self.scheduled = Some(ScheduledOperation::Set(state))

2022.01.15

添加 WindowPlugin 插件
    会 添加大量的窗口初始化事件:
        WindowResized
        CreateWindow
        WindowCreated
        WindowCloseRequested
        CloseWindow
        CursorMoved
        ...

添加 WinitPlugin 插件
    初始化 winit 环境, 创建窗口, 并设置 runner(处理事件的loop)