#窗口 #bevy #持久化 #bevy插件 #游戏开发

bevy-persistent-windows

一个Bevy插件,用于轻松创建和管理记住位置的窗口

10个版本 (5个重大更新)

0.6.1 2024年7月28日
0.5.2 2024年4月26日
0.5.1 2024年3月26日
0.4.0 2023年11月7日

#401 in 游戏开发

Download history 157/week @ 2024-04-26 2/week @ 2024-05-03 3/week @ 2024-05-10 7/week @ 2024-05-17 3/week @ 2024-05-24 3/week @ 2024-05-31 4/week @ 2024-06-07 7/week @ 2024-06-14 23/week @ 2024-06-21 139/week @ 2024-07-05 13/week @ 2024-07-12 119/week @ 2024-07-26 18/week @ 2024-08-02

每月160次下载

MIT/Apache

39KB
232

bevy-persistent-windows

一个Bevy插件,用于轻松创建和管理记住位置的窗口。

背景

当你在开发游戏时,经常重启,你可能(可以理解地)希望窗口保持在上次运行的位置。在每个你创建的项目中手动实现这一点是容易出错的,并且费时(相信我,我知道)。这个插件旨在使其尽可能无缝!

安装

我们将使用bevy-persistent来持久化存储窗口状态,所以让我们先添加它

cargo add bevy-persistent --features all  # you probably don't need all features, see installation section of bevy-persistent to learn more

现在让我们添加bevy-persistent-windows以使我们的窗口持久化

cargo add bevy-persistent-windows

使用

前缀

如前所述,我们将使用bevy-persistent来持久化存储窗口状态,所以让我们先引入它

use bevy_persistent::prelude::*;

我们需要WindowStatePersistentWindowBundlePersistentWindowsPlugin类型来使用库,它们是从前缀模块导出的

use bevy_persistent_windows::prelude::*;

设置

让我们从在main中创建一个App开始

let mut app = App::new();

我们将向此应用程序添加默认插件,但我们应该编辑窗口插件以避免创建默认主窗口

let window_plugin = WindowPlugin { primary_window: None, ..Default::default() };
app.add_plugins(DefaultPlugins.set(window_plugin).build());

我们需要一个地方来存储窗口状态,以便稍后恢复窗口

let state_directory = dirs::data_dir()
    .expect("failed to get the platforms data directory")
    .join("your-amazing-game")
    .join("state");

是时候创建主窗口了

app.world_mut().spawn((
    PrimaryWindow,
    PersistentWindowBundle {
        window: Window { title: "I am the primary window!".to_owned(), ..Default::default() },
        state: Persistent::<WindowState>::builder()
            .name("primary window state")
            .format(StorageFormat::Toml)
            .path(state_directory.join("primary-window.toml"))
            .default(WindowState::windowed(1280, 720))
            .revertible(true)
            .revert_to_default_on_deserialization_errors(true)
            .build()
            .expect("failed to create the persistent primary window state"),
    },
));

当然,你可以自由地生成额外的窗口,不需要PrimaryWindow组件!

一旦完成,你可以在应用程序中添加PersistentWindowsPlugin插件

app.add_plugins(PersistentWindowsPlugin);

并运行你的游戏

app.run();

你会在你的最好显示器中心看到一个1280x720的窗口出现,移动它,调整大小,并玩弄它。现在关闭应用程序,再次运行它。你会发现窗口将在完全相同的监视器、完全相同的分辨率和完全相同的位置打开!

请参阅examples/setup.rs以获取完整示例!

控制

您可能希望以编程方式控制持久窗口。您可以编辑窗口本身,但如果要使更改持久,则应直接修改窗口状态!

比如说,当按下空格键时,您想切换全屏,您可以将此系统添加到您的应用程序中

fn fullscreen_toggle(
    keyboard_input: Res<Input<KeyCode>>,
    mut query: Query<&mut Persistent<WindowState>, With<PrimaryWindow>>,
) {
    if keyboard_input.just_pressed(KeyCode::Space) {
        let mut primary_window_state = query.get_single_mut().unwrap();

        if primary_window_state.mode == WindowMode::Windowed {
            primary_window_state.mode = WindowMode::Fullscreen;
        } else {
            primary_window_state.mode = WindowMode::Windowed;
        }

        primary_window_state.persist().ok();
    }
}

