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 游戏开发
每月160次下载
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::*;
我们需要WindowState、PersistentWindowBundle和PersistentWindowsPlugin类型来使用库,它们是从前缀模块导出的
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一样!
此存储库中的所有代码都双重许可,适用于以下任一
- MIT许可证 (LICENSE-MIT 或 https://opensource.org/licenses/MIT)
- Apache许可证,版本2.0 (LICENSE-APACHE 或 https://apache.ac.cn/licenses/LICENSE-2.0)
这意味着您可以选择您喜欢的许可证!
依赖项
~38–74MB
~1.5M SLoC