Steel教程 - 主菜单

主菜单

通常游戏都会有一个主菜单,我们可以从主菜单开始游戏,游戏失败后回到主菜单,我们可以重新开始游戏。本章我们在Ball游戏中实现一个简单的主菜单。

标记显示主菜单的MainMenu组件

我们首先创建一个MainMenu组件,用来标记当前是否显示主菜单:

#[derive(Edit, Component, Default)]
struct MainMenu;

并注册MainMenu组件:

#[no_mangle]
pub fn create() -> Box<dyn App> {
    SteelApp::new()
        ...
        .register_component::<MainMenu>()
        ...
}

显示主菜单的系统main_menu_system

如果存在MainMenu组件,则通过系统main_menu_system来显示主菜单:

fn main_menu_system(
    main_menu_component: View<MainMenu>,
    egui_ctx: UniqueView<EguiContext>,
    mut scene_manager: UniqueViewMut<SceneManager>,
) {
    for _ in main_menu_component.iter() {
        egui::CentralPanel::default().show(&egui_ctx, |ui| {
            let available_size = ui.available_size();
            let button_center = egui::pos2(available_size.x / 2.0, available_size.y / 2.0);
            let button_size = egui::vec2(200.0, 100.0);
            let button_rect = egui::Rect::from_center_size(button_center, button_size);
            if ui
                .put(
                    button_rect,
                    egui::Button::new(egui::RichText::new("Start Game").size(30.0)),
                )
                .clicked()
            {
                scene_manager.switch_scene("game.scene".into());
            }
        });
    }
}

我们不把主菜单放到游戏场景,而是把主菜单单独放到另一个场景,当用户点击开始游戏按钮“Start Game”的时候,通过SceneManager的switch_scene方法跳转到游戏场景。

主菜单场景和游戏场景分开主要有以下好处:

  • 主菜单界面不需要加载游戏场景的所有实体;
  • 每次从主菜单场景跳转到游戏场景时,游戏场景的所有实体和组件自动还原,不需要我们手动做还原场景的工作。

我们把main_menu_system放到Schedule::PreUpdate中。Schedule::PreUpdate在编辑器中没有运行游戏的时候也会执行,这样我们在编辑器的游戏窗口一直能看到主菜单界面显示效果:

#[no_mangle]
pub fn create() -> Box<dyn App> {
    SteelApp::new()
        ...
        .add_system(Schedule::PreUpdate, main_menu_system)
        ...
}

创建主菜单场景

首先编译一下刚才写的代码,就可以开始创建我们的主菜单场景了。

点击编辑器顶部菜单的“Scene -> New”按钮创建一个新的场景,添加一个名字为“Main Menu”的实体,删除这个实体的Renderer2D组件和Transform组件,然后添加MainMenu组件。

这样我们的主菜单场景就完成了,点击编辑器顶部菜单的“Scene -> Save As”按钮,设置场景的名字为“main”,点击保存即可。

切换到游戏场景窗口,在没有运行游戏的时候,应该就可以看到主菜单的显示效果了:

请添加图片描述

游戏失败后回到主菜单

最后我们实现一个游戏失败后,延迟5秒回到主菜单功能。

为了实现5秒的计时,我们在Lose组件上添加剩余时间记录:

#[derive(Component)]
struct Lose {
    lose_time: f32,
}

impl Default for Lose {
    fn default() -> Self {
        Lose { lose_time: 5.0 }
    }
}

在border_check_system中,我们通过Lose的default方法来创建Lose组件:

fn border_check_system(
    border: View<Border>,
    ball: View<Ball>,
    mut lose: ViewMut<Lose>,
    col2d: View<Collider2D>,
    physics2d_manager: UniqueView<Physics2DManager>,
) {
    ...
    if border_entity != EntityId::dead() {
        lose.add_component_unchecked(border_entity, Lose::default());
    }
}

最后修改我们的lose_system实现5秒计时结束后切换到主菜单场景:

fn lose_system(
    mut lose: ViewMut<Lose>,
    time: UniqueView<Time>,
    egui_ctx: UniqueView<EguiContext>,
    mut scene_manager: UniqueViewMut<SceneManager>,
) {
    for lose in (&mut lose).iter() {
        ...
        lose.lose_time -= time.delta();
        if lose.lose_time < 0.0 {
            scene_manager.switch_scene("main.scene".into());
        }
    }
}

我们通过Time单例的delta方法获取上一帧的耗时,然后在lose_time中减去上一帧的耗时,直到lose_time小于0,就说明过了5秒了,我们就使用SceneManager单例的switch_scene方法切换到“main.scene”。

最后编译代码,在主菜单场景运行游戏。点击开始游戏按钮,切换到游戏场景,游戏失败后5秒会自动切回主菜单场景,这样我们的游戏界面形成了闭环,用户不再需要重新运行游戏程序来重新开始游戏了!

上一章:游戏失败

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SSSxCCC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值