feat(scene): add host game scene and world parameter handling

This commit is contained in:
2026-07-15 21:03:18 +08:00
parent e25de9f3b4
commit b9a013a916
12 changed files with 200 additions and 11 deletions

View File

@@ -18,6 +18,7 @@ public:
ChunkGenerator(ServerChunk& chunk); ChunkGenerator(ServerChunk& chunk);
static void init(); static void init();
static void init(unsigned seed);
static void reload(); static void reload();
static const unsigned& seed(); static const unsigned& seed();
static void seed(unsigned s); static void seed(unsigned s);

View File

@@ -0,0 +1,29 @@
#pragma once
#include "Cubed/scene/scene.hpp"
#include "Cubed/ui/host_game_ui.hpp"
namespace Cubed {
class SceneManager;
class HostGameScene : public Scene {
public:
HostGameScene(SceneManager& scene_manager);
~HostGameScene();
HostGameScene(const HostGameScene&) = delete;
HostGameScene(HostGameScene&&) = delete;
HostGameScene& operator=(const HostGameScene&) = delete;
HostGameScene& operator=(HostGameScene&&) = delete;
void update(float dt) override;
void render(Renderer& renderer) override;
bool handle_event(const Event& e) override;
void on_enter() override;
void on_leave() override;
void on_re_enter() override;
SceneManager& scene_manager();
private:
SceneManager& m_scene_manager;
HostGameUI m_ui;
};
} // namespace Cubed

View File

