diff options
Diffstat (limited to 'src')
82 files changed, 1181 insertions, 448 deletions
diff --git a/src/crepe/Component.cpp b/src/crepe/Component.cpp index acfd35c..ae76e65 100644 --- a/src/crepe/Component.cpp +++ b/src/crepe/Component.cpp @@ -1,5 +1,17 @@  #include "Component.h"  using namespace crepe; +using namespace std;  Component::Component(game_object_id_t id) : game_object_id(id) {} + +Component & Component::operator=(const Component & other) { +	this->active = other.active; +	return *this; +} + +unique_ptr<Component> Component::save() const { +	return unique_ptr<Component>(new Component(*this)); +} + +void Component::restore(const Component & snapshot) { *this = snapshot; } diff --git a/src/crepe/Component.h b/src/crepe/Component.h index eff5a58..52e06d5 100644 --- a/src/crepe/Component.h +++ b/src/crepe/Component.h @@ -1,5 +1,7 @@  #pragma once +#include <memory> +  #include "types.h"  namespace crepe { @@ -32,11 +34,33 @@ protected:  	//! Only ComponentManager can create components  	friend class ComponentManager; -	Component(const Component &) = delete; +	// components are never moved  	Component(Component &&) = delete; -	virtual Component & operator=(const Component &) = delete;  	virtual Component & operator=(Component &&) = delete; +protected: +	/** +	 * \name ReplayManager (Memento) functions +	 * \{ +	 */ +	/** +	 * \brief Save a snapshot of this component's state +	 * \note This function should only be implemented on components that should be saved/restored +	 * by ReplayManager. +	 * \returns Unique pointer to a deep copy of this component +	 */ +	virtual std::unique_ptr<Component> save() const; +	//! Copy constructor (used by \c save()) +	Component(const Component &) = default; +	/** +	 * \brief Restore this component from a snapshot +	 * \param snapshot Data to fill this component with (as returned by \c save()) +	 */ +	virtual void restore(const Component & snapshot); +	//! Copy assignment operator (used by \c restore()) +	virtual Component & operator=(const Component &); +	//! \} +  public:  	virtual ~Component() = default; diff --git a/src/crepe/api/Animator.cpp b/src/crepe/api/Animator.cpp index 4ce4bf0..203cef3 100644 --- a/src/crepe/api/Animator.cpp +++ b/src/crepe/api/Animator.cpp @@ -1,5 +1,5 @@ -#include "util/Log.h" +#include "util/dbg.h"  #include "Animator.h"  #include "Component.h" diff --git a/src/crepe/api/BehaviorScript.cpp b/src/crepe/api/BehaviorScript.cpp index d22afdf..af7572c 100644 --- a/src/crepe/api/BehaviorScript.cpp +++ b/src/crepe/api/BehaviorScript.cpp @@ -10,6 +10,6 @@ BehaviorScript::BehaviorScript(game_object_id_t id, Mediator & mediator)  template <>  BehaviorScript & GameObject::add_component<BehaviorScript>() { -	ComponentManager & mgr = this->component_manager; -	return mgr.add_component<BehaviorScript>(this->id, mgr.mediator); +	ComponentManager & mgr = this->mediator.component_manager; +	return mgr.add_component<BehaviorScript>(this->id, this->mediator);  } diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp index b9bb1e2..353d5e2 100644 --- a/src/crepe/api/BehaviorScript.hpp +++ b/src/crepe/api/BehaviorScript.hpp @@ -2,8 +2,6 @@  #include <type_traits> -#include "../util/Log.h" -  #include "BehaviorScript.h"  #include "Script.h" @@ -11,7 +9,6 @@ namespace crepe {  template <class T, typename... Args>  BehaviorScript & BehaviorScript::set_script(Args &&... args) { -	dbg_trace();  	static_assert(std::is_base_of<Script, T>::value);  	this->script = std::unique_ptr<Script>(new T(std::forward<Args>(args)...)); diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index e8d6f92..2bee3fb 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -13,7 +13,7 @@ target_sources(crepe PUBLIC  	Animator.cpp  	BoxCollider.cpp  	CircleCollider.cpp -	LoopManager.cpp +	Engine.cpp  	Asset.cpp  	EventHandler.cpp  	Script.cpp @@ -47,7 +47,8 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	EventHandler.h  	EventHandler.hpp  	Event.h -	LoopManager.h +	Engine.h +	Engine.hpp  	Asset.h  	Button.h  	UIObject.h diff --git a/src/crepe/api/Camera.cpp b/src/crepe/api/Camera.cpp index 179dc18..19a3296 100644 --- a/src/crepe/api/Camera.cpp +++ b/src/crepe/api/Camera.cpp @@ -1,4 +1,4 @@ -#include "util/Log.h" +#include "util/dbg.h"  #include "Camera.h"  #include "Component.h" diff --git a/src/crepe/api/Engine.cpp b/src/crepe/api/Engine.cpp new file mode 100644 index 0000000..2e9d35a --- /dev/null +++ b/src/crepe/api/Engine.cpp @@ -0,0 +1,63 @@ +#include "../util/Log.h" + +#include "Engine.h" + +using namespace crepe; +using namespace std; + +int Engine::main() noexcept { +	try { +		this->setup(); +	} catch (const exception & e) { +		Log::logf(Log::Level::ERROR, "Uncaught exception in setup: {}\n", e.what()); +		return EXIT_FAILURE; +	} + +	try { +		this->loop(); +	} catch (const exception & e) { +		Log::logf(Log::Level::ERROR, "Uncaught exception in main loop: {}\n", e.what()); +		this->event_manager.trigger_event<ShutDownEvent>(); +	} + +	return EXIT_SUCCESS; +} + +void Engine::setup() { +	this->loop_timer.start(); +	this->scene_manager.load_next_scene(); + +	this->event_manager.subscribe<ShutDownEvent>([this](const ShutDownEvent & event) { +		this->game_running = false; + +		// propagate to possible user ShutDownEvent listeners +		return false; +	}); +} + +void Engine::loop() { +	LoopTimerManager & timer = this->loop_timer; +	SystemManager & systems = this->system_manager; + +	while (this->game_running) { +		timer.update(); + +		while (timer.get_lag() >= timer.get_fixed_delta_time()) { +			try { +				systems.fixed_update(); +			} catch (const exception & e) { +				Log::logf(Log::Level::WARNING, +						  "Uncaught exception in fixed update function: {}\n", e.what()); +			} +			timer.advance_fixed_elapsed_time(); +		} + +		try { +			systems.frame_update(); +		} catch (const exception & e) { +			Log::logf(Log::Level::WARNING, "Uncaught exception in frame update function: {}\n", +					  e.what()); +		} +		timer.enforce_frame_rate(); +	} +} diff --git a/src/crepe/api/Engine.h b/src/crepe/api/Engine.h new file mode 100644 index 0000000..700a0cd --- /dev/null +++ b/src/crepe/api/Engine.h @@ -0,0 +1,81 @@ +#pragma once + +#include "../facade/SDLContext.h" +#include "../manager/ComponentManager.h" +#include "../manager/EventManager.h" +#include "../manager/LoopTimerManager.h" +#include "../manager/ReplayManager.h" +#include "../manager/ResourceManager.h" +#include "../manager/SaveManager.h" +#include "../manager/SceneManager.h" +#include "../manager/SystemManager.h" + +namespace crepe { + +/** + * \brief Main game entrypoint + * + * This class is responsible for managing the game loop, including initialization and updating. + */ +class Engine { +public: +	/** +	 * \brief Engine entrypoint +	 * +	 * This function is called by the game programmer after registering all scenes +	 * +	 * \returns process exit code +	 */ +	int main() noexcept; + +	//! \copydoc SceneManager::add_scene +	template <typename T> +	void add_scene(); + +private: +	/** +	 * \brief Setup function for one-time initialization. +	 * +	 * This function initializes necessary components for the game. +	 */ +	void setup(); +	/** +	 * \brief Main game loop function. +	 * +	 * This function runs the main loop, handling game updates and rendering. +	 */ +	void loop(); + +	//! Game loop condition +	bool game_running = true; + +private: +	//! Global context +	Mediator mediator; + +	//! SystemManager +	SystemManager system_manager{mediator}; + +	//! SDLContext instance +	SDLContext sdl_context{mediator}; + +	//! Resource manager instance +	ResourceManager resource_manager{mediator}; + +	//! Component manager instance +	ComponentManager component_manager{mediator}; +	//! Scene manager instance +	SceneManager scene_manager{mediator}; +	//! LoopTimerManager instance +	LoopTimerManager loop_timer{mediator}; +	//! EventManager instance +	EventManager event_manager{mediator}; +	//! Save manager instance +	SaveManager save_manager{mediator}; +	//! ReplayManager instance +	ReplayManager replay_manager{mediator}; +}; + +} // namespace crepe + +#include "Engine.hpp" diff --git a/src/crepe/api/Engine.hpp b/src/crepe/api/Engine.hpp new file mode 100644 index 0000000..f2fdc0a --- /dev/null +++ b/src/crepe/api/Engine.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "Engine.h" + +namespace crepe { + +template <class T> +void Engine::add_scene() { +	this->scene_manager.add_scene<T>(); +} + +} // namespace crepe diff --git a/src/crepe/api/GameObject.cpp b/src/crepe/api/GameObject.cpp index 9ef4682..9b94cad 100644 --- a/src/crepe/api/GameObject.cpp +++ b/src/crepe/api/GameObject.cpp @@ -7,20 +7,17 @@  using namespace crepe;  using namespace std; -GameObject::GameObject(ComponentManager & component_manager, game_object_id_t id, -					   const std::string & name, const std::string & tag, -					   const vec2 & position, double rotation, double scale) +GameObject::GameObject(Mediator & mediator, game_object_id_t id, const std::string & name, +					   const std::string & tag, const vec2 & position, double rotation, +					   double scale)  	: id(id), -	  component_manager(component_manager) { - -	// Add Transform and Metadata components -	ComponentManager & mgr = this->component_manager; -	mgr.add_component<Transform>(this->id, position, rotation, scale); -	mgr.add_component<Metadata>(this->id, name, tag); -} +	  mediator(mediator), +	  transform(mediator.component_manager->add_component<Transform>(this->id, position, +																	 rotation, scale)), +	  metadata(mediator.component_manager->add_component<Metadata>(this->id, name, tag)) {}  void GameObject::set_parent(const GameObject & parent) { -	ComponentManager & mgr = this->component_manager; +	ComponentManager & mgr = this->mediator.component_manager;  	// Set parent on own Metadata component  	RefVector<Metadata> this_metadata = mgr.get_components_by_id<Metadata>(this->id); @@ -32,7 +29,7 @@ void GameObject::set_parent(const GameObject & parent) {  }  void GameObject::set_persistent(bool persistent) { -	ComponentManager & mgr = this->component_manager; +	ComponentManager & mgr = this->mediator.component_manager;  	mgr.set_persistent(this->id, persistent);  } diff --git a/src/crepe/api/GameObject.h b/src/crepe/api/GameObject.h index ff80f49..572ce3a 100644 --- a/src/crepe/api/GameObject.h +++ b/src/crepe/api/GameObject.h @@ -6,7 +6,9 @@  namespace crepe { -class ComponentManager; +class Mediator; +class Transform; +class Metadata;  /**   * \brief Represents a GameObject @@ -20,7 +22,7 @@ private:  	 * This constructor creates a new GameObject. It creates a new Transform and Metadata  	 * component and adds them to the ComponentManager.  	 * -	 * \param component_manager Reference to component_manager +	 * \param mediator Reference to mediator  	 * \param id The id of the GameObject  	 * \param name The name of the GameObject  	 * \param tag The tag of the GameObject @@ -28,13 +30,20 @@ private:  	 * \param rotation The rotation of the GameObject  	 * \param scale The scale of the GameObject  	 */ -	GameObject(ComponentManager & component_manager, game_object_id_t id, -			   const std::string & name, const std::string & tag, const vec2 & position, -			   double rotation, double scale); +	GameObject(Mediator & mediator, game_object_id_t id, const std::string & name, +			   const std::string & tag, const vec2 & position, double rotation, double scale);  	//! ComponentManager instances GameObject  	friend class ComponentManager;  public: +	//! The id of the GameObject +	const game_object_id_t id; +	//! This entity's transform +	Transform & transform; +	//! This entity's metadata +	Metadata & metadata; + +public:  	/**  	 * \brief Set the parent of this GameObject  	 * @@ -68,12 +77,8 @@ public:  	 */  	void set_persistent(bool persistent = true); -public: -	//! The id of the GameObject -	const game_object_id_t id; -  protected: -	ComponentManager & component_manager; +	Mediator & mediator;  };  } // namespace crepe diff --git a/src/crepe/api/GameObject.hpp b/src/crepe/api/GameObject.hpp index a6b45b0..69f7d73 100644 --- a/src/crepe/api/GameObject.hpp +++ b/src/crepe/api/GameObject.hpp @@ -8,7 +8,7 @@ namespace crepe {  template <typename T, typename... Args>  T & GameObject::add_component(Args &&... args) { -	ComponentManager & mgr = this->component_manager; +	ComponentManager & mgr = this->mediator.component_manager;  	return mgr.add_component<T>(this->id, std::forward<Args>(args)...);  } diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp deleted file mode 100644 index 7a78019..0000000 --- a/src/crepe/api/LoopManager.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "../facade/SDLContext.h" -#include "../manager/EventManager.h" -#include "../manager/LoopTimerManager.h" -#include "../system/AISystem.h" -#include "../system/AnimatorSystem.h" -#include "../system/AudioSystem.h" -#include "../system/CollisionSystem.h" -#include "../system/InputSystem.h" -#include "../system/ParticleSystem.h" -#include "../system/PhysicsSystem.h" -#include "../system/RenderSystem.h" -#include "../system/ScriptSystem.h" -#include "../util/Log.h" - -#include "LoopManager.h" - -using namespace crepe; -using namespace std; - -LoopManager::LoopManager() { -	this->load_system<AnimatorSystem>(); -	this->load_system<CollisionSystem>(); -	this->load_system<ParticleSystem>(); -	this->load_system<PhysicsSystem>(); -	this->load_system<RenderSystem>(); -	this->load_system<ScriptSystem>(); -	this->load_system<InputSystem>(); -	this->event_manager.subscribe<ShutDownEvent>( -		[this](const ShutDownEvent & event) { return this->on_shutdown(event); }); -	this->load_system<AudioSystem>(); -	this->load_system<AISystem>(); -} -void LoopManager::start() { -	this->setup(); -	this->loop(); -} - -void LoopManager::setup() { -	this->game_running = true; -	this->loop_timer.start(); -	this->scene_manager.load_next_scene(); -} - -void LoopManager::loop() { -	try { -		while (game_running) { -			this->loop_timer.update(); - -			while (this->loop_timer.get_lag() >= this->loop_timer.get_fixed_delta_time()) { -				this->fixed_update(); -				this->loop_timer.advance_fixed_elapsed_time(); -			} - -			this->frame_update(); -			this->loop_timer.enforce_frame_rate(); -		} -	} catch (const exception & e) { -		Log::logf(Log::Level::ERROR, "Exception caught in main loop: {}", e.what()); -		this->event_manager.trigger_event<ShutDownEvent>(ShutDownEvent{}); -	} -} - -// will be called at a fixed interval -void LoopManager::fixed_update() { -	this->get_system<InputSystem>().update(); -	this->event_manager.dispatch_events(); -	this->get_system<ScriptSystem>().update(); -	this->get_system<ParticleSystem>().update(); -	this->get_system<AISystem>().update(); -	this->get_system<PhysicsSystem>().update(); -	this->get_system<CollisionSystem>().update(); -	this->get_system<AudioSystem>().update(); -} - -// will be called every frame -void LoopManager::frame_update() { -	this->scene_manager.load_next_scene(); -	this->get_system<AnimatorSystem>().update(); -	//render -	this->get_system<RenderSystem>().update(); -} - -bool LoopManager::on_shutdown(const ShutDownEvent & e) { -	this->game_running = false; -	// propagate to possible user ShutDownEvent listeners -	return false; -} diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h deleted file mode 100644 index 124cd3a..0000000 --- a/src/crepe/api/LoopManager.h +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once - -#include <memory> - -#include "../facade/SDLContext.h" -#include "../manager/ComponentManager.h" -#include "../manager/EventManager.h" -#include "../manager/LoopTimerManager.h" -#include "../manager/Mediator.h" -#include "../manager/ResourceManager.h" -#include "../manager/SaveManager.h" -#include "../manager/SceneManager.h" -#include "../system/System.h" - -namespace crepe { -/** - * \brief Main game loop manager - * - * This class is responsible for managing the game loop, including initialization and updating. - */ -class LoopManager { -public: -	LoopManager(); -	/** -	 * \brief Start the gameloop -	 * -	 * This is the start of the engine where the setup is called and then the loop keeps running until the game stops running. -	 * The Game programmer needs to call this function to run the game. This should be done after creating and adding all scenes. -	 */ -	void start(); - -	/** -	 * \brief Add a new concrete scene to the scene manager -	 * -	 * \tparam T  Type of concrete scene -	 */ -	template <typename T> -	void add_scene(); - -private: -	/** -	 * \brief Setup function for one-time initialization. -	 * -	 * This function initializes necessary components for the game. -	 */ -	void setup(); -	/** -	 * \brief Main game loop function. -	 * -	 * This function runs the main loop, handling game updates and rendering. -	 */ -	void loop(); - -	/** -	 * \brief Per-frame update. -	 * -	 * Updates the game state based on the elapsed time since the last frame. -	 */ -	virtual void frame_update(); - -	/** -	 * \brief Fixed update executed at a fixed rate. -	 * -	 * This function updates physics and game logic based on LoopTimer's fixed_delta_time. -	 */ -	virtual void fixed_update(); - -	//! Indicates whether the game is running. -	bool game_running = false; - -private: -	//! Global context -	Mediator mediator; -	/** -	 * \brief Collection of System instances -	 * -	 * This map holds System instances indexed by the system's class typeid. It is filled in the -	 * constructor of LoopManager using LoopManager::load_system. -	 */ -	std::unordered_map<std::type_index, std::unique_ptr<System>> systems; - -	//! SDLContext instance -	SDLContext sdl_context{mediator}; -	//! Resource manager instance -	ResourceManager resource_manager{mediator}; - -	//! Component manager instance -	ComponentManager component_manager{mediator}; -	//! Scene manager instance -	SceneManager scene_manager{mediator}; -	//! LoopTimerManager instance -	LoopTimerManager loop_timer{mediator}; -	//! EventManager instance -	EventManager event_manager{mediator}; -	//! Save manager instance -	SaveManager save_manager{mediator}; - -private: -	/** -	 * \brief Callback function for ShutDownEvent -	 * -	 * This function sets the game_running variable to false, stopping the gameloop and therefor quitting the game. -	 */ -	bool on_shutdown(const ShutDownEvent & e); -	/** -	 * \brief Initialize a system -	 * \tparam T System type (must be derivative of \c System) -	 */ -	template <class T> -	void load_system(); -	/** -	 * \brief Retrieve a reference to ECS system -	 * \tparam T System type -	 * \returns Reference to system instance -	 * \throws std::runtime_error if the System is not initialized -	 */ -	template <class T> -	T & get_system(); -}; - -} // namespace crepe - -#include "LoopManager.hpp" diff --git a/src/crepe/api/ParticleEmitter.cpp b/src/crepe/api/ParticleEmitter.cpp index 4f54bbd..9a70334 100644 --- a/src/crepe/api/ParticleEmitter.cpp +++ b/src/crepe/api/ParticleEmitter.cpp @@ -2,6 +2,7 @@  #include "api/Sprite.h"  using namespace crepe; +using namespace std;  ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Sprite & sprite,  								 const Data & data) @@ -12,3 +13,16 @@ ParticleEmitter::ParticleEmitter(game_object_id_t game_object_id, const Sprite &  		this->particles.emplace_back();  	}  } + +unique_ptr<Component> ParticleEmitter::save() const { +	return unique_ptr<Component>{new ParticleEmitter(*this)}; +} + +void ParticleEmitter::restore(const Component & snapshot) { +	*this = static_cast<const ParticleEmitter &>(snapshot); +} + +ParticleEmitter & ParticleEmitter::operator=(const ParticleEmitter & other) { +	this->particles = other.particles; +	return *this; +} diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h index 8ac2e72..1edd2b5 100644 --- a/src/crepe/api/ParticleEmitter.h +++ b/src/crepe/api/ParticleEmitter.h @@ -84,6 +84,12 @@ public:  	//! Configuration data for particle emission settings.  	Data data; +protected: +	virtual std::unique_ptr<Component> save() const; +	ParticleEmitter(const ParticleEmitter &) = default; +	virtual void restore(const Component & snapshot); +	virtual ParticleEmitter & operator=(const ParticleEmitter &); +  private:  	//! Only ParticleSystem can move and read particles  	friend ParticleSystem; diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp index 85016f5..6de6830 100644 --- a/src/crepe/api/Script.cpp +++ b/src/crepe/api/Script.cpp @@ -2,6 +2,7 @@  #include "../facade/SDLContext.h"  #include "../manager/SceneManager.h" +  #include "Script.h"  using namespace crepe; @@ -28,6 +29,26 @@ SaveManager & Script::get_save_manager() const { return this->mediator->save_man  LoopTimerManager & Script::get_loop_timer() const { return this->mediator->loop_timer; } +void Script::replay::record_start() { +	ReplayManager & mgr = this->mediator->replay_manager; +	return mgr.record_start(); +} + +recording_t Script::replay::record_end() { +	ReplayManager & mgr = this->mediator->replay_manager; +	return mgr.record_end(); +} + +void Script::replay::play(recording_t recording) { +	ReplayManager & mgr = this->mediator->replay_manager; +	return mgr.play(recording); +} + +void Script::replay::release(recording_t recording) { +	ReplayManager & mgr = this->mediator->replay_manager; +	return mgr.release(recording); +} +  const keyboard_state_t & Script::get_keyboard_state() const {  	SDLContext & sdl_context = this->mediator->sdl_context;  	return sdl_context.get_keyboard_state(); diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index a87af4e..2422cdc 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -6,8 +6,10 @@  #include "../manager/EventManager.h"  #include "../manager/LoopTimerManager.h"  #include "../manager/Mediator.h" +#include "../manager/ReplayManager.h"  #include "../system/CollisionSystem.h"  #include "../types.h" +#include "../util/Log.h"  #include "../util/OptionalRef.h"  namespace crepe { @@ -61,93 +63,119 @@ protected:  protected:  	/** -	 * \name Utility functions +	 * \name Component query functions +	 * \see ComponentManager  	 * \{  	 */ -  	/**  	 * \brief Get single component of type \c T on this game object -	 *  	 * \tparam T Type of component -	 *  	 * \returns Reference to component -	 *  	 * \throws std::runtime_error if this game object does not have a component with type \c T  	 */  	template <typename T>  	T & get_component() const; -	// TODO: make get_component calls for component types that can have more than 1 instance -	// cause compile-time errors -  	/**  	 * \brief Get all components of type \c T on this game object -	 *  	 * \tparam T Type of component -	 *  	 * \returns List of component references  	 */  	template <typename T>  	RefVector<T> get_components() const; - -	/** -	 * \copydoc ComponentManager::get_components_by_id -	 * \see ComponentManager::get_components_by_id -	 */ +	//! \copydoc ComponentManager::get_components_by_id  	template <typename T>  	RefVector<T> get_components_by_id(game_object_id_t id) const; -	/** -	 * \copydoc ComponentManager::get_components_by_name -	 * \see ComponentManager::get_components_by_name -	 */ +	//! \copydoc ComponentManager::get_components_by_name  	template <typename T>  	RefVector<T> get_components_by_name(const std::string & name) const; -	/** -	 * \copydoc ComponentManager::get_components_by_tag -	 * \see ComponentManager::get_components_by_tag -	 */ +	//! \copydoc ComponentManager::get_components_by_tag  	template <typename T>  	RefVector<T> get_components_by_tag(const std::string & tag) const; +	//! \}  	/** -	 * \brief Log a message using Log::logf -	 * -	 * \tparam Args Log::logf parameters -	 * \param args  Log::logf parameters +	 * \name Logging functions +	 * \see Log +	 * \{  	 */ -	template <typename... Args> -	void logf(Args &&... args); +	//! \copydoc Log::logf +	template <class... Args> +	void logf(const Log::Level & level, std::format_string<Args...> fmt, Args &&... args); +	//! \copydoc Log::logf +	template <class... Args> +	void logf(std::format_string<Args...> fmt, Args &&... args); +	// \}  	/** -	 * \brief Subscribe to an event with an explicit channel -	 * \see EventManager::subscribe +	 * \name Event manager functions +	 * \see EventManager +	 * \{  	 */ +	//! \copydoc EventManager::subscribe  	template <typename EventType>  	void subscribe(const EventHandler<EventType> & callback, event_channel_t channel); -	/** -	 * \brief Subscribe to an event on EventManager::CHANNEL_ALL -	 * \see EventManager::subscribe -	 */ +	//! \copydoc EventManager::subscribe  	template <typename EventType>  	void subscribe(const EventHandler<EventType> & callback); +	//! \copydoc EventManager::trigger_event +	template <typename EventType> +	void trigger_event(const EventType & event = {}, +					   event_channel_t channel = EventManager::CHANNEL_ALL); +	//! \copydoc EventManager::queue_event +	template <typename EventType> +	void queue_event(const EventType & event = {}, +					 event_channel_t channel = EventManager::CHANNEL_ALL); +	//! \}  	/** -	 * \brief Set the next scene using SceneManager -	 * \see SceneManager::set_next_scene +	 * \name Scene-related functions +	 * \see SceneManager +	 * \{  	 */ +	//! \copydoc SceneManager::set_next_scene  	void set_next_scene(const std::string & name); +	//! \} +	/** +	 * \name Save data management functions +	 * \see SaveManager +	 * \{ +	 */  	//! Retrieve SaveManager reference  	SaveManager & get_save_manager() const; +	//! \} +	/** +	 * \name Timing functions +	 * \see LoopTimerManager +	 * \{ +	 */  	//! Retrieve LoopTimerManager reference  	LoopTimerManager & get_loop_timer() const; +	//! \} + +	//! Replay management functions +	struct replay { // NOLINT +		//! \copydoc ReplayManager::record_start +		void record_start(); +		//! \copydoc ReplayManager::record_end +		recording_t record_end(); +		//! \copydoc ReplayManager::play +		void play(recording_t); +		//! \copydoc ReplayManager::release +		void release(recording_t); + +	private: +		OptionalRef<Mediator> & mediator; +		replay(OptionalRef<Mediator> & mediator) : mediator(mediator) {} +		friend class Script; +	} replay{mediator};  	/**  	 * \brief Utility function to retrieve the keyboard state  	 * \see SDLContext::get_keyboard_state  	 *   	 * \return current keyboard state map with Keycode as key and bool as value(true = pressed, false = not pressed) -	 *   	 */  	const keyboard_state_t & get_keyboard_state() const;  	/** @@ -155,7 +183,6 @@ protected:  	 * \see SDLContext::get_keyboard_state  	 *   	 * \return Keycode state (true if pressed, false if not pressed). -	 *   	 */  	bool get_key_state(Keycode key) const noexcept; diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index 225a51c..4462a41 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -1,6 +1,7 @@  #pragma once  #include "../manager/ComponentManager.h" +#include "../manager/ReplayManager.h"  #include "BehaviorScript.h"  #include "Script.h" @@ -23,9 +24,14 @@ RefVector<T> Script::get_components() const {  	return this->get_components_by_id<T>(this->game_object_id);  } -template <typename... Args> -void Script::logf(Args &&... args) { -	Log::logf(std::forward<Args>(args)...); +template <class... Args> +void Script::logf(const Log::Level & level, std::format_string<Args...> fmt, Args &&... args) { +	Log::logf(level, fmt, std::forward<Args>(args)...); +} + +template <class... Args> +void Script::logf(std::format_string<Args...> fmt, Args &&... args) { +	Log::logf(fmt, std::forward<Args>(args)...);  }  template <typename EventType> @@ -34,8 +40,18 @@ void Script::subscribe_internal(const EventHandler<EventType> & callback,  	EventManager & mgr = this->mediator->event_manager;  	subscription_t listener = mgr.subscribe<EventType>(  		[this, callback](const EventType & data) -> bool { +			// check if (parent) BehaviorScript component is active  			bool & active = this->active;  			if (!active) return false; + +			// check if replay manager is playing (if initialized) +			try { +				ReplayManager & replay = this->mediator->replay_manager; +				if (replay.get_state() == ReplayManager::PLAYING) return false; +			} catch (const std::runtime_error &) { +			} + +			// call user-provided callback  			return callback(data);  		},  		channel); @@ -52,6 +68,18 @@ void Script::subscribe(const EventHandler<EventType> & callback) {  	this->subscribe_internal(callback, EventManager::CHANNEL_ALL);  } +template <typename EventType> +void Script::trigger_event(const EventType & event, event_channel_t channel) { +	EventManager & mgr = this->mediator->event_manager; +	mgr.trigger_event(event, channel); +} + +template <typename EventType> +void Script::queue_event(const EventType & event, event_channel_t channel) { +	EventManager & mgr = this->mediator->event_manager; +	mgr.queue_event(event, channel); +} +  template <typename T>  RefVector<T> Script::get_components_by_id(game_object_id_t id) const {  	Mediator & mediator = this->mediator; diff --git a/src/crepe/api/Sprite.cpp b/src/crepe/api/Sprite.cpp index ba684ba..0107c7b 100644 --- a/src/crepe/api/Sprite.cpp +++ b/src/crepe/api/Sprite.cpp @@ -1,6 +1,6 @@  #include <cmath> -#include "../util/Log.h" +#include "../util/dbg.h"  #include "api/Asset.h"  #include "Component.h" diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp index a85b792..fcfce14 100644 --- a/src/crepe/api/Transform.cpp +++ b/src/crepe/api/Transform.cpp @@ -1,8 +1,9 @@ -#include "../util/Log.h" +#include "../util/dbg.h"  #include "Transform.h"  using namespace crepe; +using namespace std;  Transform::Transform(game_object_id_t id, const vec2 & point, double rotation, double scale)  	: Component(id), @@ -11,3 +12,11 @@ Transform::Transform(game_object_id_t id, const vec2 & point, double rotation, d  	  scale(scale) {  	dbg_trace();  } + +unique_ptr<Component> Transform::save() const { +	return unique_ptr<Component>{new Transform(*this)}; +} + +void Transform::restore(const Component & snapshot) { +	*this = static_cast<const Transform &>(snapshot); +} diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h index 7ee6d65..a6f3486 100644 --- a/src/crepe/api/Transform.h +++ b/src/crepe/api/Transform.h @@ -35,6 +35,12 @@ protected:  	virtual int get_instances_max() const { return 1; }  	//! ComponentManager instantiates all components  	friend class ComponentManager; + +protected: +	virtual std::unique_ptr<Component> save() const; +	Transform(const Transform &) = default; +	virtual void restore(const Component & snapshot); +	virtual Transform & operator=(const Transform &) = default;  };  } // namespace crepe diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp index ae2d4bc..7a3e473 100644 --- a/src/crepe/facade/DB.cpp +++ b/src/crepe/facade/DB.cpp @@ -1,6 +1,6 @@  #include <cstring> -#include "util/Log.h" +#include "util/dbg.h"  #include "DB.h" diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index fffbe34..9486ccd 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -20,7 +20,7 @@  #include "../api/Color.h"  #include "../api/Config.h"  #include "../api/Sprite.h" -#include "../util/Log.h" +#include "../util/dbg.h"  #include "manager/Mediator.h"  #include "SDLContext.h" diff --git a/src/crepe/facade/Sound.cpp b/src/crepe/facade/Sound.cpp index 97e455e..b1e6463 100644 --- a/src/crepe/facade/Sound.cpp +++ b/src/crepe/facade/Sound.cpp @@ -1,5 +1,5 @@  #include "../api/Asset.h" -#include "../util/Log.h" +#include "../util/dbg.h"  #include "Sound.h" diff --git a/src/crepe/facade/SoundContext.cpp b/src/crepe/facade/SoundContext.cpp index b1f8cb3..5091e07 100644 --- a/src/crepe/facade/SoundContext.cpp +++ b/src/crepe/facade/SoundContext.cpp @@ -1,4 +1,4 @@ -#include "../util/Log.h" +#include "../util/dbg.h"  #include "SoundContext.h" diff --git a/src/crepe/facade/Texture.cpp b/src/crepe/facade/Texture.cpp index b63403d..cd06439 100644 --- a/src/crepe/facade/Texture.cpp +++ b/src/crepe/facade/Texture.cpp @@ -1,10 +1,10 @@ -#include "../util/Log.h" -#include "facade/SDLContext.h" -#include "manager/Mediator.h" +#include "../util/dbg.h" +#include "../facade/SDLContext.h" +#include "../manager/Mediator.h" +#include "../types.h" +#include "../Resource.h" -#include "Resource.h"  #include "Texture.h" -#include "types.h"  using namespace crepe;  using namespace std; @@ -23,6 +23,8 @@ Texture::~Texture() {  }  const ivec2 & Texture::get_size() const noexcept { return this->size; } +  const float & Texture::get_ratio() const noexcept { return this->aspect_ratio; }  SDL_Texture * Texture::get_img() const noexcept { return this->texture.get(); } + diff --git a/src/crepe/manager/CMakeLists.txt b/src/crepe/manager/CMakeLists.txt index f73e165..48e444f 100644 --- a/src/crepe/manager/CMakeLists.txt +++ b/src/crepe/manager/CMakeLists.txt @@ -6,6 +6,8 @@ target_sources(crepe PUBLIC  	SceneManager.cpp  	LoopTimerManager.cpp  	ResourceManager.cpp +	ReplayManager.cpp +	SystemManager.cpp  )  target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -21,5 +23,8 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	LoopTimerManager.h  	ResourceManager.h  	ResourceManager.hpp +	ReplayManager.h +	SystemManager.h +	SystemManager.hpp  ) diff --git a/src/crepe/manager/ComponentManager.cpp b/src/crepe/manager/ComponentManager.cpp index df30d27..745ddae 100644 --- a/src/crepe/manager/ComponentManager.cpp +++ b/src/crepe/manager/ComponentManager.cpp @@ -1,7 +1,7 @@  #include "../api/GameObject.h"  #include "../api/Metadata.h"  #include "../types.h" -#include "../util/Log.h" +#include "../util/dbg.h"  #include "ComponentManager.h" @@ -53,7 +53,7 @@ GameObject ComponentManager::new_object(const string & name, const string & tag,  		this->next_id++;  	} -	GameObject object{*this, this->next_id, name, tag, position, rotation, scale}; +	GameObject object{this->mediator, this->next_id, name, tag, position, rotation, scale};  	this->next_id++;  	return object; @@ -72,3 +72,28 @@ set<game_object_id_t> ComponentManager::get_objects_by_tag(const string & tag) c  	return this->get_objects_by_predicate<Metadata>(  		[tag](const Metadata & data) { return data.tag == tag; });  } + +ComponentManager::Snapshot ComponentManager::save() { +	Snapshot snapshot{}; +	for (const auto & [type, by_id_index] : this->components) { +		for (game_object_id_t id = 0; id < by_id_index.size(); id++) { +			const auto & components = by_id_index[id]; +			for (size_t index = 0; index < components.size(); index++) { +				const Component & component = *components[index]; +				snapshot.components.push_back(SnapshotComponent{ +					.type = type, +					.id = id, +					.index = index, +					.component = component.save(), +				}); +			} +		} +	} +	return snapshot; +} + +void ComponentManager::restore(const Snapshot & snapshot) { +	for (const SnapshotComponent & info : snapshot.components) { +		this->components[info.type][info.id][info.index]->restore(*info.component); +	} +} diff --git a/src/crepe/manager/ComponentManager.h b/src/crepe/manager/ComponentManager.h index 19a8e81..c3a5b4a 100644 --- a/src/crepe/manager/ComponentManager.h +++ b/src/crepe/manager/ComponentManager.h @@ -21,13 +21,6 @@ class GameObject;   * This class manages all components. It provides methods to add, delete and get components.   */  class ComponentManager : public Manager { -	// TODO: This relation should be removed! I (loek) believe that the scene manager should -	// create/destroy components because the GameObject's are stored in concrete Scene classes, -	// which will in turn call GameObject's destructor, which will in turn call -	// ComponentManager::delete_components_by_id or something. This is a pretty major change, so -	// here is a comment and temporary fix instead :tada: -	friend class SceneManager; -  public:  	ComponentManager(Mediator & mediator);  	~ComponentManager(); // dbg_trace @@ -49,12 +42,7 @@ public:  						  const vec2 & position = {0, 0}, double rotation = 0,  						  double scale = 1); -protected: -	/** -	 * GameObject is used as an interface to add/remove components, and the game programmer is -	 * supposed to use it instead of interfacing with the component manager directly. -	 */ -	friend class GameObject; +public:  	/**  	 * \brief Add a component to the ComponentManager  	 * @@ -154,6 +142,40 @@ public:  	template <typename T>  	RefVector<T> get_components_by_tag(const std::string & tag) const; +	//! Snapshot of single component (including path in \c components) +	struct SnapshotComponent { +		//! \c components path +		std::type_index type; +		//! \c components path +		game_object_id_t id; +		//! \c components path +		size_t index; +		//! Actual component snapshot +		std::unique_ptr<Component> component; +	}; +	//! Snapshot of the entire component manager state +	struct Snapshot { +		//! All components +		std::vector<SnapshotComponent> components; +		// TODO: some kind of hash code that ensures components exist in all the same places as +		// this snapshot +	}; +	/** +	 * \name ReplayManager (Memento) functions +	 * \{ +	 */ +	/** +	 * \brief Save a snapshot of the component manager state +	 * \returns Deep copy of the component manager's internal state +	 */ +	Snapshot save(); +	/** +	 * \brief Restore component manager from a snapshot +	 * \param snapshot Snapshot to restore from (as returned by \c save()) +	 */ +	void restore(const Snapshot & snapshot); +	//! \} +  private:  	/**  	 * \brief Get object IDs by predicate function diff --git a/src/crepe/manager/LoopTimerManager.cpp b/src/crepe/manager/LoopTimerManager.cpp index a6e4788..e78f92f 100644 --- a/src/crepe/manager/LoopTimerManager.cpp +++ b/src/crepe/manager/LoopTimerManager.cpp @@ -1,7 +1,7 @@  #include <chrono>  #include <thread> -#include "../util/Log.h" +#include "../util/dbg.h"  #include "LoopTimerManager.h" diff --git a/src/crepe/manager/LoopTimerManager.h b/src/crepe/manager/LoopTimerManager.h index 76b02d3..2f1e6b6 100644 --- a/src/crepe/manager/LoopTimerManager.h +++ b/src/crepe/manager/LoopTimerManager.h @@ -6,6 +6,8 @@  namespace crepe { +class Engine; +  typedef std::chrono::duration<float> duration_t;  typedef std::chrono::duration<unsigned long long, std::micro> elapsed_time_t; @@ -107,7 +109,7 @@ public:  private:  	//! Friend relation to use start,enforce_frame_rate,get_lag,update,advance_fixed_update. -	friend class LoopManager; +	friend class Engine;  	/**  	 * \brief Start the loop timer.  	 * diff --git a/src/crepe/manager/Mediator.h b/src/crepe/manager/Mediator.h index a336410..842f1de 100644 --- a/src/crepe/manager/Mediator.h +++ b/src/crepe/manager/Mediator.h @@ -11,6 +11,8 @@ class LoopTimerManager;  class SaveManager;  class ResourceManager;  class SDLContext; +class ReplayManager; +class SystemManager;  /**   * Struct to pass references to classes that would otherwise need to be singletons down to @@ -32,6 +34,8 @@ struct Mediator {  	OptionalRef<LoopTimerManager> loop_timer;  	OptionalRef<SaveManager> save_manager;  	OptionalRef<ResourceManager> resource_manager; +	OptionalRef<ReplayManager> replay_manager; +	OptionalRef<SystemManager> system_manager;  };  } // namespace crepe diff --git a/src/crepe/manager/ReplayManager.cpp b/src/crepe/manager/ReplayManager.cpp new file mode 100644 index 0000000..090a94e --- /dev/null +++ b/src/crepe/manager/ReplayManager.cpp @@ -0,0 +1,70 @@ +#include <format> + +#include "Manager.h" +#include "ReplayManager.h" + +using namespace crepe; +using namespace std; + +ReplayManager::ReplayManager(Mediator & mediator) : Manager(mediator) { +	mediator.replay_manager = *this; +} + +void ReplayManager::record_start() { +	if (this->state == RECORDING) this->release(this->id); +	this->id++; +	this->memory[this->id] = make_unique<Recording>(); +	this->recording = *this->memory.at(this->id); +	this->state = RECORDING; +} + +recording_t ReplayManager::record_end() { +	this->state = IDLE; +	return this->id; +} + +void ReplayManager::play(recording_t handle) { +	if (!this->memory.contains(handle)) +		throw out_of_range(format("ReplayManager: no recording for handle {}", handle)); +	this->recording = *this->memory.at(handle); +	this->recording->frame = 0; +	this->state = PLAYING; +} + +void ReplayManager::release(recording_t handle) { +	if (!this->memory.contains(handle)) return; +	this->memory.erase(handle); +} + +void ReplayManager::frame_record() { +	if (this->state != RECORDING) +		throw runtime_error("ReplayManager: frame_step called while not playing"); + +	ComponentManager & components = this->mediator.component_manager; +	Recording & recording = this->recording; + +	recording.frames.push_back(components.save()); +	recording.frame++; +} + +bool ReplayManager::frame_step() { +	if (this->state != PLAYING) +		throw runtime_error("ReplayManager: frame_step called while not playing"); + +	ComponentManager & components = this->mediator.component_manager; +	Recording & recording = this->recording; + +	ComponentManager::Snapshot & frame = recording.frames.at(recording.frame); + +	components.restore(frame); +	recording.frame++; + +	if (recording.frame < recording.frames.size()) return false; +	// end of recording +	recording.frame = 0; +	this->state = IDLE; +	this->recording.clear(); +	return true; +} + +ReplayManager::State ReplayManager::get_state() const { return this->state; } diff --git a/src/crepe/manager/ReplayManager.h b/src/crepe/manager/ReplayManager.h new file mode 100644 index 0000000..f06a58b --- /dev/null +++ b/src/crepe/manager/ReplayManager.h @@ -0,0 +1,96 @@ +#pragma once + +#include <unordered_map> + +#include "../util/OptionalRef.h" + +#include "ComponentManager.h" +#include "Manager.h" + +namespace crepe { + +//! Handle to recording held by ReplayManager +typedef size_t recording_t; + +/** + * \brief Replay manager + * + * The replay manager is responsible for creating, storing and restoring ComponentManager + * snapshots. Sequential snapshots can be recorded and replayed in combination with + * ReplaySystem. + */ +class ReplayManager : public Manager { +	// TODO: Delete recordings at end of scene + +public: +	ReplayManager(Mediator & mediator); + +public: +	//! Start a new recording +	void record_start(); +	/** +	 * \brief End the latest recording started by \c record_start() +	 * \returns Handle to recording +	 */ +	recording_t record_end(); +	/** +	 * \brief Play a recording +	 * \param handle Handle to recording (as returned by \c record_end()) +	 */ +	void play(recording_t handle); +	/** +	 * \brief Delete a recording from memory +	 * \param handle Handle to recording (as returned by \c record_end()) +	 */ +	void release(recording_t handle); + +public: +	//! Internal state +	enum State { +		IDLE, //!< Not doing anything +		RECORDING, //!< Currently recording +		PLAYING, //!< Currently playing back a recording +	}; +	//! Get current internal state +	State get_state() const; + +public: +	/** +	 * \brief Record a single frame to the current recording +	 * +	 * This function is called by ReplaySystem after the game programmer has called \c +	 * record_start() +	 */ +	void frame_record(); +	/** +	 * \brief Play the next frame of the current recording +	 * +	 * \returns `true` if the recording is finished playing +	 * \returns `false` if there are more frames +	 * +	 * This function also automatically resets the internal state from PLAYING to IDLE at the end +	 * of a recording. +	 */ +	bool frame_step(); + +private: +	/** +	 * \brief Recording data +	 */ +	struct Recording { +		//! Current frame being shown +		size_t frame = 0; +		//! All frames in recording +		std::vector<ComponentManager::Snapshot> frames; +	}; +	//! Internal state +	State state = IDLE; +	//! Current recording handle +	recording_t id = -1; +	//! Current recording data +	OptionalRef<Recording> recording; +	//! Recording storage +	std::unordered_map<recording_t, std::unique_ptr<Recording>> memory; +}; + +} // namespace crepe diff --git a/src/crepe/manager/ResourceManager.cpp b/src/crepe/manager/ResourceManager.cpp index a141a46..5713183 100644 --- a/src/crepe/manager/ResourceManager.cpp +++ b/src/crepe/manager/ResourceManager.cpp @@ -1,4 +1,4 @@ -#include "util/Log.h" +#include "util/dbg.h"  #include "ResourceManager.h" diff --git a/src/crepe/manager/SystemManager.cpp b/src/crepe/manager/SystemManager.cpp new file mode 100644 index 0000000..5ada30f --- /dev/null +++ b/src/crepe/manager/SystemManager.cpp @@ -0,0 +1,66 @@ +#include "../system/AISystem.h" +#include "../system/AnimatorSystem.h" +#include "../system/AudioSystem.h" +#include "../system/CollisionSystem.h" +#include "../system/EventSystem.h" +#include "../system/InputSystem.h" +#include "../system/ParticleSystem.h" +#include "../system/PhysicsSystem.h" +#include "../system/RenderSystem.h" +#include "../system/ReplaySystem.h" +#include "../system/ScriptSystem.h" + +#include "SystemManager.h" + +using namespace crepe; +using namespace std; + +SystemManager::SystemManager(Mediator & mediator) : Manager(mediator) { +	this->load_system<InputSystem>(); +	this->load_system<EventSystem>(); +	this->load_system<ScriptSystem>(); +	this->load_system<AISystem>(); +	this->load_system<PhysicsSystem>(); +	this->load_system<CollisionSystem>(); +	this->load_system<AudioSystem>(); +	this->load_system<AnimatorSystem>(); +	this->load_system<ParticleSystem>(); +	this->load_system<RenderSystem>(); +	this->load_system<ReplaySystem>(); + +	this->mediator.system_manager = *this; +} + +void SystemManager::fixed_update() { +	for (auto & [type, system] : this->systems) { +		if (!system->active) continue; +		system->fixed_update(); +	} +} + +void SystemManager::frame_update() { +	for (auto & [type, system] : this->systems) { +		if (!system->active) continue; +		system->frame_update(); +	} +} + +SystemManager::Snapshot SystemManager::save() { +	Snapshot snapshot; +	for (auto & [type, system] : this->systems) { +		snapshot[type] = system->active; +	} +	return snapshot; +} + +void SystemManager::restore(const Snapshot & snapshot) { +	for (auto & [type, active] : snapshot) { +		this->systems[type]->active = active; +	} +} + +void SystemManager::disable_all() { +	for (auto & [type, system] : this->systems) { +		system->active = false; +	} +} diff --git a/src/crepe/manager/SystemManager.h b/src/crepe/manager/SystemManager.h new file mode 100644 index 0000000..50acf77 --- /dev/null +++ b/src/crepe/manager/SystemManager.h @@ -0,0 +1,85 @@ +#pragma once + +#include <memory> +#include <typeindex> +#include <unordered_map> + +#include "../system/System.h" + +#include "Manager.h" + +namespace crepe { + +/** + * \brief Collection of all systems + * + * This manager aggregates all systems and provides utility functions to retrieve references to + * and update systems. + */ +class SystemManager : public Manager { +public: +	SystemManager(Mediator &); + +	/** +	 * \brief Per-frame update. +	 * +	 * Updates the game state based on the elapsed time since the last frame. +	 */ +	void frame_update(); + +	/** +	 * \brief Fixed update executed at a fixed rate. +	 * +	 * This function updates physics and game logic based on LoopTimer's fixed_delta_time. +	 */ +	void fixed_update(); + +private: +	/** +	 * \brief Collection of System instances +	 * +	 * This map holds System instances indexed by the system's class typeid. It is filled in the +	 * constructor of \c SystemManager using SystemManager::load_system. +	 */ +	std::unordered_map<std::type_index, std::unique_ptr<System>> systems; +	/** +	 * \brief Initialize a system +	 * \tparam T System type (must be derivative of \c System) +	 */ +	template <class T> +	void load_system(); + +public: +	/** +	 * \brief Retrieve a reference to ECS system +	 * \tparam T System type +	 * \returns Reference to system instance +	 * \throws std::runtime_error if the System is not initialized +	 */ +	template <class T> +	T & get_system(); + +public: +	/** +	 * \brief SystemManager snapshot +	 * +	 * The SystemManager snapshot only stores which systems are active +	 */ +	typedef std::unordered_map<std::type_index, bool> Snapshot; +	/** +	 * \brief Save a snapshot of the systems' state +	 * \returns Copy of each system's active property +	 */ +	Snapshot save(); +	/** +	 * \brief Restore system active state from a snapshot +	 * \param snapshot Snapshot to restore from (as returned by \c save()) +	 */ +	void restore(const Snapshot & snapshot); +	//! Disable all systems +	void disable_all(); +}; + +} // namespace crepe + +#include "SystemManager.hpp" diff --git a/src/crepe/api/LoopManager.hpp b/src/crepe/manager/SystemManager.hpp index 266758a..3d26e4c 100644 --- a/src/crepe/api/LoopManager.hpp +++ b/src/crepe/manager/SystemManager.hpp @@ -4,26 +4,19 @@  #include <format>  #include <memory> -#include "../system/System.h" - -#include "LoopManager.h" +#include "SystemManager.h"  namespace crepe {  template <class T> -void LoopManager::add_scene() { -	this->scene_manager.add_scene<T>(); -} - -template <class T> -T & LoopManager::get_system() { +T & SystemManager::get_system() {  	using namespace std;  	static_assert(is_base_of<System, T>::value,  				  "get_system must recieve a derivative class of System");  	const type_info & type = typeid(T);  	if (!this->systems.contains(type)) -		throw runtime_error(format("LoopManager: {} is not initialized", type.name())); +		throw runtime_error(format("SystemManager: {} is not initialized", type.name()));  	System * system = this->systems.at(type).get();  	T * concrete_system = dynamic_cast<T *>(system); @@ -33,14 +26,14 @@ T & LoopManager::get_system() {  }  template <class T> -void LoopManager::load_system() { +void SystemManager::load_system() {  	using namespace std;  	static_assert(is_base_of<System, T>::value,  				  "load_system must recieve a derivative class of System");  	const type_info & type = typeid(T);  	if (this->systems.contains(type)) -		throw runtime_error(format("LoopManager: {} is already initialized", type.name())); +		throw runtime_error(format("SystemManager: {} is already initialized", type.name()));  	System * system = new T(this->mediator);  	this->systems[type] = unique_ptr<System>(system);  } diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp index 680dbb8..0f35010 100644 --- a/src/crepe/system/AISystem.cpp +++ b/src/crepe/system/AISystem.cpp @@ -10,7 +10,7 @@  using namespace crepe;  using namespace std::chrono; -void AISystem::update() { +void AISystem::fixed_update() {  	const Mediator & mediator = this->mediator;  	ComponentManager & mgr = mediator.component_manager;  	LoopTimerManager & loop_timer = mediator.loop_timer; diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h index d5f8a8e..04807cf 100644 --- a/src/crepe/system/AISystem.h +++ b/src/crepe/system/AISystem.h @@ -20,7 +20,7 @@ public:  	using System::System;  	//! Update the AI system -	void update() override; +	void fixed_update() override;  private:  	/** diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp index 107b25d..e5ab2fa 100644 --- a/src/crepe/system/AnimatorSystem.cpp +++ b/src/crepe/system/AnimatorSystem.cpp @@ -1,5 +1,3 @@ - -  #include "../api/Animator.h"  #include "../manager/ComponentManager.h"  #include "../manager/LoopTimerManager.h" @@ -10,7 +8,7 @@  using namespace crepe;  using namespace std::chrono; -void AnimatorSystem::update() { +void AnimatorSystem::frame_update() {  	ComponentManager & mgr = this->mediator.component_manager;  	LoopTimerManager & timer = this->mediator.loop_timer;  	RefVector<Animator> animations = mgr.get_components_by_type<Animator>(); diff --git a/src/crepe/system/AnimatorSystem.h b/src/crepe/system/AnimatorSystem.h index 7d3f565..092e131 100644 --- a/src/crepe/system/AnimatorSystem.h +++ b/src/crepe/system/AnimatorSystem.h @@ -22,7 +22,7 @@ public:  	 * Animator components, moving the animations forward and managing their behavior (e.g.,  	 * looping).  	 */ -	void update() override; +	void frame_update() override;  };  } // namespace crepe diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp index b1aa0f8..d4e8b9f 100644 --- a/src/crepe/system/AudioSystem.cpp +++ b/src/crepe/system/AudioSystem.cpp @@ -7,7 +7,7 @@  using namespace crepe;  using namespace std; -void AudioSystem::update() { +void AudioSystem::fixed_update() {  	ComponentManager & component_manager = this->mediator.component_manager;  	ResourceManager & resource_manager = this->mediator.resource_manager;  	RefVector<AudioSource> components diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h index 2ddc443..56fc98c 100644 --- a/src/crepe/system/AudioSystem.h +++ b/src/crepe/system/AudioSystem.h @@ -11,7 +11,7 @@ namespace crepe {  class AudioSystem : public System {  public:  	using System::System; -	void update() override; +	void fixed_update() override;  private:  	/** diff --git a/src/crepe/system/CMakeLists.txt b/src/crepe/system/CMakeLists.txt index 0e2db76..52369d0 100644 --- a/src/crepe/system/CMakeLists.txt +++ b/src/crepe/system/CMakeLists.txt @@ -8,6 +8,8 @@ target_sources(crepe PUBLIC  	AudioSystem.cpp  	AnimatorSystem.cpp  	InputSystem.cpp +	EventSystem.cpp +	ReplaySystem.cpp  	AISystem.cpp  ) @@ -20,5 +22,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	AudioSystem.h  	AnimatorSystem.h  	InputSystem.h +	EventSystem.h +	ReplaySystem.h  	AISystem.h  ) diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp index af8adce..9d88d9f 100644 --- a/src/crepe/system/CollisionSystem.cpp +++ b/src/crepe/system/CollisionSystem.cpp @@ -23,7 +23,7 @@  using namespace crepe; -void CollisionSystem::update() { +void CollisionSystem::fixed_update() {  	std::vector<CollisionInternal> all_colliders;  	game_object_id_t id = 0;  	ComponentManager & mgr = this->mediator.component_manager; diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h index 5b136c6..48a8f86 100644 --- a/src/crepe/system/CollisionSystem.h +++ b/src/crepe/system/CollisionSystem.h @@ -85,7 +85,7 @@ public:  public:  	//! Updates the collision system by checking for collisions between colliders and handling them. -	void update() override; +	void fixed_update() override;  private:  	/** diff --git a/src/crepe/system/EventSystem.cpp b/src/crepe/system/EventSystem.cpp new file mode 100644 index 0000000..7e168ab --- /dev/null +++ b/src/crepe/system/EventSystem.cpp @@ -0,0 +1,9 @@ +#include "EventSystem.h" +#include "../manager/EventManager.h" + +using namespace crepe; + +void EventSystem::fixed_update() { +	EventManager & ev = this->mediator.event_manager; +	ev.dispatch_events(); +} diff --git a/src/crepe/system/EventSystem.h b/src/crepe/system/EventSystem.h new file mode 100644 index 0000000..0ae48d2 --- /dev/null +++ b/src/crepe/system/EventSystem.h @@ -0,0 +1,21 @@ +#pragma once + +#include "System.h" + +namespace crepe { + +/** + * \brief EventManager dispatch helper system + */ +class EventSystem : public System { +public: +	using System::System; + +	/** +	 * \brief Dispatch queued events +	 * \see EventManager::dispatch_events +	 */ +	void fixed_update() override; +}; + +} // namespace crepe diff --git a/src/crepe/system/InputSystem.cpp b/src/crepe/system/InputSystem.cpp index 60daa55..d209282 100644 --- a/src/crepe/system/InputSystem.cpp +++ b/src/crepe/system/InputSystem.cpp @@ -8,7 +8,7 @@  using namespace crepe; -void InputSystem::update() { +void InputSystem::fixed_update() {  	ComponentManager & mgr = this->mediator.component_manager;  	SDLContext & context = this->mediator.sdl_context;  	std::vector<EventData> event_list = context.get_events(); diff --git a/src/crepe/system/InputSystem.h b/src/crepe/system/InputSystem.h index e580d8e..0f1bfa1 100644 --- a/src/crepe/system/InputSystem.h +++ b/src/crepe/system/InputSystem.h @@ -61,7 +61,7 @@ public:  	 * \brief Updates the system, processing all input events.  	 * This method processes all events and triggers corresponding actions.  	 */ -	void update() override; +	void fixed_update() override;  private:  	//! Stores the last position of the mouse when the button was pressed. diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp index 35a1d41..bbc7366 100644 --- a/src/crepe/system/ParticleSystem.cpp +++ b/src/crepe/system/ParticleSystem.cpp @@ -12,7 +12,7 @@  using namespace crepe; -void ParticleSystem::update() { +void ParticleSystem::fixed_update() {  	// Get all emitters  	const Mediator & mediator = this->mediator;  	LoopTimerManager & loop_timer = mediator.loop_timer; diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h index 154521d..4296ff3 100644 --- a/src/crepe/system/ParticleSystem.h +++ b/src/crepe/system/ParticleSystem.h @@ -20,7 +20,7 @@ public:  	 * \brief Updates all particle emitters by emitting particles, updating particle states, and  	 * checking bounds.  	 */ -	void update() override; +	void fixed_update() override;  private:  	/** diff --git a/src/crepe/system/PhysicsSystem.cpp b/src/crepe/system/PhysicsSystem.cpp index 3b3b8ab..62f8132 100644 --- a/src/crepe/system/PhysicsSystem.cpp +++ b/src/crepe/system/PhysicsSystem.cpp @@ -12,8 +12,7 @@  using namespace crepe; -void PhysicsSystem::update() { - +void PhysicsSystem::fixed_update() {  	const Mediator & mediator = this->mediator;  	ComponentManager & mgr = mediator.component_manager;  	LoopTimerManager & loop_timer = mediator.loop_timer; diff --git a/src/crepe/system/PhysicsSystem.h b/src/crepe/system/PhysicsSystem.h index 26152a5..5ed624f 100644 --- a/src/crepe/system/PhysicsSystem.h +++ b/src/crepe/system/PhysicsSystem.h @@ -18,7 +18,7 @@ public:  	 *  	 * It calculates new velocties and changes the postion in the transform.  	 */ -	void update() override; +	void fixed_update() override;  };  } // namespace crepe diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp index 62d42ec..33218f6 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -67,7 +67,7 @@ RefVector<Sprite> RenderSystem::sort(RefVector<Sprite> & objs) const {  	return sorted_objs;  } -void RenderSystem::update() { +void RenderSystem::frame_update() {  	this->clear_screen();  	this->render();  	this->present_screen(); diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h index 56a0553..656ad5b 100644 --- a/src/crepe/system/RenderSystem.h +++ b/src/crepe/system/RenderSystem.h @@ -24,7 +24,7 @@ public:  	 * \brief Updates the RenderSystem for the current frame.  	 * This method is called to perform all rendering operations for the current game frame.  	 */ -	void update() override; +	void frame_update() override;  private:  	//! Clears the screen in preparation for rendering. diff --git a/src/crepe/system/ReplaySystem.cpp b/src/crepe/system/ReplaySystem.cpp new file mode 100644 index 0000000..efc3be4 --- /dev/null +++ b/src/crepe/system/ReplaySystem.cpp @@ -0,0 +1,54 @@ +#include "../manager/ReplayManager.h" +#include "../manager/SystemManager.h" + +#include "EventSystem.h" +#include "RenderSystem.h" +#include "ReplaySystem.h" + +using namespace crepe; +using namespace std; + +void ReplaySystem::fixed_update() { +	ReplayManager & replay = this->mediator.replay_manager; +	ReplayManager::State state = replay.get_state(); +	ReplayManager::State last_state = this->last_state; +	this->last_state = state; + +	switch (state) { +		case ReplayManager::IDLE: +			break; +		case ReplayManager::RECORDING: { +			replay.frame_record(); +			break; +		} +		case ReplayManager::PLAYING: { +			if (last_state != ReplayManager::PLAYING) this->playback_begin(); +			bool last = replay.frame_step(); +			if (last) this->playback_end(); +			break; +		} +	} +} + +void ReplaySystem::playback_begin() { +	SystemManager & systems = this->mediator.system_manager; +	ComponentManager & components = this->mediator.component_manager; + +	this->playback = { +		.components = components.save(), +		.systems = systems.save(), +	}; + +	systems.disable_all(); +	systems.get_system<RenderSystem>().active = true; +	systems.get_system<ReplaySystem>().active = true; +	systems.get_system<EventSystem>().active = true; +} + +void ReplaySystem::playback_end() { +	SystemManager & systems = this->mediator.system_manager; +	ComponentManager & components = this->mediator.component_manager; + +	components.restore(this->playback.components); +	systems.restore(this->playback.systems); +} diff --git a/src/crepe/system/ReplaySystem.h b/src/crepe/system/ReplaySystem.h new file mode 100644 index 0000000..bbc8d76 --- /dev/null +++ b/src/crepe/system/ReplaySystem.h @@ -0,0 +1,44 @@ +#pragma once + +#include "../manager/ReplayManager.h" +#include "../manager/SystemManager.h" + +#include "System.h" + +namespace crepe { + +/** + * \brief ReplayManager helper system + * + * This system records and replays recordings using ReplayManager. + */ +class ReplaySystem : public System { +public: +	using System::System; + +	void fixed_update() override; + +private: +	//! Last ReplayManager state +	ReplayManager::State last_state = ReplayManager::IDLE; + +	/** +	 * \brief Playback snapshot +	 * +	 * When starting playback, the component state is saved and most systems are disabled. This +	 * struct stores the engine state before ReplayManager::play is called. +	 */ +	struct Snapshot { +		ComponentManager::Snapshot components; +		SystemManager::Snapshot systems; +	}; +	//! Before playback snapshot +	Snapshot playback; + +	//! Snapshot state and disable systems during playback +	void playback_begin(); +	//! Restore state from before \c playback_begin() +	void playback_end(); +}; + +} // namespace crepe diff --git a/src/crepe/system/ScriptSystem.cpp b/src/crepe/system/ScriptSystem.cpp index 0605c7a..746285f 100644 --- a/src/crepe/system/ScriptSystem.cpp +++ b/src/crepe/system/ScriptSystem.cpp @@ -1,13 +1,14 @@  #include "../api/BehaviorScript.h"  #include "../api/Script.h"  #include "../manager/ComponentManager.h" +#include "../util/dbg.h"  #include "ScriptSystem.h"  using namespace std;  using namespace crepe; -void ScriptSystem::update() { +void ScriptSystem::fixed_update() {  	dbg_trace();  	ComponentManager & mgr = this->mediator.component_manager; diff --git a/src/crepe/system/ScriptSystem.h b/src/crepe/system/ScriptSystem.h index 3db1b1e..612c2ae 100644 --- a/src/crepe/system/ScriptSystem.h +++ b/src/crepe/system/ScriptSystem.h @@ -22,7 +22,7 @@ public:  	 * method. It also calls Script::init() if this has not been done before on  	 * the \c BehaviorScript instance.  	 */ -	void update() override; +	void fixed_update() override;  };  } // namespace crepe diff --git a/src/crepe/system/System.h b/src/crepe/system/System.h index 063dfbf..e2ce7eb 100644 --- a/src/crepe/system/System.h +++ b/src/crepe/system/System.h @@ -14,10 +14,12 @@ class ComponentManager;   */  class System {  public: -	/** -	 * \brief Process all components this system is responsible for. -	 */ -	virtual void update() = 0; +	//! Code that runs in the fixed loop +	virtual void fixed_update() {}; +	//! Code that runs in the frame loop +	virtual void frame_update() {}; +	//! Indicates that the update functions of this system should be run +	bool active = true;  public:  	System(const Mediator & m); diff --git a/src/crepe/util/Log.cpp b/src/crepe/util/Log.cpp index 84d80a8..ce25a1d 100644 --- a/src/crepe/util/Log.cpp +++ b/src/crepe/util/Log.cpp @@ -4,6 +4,7 @@  #include "../api/Config.h"  #include "Log.h" +#include "LogColor.h"  using namespace crepe;  using namespace std; diff --git a/src/crepe/util/Log.h b/src/crepe/util/Log.h index fc0bb3a..b43fe30 100644 --- a/src/crepe/util/Log.h +++ b/src/crepe/util/Log.h @@ -2,27 +2,6 @@  #include <format> -// allow user to disable debug macros -#ifndef CREPE_DISABLE_MACROS - -#include "LogColor.h" - -// utility macros -#define _crepe_logf_here(level, fmt, ...) \ -	crepe::Log::logf(level, "{}" fmt, \ -					 crepe::LogColor().fg_white(false).str(std::format( \ -						 "{} ({}:{})", __PRETTY_FUNCTION__, __FILE_NAME__, __LINE__)), \ -					 __VA_ARGS__) - -// very illegal global function-style macros -// NOLINTBEGIN -#define dbg_logf(fmt, ...) _crepe_logf_here(crepe::Log::Level::DEBUG, ": " fmt, __VA_ARGS__) -#define dbg_log(str) _crepe_logf_here(crepe::Log::Level::DEBUG, ": {}", str) -#define dbg_trace() _crepe_logf_here(crepe::Log::Level::TRACE, "", "") -// NOLINTEND - -#endif -  namespace crepe {  /** diff --git a/src/crepe/util/dbg.h b/src/crepe/util/dbg.h new file mode 100644 index 0000000..c7283ee --- /dev/null +++ b/src/crepe/util/dbg.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Log.h" +#include "LogColor.h" + +// utility macros +#define _crepe_logf_here(level, fmt, ...) \ +	crepe::Log::logf(level, "{}" fmt, \ +					 crepe::LogColor().fg_white(false).str(std::format( \ +						 "{} ({}:{})", __PRETTY_FUNCTION__, __FILE_NAME__, __LINE__)), \ +					 __VA_ARGS__) + +// very illegal global function-style macros +// NOLINTBEGIN +#define dbg_logf(fmt, ...) _crepe_logf_here(crepe::Log::Level::DEBUG, ": " fmt, __VA_ARGS__) +#define dbg_log(str) _crepe_logf_here(crepe::Log::Level::DEBUG, ": {}", str) +#define dbg_trace() _crepe_logf_here(crepe::Log::Level::TRACE, "", "") +// NOLINTEND diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index f62414e..f9da7ce 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -19,6 +19,7 @@ endfunction()  add_example(rendering_particle)  add_example(game)  add_example(button) +add_example(replay)  add_example(loadfont)  add_example(FontExample)  add_example(AITest) diff --git a/src/example/replay.cpp b/src/example/replay.cpp new file mode 100644 index 0000000..82fd478 --- /dev/null +++ b/src/example/replay.cpp @@ -0,0 +1,84 @@ +#include <crepe/api/Config.h> +#include <crepe/api/Engine.h> +#include <crepe/api/Script.h> + +using namespace crepe; +using namespace std; + +class AnimationScript : public Script { +	Transform * transform; +	float t = 0; + +	void init() { transform = &get_component<Transform>(); } + +	void update() { +		t += 0.05; +		transform->position = {sin(t), cos(t)}; +	} +}; + +class Timeline : public Script { +	unsigned i = 0; +	recording_t recording; + +	void update() { +		switch (i++) { +			default: +				break; +			case 10: +				logf("record start"); +				replay.record_start(); +				break; +			case 60: +				logf("record end, playing recording"); +				this->recording = replay.record_end(); +				replay.play(this->recording); +				break; +			case 61: +				logf("done, releasing recording"); +				replay.release(this->recording); +				break; +			case 72: +				logf("exit"); +				queue_event<ShutDownEvent>(); +				break; +		}; +	} +}; + +class TestScene : public Scene { +public: +	using Scene::Scene; + +	void load_scene() { +		Mediator & mediator = this->mediator; +		ComponentManager & mgr = mediator.component_manager; + +		GameObject cam = mgr.new_object("cam"); +		cam.add_component<Camera>(ivec2{640, 480}, vec2{3, 3}, +								  Camera::Data{ +									  .bg_color = Color::WHITE, +								  }); + +		GameObject square = mgr.new_object("square"); +		square.add_component<Sprite>(Asset{"asset/texture/square.png"}, Sprite::Data{ +																			.size = {0.5, 0.5}, +																		}); +		square.add_component<BehaviorScript>().set_script<AnimationScript>(); + +		GameObject scapegoat = mgr.new_object(""); +		scapegoat.add_component<BehaviorScript>().set_script<Timeline>(); +	} + +	string get_name() const { return "scene1"; } +}; + +int main(int argc, char * argv[]) { +	Config & cfg = Config::get_instance(); +	cfg.log.level = Log::Level::DEBUG; + +	Engine engine; + +	engine.add_scene<TestScene>(); +	return engine.main(); +} diff --git a/src/test/AudioTest.cpp b/src/test/AudioTest.cpp index 48bba1b..415a12e 100644 --- a/src/test/AudioTest.cpp +++ b/src/test/AudioTest.cpp @@ -50,11 +50,11 @@ TEST_F(AudioTest, Default) {  	EXPECT_CALL(context, stop(_)).Times(0);  	EXPECT_CALL(context, set_volume(_, _)).Times(0);  	EXPECT_CALL(context, set_loop(_, _)).Times(0); -	system.update(); +	system.fixed_update();  }  TEST_F(AudioTest, Play) { -	system.update(); +	system.fixed_update();  	{  		InSequence seq; @@ -67,12 +67,12 @@ TEST_F(AudioTest, Play) {  		InSequence seq;  		EXPECT_CALL(context, play(_)).Times(1); -		system.update(); +		system.fixed_update();  	}  }  TEST_F(AudioTest, Stop) { -	system.update(); +	system.fixed_update();  	{  		InSequence seq; @@ -85,12 +85,12 @@ TEST_F(AudioTest, Stop) {  		InSequence seq;  		EXPECT_CALL(context, stop(_)).Times(1); -		system.update(); +		system.fixed_update();  	}  }  TEST_F(AudioTest, Volume) { -	system.update(); +	system.fixed_update();  	{  		InSequence seq; @@ -103,12 +103,12 @@ TEST_F(AudioTest, Volume) {  		InSequence seq;  		EXPECT_CALL(context, set_volume(_, component.volume)).Times(1); -		system.update(); +		system.fixed_update();  	}  }  TEST_F(AudioTest, Looping) { -	system.update(); +	system.fixed_update();  	{  		InSequence seq; @@ -121,33 +121,33 @@ TEST_F(AudioTest, Looping) {  		InSequence seq;  		EXPECT_CALL(context, set_loop(_, component.loop)).Times(1); -		system.update(); +		system.fixed_update();  	}  }  TEST_F(AudioTest, StopOnDeactivate) { -	system.update(); +	system.fixed_update();  	{  		InSequence seq;  		EXPECT_CALL(context, stop(_)).Times(1);  		component.active = false; -		system.update(); +		system.fixed_update();  	}  }  TEST_F(AudioTest, PlayOnActive) {  	component.active = false;  	component.play_on_awake = true; -	system.update(); +	system.fixed_update();  	{  		InSequence seq;  		EXPECT_CALL(context, play(_)).Times(1);  		component.active = true; -		system.update(); +		system.fixed_update();  	}  } @@ -157,5 +157,5 @@ TEST_F(AudioTest, PlayImmediately) {  	EXPECT_CALL(context, play(_)).Times(1); -	system.update(); +	system.fixed_update();  } diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 11b4ca9..ea92d96 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -15,7 +15,7 @@ target_sources(test_main PUBLIC  	ValueBrokerTest.cpp  	DBTest.cpp  	Vector2Test.cpp -	LoopManagerTest.cpp +	# LoopManagerTest.cpp  	LoopTimerTest.cpp  	InputTest.cpp  	ScriptEventTest.cpp @@ -24,4 +24,5 @@ target_sources(test_main PUBLIC  	SaveManagerTest.cpp  	ScriptSaveManagerTest.cpp  	ScriptECSTest.cpp +	ReplayManagerTest.cpp  ) diff --git a/src/test/CollisionTest.cpp b/src/test/CollisionTest.cpp index baa95c1..73a964a 100644 --- a/src/test/CollisionTest.cpp +++ b/src/test/CollisionTest.cpp @@ -108,7 +108,7 @@ public:  		ASSERT_NE(script_object2_ref, nullptr);  		// Ensure Script::init() is called on all BehaviorScript instances -		script_sys.update(); +		script_sys.fixed_update();  	}  }; @@ -123,7 +123,7 @@ TEST_F(CollisionTest, collision_example) {  		EXPECT_EQ(ev.info.this_collider.game_object_id, 2);  	};  	EXPECT_FALSE(collision_happend); -	collision_sys.update(); +	collision_sys.fixed_update();  	EXPECT_FALSE(collision_happend);  } @@ -146,7 +146,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_both_no_velocity) {  	EXPECT_FALSE(collision_happend);  	Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get();  	tf.position = {50, 30}; -	collision_sys.update(); +	collision_sys.fixed_update();  	EXPECT_TRUE(collision_happend);  } @@ -171,7 +171,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_x_direction_no_velocity) {  	EXPECT_FALSE(collision_happend);  	Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get();  	tf.position = {45, 30}; -	collision_sys.update(); +	collision_sys.fixed_update();  	EXPECT_TRUE(collision_happend);  } @@ -196,7 +196,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_y_direction_no_velocity) {  	EXPECT_FALSE(collision_happend);  	Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get();  	tf.position = {50, 25}; -	collision_sys.update(); +	collision_sys.fixed_update();  	EXPECT_TRUE(collision_happend);  } @@ -223,7 +223,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_both) {  	rg1.data.linear_velocity = {10, 10};  	Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get();  	rg2.data.linear_velocity = {10, 10}; -	collision_sys.update(); +	collision_sys.fixed_update();  	EXPECT_TRUE(collision_happend);  } @@ -252,7 +252,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_x_direction) {  	rg1.data.linear_velocity = {10, 10};  	Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get();  	rg2.data.linear_velocity = {10, 10}; -	collision_sys.update(); +	collision_sys.fixed_update();  	EXPECT_TRUE(collision_happend);  } @@ -281,7 +281,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_y_direction) {  	rg1.data.linear_velocity = {10, 10};  	Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get();  	rg2.data.linear_velocity = {10, 10}; -	collision_sys.update(); +	collision_sys.fixed_update();  	EXPECT_TRUE(collision_happend);  } @@ -303,7 +303,7 @@ TEST_F(CollisionTest, collision_box_box_static_both) {  	tf.position = {50, 30};  	Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get();  	rg2.data.body_type = crepe::Rigidbody::BodyType::STATIC; -	collision_sys.update(); +	collision_sys.fixed_update();  	EXPECT_TRUE(collision_happend);  } @@ -328,7 +328,7 @@ TEST_F(CollisionTest, collision_box_box_static_x_direction) {  	rg1.data.linear_velocity = {10, 10};  	Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get();  	rg2.data.body_type = crepe::Rigidbody::BodyType::STATIC; -	collision_sys.update(); +	collision_sys.fixed_update();  	EXPECT_TRUE(collision_happend);  } @@ -353,7 +353,7 @@ TEST_F(CollisionTest, collision_box_box_static_y_direction) {  	rg1.data.linear_velocity = {10, 10};  	Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get();  	rg2.data.body_type = crepe::Rigidbody::BodyType::STATIC; -	collision_sys.update(); +	collision_sys.fixed_update();  	EXPECT_TRUE(collision_happend);  } @@ -383,10 +383,10 @@ TEST_F(CollisionTest, collision_box_box_static_multiple) { //todo check visually  	this->game_object1.add_component<BoxCollider>(vec2{-5, 0}, vec2{10, 10});  	offset_value = 5;  	resolution = 10; -	collision_sys.update(); +	collision_sys.fixed_update();  	offset_value = -5;  	resolution = 10;  	tf.position = {55, 30}; -	collision_sys.update(); +	collision_sys.fixed_update();  	EXPECT_TRUE(collision_happend);  } diff --git a/src/test/ECSTest.cpp b/src/test/ECSTest.cpp index af2b7b0..8f86a91 100644 --- a/src/test/ECSTest.cpp +++ b/src/test/ECSTest.cpp @@ -466,3 +466,17 @@ TEST_F(ECSTest, ComponentsByTag) {  		EXPECT_EQ(objects.size(), 3);  	}  } + +TEST_F(ECSTest, Snapshot) { +	GameObject foo = mgr.new_object("foo"); + +	foo.transform.position = {1, 1}; + +	ComponentManager::Snapshot snapshot = mgr.save(); + +	foo.transform.position = {0, 0}; + +	mgr.restore(snapshot); + +	EXPECT_EQ(foo.transform.position, (vec2{1, 1})); +} diff --git a/src/test/InputTest.cpp b/src/test/InputTest.cpp index ce8ea44..f5ebaf0 100644 --- a/src/test/InputTest.cpp +++ b/src/test/InputTest.cpp @@ -43,7 +43,7 @@ protected:  		auto & camera  			= obj.add_component<Camera>(ivec2{500, 500}, vec2{500, 500},  										Camera::Data{.bg_color = Color::WHITE, .zoom = 1.0f}); -		render.update(); +		render.frame_update();  		//mediator.event_manager = event_manager;  		//mediator.component_manager = mgr;  		//event_manager.clear(); @@ -91,7 +91,7 @@ TEST_F(InputTest, MouseDown) {  	event.button.button = SDL_BUTTON_LEFT;  	SDL_PushEvent(&event); -	input_system.update(); +	input_system.fixed_update();  	event_manager.dispatch_events();  	EXPECT_TRUE(mouse_triggered);  } @@ -115,7 +115,7 @@ TEST_F(InputTest, MouseUp) {  	event.button.button = SDL_BUTTON_LEFT;  	SDL_PushEvent(&event); -	input_system.update(); +	input_system.fixed_update();  	event_manager.dispatch_events();  	EXPECT_TRUE(function_triggered);  } @@ -141,7 +141,7 @@ TEST_F(InputTest, MouseMove) {  	event.motion.yrel = 10;  	SDL_PushEvent(&event); -	input_system.update(); +	input_system.fixed_update();  	event_manager.dispatch_events();  	EXPECT_TRUE(function_triggered);  } @@ -167,7 +167,7 @@ TEST_F(InputTest, KeyDown) {  	test_event.key.repeat = 1; // Set repeat flag  	SDL_PushEvent(&test_event); -	input_system.update(); // Process the event +	input_system.fixed_update(); // Process the event  	event_manager.dispatch_events(); // Dispatch events to handlers  	EXPECT_TRUE(function_triggered); // Check if the handler was triggered @@ -188,7 +188,7 @@ TEST_F(InputTest, KeyUp) {  	event.key.keysym.scancode = SDL_SCANCODE_B;  	SDL_PushEvent(&event); -	input_system.update(); +	input_system.fixed_update();  	event_manager.dispatch_events();  	EXPECT_TRUE(function_triggered);  } @@ -205,7 +205,7 @@ TEST_F(InputTest, MouseClick) {  	event_manager.subscribe<MouseClickEvent>(on_mouse_click);  	this->simulate_mouse_click(250, 250, SDL_BUTTON_LEFT); -	input_system.update(); +	input_system.fixed_update();  	event_manager.dispatch_events();  	EXPECT_TRUE(on_click_triggered);  } @@ -223,12 +223,12 @@ TEST_F(InputTest, testButtonClick) {  	bool hover = false;  	button.active = true;  	this->simulate_mouse_click(999, 999, SDL_BUTTON_LEFT); -	input_system.update(); +	input_system.fixed_update();  	event_manager.dispatch_events();  	EXPECT_FALSE(button_clicked);  	this->simulate_mouse_click(250, 250, SDL_BUTTON_LEFT); -	input_system.update(); +	input_system.fixed_update();  	event_manager.dispatch_events();  	EXPECT_TRUE(button_clicked);  } @@ -258,7 +258,7 @@ TEST_F(InputTest, testButtonHover) {  	hover_event.motion.yrel = 10;  	SDL_PushEvent(&hover_event); -	input_system.update(); +	input_system.fixed_update();  	event_manager.dispatch_events();  	EXPECT_TRUE(button.hover);  	EXPECT_TRUE(button_hover); @@ -272,7 +272,7 @@ TEST_F(InputTest, testButtonHover) {  	event.motion.yrel = 10;  	SDL_PushEvent(&event); -	input_system.update(); +	input_system.fixed_update();  	event_manager.dispatch_events();  	EXPECT_FALSE(button.hover);  	EXPECT_FALSE(button_hover); diff --git a/src/test/LoopManagerTest.cpp b/src/test/LoopManagerTest.cpp index df132ae..f6653fa 100644 --- a/src/test/LoopManagerTest.cpp +++ b/src/test/LoopManagerTest.cpp @@ -4,7 +4,7 @@  #include <thread>  #define private public  #define protected public -#include <crepe/api/LoopManager.h> +#include <crepe/api/Engine.h>  #include <crepe/manager/EventManager.h>  #include <crepe/manager/LoopTimerManager.h>  using namespace std::chrono; @@ -12,7 +12,7 @@ using namespace crepe;  class DISABLED_LoopManagerTest : public ::testing::Test {  protected: -	class TestGameLoop : public crepe::LoopManager { +	class TestGameLoop : public crepe::Engine {  	public:  		MOCK_METHOD(void, fixed_update, (), (override));  		MOCK_METHOD(void, frame_update, (), (override)); diff --git a/src/test/ParticleTest.cpp b/src/test/ParticleTest.cpp index 9263e00..7a731a3 100644 --- a/src/test/ParticleTest.cpp +++ b/src/test/ParticleTest.cpp @@ -97,18 +97,18 @@ TEST_F(ParticlesTest, spawnParticle) {  	emitter.data.max_angle = 0.1;  	emitter.data.max_speed = 10;  	emitter.data.max_angle = 10; -	particle_system.update(); +	particle_system.fixed_update();  	//check if nothing happend  	EXPECT_EQ(emitter.particles[0].active, false);  	emitter.data.emission_rate = 50;  	//check particle spawnes -	particle_system.update(); +	particle_system.fixed_update();  	EXPECT_EQ(emitter.particles[0].active, true); -	particle_system.update(); +	particle_system.fixed_update();  	EXPECT_EQ(emitter.particles[1].active, true); -	particle_system.update(); +	particle_system.fixed_update();  	EXPECT_EQ(emitter.particles[2].active, true); -	particle_system.update(); +	particle_system.fixed_update();  	EXPECT_EQ(emitter.particles[3].active, true);  	for (auto & particle : emitter.particles) { @@ -142,7 +142,7 @@ TEST_F(ParticlesTest, moveParticleHorizontal) {  	emitter.data.max_angle = 0;  	emitter.data.emission_rate = 50;  	for (int a = 1; a < emitter.data.boundary.width / 2; a++) { -		particle_system.update(); +		particle_system.fixed_update();  		EXPECT_EQ(emitter.particles[0].position.x, a);  	}  } @@ -160,7 +160,7 @@ TEST_F(ParticlesTest, moveParticleVertical) {  	emitter.data.max_angle = 90;  	emitter.data.emission_rate = 50;  	for (int a = 1; a < emitter.data.boundary.width / 2; a++) { -		particle_system.update(); +		particle_system.fixed_update();  		EXPECT_EQ(emitter.particles[0].position.y, a);  	}  } @@ -179,7 +179,7 @@ TEST_F(ParticlesTest, boundaryParticleReset) {  	emitter.data.max_angle = 90;  	emitter.data.emission_rate = 1;  	for (int a = 0; a < emitter.data.boundary.width / 2 + 1; a++) { -		particle_system.update(); +		particle_system.fixed_update();  	}  	EXPECT_EQ(emitter.particles[0].active, false);  } @@ -198,7 +198,7 @@ TEST_F(ParticlesTest, boundaryParticleStop) {  	emitter.data.max_angle = 90;  	emitter.data.emission_rate = 1;  	for (int a = 0; a < emitter.data.boundary.width / 2 + 1; a++) { -		particle_system.update(); +		particle_system.fixed_update();  	}  	const double TOLERANCE = 0.01;  	EXPECT_NEAR(emitter.particles[0].velocity.x, 0, TOLERANCE); diff --git a/src/test/PhysicsTest.cpp b/src/test/PhysicsTest.cpp index 3afb3c7..79ed0b8 100644 --- a/src/test/PhysicsTest.cpp +++ b/src/test/PhysicsTest.cpp @@ -57,10 +57,10 @@ TEST_F(PhysicsTest, gravity) {  	ASSERT_FALSE(transforms.empty());  	EXPECT_EQ(transform.position.y, 0); -	system.update(); +	system.fixed_update();  	EXPECT_NEAR(transform.position.y, 0.0004, 0.0001); -	system.update(); +	system.fixed_update();  	EXPECT_NEAR(transform.position.y, 0.002, 0.001);  } @@ -74,14 +74,14 @@ TEST_F(PhysicsTest, max_velocity) {  	rigidbody.add_force_linear({100, 100});  	rigidbody.add_force_angular(100); -	system.update(); +	system.fixed_update();  	EXPECT_NEAR(rigidbody.data.linear_velocity.y, 7.07, 0.01);  	EXPECT_NEAR(rigidbody.data.linear_velocity.x, 7.07, 0.01);  	EXPECT_EQ(rigidbody.data.angular_velocity, 10);  	rigidbody.add_force_linear({-100, -100});  	rigidbody.add_force_angular(-100); -	system.update(); +	system.fixed_update();  	EXPECT_NEAR(rigidbody.data.linear_velocity.y, -7.07, 0.01);  	EXPECT_NEAR(rigidbody.data.linear_velocity.x, -7.07, 0.01);  	EXPECT_EQ(rigidbody.data.angular_velocity, -10); @@ -99,7 +99,7 @@ TEST_F(PhysicsTest, movement) {  	rigidbody.add_force_linear({1, 1});  	rigidbody.add_force_angular(1); -	system.update(); +	system.fixed_update();  	EXPECT_NEAR(transform.position.x, 0.02, 0.001);  	EXPECT_NEAR(transform.position.y, 0.02, 0.001);  	EXPECT_NEAR(transform.rotation, 0.02, 0.001); @@ -112,7 +112,7 @@ TEST_F(PhysicsTest, movement) {  	rigidbody.data.linear_velocity_coefficient.x = 0.5;  	rigidbody.data.linear_velocity_coefficient.y = 0.5;  	rigidbody.data.angular_velocity_coefficient = 0.5; -	system.update(); +	system.fixed_update();  	EXPECT_NEAR(rigidbody.data.linear_velocity.x, 0.98, 0.01);  	EXPECT_NEAR(rigidbody.data.linear_velocity.y, 0.98, 0.01);  	EXPECT_NEAR(rigidbody.data.angular_velocity, 0.98, 0.01); @@ -121,12 +121,12 @@ TEST_F(PhysicsTest, movement) {  	rigidbody.data.angular_velocity_coefficient = 0;  	rigidbody.data.max_angular_velocity = 1000;  	rigidbody.data.angular_velocity = 360; -	system.update(); +	system.fixed_update();  	EXPECT_NEAR(transform.rotation, 7.24, 0.01);  	rigidbody.data.angular_velocity = -360; -	system.update(); +	system.fixed_update();  	EXPECT_NEAR(transform.rotation, 0.04, 0.001); -	system.update(); +	system.fixed_update();  	EXPECT_NEAR(transform.rotation, 352.84, 0.01);  } diff --git a/src/test/Profiling.cpp b/src/test/Profiling.cpp index 16736b8..f988700 100644 --- a/src/test/Profiling.cpp +++ b/src/test/Profiling.cpp @@ -39,7 +39,7 @@ class TestScript : public Script {  		subscribe<CollisionEvent>(  			[this](const CollisionEvent & ev) -> bool { return this->oncollision(ev); });  	} -	void update() { +	void fixed_update() {  		// Retrieve component from the same GameObject this script is on  	}  }; @@ -82,9 +82,9 @@ public:  										 });  		// initialize systems here:  		//calls init -		script_sys.update(); +		script_sys.fixed_update();  		//creates window -		render_sys.update(); +		render_sys.frame_update();  	}  	// Helper function to time an update call and store its duration @@ -102,12 +102,14 @@ public:  	// Run and profile all systems, return the total time in milliseconds  	std::chrono::microseconds run_all_systems() {  		std::chrono::microseconds total_microseconds = 0us; -		total_microseconds += time_function("PhysicsSystem", [&]() { physics_sys.update(); });  		total_microseconds -			+= time_function("CollisionSystem", [&]() { collision_sys.update(); }); +			+= time_function("PhysicsSystem", [&]() { physics_sys.fixed_update(); });  		total_microseconds -			+= time_function("ParticleSystem", [&]() { particle_sys.update(); }); -		total_microseconds += time_function("RenderSystem", [&]() { render_sys.update(); }); +			+= time_function("CollisionSystem", [&]() { collision_sys.fixed_update(); }); +		total_microseconds +			+= time_function("ParticleSystem", [&]() { particle_sys.fixed_update(); }); +		total_microseconds +			+= time_function("RenderSystem", [&]() { render_sys.frame_update(); });  		return total_microseconds;  	} @@ -233,7 +235,7 @@ TEST_F(DISABLED_ProfilingTest, Profiling_3) {  							 });  		} -		render_sys.update(); +		render_sys.frame_update();  		this->game_object_count++;  		this->total_time = 0us; diff --git a/src/test/RenderSystemTest.cpp b/src/test/RenderSystemTest.cpp index b4519cb..689a6d4 100644 --- a/src/test/RenderSystemTest.cpp +++ b/src/test/RenderSystemTest.cpp @@ -85,7 +85,7 @@ public:  TEST_F(RenderSystemTest, NoCamera) {  	// No camera -	EXPECT_ANY_THROW({ this->sys.update(); }); +	EXPECT_ANY_THROW({ this->sys.frame_update(); });  }  TEST_F(RenderSystemTest, make_sprites) {} @@ -139,7 +139,7 @@ TEST_F(RenderSystemTest, Update) {  		EXPECT_EQ(sprites[2].get().game_object_id, 2);  		EXPECT_EQ(sprites[3].get().game_object_id, 3);  	} -	this->sys.update(); +	this->sys.frame_update();  	{  		vector<reference_wrapper<Sprite>> sprites = this->mgr.get_components_by_type<Sprite>();  		ASSERT_EQ(sprites.size(), 4); @@ -178,7 +178,7 @@ TEST_F(RenderSystemTest, Color) {  	EXPECT_EQ(sprite.data.color.g, Color::GREEN.g);  	EXPECT_EQ(sprite.data.color.b, Color::GREEN.b);  	EXPECT_EQ(sprite.data.color.a, Color::GREEN.a); -	this->sys.update(); +	this->sys.frame_update();  	EXPECT_EQ(sprite.data.color.r, Color::GREEN.r);  	EXPECT_EQ(sprite.data.color.g, Color::GREEN.g);  	EXPECT_EQ(sprite.data.color.b, Color::GREEN.b); diff --git a/src/test/ReplayManagerTest.cpp b/src/test/ReplayManagerTest.cpp new file mode 100644 index 0000000..5ee4b40 --- /dev/null +++ b/src/test/ReplayManagerTest.cpp @@ -0,0 +1,38 @@ +#include <gtest/gtest.h> + +#include <crepe/api/BehaviorScript.h> +#include <crepe/api/Scene.h> +#include <crepe/api/Script.h> +#include <crepe/manager/ReplayManager.h> +#include <crepe/system/ReplaySystem.h> + +using namespace std; +using namespace crepe; +using namespace testing; + +class ReplayManagerTest : public Test { +	Mediator mediator; + +public: +	ComponentManager component_manager{mediator}; +	ReplayManager replay_manager{mediator}; +	ReplaySystem replay_system{mediator}; + +	GameObject entity = component_manager.new_object("foo"); +	Transform & entity_transform +		= component_manager.get_components_by_id<Transform>(entity.id).back(); +	Metadata & entity_metadata +		= component_manager.get_components_by_id<Metadata>(entity.id).back(); +}; + +TEST_F(ReplayManagerTest, Default) { +	// replay_manager.record_start(); + +	// replay_system.fixed_update(); +	// entity_transform.position += {1, 1}; +	// replay_system.fixed_update(); +	// entity_transform.position += {1, 1}; +	// replay_system.fixed_update(); + +	// recording_t recording = replay_manager.record_end(); +} diff --git a/src/test/ScriptEventTest.cpp b/src/test/ScriptEventTest.cpp index c1b4028..479e3f5 100644 --- a/src/test/ScriptEventTest.cpp +++ b/src/test/ScriptEventTest.cpp @@ -37,7 +37,7 @@ TEST_F(ScriptEventTest, Default) {  		return true;  	}); -	system.update(); +	system.fixed_update();  	behaviorscript.active = false;  	EXPECT_EQ(0, event_count); diff --git a/src/test/ScriptTest.cpp b/src/test/ScriptTest.cpp index 846e398..66b8193 100644 --- a/src/test/ScriptTest.cpp +++ b/src/test/ScriptTest.cpp @@ -39,7 +39,7 @@ TEST_F(ScriptTest, UpdateOnce) {  		EXPECT_CALL(script, init()).Times(1);  		EXPECT_CALL(script, update(_)).Times(1); -		system.update(); +		system.fixed_update();  	}  	{ @@ -47,7 +47,7 @@ TEST_F(ScriptTest, UpdateOnce) {  		EXPECT_CALL(script, init()).Times(0);  		EXPECT_CALL(script, update(_)).Times(1); -		system.update(); +		system.fixed_update();  	}  } @@ -61,7 +61,7 @@ TEST_F(ScriptTest, UpdateInactive) {  		EXPECT_CALL(script, init()).Times(0);  		EXPECT_CALL(script, update(_)).Times(0);  		behaviorscript.active = false; -		system.update(); +		system.fixed_update();  	}  	{ @@ -70,7 +70,7 @@ TEST_F(ScriptTest, UpdateInactive) {  		EXPECT_CALL(script, init()).Times(1);  		EXPECT_CALL(script, update(_)).Times(1);  		behaviorscript.active = true; -		system.update(); +		system.fixed_update();  	}  }  |