或者,如果您想使用箭头键移动窗口,并使用ctrl + 箭头键调整窗口大小,则可以使用此系统

fn movement(
    time: Res<Time>,
    keyboard_input: Res<Input<KeyCode>>,
    mut query: Query<&mut Persistent<WindowState>, With<PrimaryWindow>>,
) {
    let mut position_change = (0.0, 0.0);
    let mut resolution_change = (0.0, 0.0);

    let change = if keyboard_input.pressed(KeyCode::ControlLeft) {
        &mut resolution_change
    } else {
        &mut position_change
    };

    if keyboard_input.pressed(KeyCode::Up) {
        change.1 -= 3.0 * time.delta().as_millis() as f32;
    }
    if keyboard_input.pressed(KeyCode::Left) {
        change.0 -= 3.0 * time.delta().as_millis() as f32;
    }
    if keyboard_input.pressed(KeyCode::Down) {
        change.1 += 3.0 * time.delta().as_millis() as f32;
    }
    if keyboard_input.pressed(KeyCode::Right) {
        change.0 += 3.0 * time.delta().as_millis() as f32;
    }

    if position_change == (0.0, 0.0) && resolution_change == (0.0, 0.0) {
        return;
    }

    let mut primary_window_state = query.get_single_mut().unwrap();
    if let Some(resolution) = &mut primary_window_state.resolution {
        resolution.0 = ((resolution.0 as f32) + (resolution_change.0)) as u32;
        resolution.1 = ((resolution.1 as f32) + (resolution_change.1)) as u32;
    }
    if let Some(position) = &mut primary_window_state.position {
        position.0 += position_change.0 as i32;
        position.1 += position_change.1 as i32;
    }
}

(ps:有人想用持久窗口写一个蛇的克隆游戏吗?)

有关完整示例,请参阅examples/controlling.rs

生成

当您想生成额外的窗口时,只需像在设置中那样生成一个新的PersistentWindowBundle即可

fn spawn_persistent_window(
    mut commands: Commands,
    persistent_windows: Query<(), (With<Persistent<WindowState>>, With<Window>)>,
) {
    let state_directory = dirs::data_dir()
        .expect("failed to get the platforms data directory")
        .join("your-amazing-game")
        .join("state");

    let persistent_window_count = persistent_windows.iter().len();
    commands.spawn((
        PersistentWindowBundle {
            window: Window {
                title: format!("I am #{}", persistent_window_count),
                ..Default::default()
            },
            state: Persistent::<WindowState>::builder()
                .name(format!("window #{} state", persistent_window_count))
                .format(StorageFormat::Toml)
                .path(state_directory.join(format!("window-{}.toml", persistent_window_count)))
                .default(WindowState::windowed(400, 400))
                .revertible(true)
                .revert_to_default_on_deserialization_errors(true)
                .build()
                .unwrap_or_else(|error| {
                    panic!(
                        "failed to create the persistent window #{} state: {}",
                        persistent_window_count, error
                    )
                }),
        },
    ));
}

当此系统运行时,它将在您的显示器中心创建一个400x400的持久窗口。

有关完整示例,请参阅examples/spawning.rs

示例

您可以使用以下命令运行所有提到的示例

cargo run --release --example name-of-the-example

限制

  • 如果您是一个喜欢将窗口的一半放在一个显示器上,另一半放在另一个显示器上的精神病患者,那么我有一个坏消息。Bevy会将窗口剪辑到它们所在的显示器上,因此无法完全恢复窗口状态。
  • 在应用程序开始运行之后生成的持久窗口无法确定最佳显示器。这是因为WinitPlugin在应用程序开始运行之前从世界中移除了EventLoop,而没有事件循环,我找不到获取可用显示器的方法。

如果您知道如何克服这些限制,请告诉我!

许可证

bevy-persistent-windows是免费、开源和许可的,就像Bevy一样!

此存储库中的所有代码都双重许可,适用于以下任一

这意味着您可以选择您喜欢的许可证!

依赖项

~38–74MB
~1.5M SLoC