@@ -3,7 +3,7 @@
namespace Cubed { namespace Cubed {
class Renderer; class Renderer;
enum class SceneType { MAIN_MENU, WORLD, CREDITS, SETTINGS }; enum class SceneType { MAIN_MENU, WORLD, CREDITS, SETTINGS, HOST_GAME };
class Scene { class Scene {
public: public:

View File

@@ -6,6 +6,14 @@
#include <vector> #include <vector>
namespace Cubed { namespace Cubed {
class App; class App;
struct WorldSceneParam {
bool host_game = true;
std::optional<unsigned> seed = std::nullopt;
std::string ip{"127.0.0.1"};
int port = 25530;
};
class SceneManager { class SceneManager {
public: public:
SceneManager(const SceneManager&) = delete; SceneManager(const SceneManager&) = delete;
@@ -25,6 +33,7 @@ public:
void request_pop(); void request_pop();
App& app(); App& app();
WorldSceneParam& world_scene_param();
private: private:
enum class OperationType { PUSH, POP, CHANGE }; enum class OperationType { PUSH, POP, CHANGE };
@@ -34,6 +43,7 @@ private:
}; };
App& m_app; App& m_app;
WorldSceneParam m_world_param;
std::vector<std::unique_ptr<Scene>> m_pending_delete_scene; std::vector<std::unique_ptr<Scene>> m_pending_delete_scene;
std::optional<SceneOperation> m_operation; std::optional<SceneOperation> m_operation;
std::stack<std::unique_ptr<Scene>> m_scenes; std::stack<std::unique_ptr<Scene>> m_scenes;

View File

@@ -0,0 +1,16 @@
#pragma once
#include "Cubed/ui/ui_manager.hpp"
namespace Cubed {
class HostGameScene;
class HostGameUI : public UIManager {
public:
HostGameUI(HostGameScene& scene);
void init() override;
void on_re_enter();
private:
HostGameScene& m_scene;
};
} // namespace Cubed

View File

@@ -78,4 +78,7 @@ target_sources(${PROJECT_NAME}
scene/settings_scene.cpp scene/settings_scene.cpp
ui/combo_button.cpp ui/combo_button.cpp
ui/text_field.cpp ui/text_field.cpp
scene/host_game_scene.cpp
scene/join_game_scene.cpp
ui/host_game_ui.cpp
) )

View File

@@ -109,7 +109,11 @@ ChunkGenerator::ChunkGenerator(ServerChunk& chunk) : m_chunk(chunk) {
void ChunkGenerator::init() { void ChunkGenerator::init() {
std::random_device d; std::random_device d;
m_generator_seed = d(); init(d());
}
void ChunkGenerator::init(unsigned seed) {
m_generator_seed = seed;
Logger::info("Chunk Generator Seed {}", m_generator_seed); Logger::info("Chunk Generator Seed {}", m_generator_seed);
PerlinNoise3D::init(m_generator_seed); PerlinNoise3D::init(m_generator_seed);
PerlinNoise2D::init(m_generator_seed); PerlinNoise2D::init(m_generator_seed);

View File

@@ -0,0 +1,18 @@
#include "Cubed/scene/host_game_scene.hpp"
namespace Cubed {
HostGameScene::HostGameScene(SceneManager& scene_manager)
: m_scene_manager(scene_manager), m_ui(*this) {}
HostGameScene::~HostGameScene() {}
void HostGameScene::update(float dt) { m_ui.update(dt); }
void HostGameScene::render(Renderer& renderer) { m_ui.render(renderer); }
bool HostGameScene::handle_event(const Event& e) {
return m_ui.handle_event(e);
}
void HostGameScene::on_enter() { m_ui.init(); }
void HostGameScene::on_leave() {}
void HostGameScene::on_re_enter() { m_ui.on_re_enter(); }
SceneManager& HostGameScene::scene_manager() { return m_scene_manager; }
} // namespace Cubed

View File

@@ -1,6 +1,7 @@
#include "Cubed/scene/scene_manager.hpp" #include "Cubed/scene/scene_manager.hpp"
#include "Cubed/scene/credits_scene.hpp" #include "Cubed/scene/credits_scene.hpp"
#include "Cubed/scene/host_game_scene.hpp"
#include "Cubed/scene/main_menu_scene.hpp" #include "Cubed/scene/main_menu_scene.hpp"
#include "Cubed/scene/settings_scene.hpp" #include "Cubed/scene/settings_scene.hpp"
#include "Cubed/scene/world_scene.hpp" #include "Cubed/scene/world_scene.hpp"
@@ -97,6 +98,8 @@ std::unique_ptr<Scene> SceneManager::create_scene(SceneType type) {
return std::make_unique<CreditsScene>(*this); return std::make_unique<CreditsScene>(*this);
case SceneType::SETTINGS: case SceneType::SETTINGS:
return std::make_unique<SettingsScene>(*this); return std::make_unique<SettingsScene>(*this);
case SceneType::HOST_GAME:
return std::make_unique<HostGameScene>(*this);
} }
std::string err = std::format("Unknown Scene"); std::string err = std::format("Unknown Scene");
@@ -105,4 +108,5 @@ std::unique_ptr<Scene> SceneManager::create_scene(SceneType type) {
} }
App& SceneManager::app() { return m_app; } App& SceneManager::app() { return m_app; }
WorldSceneParam& SceneManager::world_scene_param() { return m_world_param; }
} // namespace Cubed } // namespace Cubed

View File

@@ -97,15 +97,20 @@ bool WorldScene::handle_event(const Event& e) {
e); e);
} }
void WorldScene::on_enter() { void WorldScene::on_enter() {
auto& param = m_scene_manager.world_scene_param();
if (param.seed) {
ChunkGenerator::init(*param.seed);
param.seed = std::nullopt;
} else {
ChunkGenerator::init();
}
ChunkGenerator::init(); if (param.host_game) {
m_server.start_server(param.port);
if (!m_argument.is_client) {
m_server.start_server(m_argument.port);
} }
m_client = std::make_shared<NetworkClient>(m_client_world); m_client = std::make_shared<NetworkClient>(m_client_world);
m_client->start(m_argument.ip, m_argument.port); m_client->start(param.ip, param.port);
// init will send packet // init will send packet
m_client_world.init(m_argument.player, m_client); m_client_world.init(m_argument.player, m_client);

92
src/ui/host_game_ui.cpp Normal file
View File

@@ -0,0 +1,92 @@
#include "Cubed/ui/host_game_ui.hpp"
#include "Cubed/app.hpp"
#include "Cubed/scene/host_game_scene.hpp"
#include "Cubed/scene/scene_manager.hpp"
#include "Cubed/ui/button.hpp"
#include "Cubed/ui/column_layout.hpp"
#include "Cubed/ui/image.hpp"
#include "Cubed/ui/text_field.hpp"
namespace Cubed {
HostGameUI::HostGameUI(HostGameScene& scene) : m_scene(scene) {}
void HostGameUI::init() {
auto bi = std::make_unique<Image>(nullptr);
auto& renderer = m_scene.scene_manager().app().renderer();
auto& texture_manager = m_scene.scene_manager().app().texture_manager();
bi->set_window_size(renderer.window_width(), renderer.window_height());
bi->set_anchor(Anchor::TOP_LEFT);
bi->set_image("texture/ui/background.png", texture_manager);
bi->set_fill(true);
auto& rect = bi->add_child<Rect>();
rect.set_fill(true);
rect.set_alpha(0.7f);
rect.set_color(Color::BLACK);
auto& layout = rect.add_child<ColumnLayout>();
layout.set_anchor(Anchor::CENTER);
layout.set_offset({0, 20});
layout.set_spacing(20.0f);
auto& param = m_scene.scene_manager().world_scene_param();
param.host_game = true;
{
auto& text_seed = layout.add_child<TextField>();
text_seed.set_show_text("WorldSeed");
text_seed.set_default_image(texture_manager);
text_seed.set_on_finish([this, &text_seed]() {
unsigned seed = 0;
auto& text = text_seed.input_text();
auto r =
std::from_chars(text.data(), text.data() + text.size(), seed);
if (r.ec != std::errc{} || r.ptr != text.data() + text.size()) {
Logger::error("Invalid seed: {}", text);
return;
}
m_scene.scene_manager().world_scene_param().seed = seed;
});
}
{
auto& text_port = layout.add_child<TextField>();
text_port.set_default_image(texture_manager);
text_port.set_show_text("Port");
text_port.set_on_finish([this, &text_port]() {
int port = 25530;
auto& text = text_port.input_text();
auto r =
std::from_chars(text.data(), text.data() + text.size(), port);
if (r.ec != std::errc{} || r.ptr != text.data() + text.size()) {
Logger::error("Invalid port: {}", text);
return;
}
if (port > 65535 || port < 0) {
Logger::error("Port {} out of range", port);
return;
}
m_scene.scene_manager().world_scene_param().port = port;
});
}
{
auto& button = layout.add_child<Button>();
button.set_default_image(texture_manager);
button.set_text("Start Game");
button.set_clicked([this, &button]() {
button.set_enable(false);
m_scene.scene_manager().request_change(SceneType::WORLD);
});
}
{
auto& button = layout.add_child<Button>();
button.set_default_image(texture_manager);
button.set_text("Return");
button.set_clicked([this, &button]() {
button.set_enable(false);
m_scene.scene_manager().request_pop();
});
}
m_root_widget = std::move(bi);
}
void HostGameUI::on_re_enter() {}
} // namespace Cubed

View File

@@ -32,10 +32,10 @@ void MainMenuUIManager::init() {
start_game_button.set_background_image("texture/ui/button001.png", start_game_button.set_background_image("texture/ui/button001.png",
texture_manager); texture_manager);
start_game_button.set_text("Start Game"); start_game_button.set_text("Host Game");
start_game_button.set_clicked([this, &start_game_button]() { start_game_button.set_clicked([this, &start_game_button]() {
start_game_button.set_enable(false); start_game_button.set_enable(false);
m_scene.scene_manager().request_push(SceneType::WORLD); m_scene.scene_manager().request_push(SceneType::HOST_GAME);
}); });
m_pending_enable.emplace_back(&start_game_button); m_pending_enable.emplace_back(&start_game_button);
} }
@@ -67,7 +67,7 @@ void MainMenuUIManager::init() {
auto& exit_game = layout.add_child<Button>(); auto& exit_game = layout.add_child<Button>();
exit_game.set_background_image("texture/ui/button001.png", exit_game.set_background_image("texture/ui/button001.png",
texture_manager); texture_manager);
exit_game.set_text("Exit"); exit_game.set_text("Quit");
exit_game.set_clicked([this]() { exit_game.set_clicked([this]() {
m_scene.scene_manager().app().window().should_close_window(); m_scene.scene_manager().app().window().should_close_window();
@@ -76,11 +76,18 @@ void MainMenuUIManager::init() {
m_widgets.try_emplace("background", image.get()); m_widgets.try_emplace("background", image.get());
m_widgets.try_emplace("main menu layout", &layout); m_widgets.try_emplace("main menu layout", &layout);
{ {
constexpr float SCALE = 0.5f;
auto& info_layout = image->add_child<ColumnLayout>(); auto& info_layout = image->add_child<ColumnLayout>();
info_layout.set_anchor(Anchor::BOTTOM_LEFT); info_layout.set_anchor(Anchor::BOTTOM_LEFT);
info_layout.set_spacing(10); info_layout.set_spacing(10);
info_layout.set_child_anchor(ColumnLayoutAnchor::LEFT);
auto& player_name = info_layout.add_child<Label>();
player_name.set_scale(SCALE);
player_name.set_text(std::format(
"Player: {}", m_scene.scene_manager().app().argument().player));
auto& version = info_layout.add_child<Label>(); auto& version = info_layout.add_child<Label>();
version.set_scale(0.5f); version.set_scale(SCALE);
std::string version_str = "Cubed: " CUBED_VERSION; std::string version_str = "Cubed: " CUBED_VERSION;
#ifdef DEBUG_MODE #ifdef DEBUG_MODE
version_str.append("-debug"); version_str.append("-debug");