diff options
Diffstat (limited to 'src/crepe/system')
24 files changed, 379 insertions, 216 deletions
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 a710ae2..38d12c5 100644 --- a/src/crepe/system/InputSystem.cpp +++ b/src/crepe/system/InputSystem.cpp @@ -8,14 +8,16 @@ using namespace crepe; -void InputSystem::update() { +void InputSystem::fixed_update() { ComponentManager & mgr = this->mediator.component_manager; - EventManager & event_mgr = this->mediator.event_manager; + SDLContext & context = this->mediator.sdl_context; + context.update_keyboard_state(); std::vector<SDLContext::EventData> event_list = context.get_events(); RefVector<Button> buttons = mgr.get_components_by_type<Button>(); RefVector<Camera> cameras = mgr.get_components_by_type<Camera>(); OptionalRef<Camera> curr_cam_ref; + // Find the active camera for (Camera & cam : cameras) { if (!cam.active) continue; @@ -23,150 +25,190 @@ void InputSystem::update() { break; } if (!curr_cam_ref) return; + Camera & current_cam = curr_cam_ref; RefVector<Transform> transform_vec = mgr.get_components_by_id<Transform>(current_cam.game_object_id); Transform & cam_transform = transform_vec.front().get(); - int camera_origin_x = cam_transform.position.x + current_cam.data.postion_offset.x - - (current_cam.viewport_size.x / 2); - int camera_origin_y = cam_transform.position.y + current_cam.data.postion_offset.y - - (current_cam.viewport_size.y / 2); + + vec2 camera_origin = cam_transform.position + current_cam.data.postion_offset + - (current_cam.viewport_size / 2); for (const SDLContext::EventData & event : event_list) { - int world_mouse_x = event.mouse_position.x + camera_origin_x; - int world_mouse_y = event.mouse_position.y + camera_origin_y; - // check if the mouse is within the viewport - bool mouse_in_viewport - = !(world_mouse_x < camera_origin_x - || world_mouse_x > camera_origin_x + current_cam.viewport_size.x - || world_mouse_y < camera_origin_y - || world_mouse_y > camera_origin_y + current_cam.viewport_size.y); - - switch (event.event_type) { - case SDLContext::EventType::KEYDOWN: - event_mgr.queue_event<KeyPressEvent>(KeyPressEvent{ - .repeat = event.key_repeat, - .key = event.key, - }); - break; - case SDLContext::EventType::KEYUP: - event_mgr.queue_event<KeyReleaseEvent>(KeyReleaseEvent{ - .key = event.key, - }); - break; - case SDLContext::EventType::MOUSEDOWN: - if (!mouse_in_viewport) { - break; - } - event_mgr.queue_event<MousePressEvent>(MousePressEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .button = event.mouse_button, - }); - this->last_mouse_down_position = {world_mouse_x, world_mouse_y}; - this->last_mouse_button = event.mouse_button; - break; - case SDLContext::EventType::MOUSEUP: { - if (!mouse_in_viewport) { - break; - } - event_mgr.queue_event<MouseReleaseEvent>(MouseReleaseEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .button = event.mouse_button, - }); - //check if its a click by checking the last button down - int delta_x = world_mouse_x - this->last_mouse_down_position.x; - int delta_y = world_mouse_y - this->last_mouse_down_position.y; - - if (this->last_mouse_button == event.mouse_button - && std::abs(delta_x) <= click_tolerance - && std::abs(delta_y) <= click_tolerance) { - event_mgr.queue_event<MouseClickEvent>(MouseClickEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .button = event.mouse_button, - }); - - this->handle_click(event.mouse_button, world_mouse_x, world_mouse_y); - } - } break; - case SDLContext::EventType::MOUSEMOVE: - if (!mouse_in_viewport) { - break; - } - event_mgr.queue_event<MouseMoveEvent>(MouseMoveEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .delta_x = event.rel_mouse_move.x, - .delta_y = event.rel_mouse_move.y, - }); - this->handle_move(event, world_mouse_x, world_mouse_y); - break; - case SDLContext::EventType::MOUSEWHEEL: - event_mgr.queue_event<MouseScrollEvent>(MouseScrollEvent{ - .mouse_x = world_mouse_x, - .mouse_y = world_mouse_y, - .scroll_direction = event.scroll_direction, - .scroll_delta = event.scroll_delta, + // Only calculate mouse coordinates for relevant events + if (this->is_mouse_event(event.event_type)) { + this->handle_mouse_event(event, camera_origin, current_cam); + + } else { + this->handle_non_mouse_event(event); + } + } +} + +void InputSystem::handle_mouse_event(const SDLContext::EventData & event, + const vec2 & camera_origin, const Camera & current_cam) { + EventManager & event_mgr = this->mediator.event_manager; + vec2 adjusted_mouse; + adjusted_mouse.x = event.data.mouse_data.mouse_position.x + camera_origin.x; + adjusted_mouse.x = event.data.mouse_data.mouse_position.y + camera_origin.y; + // Check if the mouse is within the viewport + if ((adjusted_mouse.x < camera_origin.x + || adjusted_mouse.x > camera_origin.x + current_cam.viewport_size.x + || adjusted_mouse.y < camera_origin.y + || adjusted_mouse.y > camera_origin.y + current_cam.viewport_size.y)) + return; + + // Handle mouse-specific events + switch (event.event_type) { + case SDLContext::EventType::MOUSEDOWN: + event_mgr.queue_event<MousePressEvent>({ + .mouse_pos = adjusted_mouse, + .button = event.data.mouse_data.mouse_button, + }); + this->last_mouse_down_position = adjusted_mouse; + this->last_mouse_button = event.data.mouse_data.mouse_button; + break; + + case SDLContext::EventType::MOUSEUP: { + event_mgr.queue_event<MouseReleaseEvent>({ + .mouse_pos = adjusted_mouse, + .button = event.data.mouse_data.mouse_button, + }); + vec2 delta_move = adjusted_mouse - this->last_mouse_down_position; + int click_tolerance = Config::get_instance().input.click_tolerance; + if (this->last_mouse_button == event.data.mouse_data.mouse_button + && std::abs(delta_move.x) <= click_tolerance + && std::abs(delta_move.y) <= click_tolerance) { + event_mgr.queue_event<MouseClickEvent>({ + .mouse_pos = adjusted_mouse, + .button = event.data.mouse_data.mouse_button, }); - break; - case SDLContext::EventType::SHUTDOWN: - event_mgr.queue_event<ShutDownEvent>(ShutDownEvent{}); - break; - default: - break; + this->handle_click(event.data.mouse_data.mouse_button, adjusted_mouse); + } + break; } + + case SDLContext::EventType::MOUSEMOVE: + event_mgr.queue_event<MouseMoveEvent>({ + .mouse_pos = adjusted_mouse, + .mouse_delta = event.data.mouse_data.rel_mouse_move, + }); + this->handle_move(event, adjusted_mouse); + break; + + case SDLContext::EventType::MOUSEWHEEL: + event_mgr.queue_event<MouseScrollEvent>({ + .mouse_pos = adjusted_mouse, + .scroll_direction = event.data.mouse_data.scroll_direction, + .scroll_delta = event.data.mouse_data.scroll_delta, + }); + break; + + default: + break; + } +} + +void InputSystem::handle_non_mouse_event(const SDLContext::EventData & event) { + EventManager & event_mgr = this->mediator.event_manager; + switch (event.event_type) { + case SDLContext::EventType::KEYDOWN: + + event_mgr.queue_event<KeyPressEvent>( + {.repeat = event.data.key_data.key_repeat, .key = event.data.key_data.key}); + break; + case SDLContext::EventType::KEYUP: + event_mgr.queue_event<KeyReleaseEvent>({.key = event.data.key_data.key}); + break; + case SDLContext::EventType::SHUTDOWN: + event_mgr.queue_event<ShutDownEvent>({}); + break; + case SDLContext::EventType::WINDOW_EXPOSE: + event_mgr.queue_event<WindowExposeEvent>({}); + break; + case SDLContext::EventType::WINDOW_RESIZE: + event_mgr.queue_event<WindowResizeEvent>( + WindowResizeEvent{.dimensions = event.data.window_data.resize_dimension}); + break; + case SDLContext::EventType::WINDOW_MOVE: + event_mgr.queue_event<WindowMoveEvent>( + {.delta_move = event.data.window_data.move_delta}); + break; + case SDLContext::EventType::WINDOW_MINIMIZE: + event_mgr.queue_event<WindowMinimizeEvent>({}); + break; + case SDLContext::EventType::WINDOW_MAXIMIZE: + event_mgr.queue_event<WindowMaximizeEvent>({}); + break; + case SDLContext::EventType::WINDOW_FOCUS_GAIN: + event_mgr.queue_event<WindowFocusGainEvent>({}); + break; + case SDLContext::EventType::WINDOW_FOCUS_LOST: + event_mgr.queue_event<WindowFocusLostEvent>({}); + break; + default: + break; } } + +bool InputSystem::is_mouse_event(SDLContext::EventType event_type) { + return (event_type == SDLContext::EventType::MOUSEDOWN + || event_type == SDLContext::EventType::MOUSEUP + || event_type == SDLContext::EventType::MOUSEMOVE + || event_type == SDLContext::EventType::MOUSEWHEEL); +} + void InputSystem::handle_move(const SDLContext::EventData & event_data, - const int world_mouse_x, const int world_mouse_y) { + const vec2 & mouse_pos) { ComponentManager & mgr = this->mediator.component_manager; RefVector<Button> buttons = mgr.get_components_by_type<Button>(); for (Button & button : buttons) { + if (!button.active) continue; RefVector<Transform> transform_vec = mgr.get_components_by_id<Transform>(button.game_object_id); Transform & transform(transform_vec.front().get()); bool was_hovering = button.hover; - if (button.active - && this->is_mouse_inside_button(world_mouse_x, world_mouse_y, button, transform)) { + if (this->is_mouse_inside_button(mouse_pos, button, transform)) { button.hover = true; - if (!was_hovering && button.on_mouse_enter) { + if (!button.on_mouse_enter) continue; + if (!was_hovering) { button.on_mouse_enter(); } } else { button.hover = false; // Trigger the on_exit callback if the hover state just changed to false - if (was_hovering && button.on_mouse_exit) { + if (!button.on_mouse_exit) continue; + if (was_hovering) { button.on_mouse_exit(); } } } } -void InputSystem::handle_click(const MouseButton & mouse_button, const int world_mouse_x, - const int world_mouse_y) { +void InputSystem::handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos) { ComponentManager & mgr = this->mediator.component_manager; RefVector<Button> buttons = mgr.get_components_by_type<Button>(); for (Button & button : buttons) { + if (!button.active) continue; + if (!button.on_click) continue; RefVector<Transform> transform_vec = mgr.get_components_by_id<Transform>(button.game_object_id); Transform & transform = transform_vec.front().get(); - if (button.active - && this->is_mouse_inside_button(world_mouse_x, world_mouse_y, button, transform)) { - this->handle_button_press(button); + if (this->is_mouse_inside_button(mouse_pos, button, transform)) { + + button.on_click(); } } } -bool InputSystem::is_mouse_inside_button(const int mouse_x, const int mouse_y, - const Button & button, const Transform & transform) { +bool InputSystem::is_mouse_inside_button(const vec2 & mouse_pos, const Button & button, + const Transform & transform) { int actual_x = transform.position.x + button.offset.x; int actual_y = transform.position.y + button.offset.y; @@ -174,17 +216,6 @@ bool InputSystem::is_mouse_inside_button(const int mouse_x, const int mouse_y, int half_height = button.dimensions.y / 2; // Check if the mouse is within the button's boundaries - return mouse_x >= actual_x - half_width && mouse_x <= actual_x + half_width - && mouse_y >= actual_y - half_height && mouse_y <= actual_y + half_height; -} - -void InputSystem::handle_button_press(Button & button) { - if (button.is_toggle) { - if (!button.is_pressed && button.on_click) { - button.on_click(); - } - button.is_pressed = !button.is_pressed; - } else if (button.on_click) { - button.on_click(); - } + return mouse_pos.x >= actual_x - half_width && mouse_pos.x <= actual_x + half_width + && mouse_pos.y >= actual_y - half_height && mouse_pos.y <= actual_y + half_height; } diff --git a/src/crepe/system/InputSystem.h b/src/crepe/system/InputSystem.h index 87e86f8..b03805a 100644 --- a/src/crepe/system/InputSystem.h +++ b/src/crepe/system/InputSystem.h @@ -1,5 +1,6 @@ #pragma once +#include "../api/Config.h" #include "../facade/SDLContext.h" #include "../types.h" #include "../util/OptionalRef.h" @@ -27,19 +28,40 @@ 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. - ivec2 last_mouse_down_position; + vec2 last_mouse_down_position; // TODO: specify world/hud space and make regular `vec2` //! Stores the last mouse button pressed. MouseButton last_mouse_button = MouseButton::NONE; - - //! The maximum allowable distance between mouse down and mouse up to register as a click. - const int click_tolerance = 5; - + /** + * \brief Determines whether the given event type is a mouse event. + * \param event_type The event type to check. + * \return True if the event type corresponds to a mouse event, false otherwise. + */ + bool is_mouse_event(SDLContext::EventType event_type); + /** + * \brief Handles mouse-related events. + * \param event The event data for the mouse event. + * \param camera_origin The origin position of the camera in world space. + * \param current_cam The currently active camera. + * + * This method processes mouse events, adjusts the mouse position to world coordinates, + * and triggers the appropriate mouse-specific event handling logic. + */ + void handle_mouse_event(const SDLContext::EventData & event, const vec2 & camera_origin, + const Camera & current_cam); + /** + * \brief Handles non-mouse-related events. + * \param event The event data for the non-mouse event. + * + * This method processes events that do not involve the mouse, such as keyboard events, + * window events, and shutdown events, and triggers the corresponding event actions. + */ + void handle_non_mouse_event(const SDLContext::EventData & event); /** * \brief Handles the mouse click event. * \param mouse_button The mouse button involved in the click. @@ -48,8 +70,7 @@ private: * * This method processes the mouse click event and triggers the corresponding button action. */ - void handle_click(const MouseButton & mouse_button, const int world_mouse_x, - const int world_mouse_y); + void handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos); /** * \brief Handles the mouse movement event. @@ -59,8 +80,7 @@ private: * * This method processes the mouse movement event and updates the button hover state. */ - void handle_move(const SDLContext::EventData & event_data, const int world_mouse_x, - const int world_mouse_y); + void handle_move(const SDLContext::EventData & event_data, const vec2 & mouse_pos); /** * \brief Checks if the mouse position is inside the bounds of the button. @@ -70,8 +90,8 @@ private: * \param transform The transform component of the button. * \return True if the mouse is inside the button, false otherwise. */ - bool is_mouse_inside_button(const int world_mouse_x, const int world_mouse_y, - const Button & button, const Transform & transform); + bool is_mouse_inside_button(const vec2 & mouse_pos, const Button & button, + const Transform & transform); /** * \brief Handles the button press event, calling the on_click callback if necessary. diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp index b14c52f..4684ada 100644 --- a/src/crepe/system/ParticleSystem.cpp +++ b/src/crepe/system/ParticleSystem.cpp @@ -1,3 +1,4 @@ +#include <chrono> #include <cmath> #include <cstdlib> #include <ctime> @@ -5,14 +6,19 @@ #include "../api/ParticleEmitter.h" #include "../api/Transform.h" #include "../manager/ComponentManager.h" +#include "../manager/LoopTimerManager.h" #include "ParticleSystem.h" using namespace crepe; -void ParticleSystem::update() { +void ParticleSystem::frame_update() { // Get all emitters - ComponentManager & mgr = this->mediator.component_manager; + const Mediator & mediator = this->mediator; + LoopTimerManager & loop_timer = mediator.loop_timer; + ComponentManager & mgr = mediator.component_manager; + float dt = loop_timer.get_scaled_fixed_delta_time().count(); + RefVector<ParticleEmitter> emitters = mgr.get_components_by_type<ParticleEmitter>(); for (ParticleEmitter & emitter : emitters) { @@ -21,38 +27,39 @@ void ParticleSystem::update() { = mgr.get_components_by_id<Transform>(emitter.game_object_id).front().get(); // Emit particles based on emission_rate - int updates = calculate_update(this->update_count, emitter.data.emission_rate); - for (size_t i = 0; i < updates; i++) { - emit_particle(emitter, transform); + emitter.spawn_accumulator = emitter.data.emission_rate * dt; + while (emitter.spawn_accumulator >= 1.0) { + this->emit_particle(emitter, transform); + emitter.spawn_accumulator -= 1.0; } // Update all particles - for (Particle & particle : emitter.data.particles) { + for (Particle & particle : emitter.particles) { if (particle.active) { - particle.update(); + particle.update(dt); } } // Check if within boundary - check_bounds(emitter, transform); + this->check_bounds(emitter, transform); } - - this->update_count = (this->update_count + 1) % this->MAX_UPDATE_COUNT; } void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & transform) { constexpr float DEG_TO_RAD = M_PI / 180.0; vec2 initial_position = emitter.data.position + transform.position; - float random_angle = generate_random_angle(emitter.data.min_angle, emitter.data.max_angle); + float random_angle + = this->generate_random_angle(emitter.data.min_angle, emitter.data.max_angle); - float random_speed = generate_random_speed(emitter.data.min_speed, emitter.data.max_speed); + float random_speed + = this->generate_random_speed(emitter.data.min_speed, emitter.data.max_speed); float angle_radians = random_angle * DEG_TO_RAD; vec2 velocity = {random_speed * std::cos(angle_radians), random_speed * std::sin(angle_radians)}; - for (Particle & particle : emitter.data.particles) { + for (Particle & particle : emitter.particles) { if (!particle.active) { particle.reset(emitter.data.end_lifespan, initial_position, velocity, random_angle); @@ -61,66 +68,54 @@ void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & } } -int ParticleSystem::calculate_update(int count, double emission) const { - double integer_part = std::floor(emission); - double fractional_part = emission - integer_part; - - if (fractional_part > 0) { - int denominator = static_cast<int>(1.0 / fractional_part); - return (count % denominator == 0) ? 1 : 0; - } - - return static_cast<int>(emission); -} - void ParticleSystem::check_bounds(ParticleEmitter & emitter, const Transform & transform) { vec2 offset = emitter.data.boundary.offset + transform.position + emitter.data.position; - double half_width = emitter.data.boundary.width / 2.0; - double half_height = emitter.data.boundary.height / 2.0; + float half_width = emitter.data.boundary.width / 2.0; + float half_height = emitter.data.boundary.height / 2.0; - const double LEFT = offset.x - half_width; - const double RIGHT = offset.x + half_width; - const double TOP = offset.y - half_height; - const double BOTTOM = offset.y + half_height; + float left = offset.x - half_width; + float right = offset.x + half_width; + float top = offset.y - half_height; + float bottom = offset.y + half_height; - for (Particle & particle : emitter.data.particles) { + for (Particle & particle : emitter.particles) { const vec2 & position = particle.position; - bool within_bounds = (position.x >= LEFT && position.x <= RIGHT && position.y >= TOP - && position.y <= BOTTOM); - + bool within_bounds = (position.x >= left && position.x <= right && position.y >= top + && position.y <= bottom); + //if not within bounds do a reset or stop velocity if (!within_bounds) { if (emitter.data.boundary.reset_on_exit) { particle.active = false; } else { particle.velocity = {0, 0}; - if (position.x < LEFT) particle.position.x = LEFT; - else if (position.x > RIGHT) particle.position.x = RIGHT; - if (position.y < TOP) particle.position.y = TOP; - else if (position.y > BOTTOM) particle.position.y = BOTTOM; + if (position.x < left) particle.position.x = left; + else if (position.x > right) particle.position.x = right; + if (position.y < top) particle.position.y = top; + else if (position.y > bottom) particle.position.y = bottom; } } } } -double ParticleSystem::generate_random_angle(double min_angle, double max_angle) const { +float ParticleSystem::generate_random_angle(float min_angle, float max_angle) const { if (min_angle == max_angle) { return min_angle; } else if (min_angle < max_angle) { return min_angle - + static_cast<double>(std::rand() % static_cast<int>(max_angle - min_angle)); + + static_cast<float>(std::rand() % static_cast<int>(max_angle - min_angle)); } else { - double angle_offset = (360 - min_angle) + max_angle; - double random_angle - = min_angle + static_cast<double>(std::rand() % static_cast<int>(angle_offset)); + float angle_offset = (360 - min_angle) + max_angle; + float random_angle + = min_angle + static_cast<float>(std::rand() % static_cast<int>(angle_offset)); return (random_angle >= 360) ? random_angle - 360 : random_angle; } } -double ParticleSystem::generate_random_speed(double min_speed, double max_speed) const { +float ParticleSystem::generate_random_speed(float min_speed, float max_speed) const { if (min_speed == max_speed) { return min_speed; } else { return min_speed - + static_cast<double>(std::rand() % static_cast<int>(max_speed - min_speed)); + + static_cast<float>(std::rand() % static_cast<int>(max_speed - min_speed)); } } diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h index 068f01c..9eacc70 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 frame_update() override; private: /** @@ -32,16 +32,6 @@ private: void emit_particle(ParticleEmitter & emitter, const Transform & transform); /** - * \brief Calculates the number of times particles should be emitted based on emission rate - * and update count. - * - * \param count Current update count. - * \param emission Emission rate. - * \return The number of particles to emit. - */ - int calculate_update(int count, double emission) const; - - /** * \brief Checks whether particles are within the emitter’s boundary, resets or stops * particles if they exit. * @@ -57,7 +47,7 @@ private: * \param max_angle Maximum emission angle in degrees. * \return Random angle in degrees. */ - double generate_random_angle(double min_angle, double max_angle) const; + float generate_random_angle(float min_angle, float max_angle) const; /** * \brief Generates a random speed for particle emission within the specified range. @@ -66,15 +56,7 @@ private: * \param max_speed Maximum emission speed. * \return Random speed. */ - double generate_random_speed(double min_speed, double max_speed) const; - -private: - //! Counter to count updates to determine how many times emit_particle is - // called. - unsigned int update_count = 0; - //! Determines the lowest amount of emission rate (1000 = 0.001 = 1 particle per 1000 - // updates). - static constexpr unsigned int MAX_UPDATE_COUNT = 100; + float generate_random_speed(float min_speed, float max_speed) const; }; } // namespace crepe 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 afd9548..c403f6c 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -64,7 +64,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(); @@ -83,11 +83,11 @@ bool RenderSystem::render_particle(const Sprite & sprite, const double & scale) bool rendering_particles = false; for (const ParticleEmitter & em : emitters) { - if (&em.data.sprite != &sprite) continue; + if (&em.sprite != &sprite) continue; rendering_particles = true; if (!em.active) continue; - for (const Particle & p : em.data.particles) { + for (const Particle & p : em.particles) { if (!p.active) continue; ctx.draw(SDLContext::RenderContext{ diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h index fc7b46e..1a61f99 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 d6b2ca1..58055d6 100644 --- a/src/crepe/system/ScriptSystem.cpp +++ b/src/crepe/system/ScriptSystem.cpp @@ -1,16 +1,18 @@ #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; + LoopTimerManager & timer = this->mediator.loop_timer; RefVector<BehaviorScript> behavior_scripts = mgr.get_components_by_type<BehaviorScript>(); for (BehaviorScript & behavior_script : behavior_scripts) { @@ -23,6 +25,8 @@ void ScriptSystem::update() { script->init(); script->initialized = true; } - script->update(); + + duration_t delta_time = timer.get_delta_time(); + script->update(delta_time); } } 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); |