diff --git a/src/entity/level.cpp b/src/entity/level.cpp index 7930d85..7d98c5b 100644 --- a/src/entity/level.cpp +++ b/src/entity/level.cpp @@ -28,6 +28,10 @@ namespace rl godot::Node* box{ this->find_child(name::level::physics_box) }; m_physics_box = gdcast(box); + resource::preload::packed_scene player_scene{ path::scene::Player }; + m_player = player_scene.instantiate(); + m_player->set_controller(memnew(PlayerController)); + this->add_child(m_player); this->add_child(m_projectile_spawner); diff --git a/src/entity/level.hpp b/src/entity/level.hpp index 950bb12..92be857 100644 --- a/src/entity/level.hpp +++ b/src/entity/level.hpp @@ -50,8 +50,7 @@ namespace rl std::atomic m_active{ false }; godot::Node* m_background{ nullptr }; ProjectileSpawner* m_projectile_spawner{ memnew(rl::ProjectileSpawner) }; - resource::preload::scene player_scene{ path::scene::Player }; - Player* m_player{ player_scene.instantiate(memnew(PlayerController)) }; + Player* m_player{ nullptr }; godot::RigidBody2D* m_physics_box{ nullptr }; }; } diff --git a/src/entity/projectile/projectile_spawner.hpp b/src/entity/projectile/projectile_spawner.hpp index 9305736..06f81eb 100644 --- a/src/entity/projectile/projectile_spawner.hpp +++ b/src/entity/projectile/projectile_spawner.hpp @@ -43,6 +43,6 @@ namespace rl // the time point that keeps track of when the last projectile was spawned. clock_t::time_point m_prev_spawn_time{ clock_t::now() }; // preloaded packed scene that will be instantiated per spawn - resource::preload::scene m_scene{ path::scene::Bullet }; + resource::preload::packed_scene m_scene{ path::scene::Bullet }; }; } diff --git a/src/main.cpp b/src/main.cpp index bd6657d..e6074a6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,8 +8,8 @@ namespace rl { Main::Main() { - resource::preload::scene level{ path::scene::Level1 }; - resource::preload::scene dialog{ path::ui::MainDialog }; + resource::preload::packed_scene level{ path::scene::Level1 }; + resource::preload::packed_scene dialog{ path::ui::MainDialog }; m_active_level = level.instantiate(); runtime_assert(m_active_level != nullptr); diff --git a/src/util/conversions.hpp b/src/util/conversions.hpp index cf9030e..790df1f 100644 --- a/src/util/conversions.hpp +++ b/src/util/conversions.hpp @@ -14,6 +14,30 @@ namespace rl::inline utils { + /** Converts std string types to godot::String at compile time */ + template + struct gd_str_conv + { + explicit constexpr gd_str_conv(TStr&& s) + : m_str{ std::forward(s) } + { + } + + explicit operator godot::String() + requires std::same_as + { + return godot::String(m_str.c_str()); + } + + explicit operator godot::String() + requires std::same_as + { + return godot::String(m_str.data()); + } + + TStr m_str{}; + }; + template requires std::derived_from, godot::Object> constexpr inline TOut* gdcast(TIn* obj) @@ -37,26 +61,11 @@ namespace rl::inline utils return std::string(in.ascii().ptr()); } - template <> - inline auto to(const godot::String& in) -> std::string_view - { - static_assert(std::is_same_v>); - return std::string_view(in.ascii().ptr()); - } - template <> inline auto to(const godot::StringName& in) -> std::string { static_assert(std::is_same_v>); godot::String tmp(in); - return std::string(tmp.ascii().ptr()); - } - - template <> - inline auto to(const godot::StringName& in) -> std::string_view - { - static_assert(std::is_same_v>); - godot::String tmp(in); - return std::string_view(tmp.ascii().ptr()); + return std::string(tmp.utf8().ptr()); } } diff --git a/src/util/scene.hpp b/src/util/scene.hpp index 1048af9..1f46994 100644 --- a/src/util/scene.hpp +++ b/src/util/scene.hpp @@ -2,62 +2,91 @@ #include -#include -#include #include #include #include +#include #include #include #include "core/assert.hpp" #include "util/conversions.hpp" -namespace rl -{ - class Character; -} - namespace rl::inline utils { - namespace scene::node + namespace scene { - template - requires std::derived_from - static inline void set_unique_name(TNode* node, const char* name) + namespace node { - runtime_assert(node != nullptr); - node->set_name(name); - node->set_unique_name_in_owner(true); - } - } + template + requires std::derived_from + static inline void set_unique_name(TNode* node, const char* name) + { + runtime_assert(node != nullptr); + node->set_name(name); + node->set_unique_name_in_owner(true); + } - namespace scene::tree - { - template - requires std::derived_from - static inline godot::SceneTree* get(TNode* node) - { - godot::SceneTree* scene_tree{ node->get_tree() }; - return scene_tree; + /** Sets the owner of a node and all it's children. */ + template + requires std::derived_from && + std::derived_from + static inline void set_owner(TNodeA* node, TNodeB* owner) + { + runtime_assert(node != nullptr && owner != nullptr); + const int node_child_count = node->get_child_count(); + for (int i = 0; i < node_child_count; ++i) + { + auto child = node->get_child(i); + child->set_owner(owner); + set_owner(child, owner); + } + } } - template - requires std::derived_from - static inline godot::Node* edited_root(TNode* node) + namespace tree { - godot::Node* edited_root{ node->get_tree()->get_edited_scene_root() }; - return edited_root; + template + requires std::derived_from + static inline godot::SceneTree* get(TNode* node) + { + godot::SceneTree* scene_tree{ node->get_tree() }; + return scene_tree; + } + + template + requires std::derived_from + static inline godot::Node* edited_root(TNode* node) + { + godot::Node* edited_root{ node->get_tree()->get_edited_scene_root() }; + return edited_root; + } + + template + requires std::derived_from + static inline godot::Node* root_node(TNode* node) + { + godot::SceneTree* scene_tree{ tree::get(node) }; + godot::Window* root_window{ scene_tree->get_root() }; + godot::Node* root_node{ gdcast(root_window) }; + return root_node; + } } - template - requires std::derived_from - static inline godot::Node* root_node(TNode* node) + namespace packer { - godot::SceneTree* scene_tree{ tree::get(node) }; - godot::Window* root_window{ scene_tree->get_root() }; - godot::Node* root_node{ gdcast(root_window) }; - return root_node; + /** + @return PackedScene from godot::Node parameter. + */ + template + requires std::derived_from + static inline godot::PackedScene* pack(TNode* node) + { + node::set_owner(node, node); + godot::PackedScene* package = memnew(godot::PackedScene); + package->pack(node); + return package; + } } } @@ -71,49 +100,68 @@ namespace rl::inline utils } } + namespace saver + { + static inline godot::ResourceSaver* get() + { + return godot::ResourceSaver::get_singleton(); + } + } + namespace preload { template requires std::derived_from && std::convertible_to - class scene + class packed_scene { public: using scene_t = TScene; using object_t = TObj; - public: - scene(const char* resource_path) + /* Pack as preload from path. */ + packed_scene(godot::String resource_load_path) { - auto fh{ godot::FileAccess::open(resource_path, godot::FileAccess::READ) }; - bool file_access_success{ !fh.is_null() }; + godot::ResourceLoader* resource_loader{ resource::loader::get() }; + + bool file_access_success{ resource_loader->exists(resource_load_path) }; runtime_assert(file_access_success); if (file_access_success) { - godot::ResourceLoader* resource_loader{ resource::loader::get() }; - m_packed_resource = resource_loader->load(resource_path); + m_packed_resource = resource_loader->load(resource_load_path); initialized = m_packed_resource.is_valid(); } } - template - [[nodiscard]] auto instantiate(TArgs... args) -> TObj* + /* Pack from existing instance. */ + packed_scene(godot::Node* node) { - assertion(initialized, "instantiation invoked from uninitialized scene loader"); + m_packed_resource = scene::packer::pack(node); + initialized = m_packed_resource.is_valid(); + } + + [[nodiscard]] auto instantiate() -> object_t* + { + assertion(initialized, + "Resource instantiation invoked from uninitialized scene loader."); if (!initialized) [[unlikely]] return nullptr; - TObj* obj{ gdcast(m_packed_resource->instantiate()) }; + object_t* obj{ gdcast(m_packed_resource->instantiate()) }; runtime_assert(obj != nullptr); - if constexpr (std::is_base_of_v) + return obj; + } + + /** Save this resource to specified path. */ + void save(godot::String& resource_save_path) + { + if (initialized) { - if (obj != nullptr) - obj->set_controller(args...); + auto error = saver::get()->save(m_packed_resource, resource_save_path); + assertion(error != godot::Error::OK, "Packed resource save failed."); } - - return obj; } private: