From d0c5ef1a1d1a041e1e18ac3d0977acf1bc6896f5 Mon Sep 17 00:00:00 2001 From: WiggleWizard <1405402+WiggleWizard@users.noreply.github.com> Date: Thu, 19 Sep 2024 17:52:01 +0100 Subject: [PATCH] Initial infrastructure for C# scene callbacks in subsystems --- Nuake/Engine.cpp | 33 +++++++ Nuake/Engine.h | 4 + Nuake/src/Core/MulticastDelegate.h | 86 +++++++++++++++++++ Nuake/src/Scene/Scene.cpp | 4 + Nuake/src/Scene/Scene.h | 10 +++ Nuake/src/Scene/Systems/ScriptingSystem.cpp | 4 + Nuake/src/Scene/Systems/ScriptingSystem.h | 1 + Nuake/src/Scene/Systems/System.h | 9 ++ Nuake/src/Subsystems/EngineSubsystem.h | 7 ++ .../Subsystems/EngineSubsystemScriptable.cpp | 12 +++ .../Subsystems/EngineSubsystemScriptable.h | 3 + Nuake/src/Window.cpp | 7 +- Nuake/src/Window.h | 11 ++- NuakeNet/src/EngineSubsystem.cs | 3 + 14 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 Nuake/src/Core/MulticastDelegate.h diff --git a/Nuake/Engine.cpp b/Nuake/Engine.cpp index 52e4b895..20deae85 100644 --- a/Nuake/Engine.cpp +++ b/Nuake/Engine.cpp @@ -40,6 +40,8 @@ namespace Nuake void Engine::Init() { + Window::Get()->OnWindowSetScene().AddStatic(&Engine::OnWindowSetScene); + ScriptingEngineNet::Get().AddListener(&Engine::OnScriptingEngineGameAssemblyLoaded); AudioManager::Get().Initialize(); @@ -257,6 +259,15 @@ namespace Nuake return std::reinterpret_pointer_cast(subsystems[subsystemId]); } + void Engine::OnWindowSetScene(Ref scene) + { + if (scene != nullptr) + { + scene->OnPreInitialize().AddStatic(&Engine::OnScenePreInitialize, scene); + scene->OnPostInitialize().AddStatic(&Engine::OnScenePostInitialize, scene); + } + } + void Engine::InitializeCoreSubsystems() { } @@ -296,6 +307,28 @@ namespace Nuake } } + void Engine::OnScenePreInitialize(Ref scene) + { + for (auto subsystem : subsystems) + { + if (subsystem == nullptr) + continue; + + subsystem->OnScenePreInitialize(scene); + } + } + + void Engine::OnScenePostInitialize(Ref scene) + { + for (auto subsystem : subsystems) + { + if (subsystem == nullptr) + continue; + + subsystem->OnScenePostInitialize(scene); + } + } + bool Engine::LoadProject(Ref project) { currentProject = project; diff --git a/Nuake/Engine.h b/Nuake/Engine.h index d20ca984..00bc623e 100644 --- a/Nuake/Engine.h +++ b/Nuake/Engine.h @@ -58,9 +58,13 @@ namespace Nuake static Ref GetScriptedSubsystem(const int subsystemId); protected: + static void OnWindowSetScene(Ref scene); static void InitializeCoreSubsystems(); static void OnScriptingEngineGameAssemblyLoaded(); + static void OnScenePreInitialize(Ref scene); + static void OnScenePostInitialize(Ref scene); + private: static Ref currentWindow; static Ref currentProject; diff --git a/Nuake/src/Core/MulticastDelegate.h b/Nuake/src/Core/MulticastDelegate.h new file mode 100644 index 00000000..fb2a3160 --- /dev/null +++ b/Nuake/src/Core/MulticastDelegate.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include + +#define DECLARE_MULTICAST_DELEGATE(multicastDelegateName, ...) typedef MulticastDelegate<__VA_ARGS__> multicastDelegateName; + +template +class MulticastDelegate +{ +public: + using DelegateID = size_t; + + // Add a callable with bound variables (supports no arguments as well) + template + DelegateID AddStatic(Callable&& func, BoundArgs&&... boundArgs) + { + DelegateID id = nextID++; + auto boundFunction = [=](Args... args) { + if constexpr (sizeof...(Args) > 0) + { + func(boundArgs..., std::forward(args)...); + } + else + { + func(boundArgs...); + } + }; + delegates.push_back({id, boundFunction}); + return id; + } + + template + DelegateID AddObject(Callable&& func, Obj* object, BoundArgs&&... boundArgs) + { + DelegateID id = nextID++; + auto boundFunction = [=](Args... args) { + if constexpr (sizeof...(Args) > 0) + { + (object->*func)(boundArgs..., std::forward(args)...); + } + else + { + (object->*func)(boundArgs...); + } + }; + delegates.push_back({id, boundFunction}); + return id; + } + + // Remove a callable using the token returned by Add() + void Remove(DelegateID id) + { + auto it = std::remove_if(delegates.begin(), delegates.end(), [id](const auto& pair) + { + return pair.first == id; + }); + + if (it != delegates.end()) + { + delegates.erase(it, delegates.end()); + } + } + + // Clear all delegates + void Clear() + { + delegates.clear(); + } + + // Invoke all callables + void Broadcast(Args... args) + { + for (auto& [id, delegate] : delegates) + { + delegate(std::forward(args)...); + } + } + +private: + using DelegatePair = std::pair>; + + std::vector delegates; // Vector of (ID, callable) pairs + DelegateID nextID = 0; // Unique ID generator +}; diff --git a/Nuake/src/Scene/Scene.cpp b/Nuake/src/Scene/Scene.cpp index 09cc2154..4cdb06da 100644 --- a/Nuake/src/Scene/Scene.cpp +++ b/Nuake/src/Scene/Scene.cpp @@ -243,6 +243,8 @@ namespace Nuake bool Scene::OnInit() { + preInitializeDelegate.Broadcast(); + for (auto& system : m_Systems) { if (!system->Init()) @@ -250,6 +252,8 @@ namespace Nuake return false; } } + + postInitializeDelegate.Broadcast(); return true; } diff --git a/Nuake/src/Scene/Scene.h b/Nuake/src/Scene/Scene.h index aa3e6ff7..fadf220a 100644 --- a/Nuake/src/Scene/Scene.h +++ b/Nuake/src/Scene/Scene.h @@ -19,6 +19,9 @@ namespace Nuake class Entity; class SceneRenderer; + DECLARE_MULTICAST_DELEGATE(PreInitializeDelegate) + DECLARE_MULTICAST_DELEGATE(PostInitializeDelegate) + class Scene : public ISerializable { friend Entity; @@ -92,6 +95,13 @@ namespace Nuake // Component specific utilies void CreateSkeleton(Entity& entity); + PreInitializeDelegate& OnPreInitialize() { return preInitializeDelegate; } + PostInitializeDelegate& OnPostInitialize() { return postInitializeDelegate; } + + protected: + PreInitializeDelegate preInitializeDelegate; + PostInitializeDelegate postInitializeDelegate; + private: void CreateSkeletonTraverse(Entity& entity, SkeletonNode& skeletonNode); }; diff --git a/Nuake/src/Scene/Systems/ScriptingSystem.cpp b/Nuake/src/Scene/Systems/ScriptingSystem.cpp index 6aaaae8c..9c094ea2 100644 --- a/Nuake/src/Scene/Systems/ScriptingSystem.cpp +++ b/Nuake/src/Scene/Systems/ScriptingSystem.cpp @@ -18,6 +18,8 @@ namespace Nuake { Logger::Log("Initializing ScriptingSystem"); + preInitDelegate.Broadcast(); + auto& scriptingEngineNet = ScriptingEngineNet::Get(); scriptingEngineNet.Uninitialize(); scriptingEngineNet.Initialize(); @@ -70,6 +72,8 @@ namespace Nuake } } + postInitDelegate.Broadcast(); + return true; } diff --git a/Nuake/src/Scene/Systems/ScriptingSystem.h b/Nuake/src/Scene/Systems/ScriptingSystem.h index 106e7ba8..20f46794 100644 --- a/Nuake/src/Scene/Systems/ScriptingSystem.h +++ b/Nuake/src/Scene/Systems/ScriptingSystem.h @@ -1,5 +1,6 @@ #pragma once #include "System.h" +#include "src/Core/MulticastDelegate.h" namespace Nuake { class Scene; diff --git a/Nuake/src/Scene/Systems/System.h b/Nuake/src/Scene/Systems/System.h index 382fa84d..fa954e7a 100644 --- a/Nuake/src/Scene/Systems/System.h +++ b/Nuake/src/Scene/Systems/System.h @@ -1,6 +1,8 @@ #pragma once + #include "src/Core/Timestep.h" #include "src/Core/Core.h" +#include "src/Core/MulticastDelegate.h" namespace Nuake { @@ -17,5 +19,12 @@ namespace Nuake virtual void FixedUpdate(Timestep ts) = 0; virtual void EditorUpdate() {} virtual void Exit() = 0; + + MulticastDelegate<>& OnPreInit() { return preInitDelegate; } + MulticastDelegate<>& OnPostInit() { return postInitDelegate; } + + protected: + MulticastDelegate<> preInitDelegate; + MulticastDelegate<> postInitDelegate; }; } diff --git a/Nuake/src/Subsystems/EngineSubsystem.h b/Nuake/src/Subsystems/EngineSubsystem.h index f1c25b30..3fcc93b5 100644 --- a/Nuake/src/Subsystems/EngineSubsystem.h +++ b/Nuake/src/Subsystems/EngineSubsystem.h @@ -1,11 +1,15 @@ #pragma once +#include "src/Core/Core.h" + /** * Specific type of subsystem that runs within the context of the engine, being created at the start of the * engine's lifetime and destroyed just before the engine shuts down. */ namespace Nuake { + class Scene; + class EngineSubsystem { public: @@ -15,6 +19,9 @@ namespace Nuake virtual void Initialize() {} virtual void Tick(float deltaTime) {} + virtual void OnScenePreInitialize(Ref scene) {} + virtual void OnScenePostInitialize(Ref scene) {} + private: bool canEverTick = false; }; diff --git a/Nuake/src/Subsystems/EngineSubsystemScriptable.cpp b/Nuake/src/Subsystems/EngineSubsystemScriptable.cpp index 4076ae5f..c5d8a17d 100644 --- a/Nuake/src/Subsystems/EngineSubsystemScriptable.cpp +++ b/Nuake/src/Subsystems/EngineSubsystemScriptable.cpp @@ -29,6 +29,18 @@ void EngineSubsystemScriptable::Tick(float deltaTime) cSharpObjectInstance.InvokeMethod("OnTick", deltaTime); } + +void EngineSubsystemScriptable::OnScenePreInitialize(Ref scene) +{ +} + +void EngineSubsystemScriptable::OnScenePostInitialize(Ref scene) +{ + if (!cSharpObjectInstance.IsValid()) + return; + + cSharpObjectInstance.InvokeMethod("OnScenePostInitialize", scene); +} } diff --git a/Nuake/src/Subsystems/EngineSubsystemScriptable.h b/Nuake/src/Subsystems/EngineSubsystemScriptable.h index 69acae01..14823401 100644 --- a/Nuake/src/Subsystems/EngineSubsystemScriptable.h +++ b/Nuake/src/Subsystems/EngineSubsystemScriptable.h @@ -19,6 +19,9 @@ namespace Nuake virtual void Initialize() override; virtual void Tick(float deltaTime) override; + virtual void OnScenePreInitialize(Ref scene) override; + virtual void OnScenePostInitialize(Ref scene) override; + private: Coral::ManagedObject cSharpObjectInstance; }; diff --git a/Nuake/src/Window.cpp b/Nuake/src/Window.cpp index 18508d7a..fc03b7ff 100644 --- a/Nuake/src/Window.cpp +++ b/Nuake/src/Window.cpp @@ -303,9 +303,12 @@ Ref Window::GetScene() return this->scene; } -bool Window::SetScene(Ref scene) +bool Window::SetScene(Ref newScene) { - this->scene = scene; + scene = newScene; + + windowSetSceneDelegate.Broadcast(newScene); + return true; } diff --git a/Nuake/src/Window.h b/Nuake/src/Window.h index 63817314..b62bd186 100644 --- a/Nuake/src/Window.h +++ b/Nuake/src/Window.h @@ -1,11 +1,12 @@ #pragma once + #include "Core/Core.h" #include "Core/Maths.h" +#include "Core/MulticastDelegate.h" #include "Core/Timestep.h" #include - struct GLFWwindow; namespace Nuake @@ -13,6 +14,8 @@ namespace Nuake class Scene; class FrameBuffer; + DECLARE_MULTICAST_DELEGATE(OnWindowSetSceneDelegate, Ref) + class Window { public: @@ -44,7 +47,7 @@ namespace Nuake void Center(); Ref GetScene(); - bool SetScene(Ref scene); + bool SetScene(Ref newScene); void SetTitle(const std::string& title); std::string GetTitle(); @@ -63,6 +66,8 @@ namespace Nuake void SetOnWindowClosedCallback(std::function callback); void SetOnDragNDropCallback(std::function& paths)> callback); + OnWindowSetSceneDelegate& OnWindowSetScene() { return windowSetSceneDelegate; } + private: const std::string DEFAULT_TITLE = "Untitled Window"; const uint32_t DEFAULT_WIDTH = 1280; @@ -85,6 +90,8 @@ namespace Nuake void InitImgui(); + OnWindowSetSceneDelegate windowSetSceneDelegate; + private: }; } diff --git a/NuakeNet/src/EngineSubsystem.cs b/NuakeNet/src/EngineSubsystem.cs index dfb2eb67..09acd603 100644 --- a/NuakeNet/src/EngineSubsystem.cs +++ b/NuakeNet/src/EngineSubsystem.cs @@ -20,6 +20,9 @@ } public virtual void Initialize() {} + public virtual void OnScenePreInit(Scene scene) {} + public virtual void OnScenePostInit(Scene scene) {} + public virtual void OnSceneUnloaded(Scene scene) {} public virtual void OnTick(float deltaTime) {} } }