From 48b70b0bc8519a4dfa591db7009fd34c5c34c698 Mon Sep 17 00:00:00 2001 From: binaryDiv Date: Sat, 18 Jul 2026 21:53:36 +0200 Subject: [PATCH] Implement scene system; move render loop from Game to Engine --- src/rutile/app.cppm | 11 ++- src/rutile/core/engine.cppm | 53 ++++++++++++-- src/rutile/core/scenes/base_scene.cppm | 27 +++++++ src/rutile/core/scenes/scene_manager.cppm | 26 +++++++ src/rutile/game/game.cppm | 83 ++-------------------- src/rutile/game/scenes/game_scene.cppm | 85 +++++++++++++++++++++++ 6 files changed, 196 insertions(+), 89 deletions(-) create mode 100644 src/rutile/core/scenes/base_scene.cppm create mode 100644 src/rutile/core/scenes/scene_manager.cppm create mode 100644 src/rutile/game/scenes/game_scene.cppm diff --git a/src/rutile/app.cppm b/src/rutile/app.cppm index 37deff0..53fa8bb 100644 --- a/src/rutile/app.cppm +++ b/src/rutile/app.cppm @@ -47,12 +47,10 @@ export namespace rutile return sdl::AppResult::Continue; } - sdl::AppResult handle_event(const sdl::Event& event) + sdl::AppResult handle_event(const sdl::Event& event) const { try { - if (!engine_->handle_event(event)) { - game_->handle_event(event); - } + engine_->handle_event(event); } catch (const std::runtime_error& e) { std::cerr << "Unhandled exception during event handling: " << e.what() << '\n'; @@ -62,12 +60,11 @@ export namespace rutile return engine_->keep_running() ? sdl::AppResult::Continue : sdl::AppResult::Success; } - sdl::AppResult iterate() + sdl::AppResult iterate() const { try { engine_->update(); - game_->update(); - game_->render(); + engine_->render(); } catch (const std::runtime_error& e) { std::cerr << "Unhandled exception during updating: " << e.what() << '\n'; diff --git a/src/rutile/core/engine.cppm b/src/rutile/core/engine.cppm index 5ac1379..52e3014 100644 --- a/src/rutile/core/engine.cppm +++ b/src/rutile/core/engine.cppm @@ -8,6 +8,7 @@ import std; import rutile.config; import rutile.core.render_server; import rutile.core.resource_server; +import rutile.core.scenes.scene_manager; import rutile.core.ui.ui_server; import wrappers.sdl; @@ -16,7 +17,7 @@ export namespace rutile class Engine { // Whether this class is currently instantiated (to prevent multiple instances) - static bool instantiated_; + static Engine* instance_; // If this is set to false, the application will exit bool keep_running_ = true; @@ -24,6 +25,7 @@ export namespace rutile sdl::Window window_; RenderServer render_server_; ResourceServer resource_server_; + SceneManager scene_manager_; UIServer ui_server_; // Private constructor @@ -33,7 +35,7 @@ export namespace rutile resource_server_{render_server_.get_renderer()}, ui_server_{render_server_.get_renderer(), window_} { - instantiated_ = true; + instance_ = this; } public: @@ -47,13 +49,13 @@ export namespace rutile ~Engine() { - instantiated_ = false; + instance_ = nullptr; } static std::unique_ptr create() { // Prevent the class from being instantiated multiple times - assert(!instantiated_); + assert(!instance_); auto [sdl_window, sdl_renderer] = sdl::create_window_and_renderer( config::get_window_title(), @@ -72,6 +74,13 @@ export namespace rutile // NOLINTEND } + // Get singleton instance of engine + static Engine& get() + { + assert(instance_); + return *instance_; + } + bool keep_running() const { return keep_running_; @@ -92,6 +101,11 @@ export namespace rutile return resource_server_; } + SceneManager& get_scene_manager() + { + return scene_manager_; + } + UIServer& get_ui_server() { return ui_server_; @@ -110,11 +124,38 @@ export namespace rutile return true; } + const auto scene = scene_manager_.get_current_scene(); + if (scene && scene->handle_event(event)) { + return true; + } + return false; } - void update() + void update() const { + // Update current scene + if (const auto scene = scene_manager_.get_current_scene()) { + scene->update(); + } + + // Update UI + ui_server_.update(); + } + + void render() const + { + render_server_.start_frame(); + + // Render current scene + if (const auto scene = scene_manager_.get_current_scene()) { + scene->render(render_server_); + } + + // Render UI + ui_server_.render(); + + render_server_.finish_frame(); } void shutdown() @@ -122,5 +163,5 @@ export namespace rutile } }; - bool Engine::instantiated_ = false; + Engine* Engine::instance_ = nullptr; } diff --git a/src/rutile/core/scenes/base_scene.cppm b/src/rutile/core/scenes/base_scene.cppm new file mode 100644 index 0000000..e815114 --- /dev/null +++ b/src/rutile/core/scenes/base_scene.cppm @@ -0,0 +1,27 @@ +module; + +export module rutile.core.scenes.base_scene; + +import std; +import rutile.core.render_server; +import rutile.core.resource_server; +import wrappers.sdl; + +export namespace rutile +{ + class BaseScene + { + public: + // Always define virtual destructor in abstract classes + virtual ~BaseScene() = default; + + // Handle an SDL event. Return true if the event has been handled. + virtual bool handle_event(const sdl::Event& event) = 0; + + // Update the scene + virtual void update() = 0; + + // Render the scene + virtual void render(const RenderServer& render_server) = 0; + }; +} diff --git a/src/rutile/core/scenes/scene_manager.cppm b/src/rutile/core/scenes/scene_manager.cppm new file mode 100644 index 0000000..8e063f8 --- /dev/null +++ b/src/rutile/core/scenes/scene_manager.cppm @@ -0,0 +1,26 @@ +module; + +export module rutile.core.scenes.scene_manager; + +import rutile.core.scenes.base_scene; + +export namespace rutile +{ + class SceneManager + { + // Pointer to current scene + // TODO: Ownership? Should this be a regular pointer or maybe a shared_ptr shared with Game? + BaseScene* current_scene_ = nullptr; + + public: + BaseScene* get_current_scene() const + { + return current_scene_; + } + + void set_current_scene(BaseScene& scene) + { + current_scene_ = &scene; + } + }; +} diff --git a/src/rutile/game/game.cppm b/src/rutile/game/game.cppm index ff6822a..844ecfd 100644 --- a/src/rutile/game/game.cppm +++ b/src/rutile/game/game.cppm @@ -5,13 +5,12 @@ module; export module rutile.game.game; import std; -import rutile.core.drawing.sprite; -import rutile.core.drawing.tile_map; import rutile.core.engine; import rutile.core.render_server; +import rutile.core.scenes.base_scene; +import rutile.game.scenes.game_scene; import wrappers.rmlui.core; import wrappers.sdl; -import wrappers.sdl_image; export namespace rutile { @@ -23,26 +22,12 @@ export namespace rutile // Reference to the engine Engine& engine_; - // Sprites and stuff for testing - Sprite player_sprite_; - std::vector sprites_; - TileMap tile_map_; + // Main game scene + GameScene game_scene_; // Private constructor explicit Game(Engine& engine) - : engine_(engine), - player_sprite_{ - Sprite::create_from_texture( - engine_.get_resource_server().load_texture("assets/sprites/neocat_64.png"), - std::nullopt, - sdl::FPoint{0, 0}, - sdl::FPoint{-32, -32} - ) - }, - tile_map_{ - engine_.get_resource_server().load_tile_set("assets/tilesets/terrain.json"), - 32, 16, 12, {50, 50} - } + : engine_{engine} { // Initialize UI const auto& ui_server = engine_.get_ui_server(); @@ -50,13 +35,8 @@ export namespace rutile Rml::ElementDocument* document = ui_server.load_document("assets/ui/main_ui.rml"); document->Show(); - // Initialize tile map with example data - for (auto x = 0; x < 10; x++) { - for (auto y = 0; y < 10; y++) { - const auto tile_index = 2 + (x + y) % 2; - tile_map_.set_tile(x, y, tile_index); - } - } + // Set current scene + engine_.get_scene_manager().set_current_scene(game_scene_); instantiated_ = true; } @@ -84,55 +64,6 @@ export namespace rutile }; } - // Handles an SDL event. Returns true if the event has been handled. - bool handle_event(const sdl::Event& event) - { - if (event.type == sdl::EventTypes::MouseMotion) { - player_sprite_.set_position({event.motion.x, event.motion.y}); - return true; - } - - if (event.type == sdl::EventTypes::MouseButtonUp) { - sprites_.push_back( - Sprite::create_from_texture( - engine_.get_resource_server().load_texture("assets/sprites/neofox_64.png"), - std::nullopt, - sdl::FPoint{event.motion.x, event.motion.y}, - sdl::FPoint{-32, -32} - ) - ); - return true; - } - - return false; - } - - void update() - { - // TODO: Move this to Engine - engine_.get_ui_server().update(); - } - - void render() const - { - const auto& render_server = engine_.get_render_server(); - render_server.start_frame(); - - // Render tilemap - tile_map_.draw(render_server); - - // Render sprites - for (const Sprite& sprite : sprites_) { - sprite.draw(render_server); - } - player_sprite_.draw(render_server); - - // TODO: Move this to Engine - engine_.get_ui_server().render(); - - render_server.finish_frame(); - } - void shutdown() { } diff --git a/src/rutile/game/scenes/game_scene.cppm b/src/rutile/game/scenes/game_scene.cppm new file mode 100644 index 0000000..b7c0bca --- /dev/null +++ b/src/rutile/game/scenes/game_scene.cppm @@ -0,0 +1,85 @@ +module; + +export module rutile.game.scenes.game_scene; + +import std; +import rutile.core.drawing.sprite; +import rutile.core.drawing.tile_map; +import rutile.core.engine; +import rutile.core.render_server; +import rutile.core.scenes.base_scene; +import wrappers.sdl; + +export namespace rutile +{ + class GameScene : public BaseScene + { + // Sprites and stuff for testing + Sprite player_sprite_; + std::vector sprites_; + TileMap tile_map_; + + public: + GameScene() + : player_sprite_{ + Sprite::create_from_texture( + Engine::get().get_resource_server().load_texture("assets/sprites/neocat_64.png"), + std::nullopt, + sdl::FPoint{0, 0}, + sdl::FPoint{-32, -32} + ) + }, + tile_map_{ + Engine::get().get_resource_server().load_tile_set("assets/tilesets/terrain.json"), + 32, 16, 12, {50, 50} + } + { + // Initialize tile map with example data + for (auto x = 0; x < 10; x++) { + for (auto y = 0; y < 10; y++) { + const auto tile_index = 2 + (x + y) % 2; + tile_map_.set_tile(x, y, tile_index); + } + } + } + + // Handles an SDL event. Returns true if the event has been handled. + bool handle_event(const sdl::Event& event) override + { + if (event.type == sdl::EventTypes::MouseMotion) { + player_sprite_.set_position({event.motion.x, event.motion.y}); + return true; + } + + if (event.type == sdl::EventTypes::MouseButtonUp) { + sprites_.push_back( + Sprite::create_from_texture( + Engine::get().get_resource_server().load_texture("assets/sprites/neofox_64.png"), + std::nullopt, + sdl::FPoint{event.motion.x, event.motion.y}, + sdl::FPoint{-32, -32} + ) + ); + return true; + } + + return false; + } + + void update() override + { + } + + void render(const RenderServer& render_server) override + { + // Render tilemap + tile_map_.draw(render_server); + + // Render sprites + for (const Sprite& sprite : sprites_) { + sprite.draw(render_server); + } + player_sprite_.draw(render_server); + } + }; +}