feat(scene): implement pause menu with UI manager and escape key toggling

Add PauseMenuUIManager class and integrate pause state into WorldScene.
Escape key toggles pause in ClientPlayer and PauseMenuUIManager.
Refactor Window mouse and imgui control to support pause state.
This commit is contained in:
2026-07-14 11:27:39 +08:00
parent d1db165ca3
commit 03f539eb9f
11 changed files with 187 additions and 74 deletions

View File

@@ -70,4 +70,5 @@ target_sources(${PROJECT_NAME}
ui/column_layout.cpp
ui/rect.cpp
ui/ui_manager.cpp
ui/pause_menu_ui_manager.cpp
)

View File

@@ -28,14 +28,13 @@ void App::cursor_position_callback(GLFWwindow* window, double xpos,
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
if (io.WantCaptureMouse && app->m_window.is_mouse_enable()) {
if (io.WantCaptureMouse && app->m_window.is_enable_imgui()) {
ImGui_ImplGlfw_CursorPosCallback(window, xpos, ypos);
return;
}
if (!app->m_window.is_mouse_enable()) {
app->m_scene_manager.handle_event(
MouseMoveEvent{static_cast<float>(xpos), static_cast<float>(ypos)});
}
app->m_scene_manager.handle_event(
MouseMoveEvent{static_cast<float>(xpos), static_cast<float>(ypos)});
}
void App::init(int argc, char** argv) {
handle_toml();
@@ -173,7 +172,7 @@ void App::key_callback(GLFWwindow* window, int key, int scancode, int action,
ASSERT_MSG(app, "nullptr");
// ImGui_ImplGlfw_CursorEnterCallback(window,
// !app->m_window.is_mouse_enable());
if (io.WantCaptureKeyboard && app->m_window.is_mouse_enable()) {
if (io.WantCaptureKeyboard && app->m_window.is_enable_imgui()) {
if ((key == GLFW_KEY_LEFT_ALT) && action == GLFW_PRESS) {
app->dispatch_event(KeyEvent{Key::LEFT_ALT, KeyAction::PRESS});
return;
@@ -532,7 +531,7 @@ void App::mouse_button_callback(GLFWwindow* window, int button, int action,
ImGuiIO& io = ImGui::GetIO();
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
if (io.WantCaptureMouse && app->m_window.is_mouse_enable()) {
if (io.WantCaptureMouse && app->m_window.is_enable_imgui()) {
ImGui_ImplGlfw_MouseButtonCallback(window, button, action, mods);
return;
}
@@ -582,7 +581,7 @@ void App::window_focus_callback(GLFWwindow* window, int focused) {
ImGuiIO& io = ImGui::GetIO();
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
if (io.WantCaptureMouse && app->m_window.is_mouse_enable()) {
if (io.WantCaptureMouse && app->m_window.is_enable_imgui()) {
ImGui_ImplGlfw_WindowFocusCallback(window, focused);
return;
}
@@ -616,7 +615,7 @@ void App::mouse_scroll_callback(GLFWwindow* window, double xoffset,
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
if (io.WantCaptureMouse && app->m_window.is_mouse_enable()) {
if (io.WantCaptureMouse && app->m_window.is_enable_imgui()) {
ImGui_ImplGlfw_ScrollCallback(window, xoffset, yoffset);
return;
}
@@ -627,7 +626,7 @@ void App::cursor_enter_callback(GLFWwindow* window, int entered) {
ImGuiIO& io = ImGui::GetIO();
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
if (io.WantCaptureMouse && app->m_window.is_mouse_enable()) {
if (io.WantCaptureMouse && app->m_window.is_enable_imgui()) {
ImGui_ImplGlfw_CursorEnterCallback(window, entered);
return;
}
@@ -637,8 +636,9 @@ void App::char_callback(GLFWwindow* window, unsigned int c) {
ImGuiIO& io = ImGui::GetIO();
App* app = static_cast<App*>(glfwGetWindowUserPointer(window));
ASSERT_MSG(app, "nullptr");
if (io.WantCaptureKeyboard && app->m_window.is_mouse_enable()) {
if (io.WantCaptureKeyboard && app->m_window.is_enable_imgui()) {
ImGui_ImplGlfw_CharCallback(window, c);
return;
}
}

View File

@@ -4,6 +4,7 @@
#include "Cubed/config.hpp"
#include "Cubed/debug_collector.hpp"
#include "Cubed/gameplay/client_world.hpp"
#include "Cubed/scene/world_scene.hpp"
namespace {} // namespace
@@ -582,6 +583,12 @@ bool ClientPlayer::handle_mouse_button_event(const MouseButtonEvent& e) {
}
bool ClientPlayer::handle_key_event(const KeyEvent& e) {
if (e.key == Key::ESCAPE && e.action == KeyAction::PRESS) {
m_world.world_scene().set_pause(true);
return true;
}
if (update_player_move_state(e.key, e.action)) {
return true;
}

View File

@@ -8,7 +8,8 @@ WorldScene::WorldScene(SceneManager& scene_manager)
: m_scene_manager(scene_manager), m_dev_panel(*this),
m_client_world(scene_manager.app().audio(), scene_manager.app().config(),
*this),
m_ui_manager(*this), m_argument(scene_manager.app().argument()) {}
m_pasue_menu(*this), m_ui_manager(*this),
m_argument(scene_manager.app().argument()) {}
WorldScene::~WorldScene() {
if (m_client) {
@@ -17,45 +18,61 @@ WorldScene::~WorldScene() {
}
void WorldScene::update(float dt) {
m_client_world.update(dt);
m_camera.update_move_camera();
m_client_world.get_audio().update_listener(m_camera.get_camera_pos(),
m_camera.get_camera_front(),
glm::vec3(0, 1, 0));
if (m_paused) {
m_pasue_menu.update(dt);
} else {
m_client_world.update(dt);
m_camera.update_move_camera();
m_client_world.get_audio().update_listener(m_camera.get_camera_pos(),
m_camera.get_camera_front(),
glm::vec3(0, 1, 0));
/*
const auto& player = m_client_world.get_player();
if (player_gait != player.get_gait()) {
player_gait = player.get_gait();
float fov = m_client_world.get("player.fov", 70.0f);
if (player_gait == Gait::WALK) {
m_renderer.update_fov(fov);
}
if (player_gait == Gait::RUN) {
m_renderer.update_fov(fov + 5.0f);
}
}*/
m_ui_manager.update(dt);
/*
const auto& player = m_client_world.get_player();
if (player_gait != player.get_gait()) {
player_gait = player.get_gait();
float fov = m_client_world.get("player.fov", 70.0f);
if (player_gait == Gait::WALK) {
m_renderer.update_fov(fov);
}
if (player_gait == Gait::RUN) {
m_renderer.update_fov(fov + 5.0f);
}
}*/
m_ui_manager.update(dt);
}
}
void WorldScene::render(Renderer& renderer) {
renderer.render_world(m_client_world);
renderer.render_dev_panel(m_dev_panel);
m_ui_manager.render(renderer);
if (m_paused) {
m_pasue_menu.render(renderer);
} else {
renderer.render_dev_panel(m_dev_panel);
}
}
bool WorldScene::handle_event(const Event& e) {
if (m_ui_manager.handle_event(e)) {
return true;
}
if (m_camera.handle_event(e)) {
return true;
}
// world event needs to be processed last
if (m_client_world.handle_event(e)) {
return true;
if (m_paused) {
if (m_pasue_menu.handle_event(e)) {
return true;
}
} else {
if (m_ui_manager.handle_event(e)) {
return true;
}
if (m_camera.handle_event(e)) {
return true;
}
// world event needs to be processed last
if (m_client_world.handle_event(e)) {
return true;
}
}
return false;
}
@@ -76,6 +93,7 @@ void WorldScene::on_enter() {
m_camera.camera_init(&m_client_world.get_player());
m_scene_manager.app().window().set_camera(&m_camera);
m_dev_panel.init();
m_pasue_menu.init();
m_ui_manager.init();
m_scene_manager.app().window().set_game_running(true);
@@ -95,4 +113,11 @@ Camera& WorldScene::camera() { return m_camera; }
SceneManager& WorldScene::scene_manager() { return m_scene_manager; }
ClientWorld& WorldScene::client_world() { return m_client_world; }
ServerWorld& WorldScene::server_world() { return m_server.server_world(); }
bool WorldScene::pause() const { return m_paused; }
void WorldScene::set_pause(bool pause) {
m_paused = pause;
auto& window = m_scene_manager.app().window();
Logger::info("pause {}", m_paused);
window.set_game_running(!m_paused);
}
} // namespace Cubed

View File

@@ -23,11 +23,9 @@ void Button::on_render(Renderer& renderer) {
bool Button::handle_mouse_move_event(const MouseMoveEvent& e) {
auto pos = m_background->pos();
if (e.xpos >= pos.x && e.xpos <= pos.x + width() && e.ypos >= pos.y &&
e.ypos <= pos.y + height()) {
m_hovered = true;
return true;
}
m_hovered = false;

View File

@@ -0,0 +1,41 @@
#include "Cubed/app.hpp"
#include "Cubed/render/renderer.hpp"
#include "Cubed/scene/scene_manager.hpp"
#include "Cubed/scene/world_scene.hpp"
#include "Cubed/ui/button.hpp"
#include "Cubed/ui/pasue_menu_ui_manager.hpp"
#include "Cubed/ui/rect.hpp"
namespace Cubed {
PauseMenuUIManager::PauseMenuUIManager(WorldScene& scene) : m_scene(scene) {}
void PauseMenuUIManager::init() {
auto rect = std::make_unique<Rect>(nullptr);
rect->set_fill(true);
rect->set_color(Color::BLACK);
rect->set_alpha(0.5f);
rect->set_anchor(Anchor::TOP_LEFT);
auto& renderer = m_scene.scene_manager().app().renderer();
rect->set_window_size(renderer.window_width(), renderer.window_height());
auto& back_main = rect->add_child<Button>();
auto& bg = back_main.set_background<Image>();
bg.set_image("texture/ui/button001.png",
m_scene.scene_manager().app().texture_manager());
auto& fg = back_main.set_foreground<Label>();
fg.set_text("return to main menu");
fg.set_scale(0.5f);
back_main.set_scale(5.0f);
back_main.set_anchor(Anchor::CENTER);
back_main.set_clicked([this]() { m_scene.scene_manager().request_pop(); });
m_root_widget = std::move(rect);
}
bool PauseMenuUIManager::handle_key_event(const KeyEvent& e) {
if (e.key == Key::ESCAPE && e.action == KeyAction::PRESS) {
m_scene.set_pause(false);
return true;
}
return UIManager::handle_key_event(e);
}
} // namespace Cubed

View File

@@ -73,14 +73,18 @@ bool Window::handle_key_event(const KeyEvent& e) {
return true;
}
if (e.key == Key::ESCAPE && e.action == KeyAction::PRESS) {
if (!m_game_running) {
glfwSetWindowShouldClose(m_window, GLFW_TRUE);
return true;
}
}
if (e.key == Key::LEFT_ALT && e.action == KeyAction::PRESS) {
toggle_mouse_able();
return true;
if (m_game_running) {
if (m_mouse_enable) {
disable_mouse();
set_imgui_enabled(false);
} else {
enable_mouse();
set_imgui_enabled(true);
}
return true;
}
}
return false;
@@ -94,9 +98,12 @@ bool Window::handle_window_resize_event(const WindowResizeEvent& e) {
bool Window::handle_mouse_button_event(const MouseButtonEvent& e) {
if (e.key == MouseKey::LEFT_BUTTON && e.action == KeyAction::PRESS) {
if (is_mouse_enable()) {
toggle_mouse_able();
return true;
if (m_game_running) {
if (m_mouse_enable) {
disable_mouse();
set_imgui_enabled(false);
return true;
}
}
}
return false;
@@ -209,39 +216,37 @@ void Window::toggle_fullscreen() {
m_config.set("window.height", windowed_height);
}
void Window::toggle_mouse_able() {
if (!m_game_running) {
m_mouse_enable = true;
}
// auto& io = ImGui::GetIO();
if (m_mouse_enable) {
// io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
// Logger::info("ImGuiConfigFlags_NoMouseCursorChange");
// ImGui::SetMouseCursor(ImGuiMouseCursor_None);
// glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
m_mouse_enable = false;
} else {
// io.ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange;
// Logger::info("Disable ImGuiConfigFlags_NoMouseCursorChange");
glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
m_mouse_enable = true;
}
void Window::enable_mouse() {
glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
m_mouse_enable = true;
}
void Window::disable_mouse() {
glfwSetInputMode(m_window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
if (m_camera) {
m_camera->reset_camera();
}
m_mouse_enable = false;
}
void Window::set_camera(Camera* camera) { m_camera = camera; }
Camera* Window::camera() { return m_camera; }
void Window::set_game_running(bool running) {
m_game_running = running;
m_mouse_enable = running;
toggle_mouse_able();
if (running) {
disable_mouse();
} else {
enable_mouse();
}
set_imgui_enabled(false);
Logger::info("Window Set Game Running {}", m_game_running);
}
void Window::should_close_window() { glfwSetWindowShouldClose(m_window, true); }
bool Window::is_enable_imgui() const { return m_imgui_enable; }
void Window::set_imgui_enabled(bool enable) { m_imgui_enable = enable; }
void Window::imgui_init() {
float dpi_scale_x, dpi_scale_y;
glfwGetWindowContentScale(m_window, &dpi_scale_x, &dpi_scale_y);