diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | contributing.md | 40 | ||||
-rw-r--r-- | src/crepe/api/Rigidbody.h | 44 | ||||
-rw-r--r-- | src/crepe/api/Sprite.h | 10 | ||||
-rw-r--r-- | src/crepe/api/Text.cpp | 2 | ||||
-rw-r--r-- | src/crepe/api/Text.h | 2 | ||||
-rw-r--r-- | src/crepe/api/Vector2.h | 3 | ||||
-rw-r--r-- | src/crepe/api/Vector2.hpp | 5 | ||||
-rw-r--r-- | src/crepe/facade/SDLContext.cpp | 75 | ||||
-rw-r--r-- | src/crepe/facade/SDLContext.h | 23 | ||||
-rw-r--r-- | src/crepe/facade/Texture.cpp | 1 | ||||
-rw-r--r-- | src/crepe/system/CollisionSystem.cpp | 868 | ||||
-rw-r--r-- | src/crepe/system/CollisionSystem.h | 290 | ||||
-rw-r--r-- | src/crepe/system/ParticleSystem.cpp | 6 | ||||
-rw-r--r-- | src/crepe/system/RenderSystem.cpp | 66 | ||||
-rw-r--r-- | src/crepe/system/RenderSystem.h | 30 | ||||
-rw-r--r-- | src/crepe/util/AbsolutePosition.cpp | 20 | ||||
-rw-r--r-- | src/crepe/util/AbsolutePosition.h | 29 | ||||
-rw-r--r-- | src/crepe/util/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/example/game.cpp | 469 | ||||
-rw-r--r-- | src/example/rendering_particle.cpp | 45 | ||||
-rw-r--r-- | src/test/CollisionTest.cpp | 93 | ||||
-rw-r--r-- | src/test/Profiling.cpp | 6 |
23 files changed, 1142 insertions, 988 deletions
@@ -16,3 +16,4 @@ CTestTestfile.cmake _deps CMakeUserPresets.json compile.sh +asset/jetpack_joyride diff --git a/contributing.md b/contributing.md index 5555892..d217410 100644 --- a/contributing.md +++ b/contributing.md @@ -19,6 +19,20 @@ that you can click on to open them. an explanation as to why the code can not (reliably) be tested - Non-bugfix pull requests must be approved by at least 2 reviewers before being merged +- Pull requests should have the following labels (where appropriate) + |label|meaning| + |:-:|-| + |`fix me`|has feedback that should be resolved/discussed by its author| + |`review me`|needs additional reviewers (minimum of 2 per PR)| + |`do not review`|is actively being worked on or not ready for feedback| + |`high priority`|should be worked on before all the others| + - PRs start with the `review me` label + - Reviewers— + - Add the `fix me` label after adding comments + - Authors— + - Remove the `review me` label if the pull request has enough reviewers + - Add the `do not review` label while processing feedback / pushing + additional commits <!-- - TODO: tagging / versions @@ -163,40 +177,28 @@ that you can click on to open them. ``` </td></tr></table></details> - <details><summary> - <code>using namespace</code> may not be used in header files (.h, .hpp), only - in source files (.cpp). + <a href="https://en.cppreference.com/w/cpp/language/using_declaration">Using-declarations</a> + may not be used in header files (<code>.h</code>, <code>.hpp</code>), only in + source files (<code>.cpp</code>). </summary><table><tr><th>Good</th><th>Bad</th></tr><tr><td> example.h: ```cpp namespace crepe { - void foo(); + std::string foo(); } ``` - example.cpp: - ```cpp - #include "example.h" - using namespace crepe; - void foo() {} - ``` </td><td> example.h: ```cpp + using namespace std; + namespace crepe { - template <typename T> - T foo(); + string foo(); } ``` - - example.hpp: - ```cpp - #include "example.h" - using namespace crepe; - template <typename T> - T foo(); - ``` </td></tr></table></details> - <details><summary> diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h index 6900295..b63d941 100644 --- a/src/crepe/api/Rigidbody.h +++ b/src/crepe/api/Rigidbody.h @@ -2,6 +2,7 @@ #include <cmath> #include <set> +#include <string> #include "../Component.h" @@ -120,26 +121,49 @@ public: * above 0.0. * */ - float elastisity_coefficient = 0.0; + float elasticity_coefficient = 0.0; /** - * \brief Offset of all colliders relative to the object's transform position. + * \brief Enables collision handling for objects colliding with kinematic objects. + * + * Enables collision handling for objects colliding with kinematic objects in the collision system. + * If `kinematic_collision` is true, dynamic objects cannot pass through this kinematic object. + * This ensures that kinematic objects delegate collision handling to the collision system. + */ + bool kinematic_collision = true; + + /** + * \brief Defines the collision layers a GameObject interacts with. * - * The `offset` defines a positional shift applied to all colliders associated with the object, relative to the object's - * transform position. This allows for the colliders to be placed at a different position than the object's actual - * position, without modifying the object's transform itself. + * The `collision_layers` represent the set of layers the GameObject can detect collisions with. + * Each element in this set corresponds to a layer ID. The GameObject will only collide with other + * GameObjects that belong to one these layers. + */ + std::set<int> collision_layers = {0}; + + /** + * \brief Specifies the collision layer of the GameObject. * + * The `collision_layer` indicates the single layer that this GameObject belongs to. + * This determines which layers other objects must match to detect collisions with this object. */ - vec2 offset; + int collision_layer = 0; /** * \brief Defines the collision layers of a GameObject. * - * The `collision_layers` specifies the layers that the GameObject will collide with. - * Each element represents a layer ID, and the GameObject will only detect - * collisions with other GameObjects that belong to these layers. + * The `collision_names` specifies where the GameObject will collide with. + * Each element represents a name from the Metadata of the gameobject. */ - std::set<int> collision_layers = {0}; + std::set<std::string> collision_names; + + /** + * \brief Defines the collision layers of a GameObject. + * + * The `collision_tags` specifies where the GameObject will collide with. + * Each element represents a tag from the Metadata of the gameobject. + */ + std::set<std::string> collision_tags; }; public: diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h index a2409c2..a3fc319 100644 --- a/src/crepe/api/Sprite.h +++ b/src/crepe/api/Sprite.h @@ -66,6 +66,16 @@ public: //! independent sprite offset position vec2 position_offset; + + /** + * \brief gives the user the option to render this in world space or in camera space + * + * - if true will this be rendered in world space this means that the sprite can be + * rendered off the screen + * - if false --> will the sprite be rendered in camera space. this means that the + * coordinates given on the \c Sprite and \c Transform will be inside the camera + */ + bool world_space = true; }; public: diff --git a/src/crepe/api/Text.cpp b/src/crepe/api/Text.cpp index 54a4370..4a94180 100644 --- a/src/crepe/api/Text.cpp +++ b/src/crepe/api/Text.cpp @@ -1,5 +1,3 @@ -#include "../facade/FontFacade.h" - #include "Text.h" using namespace crepe; diff --git a/src/crepe/api/Text.h b/src/crepe/api/Text.h index c30dc80..da40141 100644 --- a/src/crepe/api/Text.h +++ b/src/crepe/api/Text.h @@ -3,8 +3,6 @@ #include <optional> #include <string> -#include "../Component.h" - #include "Asset.h" #include "Color.h" #include "UIObject.h" diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h index bf9d124..52e1bb6 100644 --- a/src/crepe/api/Vector2.h +++ b/src/crepe/api/Vector2.h @@ -90,6 +90,9 @@ struct Vector2 { //! Returns the perpendicular vector to this vector. Vector2 perpendicular() const; + + //! Checks if both components of the vector are NaN. + bool is_nan() const; }; } // namespace crepe diff --git a/src/crepe/api/Vector2.hpp b/src/crepe/api/Vector2.hpp index ff53cb0..e195760 100644 --- a/src/crepe/api/Vector2.hpp +++ b/src/crepe/api/Vector2.hpp @@ -163,4 +163,9 @@ Vector2<T> Vector2<T>::perpendicular() const { return {-y, x}; } +template <class T> +bool Vector2<T>::is_nan() const { + return std::isnan(x) && std::isnan(y); +} + } // namespace crepe diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index 9486ccd..536ea43 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -11,7 +11,6 @@ #include <array> #include <cmath> #include <cstddef> -#include <cstdint> #include <functional> #include <memory> #include <stdexcept> @@ -21,7 +20,11 @@ #include "../api/Config.h" #include "../api/Sprite.h" #include "../util/dbg.h" +#include "api/Text.h" +#include "api/Transform.h" +#include "facade/Font.h" #include "manager/Mediator.h" +#include "util/AbsolutePosition.h" #include "SDLContext.h" #include "Texture.h" @@ -32,9 +35,6 @@ using namespace std; SDLContext::SDLContext(Mediator & mediator) { dbg_trace(); - if (TTF_Init() == -1) { - throw runtime_error(format("SDL_ttf initialization failed: {}", TTF_GetError())); - } if (SDL_Init(SDL_INIT_VIDEO) != 0) { throw runtime_error(format("SDLContext: SDL_Init error: {}", SDL_GetError())); } @@ -63,6 +63,10 @@ SDLContext::SDLContext(Mediator & mediator) { throw runtime_error("SDLContext: SDL_image could not initialize!"); } + if (TTF_Init() == -1) { + throw runtime_error(format("SDL_ttf initialization failed: {}", TTF_GetError())); + } + mediator.sdl_context = *this; } @@ -75,8 +79,8 @@ SDLContext::~SDLContext() { // TODO: how are we going to ensure that these are called from the same // thread that SDL_Init() was called on? This has caused problems for me // before. - IMG_Quit(); TTF_Quit(); + IMG_Quit(); SDL_Quit(); } @@ -137,6 +141,8 @@ SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const { = (ctx.sprite.aspect_ratio == 0) ? ctx.texture.get_ratio() : ctx.sprite.aspect_ratio; vec2 size = data.size; + vec2 screen_pos = ctx.pos; + if (data.size.x == 0 && data.size.y != 0) { size.x = data.size.y * aspect_ratio; } @@ -145,10 +151,15 @@ SDL_FRect SDLContext::get_dst_rect(const DestinationRectangleData & ctx) const { } size *= cam_aux_data.render_scale * ctx.img_scale * data.scale_offset; - vec2 screen_pos = (ctx.pos + data.position_offset - cam_aux_data.cam_pos - + (cam_aux_data.zoomed_viewport) / 2) - * cam_aux_data.render_scale - - size / 2 + cam_aux_data.bar_size; + if (ctx.sprite.data.world_space) { + screen_pos = (screen_pos - cam_aux_data.cam_pos + cam_aux_data.zoomed_viewport / 2) + * cam_aux_data.render_scale + - size / 2 + cam_aux_data.bar_size; + } else { + screen_pos + = (screen_pos + cam_aux_data.zoomed_viewport / 2) * cam_aux_data.render_scale + - size / 2 + cam_aux_data.bar_size; + } return SDL_FRect{ .x = screen_pos.x, @@ -188,6 +199,52 @@ void SDLContext::draw(const RenderContext & ctx) { angle, NULL, render_flip); } +void SDLContext::draw_text(const RenderText & data) { + + const Text & text = data.text; + const Font & font = data.font; + vec2 absoluut_pos = AbsolutePosition::get_position(data.transform, data.text.offset); + std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>> font_surface; + std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> font_texture; + + SDL_Color color{ + .r = text.data.text_color.r, + .g = text.data.text_color.g, + .b = text.data.text_color.b, + .a = text.data.text_color.a, + }; + SDL_Surface * tmp_font_surface + = TTF_RenderText_Solid(font.get_font(), text.text.c_str(), color); + if (tmp_font_surface == NULL) { + throw runtime_error(format("draw_text: font surface error: {}", SDL_GetError())); + } + font_surface = {tmp_font_surface, [](SDL_Surface * surface) { SDL_FreeSurface(surface); }}; + + SDL_Texture * tmp_font_texture + = SDL_CreateTextureFromSurface(this->game_renderer.get(), font_surface.get()); + if (tmp_font_texture == NULL) { + throw runtime_error(format("draw_text: font texture error: {}", SDL_GetError())); + } + font_texture + = {tmp_font_texture, [](SDL_Texture * texture) { SDL_DestroyTexture(texture); }}; + + vec2 size = text.dimensions * cam_aux_data.render_scale * data.transform.scale; + vec2 screen_pos + = (absoluut_pos - cam_aux_data.cam_pos + (cam_aux_data.zoomed_viewport) / 2) + * cam_aux_data.render_scale + - size / 2 + cam_aux_data.bar_size; + + SDL_FRect dstrect{ + .x = screen_pos.x, + .y = screen_pos.y, + .w = size.x, + .h = size.y, + }; + + SDL_RenderCopyExF(this->game_renderer.get(), font_texture.get(), NULL, &dstrect, + data.transform.rotation, NULL, SDL_FLIP_NONE); +} + void SDLContext::update_camera_view(const Camera & cam, const vec2 & new_pos) { const Camera::Data & cam_data = cam.data; diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h index b687f87..e570073 100644 --- a/src/crepe/facade/SDLContext.h +++ b/src/crepe/facade/SDLContext.h @@ -12,18 +12,21 @@ #include <unordered_map> #include "../types.h" +#include "EventData.h" #include "api/Camera.h" #include "api/Color.h" #include "api/KeyCodes.h" #include "api/Sprite.h" #include "api/Transform.h" -#include "types.h" #include "EventData.h" #include "FontFacade.h" +#include "types.h" namespace crepe { class Texture; +class Text; +class Font; class Mediator; /** @@ -71,12 +74,11 @@ public: const double & scale; }; -public: - /** - * \brief Gets the singleton instance of SDLContext. - * \return Reference to the SDLContext instance. - */ - static SDLContext & get_instance(); + struct RenderText { + const Text & text; + const Font & font; + const Transform & transform; + }; public: SDLContext(const SDLContext &) = delete; @@ -186,6 +188,13 @@ public: */ void draw(const RenderContext & ctx); + /** + * \brief draws a text to the screen + * + * \param data Reference to the rendering data needed to draw + */ + void draw_text(const RenderText & data); + //! Clears the screen, preparing for a new frame. void clear_screen(); diff --git a/src/crepe/facade/Texture.cpp b/src/crepe/facade/Texture.cpp index 6fb22e1..06caa54 100644 --- a/src/crepe/facade/Texture.cpp +++ b/src/crepe/facade/Texture.cpp @@ -4,6 +4,7 @@ #include "../types.h" #include "../util/dbg.h" +#include "SDLContext.h" #include "Texture.h" using namespace crepe; diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp index 9d88d9f..654d4c6 100644 --- a/src/crepe/system/CollisionSystem.cpp +++ b/src/crepe/system/CollisionSystem.cpp @@ -1,6 +1,7 @@ #include <algorithm> #include <cmath> #include <cstddef> +#include <emmintrin.h> #include <functional> #include <optional> #include <utility> @@ -15,13 +16,23 @@ #include "api/Rigidbody.h" #include "api/Transform.h" #include "api/Vector2.h" +#include "util/AbsolutePosition.h" +#include "util/OptionalRef.h" -#include "Collider.h" #include "CollisionSystem.h" #include "types.h" -#include "util/OptionalRef.h" using namespace crepe; +using enum Rigidbody::BodyType; + +CollisionSystem::CollisionInfo CollisionSystem::CollisionInfo::operator-() const { + return { + .self = this->other, + .other = this->self, + .resolution = -this->resolution, + .resolution_direction = this->resolution_direction, + }; +} void CollisionSystem::fixed_update() { std::vector<CollisionInternal> all_colliders; @@ -33,6 +44,7 @@ void CollisionSystem::fixed_update() { if (!rigidbody.active) continue; id = rigidbody.game_object_id; Transform & transform = mgr.get_components_by_id<Transform>(id).front().get(); + Metadata & metadata = mgr.get_components_by_id<Metadata>(id).front().get(); // Check if the boxcollider is active and has the same id as the rigidbody. RefVector<BoxCollider> boxcolliders = mgr.get_components_by_type<BoxCollider>(); for (BoxCollider & boxcollider : boxcolliders) { @@ -40,8 +52,7 @@ void CollisionSystem::fixed_update() { if (!boxcollider.active) continue; all_colliders.push_back({.id = id, .collider = collider_variant{boxcollider}, - .transform = transform, - .rigidbody = rigidbody}); + .info = {transform, rigidbody, metadata}}); } // Check if the circlecollider is active and has the same id as the rigidbody. RefVector<CircleCollider> circlecolliders @@ -51,310 +62,450 @@ void CollisionSystem::fixed_update() { if (!circlecollider.active) continue; all_colliders.push_back({.id = id, .collider = collider_variant{circlecollider}, - .transform = transform, - .rigidbody = rigidbody}); + .info = {transform, rigidbody, metadata}}); } } - // Check between all colliders if there is a collision + // Check between all colliders if there is a collision (collision handling) std::vector<std::pair<CollisionInternal, CollisionInternal>> collided = this->gather_collisions(all_colliders); - // For both objects call the collision handler + // For the object convert the info and call the collision handler if needed for (auto & collision_pair : collided) { - this->collision_handler_request(collision_pair.first, collision_pair.second); - this->collision_handler_request(collision_pair.second, collision_pair.first); + // Convert internal struct to external struct + CollisionInfo info + = this->get_collision_info(collision_pair.first, collision_pair.second); + // Determine if and/or what collison handler is needed. + this->determine_collision_handler(info); } } -void CollisionSystem::collision_handler_request(CollisionInternal & this_data, - CollisionInternal & other_data) { +// Below is for collision detection +std::vector<std::pair<CollisionSystem::CollisionInternal, CollisionSystem::CollisionInternal>> +CollisionSystem::gather_collisions(std::vector<CollisionInternal> & colliders) { - CollisionInternalType type - = this->get_collider_type(this_data.collider, other_data.collider); - std::pair<vec2, CollisionSystem::Direction> resolution_data - = this->collision_handler(this_data, other_data, type); - ComponentManager & mgr = this->mediator.component_manager; - OptionalRef<Metadata> this_metadata - = mgr.get_components_by_id<Metadata>(this_data.id).front().get(); - OptionalRef<Metadata> other_metadata - = mgr.get_components_by_id<Metadata>(other_data.id).front().get(); - OptionalRef<Collider> this_collider; - OptionalRef<Collider> other_collider; - switch (type) { - case CollisionInternalType::BOX_BOX: { - this_collider = std::get<std::reference_wrapper<BoxCollider>>(this_data.collider); - other_collider - = std::get<std::reference_wrapper<BoxCollider>>(other_data.collider); - break; - } - case CollisionInternalType::BOX_CIRCLE: { - this_collider = std::get<std::reference_wrapper<BoxCollider>>(this_data.collider); - other_collider - = std::get<std::reference_wrapper<CircleCollider>>(other_data.collider); - break; - } - case CollisionInternalType::CIRCLE_BOX: { - this_collider - = std::get<std::reference_wrapper<CircleCollider>>(this_data.collider); - other_collider - = std::get<std::reference_wrapper<BoxCollider>>(other_data.collider); - break; - } - case CollisionInternalType::CIRCLE_CIRCLE: { - this_collider - = std::get<std::reference_wrapper<CircleCollider>>(this_data.collider); - other_collider - = std::get<std::reference_wrapper<CircleCollider>>(other_data.collider); - break; + // TODO: + // If no colliders skip + // Check if colliders has rigidbody if not skip + + // TODO: + // If amount is higer than lets say 16 for now use quadtree otwerwise skip + // Quadtree code + // Quadtree is placed over the input vector + + // Return data of collided colliders which are variants + std::vector<std::pair<CollisionInternal, CollisionInternal>> collisions_ret; + //using visit to visit the variant to access the active and id. + for (size_t i = 0; i < colliders.size(); ++i) { + for (size_t j = i + 1; j < colliders.size(); ++j) { + if (colliders[i].id == colliders[j].id) continue; + if (!should_collide(colliders[i], colliders[j])) continue; + CollisionInternalType type + = get_collider_type(colliders[i].collider, colliders[j].collider); + if (!detect_collision(colliders[i], colliders[j], type)) continue; + //fet + collisions_ret.emplace_back(colliders[i], colliders[j]); } } + return collisions_ret; +} - // collision info - crepe::CollisionSystem::CollisionInfo collision_info{ - .this_collider = this_collider, - .this_transform = this_data.transform, - .this_rigidbody = this_data.rigidbody, - .this_metadata = this_metadata, - .other_collider = other_collider, - .other_transform = other_data.transform, - .other_rigidbody = other_data.rigidbody, - .other_metadata = other_metadata, - .resolution = resolution_data.first, - .resolution_direction = resolution_data.second, - }; +bool CollisionSystem::should_collide(const CollisionInternal & self, + const CollisionInternal & other) const { + + const Rigidbody::Data & self_rigidbody = self.info.rigidbody.data; + const Rigidbody::Data & other_rigidbody = other.info.rigidbody.data; + const Metadata & self_metadata = self.info.metadata; + const Metadata & other_metadata = other.info.metadata; + + // Check collision layers + if (self_rigidbody.collision_layers.contains(other_rigidbody.collision_layer)) return true; + if (other_rigidbody.collision_layers.contains(self_rigidbody.collision_layer)) return true; - // Determine if static needs to be called - this->determine_collision_handler(collision_info); + // Check names + if (self_rigidbody.collision_names.contains(other_metadata.name)) return true; + if (other_rigidbody.collision_names.contains(self_metadata.name)) return true; + + // Check tags + if (self_rigidbody.collision_tags.contains(other_metadata.tag)) return true; + if (other_rigidbody.collision_tags.contains(self_metadata.tag)) return true; + + return false; +} + +CollisionSystem::CollisionInternalType +CollisionSystem::get_collider_type(const collider_variant & collider1, + const collider_variant & collider2) const { + if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider1)) { + if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) { + return CollisionInternalType::CIRCLE_CIRCLE; + } else { + return CollisionInternalType::CIRCLE_BOX; + } + } else { + if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) { + return CollisionInternalType::BOX_CIRCLE; + } else { + return CollisionInternalType::BOX_BOX; + } + } } -std::pair<vec2, CollisionSystem::Direction> -CollisionSystem::collision_handler(CollisionInternal & data1, CollisionInternal & data2, - CollisionInternalType type) { +bool CollisionSystem::detect_collision(CollisionInternal & self, CollisionInternal & other, + const CollisionInternalType & type) { vec2 resolution; switch (type) { case CollisionInternalType::BOX_BOX: { - const BoxCollider & collider1 - = std::get<std::reference_wrapper<BoxCollider>>(data1.collider); - const BoxCollider & collider2 - = std::get<std::reference_wrapper<BoxCollider>>(data2.collider); - vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, - data1.rigidbody); - vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, - data2.rigidbody); - resolution = this->get_box_box_resolution(collider1, collider2, collider_pos1, - collider_pos2); + // Box-Box collision detection + const BoxColliderInternal BOX1 + = {.collider = std::get<std::reference_wrapper<BoxCollider>>(self.collider), + .transform = self.info.transform, + .rigidbody = self.info.rigidbody}; + const BoxColliderInternal BOX2 + = {.collider = std::get<std::reference_wrapper<BoxCollider>>(other.collider), + .transform = other.info.transform, + .rigidbody = other.info.rigidbody}; + // Get resolution vector from box-box collision detection + resolution = this->get_box_box_detection(BOX1, BOX2); + // If no collision (NaN values), return false + if (resolution.is_nan()) return false; break; } case CollisionInternalType::BOX_CIRCLE: { - const BoxCollider & collider1 - = std::get<std::reference_wrapper<BoxCollider>>(data1.collider); - const CircleCollider & collider2 - = std::get<std::reference_wrapper<CircleCollider>>(data2.collider); - vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, - data1.rigidbody); - vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, - data2.rigidbody); - resolution = -this->get_circle_box_resolution(collider2, collider1, collider_pos2, - collider_pos1); + // Box-Circle collision detection + const BoxColliderInternal BOX1 + = {.collider = std::get<std::reference_wrapper<BoxCollider>>(self.collider), + .transform = self.info.transform, + .rigidbody = self.info.rigidbody}; + const CircleColliderInternal CIRCLE2 = { + .collider = std::get<std::reference_wrapper<CircleCollider>>(other.collider), + .transform = other.info.transform, + .rigidbody = other.info.rigidbody}; + // Get resolution vector from box-circle collision detection + resolution = this->get_box_circle_detection(BOX1, CIRCLE2); + // If no collision (NaN values), return false + if (resolution.is_nan()) return false; + // Invert the resolution vector for proper collision response + resolution = -resolution; break; } case CollisionInternalType::CIRCLE_CIRCLE: { - const CircleCollider & collider1 - = std::get<std::reference_wrapper<CircleCollider>>(data1.collider); - const CircleCollider & collider2 - = std::get<std::reference_wrapper<CircleCollider>>(data2.collider); - vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, - data1.rigidbody); - vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, - data2.rigidbody); - resolution = this->get_circle_circle_resolution(collider1, collider2, - collider_pos1, collider_pos2); + // Circle-Circle collision detection + const CircleColliderInternal CIRCLE1 + = {.collider = std::get<std::reference_wrapper<CircleCollider>>(self.collider), + .transform = self.info.transform, + .rigidbody = self.info.rigidbody}; + const CircleColliderInternal CIRCLE2 = { + .collider = std::get<std::reference_wrapper<CircleCollider>>(other.collider), + .transform = other.info.transform, + .rigidbody = other.info.rigidbody}; + // Get resolution vector from circle-circle collision detection + resolution = this->get_circle_circle_detection(CIRCLE1, CIRCLE2); + // If no collision (NaN values), return false + if (resolution.is_nan()) return false; break; } case CollisionInternalType::CIRCLE_BOX: { - const CircleCollider & collider1 - = std::get<std::reference_wrapper<CircleCollider>>(data1.collider); - const BoxCollider & collider2 - = std::get<std::reference_wrapper<BoxCollider>>(data2.collider); - vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform, - data1.rigidbody); - vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, - data2.rigidbody); - resolution = this->get_circle_box_resolution(collider1, collider2, collider_pos1, - collider_pos2); + // Circle-Box collision detection + const CircleColliderInternal CIRCLE1 + = {.collider = std::get<std::reference_wrapper<CircleCollider>>(self.collider), + .transform = self.info.transform, + .rigidbody = self.info.rigidbody}; + const BoxColliderInternal BOX2 + = {.collider = std::get<std::reference_wrapper<BoxCollider>>(other.collider), + .transform = other.info.transform, + .rigidbody = other.info.rigidbody}; + // Get resolution vector from box-circle collision detection (order swapped) + resolution = this->get_box_circle_detection(BOX2, CIRCLE1); + // If no collision (NaN values), return false + if (resolution.is_nan()) return false; break; } + case CollisionInternalType::NONE: + // No collision detection needed if the type is NONE + return false; + break; } + // Store the calculated resolution vector for the 'self' collider + self.resolution = resolution; + // Calculate the resolution direction based on the rigidbody data + self.resolution_direction + = this->resolution_correction(self.resolution, self.info.rigidbody.data); + // For the 'other' collider, the resolution is the opposite direction of 'self' + other.resolution = -self.resolution; + other.resolution_direction = self.resolution_direction; + + // Return true if a collision was detected and resolution was calculated + return true; +} - Direction resolution_direction = Direction::NONE; - if (resolution.x != 0 && resolution.y != 0) { - resolution_direction = Direction::BOTH; - } else if (resolution.x != 0) { - resolution_direction = Direction::X_DIRECTION; - //checks if the other velocity has a value and if this object moved - if (data1.rigidbody.data.linear_velocity.x != 0 - && data1.rigidbody.data.linear_velocity.y != 0) - resolution.y = -data1.rigidbody.data.linear_velocity.y - * (resolution.x / data1.rigidbody.data.linear_velocity.x); - } else if (resolution.y != 0) { - resolution_direction = Direction::Y_DIRECTION; - //checks if the other velocity has a value and if this object moved - if (data1.rigidbody.data.linear_velocity.x != 0 - && data1.rigidbody.data.linear_velocity.y != 0) - resolution.x = -data1.rigidbody.data.linear_velocity.x - * (resolution.y / data1.rigidbody.data.linear_velocity.y); - } +vec2 CollisionSystem::get_box_box_detection(const BoxColliderInternal & box1, + const BoxColliderInternal & box2) const { + vec2 resolution{NAN, NAN}; + // Get current positions of colliders + vec2 pos1 = AbsolutePosition::get_position(box1.transform, box1.collider.offset); + vec2 pos2 = AbsolutePosition::get_position(box2.transform, box2.collider.offset); - return std::make_pair(resolution, resolution_direction); -} + // Scale dimensions + vec2 scaled_box1 = box1.collider.dimensions * box1.transform.scale; + vec2 scaled_box2 = box2.collider.dimensions * box2.transform.scale; + vec2 delta = pos2 - pos1; -vec2 CollisionSystem::get_box_box_resolution(const BoxCollider & box_collider1, - const BoxCollider & box_collider2, - const vec2 & final_position1, - const vec2 & final_position2) const { - vec2 resolution; // Default resolution vector - vec2 delta = final_position2 - final_position1; - - // Compute half-dimensions of the boxes - float half_width1 = box_collider1.dimensions.x / 2.0; - float half_height1 = box_collider1.dimensions.y / 2.0; - float half_width2 = box_collider2.dimensions.x / 2.0; - float half_height2 = box_collider2.dimensions.y / 2.0; - - // Calculate overlaps along X and Y axes - float overlap_x = (half_width1 + half_width2) - std::abs(delta.x); - float overlap_y = (half_height1 + half_height2) - std::abs(delta.y); - - // Check if there is a collision should always be true - if (overlap_x > 0 && overlap_y > 0) { - // Determine the direction of resolution - if (overlap_x < overlap_y) { - // Resolve along the X-axis (smallest overlap) - resolution.x = (delta.x > 0) ? -overlap_x : overlap_x; - } else if (overlap_y < overlap_x) { - // Resolve along the Y-axis (smallest overlap) - resolution.y = (delta.y > 0) ? -overlap_y : overlap_y; - } else { - // Equal overlap, resolve both directions with preference - resolution.x = (delta.x > 0) ? -overlap_x : overlap_x; - resolution.y = (delta.y > 0) ? -overlap_y : overlap_y; + // Calculate half-extents (half width and half height) + float half_width1 = scaled_box1.x / 2.0; + float half_height1 = scaled_box1.y / 2.0; + float half_width2 = scaled_box2.x / 2.0; + float half_height2 = scaled_box2.y / 2.0; + + if (pos1.x + half_width1 > pos2.x - half_width2 + && pos1.x - half_width1 < pos2.x + half_width2 + && pos1.y + half_height1 > pos2.y - half_height2 + && pos1.y - half_height1 < pos2.y + half_height2) { + resolution = {0, 0}; + float overlap_x = (half_width1 + half_width2) - std::abs(delta.x); + float overlap_y = (half_height1 + half_height2) - std::abs(delta.y); + if (overlap_x > 0 && overlap_y > 0) { + // Determine the direction of resolution + if (overlap_x < overlap_y) { + // Resolve along the X-axis (smallest overlap) + resolution.x = (delta.x > 0) ? -overlap_x : overlap_x; + } else if (overlap_y < overlap_x) { + // Resolve along the Y-axis (smallest overlap) + resolution.y = (delta.y > 0) ? -overlap_y : overlap_y; + } else { + // Equal overlap, resolve both directions with preference + resolution.x = (delta.x > 0) ? -overlap_x : overlap_x; + resolution.y = (delta.y > 0) ? -overlap_y : overlap_y; + } } } - return resolution; } -vec2 CollisionSystem::get_circle_circle_resolution(const CircleCollider & circle_collider1, - const CircleCollider & circle_collider2, - const vec2 & final_position1, - const vec2 & final_position2) const { - vec2 delta = final_position2 - final_position1; +vec2 CollisionSystem::get_box_circle_detection(const BoxColliderInternal & box, + const CircleColliderInternal & circle) const { + /// Get current positions of colliders + vec2 box_pos = AbsolutePosition::get_position(box.transform, box.collider.offset); + vec2 circle_pos = AbsolutePosition::get_position(circle.transform, circle.collider.offset); - // Compute the distance between the two circle centers - float distance = std::sqrt(delta.x * delta.x + delta.y * delta.y); + // Scale dimensions + vec2 scaled_box = box.collider.dimensions * box.transform.scale; + float scaled_circle_radius = circle.collider.radius * circle.transform.scale; - // Compute the combined radii of the two circles - float combined_radius = circle_collider1.radius + circle_collider2.radius; + // Calculate box half-extents + float half_width = scaled_box.x / 2.0f; + float half_height = scaled_box.y / 2.0f; - // Compute the penetration depth - float penetration_depth = combined_radius - distance; + // Find the closest point on the box to the circle's center + float closest_x + = std::max(box_pos.x - half_width, std::min(circle_pos.x, box_pos.x + half_width)); + float closest_y + = std::max(box_pos.y - half_height, std::min(circle_pos.y, box_pos.y + half_height)); - // Normalize the delta vector to get the collision direction - vec2 collision_normal = delta / distance; + float distance_x = circle_pos.x - closest_x; + float distance_y = circle_pos.y - closest_y; + float distance_squared = distance_x * distance_x + distance_y * distance_y; + if (distance_squared < scaled_circle_radius * scaled_circle_radius) { + vec2 delta = circle_pos - box_pos; - // Compute the resolution vector - vec2 resolution = -collision_normal * penetration_depth; + // Clamp circle center to the nearest point on the box + vec2 closest_point; + closest_point.x = std::clamp(delta.x, -half_width, half_width); + closest_point.y = std::clamp(delta.y, -half_height, half_height); - return resolution; + // Find the vector from the circle center to the closest point + vec2 closest_delta = delta - closest_point; + + float distance + = std::sqrt(closest_delta.x * closest_delta.x + closest_delta.y * closest_delta.y); + vec2 collision_normal = closest_delta / distance; + + // Compute penetration depth + float penetration_depth = scaled_circle_radius - distance; + + // Compute the resolution vector + return vec2{collision_normal * penetration_depth}; + } + // No collision + return vec2{NAN, NAN}; } -vec2 CollisionSystem::get_circle_box_resolution(const CircleCollider & circle_collider, - const BoxCollider & box_collider, - const vec2 & circle_position, - const vec2 & box_position) const { - vec2 delta = circle_position - box_position; +vec2 CollisionSystem::get_circle_circle_detection( + const CircleColliderInternal & circle1, const CircleColliderInternal & circle2) const { + // Get current positions of colliders + vec2 final_position1 + = AbsolutePosition::get_position(circle1.transform, circle1.collider.offset); + vec2 final_position2 + = AbsolutePosition::get_position(circle2.transform, circle2.collider.offset); - // Compute half-dimensions of the box - float half_width = box_collider.dimensions.x / 2.0f; - float half_height = box_collider.dimensions.y / 2.0f; + // Scale dimensions + float scaled_circle1 = circle1.collider.radius * circle1.transform.scale; + float scaled_circle2 = circle2.collider.radius * circle2.transform.scale; - // Clamp circle center to the nearest point on the box - vec2 closest_point; - closest_point.x = std::clamp(delta.x, -half_width, half_width); - closest_point.y = std::clamp(delta.y, -half_height, half_height); + float distance_x = final_position1.x - final_position2.x; + float distance_y = final_position1.y - final_position2.y; + float distance_squared = distance_x * distance_x + distance_y * distance_y; - // Find the vector from the circle center to the closest point - vec2 closest_delta = delta - closest_point; + // Calculate the sum of the radii + float radius_sum = scaled_circle1 + scaled_circle2; - // Normalize the delta to get the collision direction - float distance - = std::sqrt(closest_delta.x * closest_delta.x + closest_delta.y * closest_delta.y); - vec2 collision_normal = closest_delta / distance; + // Check for collision (distance squared must be less than the square of the radius sum) + if (distance_squared < radius_sum * radius_sum) { + vec2 delta = final_position2 - final_position1; - // Compute penetration depth - float penetration_depth = circle_collider.radius - distance; + // Compute the distance between the two circle centers + float distance = std::sqrt(delta.x * delta.x + delta.y * delta.y); - // Compute the resolution vector - vec2 resolution = collision_normal * penetration_depth; + // Compute the combined radii of the two circles + float combined_radius = scaled_circle1 + scaled_circle2; - return resolution; + // Compute the penetration depth + float penetration_depth = combined_radius - distance; + + // Normalize the delta vector to get the collision direction + vec2 collision_normal = delta / distance; + + // Compute the resolution vector + vec2 resolution = -collision_normal * penetration_depth; + + return resolution; + } + // No collision + return vec2{NAN, NAN}; + ; } -void CollisionSystem::determine_collision_handler(CollisionInfo & info) { - // Check rigidbody type for static - if (info.this_rigidbody.data.body_type == Rigidbody::BodyType::STATIC) return; - // If second body is static perform the static collision handler in this system - if (info.other_rigidbody.data.body_type == Rigidbody::BodyType::STATIC) { - this->static_collision_handler(info); +CollisionSystem::Direction +CollisionSystem::resolution_correction(vec2 & resolution, const Rigidbody::Data & rigidbody) { + + // Calculate the other value to move back correctly + // If only X or Y has a value determine what is should be to move back. + Direction resolution_direction = Direction::NONE; + // If both are not zero a perfect corner has been hit + if (resolution.x != 0 && resolution.y != 0) { + resolution_direction = Direction::BOTH; + // If x is not zero a horizontal action was latest action. + } else if (resolution.x != 0) { + resolution_direction = Direction::X_DIRECTION; + // If both are 0 resolution y should not be changed (y_velocity can be 0 by kinematic object movement) + if (rigidbody.linear_velocity.x != 0 && rigidbody.linear_velocity.y != 0) + resolution.y + = -rigidbody.linear_velocity.y * (resolution.x / rigidbody.linear_velocity.x); + } else if (resolution.y != 0) { + resolution_direction = Direction::Y_DIRECTION; + // If both are 0 resolution x should not be changed (x_velocity can be 0 by kinematic object movement) + if (rigidbody.linear_velocity.x != 0 && rigidbody.linear_velocity.y != 0) + resolution.x + = -rigidbody.linear_velocity.x * (resolution.y / rigidbody.linear_velocity.y); + } + + return resolution_direction; +} + +CollisionSystem::CollisionInfo +CollisionSystem::get_collision_info(const CollisionInternal & in_self, + const CollisionInternal & in_other) const { + + crepe::CollisionSystem::ColliderInfo self{ + .transform = in_self.info.transform, + .rigidbody = in_self.info.rigidbody, + .metadata = in_self.info.metadata, }; - // Call collision event for user - CollisionEvent data(info); - EventManager & emgr = this->mediator.event_manager; - emgr.trigger_event<CollisionEvent>(data, info.this_collider.game_object_id); + + crepe::CollisionSystem::ColliderInfo other{ + .transform = in_other.info.transform, + .rigidbody = in_other.info.rigidbody, + .metadata = in_other.info.metadata, + }; + + struct CollisionInfo collision_info { + .self = self, .other = other, .resolution = in_self.resolution, + .resolution_direction = in_self.resolution_direction, + }; + return collision_info; } -void CollisionSystem::static_collision_handler(CollisionInfo & info) { +void CollisionSystem::determine_collision_handler(const CollisionInfo & info) { + Rigidbody::BodyType self_type = info.self.rigidbody.data.body_type; + Rigidbody::BodyType other_type = info.other.rigidbody.data.body_type; + bool self_kinematic = info.self.rigidbody.data.kinematic_collision; + bool other_kinematic = info.other.rigidbody.data.kinematic_collision; + // Inverted collision info + CollisionInfo inverted = -info; + // If both objects are static skip handle call collision script + if (self_type == STATIC && other_type == STATIC) return; + + // First body is not dynamic + if (self_type != DYNAMIC) { + bool static_collision = self_type == STATIC && other_type == DYNAMIC; + bool kinematic_collision + = self_type == KINEMATIC && other_type == DYNAMIC && self_kinematic; + + // Handle collision + if (static_collision || kinematic_collision) this->static_collision_handler(inverted); + // Call scripts + this->call_collision_events(inverted); + return; + } + + // Second body is not dynamic + if (other_type != DYNAMIC) { + bool static_collision = other_type == STATIC; + bool kinematic_collision = other_type == KINEMATIC && other_kinematic; + // Handle collision + if (static_collision || kinematic_collision) this->static_collision_handler(info); + // Call scripts + this->call_collision_events(info); + return; + } + + // Dynamic + // Handle collision + this->dynamic_collision_handler(info); + // Call scripts + this->call_collision_events(info); +} + +void CollisionSystem::static_collision_handler(const CollisionInfo & info) { + + vec2 & transform_pos = info.self.transform.position; + float elasticity = info.self.rigidbody.data.elasticity_coefficient; + vec2 & rigidbody_vel = info.self.rigidbody.data.linear_velocity; + // Move object back using calculate move back value - info.this_transform.position += info.resolution; + transform_pos += info.resolution; switch (info.resolution_direction) { case Direction::BOTH: //bounce - if (info.this_rigidbody.data.elastisity_coefficient > 0) { - info.this_rigidbody.data.linear_velocity - = -info.this_rigidbody.data.linear_velocity - * info.this_rigidbody.data.elastisity_coefficient; + if (elasticity > 0) { + rigidbody_vel = -rigidbody_vel * elasticity; } //stop movement else { - info.this_rigidbody.data.linear_velocity = {0, 0}; + rigidbody_vel = {0, 0}; } break; case Direction::Y_DIRECTION: // Bounce - if (info.this_rigidbody.data.elastisity_coefficient > 0) { - info.this_rigidbody.data.linear_velocity.y - = -info.this_rigidbody.data.linear_velocity.y - * info.this_rigidbody.data.elastisity_coefficient; + if (elasticity > 0) { + rigidbody_vel.y = -rigidbody_vel.y * elasticity; } // Stop movement else { - info.this_rigidbody.data.linear_velocity.y = 0; - info.this_transform.position.x -= info.resolution.x; + rigidbody_vel.y = 0; + transform_pos.x -= info.resolution.x; } break; case Direction::X_DIRECTION: // Bounce - if (info.this_rigidbody.data.elastisity_coefficient > 0) { - info.this_rigidbody.data.linear_velocity.x - = -info.this_rigidbody.data.linear_velocity.x - * info.this_rigidbody.data.elastisity_coefficient; + if (elasticity > 0) { + rigidbody_vel.x = -rigidbody_vel.x * elasticity; } // Stop movement else { - info.this_rigidbody.data.linear_velocity.x = 0; - info.this_transform.position.y -= info.resolution.y; + rigidbody_vel.x = 0; + transform_pos.y -= info.resolution.y; } break; case Direction::NONE: @@ -363,213 +514,80 @@ void CollisionSystem::static_collision_handler(CollisionInfo & info) { } } -std::vector<std::pair<CollisionSystem::CollisionInternal, CollisionSystem::CollisionInternal>> -CollisionSystem::gather_collisions(std::vector<CollisionInternal> & colliders) { - - // TODO: - // If no colliders skip - // Check if colliders has rigidbody if not skip - - // TODO: - // If amount is higer than lets say 16 for now use quadtree otwerwise skip - // Quadtree code - // Quadtree is placed over the input vector +void CollisionSystem::dynamic_collision_handler(const CollisionInfo & info) { - // Return data of collided colliders which are variants - std::vector<std::pair<CollisionInternal, CollisionInternal>> collisions_ret; - //using visit to visit the variant to access the active and id. - for (size_t i = 0; i < colliders.size(); ++i) { - for (size_t j = i + 1; j < colliders.size(); ++j) { - if (colliders[i].id == colliders[j].id) continue; - if (!have_common_layer(colliders[i].rigidbody.data.collision_layers, - colliders[j].rigidbody.data.collision_layers)) - continue; - CollisionInternalType type - = get_collider_type(colliders[i].collider, colliders[j].collider); - if (!get_collision( - { - .collider = colliders[i].collider, - .transform = colliders[i].transform, - .rigidbody = colliders[i].rigidbody, - }, - { - .collider = colliders[j].collider, - .transform = colliders[j].transform, - .rigidbody = colliders[j].rigidbody, - }, - type)) - continue; - collisions_ret.emplace_back(colliders[i], colliders[j]); - } - } + vec2 & self_transform_pos = info.self.transform.position; + vec2 & other_transform_pos = info.other.transform.position; + float self_elasticity = info.self.rigidbody.data.elasticity_coefficient; + float other_elasticity = info.other.rigidbody.data.elasticity_coefficient; + vec2 & self_rigidbody_vel = info.self.rigidbody.data.linear_velocity; + vec2 & other_rigidbody_vel = info.other.rigidbody.data.linear_velocity; - return collisions_ret; -} + self_transform_pos += info.resolution / 2; + other_transform_pos += -(info.resolution / 2); -bool CollisionSystem::have_common_layer(const std::set<int> & layers1, - const std::set<int> & layers2) { + switch (info.resolution_direction) { + case Direction::BOTH: + if (self_elasticity > 0) { + self_rigidbody_vel = -self_rigidbody_vel * self_elasticity; + } else { + self_rigidbody_vel = {0, 0}; + } - // Check if any number is equal in the layers - for (int num : layers1) { - if (layers2.contains(num)) { - // Common layer found - return true; + if (other_elasticity > 0) { + other_rigidbody_vel = -other_rigidbody_vel * other_elasticity; + } else { + other_rigidbody_vel = {0, 0}; + } break; - } - } - // No common layer found - return false; -} + case Direction::Y_DIRECTION: + if (self_elasticity > 0) { + self_rigidbody_vel.y = -self_rigidbody_vel.y * self_elasticity; + } + // Stop movement + else { + self_rigidbody_vel.y = 0; + self_transform_pos.x -= info.resolution.x; + } -CollisionSystem::CollisionInternalType -CollisionSystem::get_collider_type(const collider_variant & collider1, - const collider_variant & collider2) const { - if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider1)) { - if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) { - return CollisionInternalType::CIRCLE_CIRCLE; - } else { - return CollisionInternalType::CIRCLE_BOX; - } - } else { - if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) { - return CollisionInternalType::BOX_CIRCLE; - } else { - return CollisionInternalType::BOX_BOX; - } - } -} + if (other_elasticity > 0) { + other_rigidbody_vel.y = -other_rigidbody_vel.y * other_elasticity; + } + // Stop movement + else { + other_rigidbody_vel.y = 0; + other_transform_pos.x -= info.resolution.x; + } + break; + case Direction::X_DIRECTION: + if (self_elasticity > 0) { + self_rigidbody_vel.x = -self_rigidbody_vel.x * self_elasticity; + } + // Stop movement + else { + self_rigidbody_vel.x = 0; + self_transform_pos.y -= info.resolution.y; + } -bool CollisionSystem::get_collision(const CollisionInternal & first_info, - const CollisionInternal & second_info, - CollisionInternalType type) const { - switch (type) { - case CollisionInternalType::BOX_BOX: { - const BoxCollider & box_collider1 - = std::get<std::reference_wrapper<BoxCollider>>(first_info.collider); - const BoxCollider & box_collider2 - = std::get<std::reference_wrapper<BoxCollider>>(second_info.collider); - return this->get_box_box_collision(box_collider1, box_collider2, - first_info.transform, second_info.transform, - second_info.rigidbody, second_info.rigidbody); - } - case CollisionInternalType::BOX_CIRCLE: { - const BoxCollider & box_collider - = std::get<std::reference_wrapper<BoxCollider>>(first_info.collider); - const CircleCollider & circle_collider - = std::get<std::reference_wrapper<CircleCollider>>(second_info.collider); - return this->get_box_circle_collision( - box_collider, circle_collider, first_info.transform, second_info.transform, - second_info.rigidbody, second_info.rigidbody); - } - case CollisionInternalType::CIRCLE_CIRCLE: { - const CircleCollider & circle_collider1 - = std::get<std::reference_wrapper<CircleCollider>>(first_info.collider); - const CircleCollider & circle_collider2 - = std::get<std::reference_wrapper<CircleCollider>>(second_info.collider); - return this->get_circle_circle_collision( - circle_collider1, circle_collider2, first_info.transform, - second_info.transform, second_info.rigidbody, second_info.rigidbody); - } - case CollisionInternalType::CIRCLE_BOX: { - const CircleCollider & circle_collider - = std::get<std::reference_wrapper<CircleCollider>>(first_info.collider); - const BoxCollider & box_collider - = std::get<std::reference_wrapper<BoxCollider>>(second_info.collider); - return this->get_box_circle_collision( - box_collider, circle_collider, first_info.transform, second_info.transform, - second_info.rigidbody, second_info.rigidbody); - } + if (other_elasticity > 0) { + other_rigidbody_vel.x = -other_rigidbody_vel.x * other_elasticity; + } + // Stop movement + else { + other_rigidbody_vel.x = 0; + other_transform_pos.y -= info.resolution.y; + } + break; + case Direction::NONE: + // Not possible + break; } - return false; } -bool CollisionSystem::get_box_box_collision(const BoxCollider & box1, const BoxCollider & box2, - const Transform & transform1, - const Transform & transform2, - const Rigidbody & rigidbody1, - const Rigidbody & rigidbody2) const { - // Get current positions of colliders - vec2 final_position1 = this->get_current_position(box1.offset, transform1, rigidbody1); - vec2 final_position2 = this->get_current_position(box2.offset, transform2, rigidbody2); - - // Calculate half-extents (half width and half height) - float half_width1 = box1.dimensions.x / 2.0; - float half_height1 = box1.dimensions.y / 2.0; - float half_width2 = box2.dimensions.x / 2.0; - float half_height2 = box2.dimensions.y / 2.0; - - // Check if the boxes overlap along the X and Y axes - return (final_position1.x + half_width1 > final_position2.x - half_width2 - && final_position1.x - half_width1 < final_position2.x + half_width2 - && final_position1.y + half_height1 > final_position2.y - half_height2 - && final_position1.y - half_height1 < final_position2.y + half_height2); -} - -bool CollisionSystem::get_box_circle_collision(const BoxCollider & box1, - const CircleCollider & circle2, - const Transform & transform1, - const Transform & transform2, - const Rigidbody & rigidbody1, - const Rigidbody & rigidbody2) const { - // Get current positions of colliders - vec2 final_position1 = this->get_current_position(box1.offset, transform1, rigidbody1); - vec2 final_position2 = this->get_current_position(circle2.offset, transform2, rigidbody2); - - // Calculate box half-extents - float half_width = box1.dimensions.x / 2.0; - float half_height = box1.dimensions.y / 2.0; - - // Find the closest point on the box to the circle's center - float closest_x = std::max(final_position1.x - half_width, - std::min(final_position2.x, final_position1.x + half_width)); - float closest_y = std::max(final_position1.y - half_height, - std::min(final_position2.y, final_position1.y + half_height)); - - // Calculate the distance squared between the circle's center and the closest point on the box - float distance_x = final_position2.x - closest_x; - float distance_y = final_position2.y - closest_y; - float distance_squared = distance_x * distance_x + distance_y * distance_y; - - // Compare distance squared with the square of the circle's radius - return distance_squared < circle2.radius * circle2.radius; -} - -bool CollisionSystem::get_circle_circle_collision(const CircleCollider & circle1, - const CircleCollider & circle2, - const Transform & transform1, - const Transform & transform2, - const Rigidbody & rigidbody1, - const Rigidbody & rigidbody2) const { - // Get current positions of colliders - vec2 final_position1 = this->get_current_position(circle1.offset, transform1, rigidbody1); - vec2 final_position2 = this->get_current_position(circle2.offset, transform2, rigidbody2); - - float distance_x = final_position1.x - final_position2.x; - float distance_y = final_position1.y - final_position2.y; - float distance_squared = distance_x * distance_x + distance_y * distance_y; - - // Calculate the sum of the radii - float radius_sum = circle1.radius + circle2.radius; - - // Check if the distance between the centers is less than or equal to the sum of the radii - return distance_squared < radius_sum * radius_sum; -} - -vec2 CollisionSystem::get_current_position(const vec2 & collider_offset, - const Transform & transform, - const Rigidbody & rigidbody) const { - // Get the rotation in radians - float radians1 = transform.rotation * (M_PI / 180.0); - - // Calculate total offset with scale - vec2 total_offset = (rigidbody.data.offset + collider_offset) * transform.scale; - - // Rotate - float rotated_total_offset_x1 - = total_offset.x * cos(radians1) - total_offset.y * sin(radians1); - float rotated_total_offset_y1 - = total_offset.x * sin(radians1) + total_offset.y * cos(radians1); - - // Final positions considering scaling and rotation - return (transform.position + vec2(rotated_total_offset_x1, rotated_total_offset_y1)); +void CollisionSystem::call_collision_events(const CollisionInfo & info) { + CollisionEvent data(info); + CollisionEvent data_inverted(-info); + EventManager & emgr = this->mediator.event_manager; + emgr.trigger_event<CollisionEvent>(data, info.self.transform.game_object_id); + emgr.trigger_event<CollisionEvent>(data_inverted, info.other.transform.game_object_id); } diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h index 48a8f86..3fb9723 100644 --- a/src/crepe/system/CollisionSystem.h +++ b/src/crepe/system/CollisionSystem.h @@ -23,6 +23,42 @@ public: using System::System; private: + //! Enum representing movement directions during collision resolution. + enum class Direction { + //! No movement required. + NONE, + //! Movement in the X direction. + X_DIRECTION, + //! Movement in the Y direction. + Y_DIRECTION, + //! Movement in both X and Y directions. + BOTH, + }; + +public: + //! Structure representing components of the collider + struct ColliderInfo { + Transform & transform; + Rigidbody & rigidbody; + Metadata & metadata; + }; + + /** + * \brief Structure representing detailed collision information between two colliders. + * + * Includes information about the colliding objects and the resolution data for handling the collision. + */ + struct CollisionInfo { + ColliderInfo self; + ColliderInfo other; + //! The resolution vector for the collision. + vec2 resolution; + //! The direction of movement for resolving the collision. + Direction resolution_direction = Direction::NONE; + CollisionInfo operator-() const; + }; + +private: //! A variant type that can hold either a BoxCollider or a CircleCollider. using collider_variant = std::variant<std::reference_wrapper<BoxCollider>, std::reference_wrapper<CircleCollider>>; @@ -33,12 +69,13 @@ private: CIRCLE_CIRCLE, BOX_CIRCLE, CIRCLE_BOX, + NONE, }; /** * \brief A structure to store the collision data of a single collider. * - * This structure all components and id that are for needed within this system when calculating or handeling collisions. + * This structure all components and id that are for needed within this system when calculating or handling collisions. * The transform and rigidbody are mostly needed for location and rotation. * In rigidbody additional info is written about what the body of the object is, * and how it should respond on a collision. @@ -46,43 +83,23 @@ private: struct CollisionInternal { game_object_id_t id = 0; collider_variant collider; - Transform & transform; - Rigidbody & rigidbody; - }; - - //! Enum representing movement directions during collision resolution. - enum class Direction { - //! No movement required. - NONE, - //! Movement in the X direction. - X_DIRECTION, - //! Movement in the Y direction. - Y_DIRECTION, - //! Movement in both X and Y directions. - BOTH - }; - -public: - /** - * \brief Structure representing detailed collision information between two colliders. - * - * Includes information about the colliding objects and the resolution data for handling the collision. - */ - struct CollisionInfo { - Collider & this_collider; - Transform & this_transform; - Rigidbody & this_rigidbody; - Metadata & this_metadata; - Collider & other_collider; - Transform & other_transform; - Rigidbody & other_rigidbody; - Metadata & other_metadata; - //! The resolution vector for the collision. + ColliderInfo info; vec2 resolution; - //! The direction of movement for resolving the collision. Direction resolution_direction = Direction::NONE; }; + //! Structure of a collider with additional components + template <typename ColliderType> + struct ColliderInternal { + ColliderType & collider; + Transform & transform; + Rigidbody & rigidbody; + }; + //! Predefined BoxColliderInternal. (System is only made for this type) + using BoxColliderInternal = ColliderInternal<BoxCollider>; + //! Predefined CircleColliderInternal. (System is only made for this type) + using CircleColliderInternal = ColliderInternal<CircleCollider>; + public: //! Updates the collision system by checking for collisions between colliders and handling them. void fixed_update() override; @@ -100,114 +117,90 @@ private: CollisionInternalType get_collider_type(const collider_variant & collider1, const collider_variant & collider2) const; - /** - * \brief Calculates the current position of a collider. - * - * Combines the Collider offset, Transform position, and Rigidbody offset to compute the position of the collider. - * - * \param collider_offset The offset of the collider. - * \param transform The Transform of the associated game object. - * \param rigidbody The Rigidbody of the associated game object. - * \return The calculated position of the collider. - */ - vec2 get_current_position(const vec2 & collider_offset, const Transform & transform, - const Rigidbody & rigidbody) const; - private: /** - * \brief Handles collision resolution between two colliders. + * \brief Converts internal collision data into user-accessible collision information. * - * Processes collision data and adjusts objects to resolve collisions and/or calls the user oncollision script function. + * This function processes collision data from two colliding entities and packages it + * into a structured format that is accessible for further use, + * such as resolving collisions and triggering user-defined collision scripts. * * \param data1 Collision data for the first collider. * \param data2 Collision data for the second collider. */ - void collision_handler_request(CollisionInternal & data1, CollisionInternal & data2); + CollisionInfo get_collision_info(const CollisionInternal & data1, + const CollisionInternal & data2) const; /** - * \brief Resolves collision between two colliders and calculates the movement required. + * \brief Corrects the collision resolution vector and determines its direction. * - * Determines the displacement and direction needed to separate colliders based on their types. + * This function adjusts the provided resolution vector based on the + * rigidbody's linear velocity to ensure consistent collision correction. If the resolution + * vector has only one non-zero component (either x or y), the missing component is computed + * based on the rigidbody's velocity. If both components are non-zero, it indicates a corner + * collision. The function also identifies the direction of the resolution and returns it. * - * \param data1 Collision data for the first collider. - * \param data2 Collision data for the second collider. - * \param type The type of collider pair. - * \return A pair containing the resolution vector and direction for the first collider. + * \param resolution resolution vector that needs to be corrected + * \param rigidbody rigidbody data used to correct resolution + * \return A Direction indicating the resolution direction */ - std::pair<vec2, Direction> collision_handler(CollisionInternal & data1, - CollisionInternal & data2, - CollisionInternalType type); + Direction resolution_correction(vec2 & resolution, const Rigidbody::Data & rigidbody); /** - * \brief Calculates the resolution vector for two BoxColliders. + * \brief Determines the appropriate collision handler for a given collision event. * - * Computes the displacement required to separate two overlapping BoxColliders. + * This function identifies the correct collision resolution process based on the body types + * of the colliders involved in the collision. It delegates + * collision handling to specific handlers and calls collision event scripts + * as needed. * - * \param box_collider1 The first BoxCollider. - * \param box_collider2 The second BoxCollider. - * \param position1 The position of the first BoxCollider. - * \param position2 The position of the second BoxCollider. - * \return The resolution vector for the collision. + * \param info Collision information containing data about both colliders. */ - vec2 get_box_box_resolution(const BoxCollider & box_collider1, - const BoxCollider & box_collider2, const vec2 & position1, - const vec2 & position2) const; + void determine_collision_handler(const CollisionInfo & info); /** - * \brief Calculates the resolution vector for two CircleCollider. + * \brief Calls both collision script * - * Computes the displacement required to separate two overlapping CircleCollider. + * Calls both collision script to let user add additonal handling or handle full collision. * - * \param circle_collider1 The first CircleCollider. - * \param circle_collider2 The second CircleCollider. - * \param final_position1 The position of the first CircleCollider. - * \param final_position2 The position of the second CircleCollider. - * \return The resolution vector for the collision. + * \param info Collision information containing data about both colliders. */ - vec2 get_circle_circle_resolution(const CircleCollider & circle_collider1, - const CircleCollider & circle_collider2, - const vec2 & final_position1, - const vec2 & final_position2) const; + void call_collision_events(const CollisionInfo & info); /** - * \brief Calculates the resolution vector for two CircleCollider. - * - * Computes the displacement required to separate two overlapping CircleCollider. + * \brief Handles collisions involving static objects. * - * \param circle_collider The first CircleCollider. - * \param box_collider The second CircleCollider. - * \param circle_position The position of the CircleCollider. - * \param box_position The position of the BoxCollider. - * \return The resolution vector for the collision. - */ - vec2 get_circle_box_resolution(const CircleCollider & circle_collider, - const BoxCollider & box_collider, - const vec2 & circle_position, - const vec2 & box_position) const; - - /** - * \brief Determines the appropriate collision handler for a collision. + * This function resolves collisions between static and dynamic objects by adjusting + * the position of the static object and modifying the velocity of the dynamic object + * if elasticity is enabled. The position of the static object is corrected + * based on the collision resolution, and the dynamic object's velocity is adjusted + * accordingly to reflect the collision response. * - * Decides the correct resolution process based on the dynamic or static nature of the colliders involved. + * The handling includes stopping movement, applying bouncing based on the elasticity + * coefficient, and adjusting the position of the dynamic object if needed. * * \param info Collision information containing data about both colliders. */ - void determine_collision_handler(CollisionInfo & info); + void static_collision_handler(const CollisionInfo & info); /** - * \brief Handles collisions involving static objects. + * \brief Handles collisions involving dynamic objects. * - * Resolves collisions by adjusting positions and modifying velocities if bounce is enabled. + * Resolves collisions between two dynamic objects by adjusting their positions and modifying + * their velocities based on the collision resolution. If elasticity is enabled, + * the velocity of both objects is reversed and scaled by the respective elasticity coefficient. + * The positions of the objects are adjusted based on the collision resolution. * * \param info Collision information containing data about both colliders. */ - void static_collision_handler(CollisionInfo & info); + void dynamic_collision_handler(const CollisionInfo & info); private: /** * \brief Checks for collisions between colliders. * - * Identifies collisions and generates pairs of colliding objects for further processing. + * This function checks all active colliders and identifies pairs of colliding objects. + * For each identified collision, the appropriate collision data is returned as pairs for further processing. * * \param colliders A collection of all active colliders. * \return A list of collision pairs with their associated data. @@ -216,86 +209,77 @@ private: gather_collisions(std::vector<CollisionInternal> & colliders); /** - * \brief Checks if two collision layers have at least one common layer. + * \brief Checks if the settings allow collision * - * This function checks if there is any overlapping layer between the two inputs. - * It compares each layer from the first input to see - * if it exists in the second input. If at least one common layer is found, - * the function returns true, indicating that the two colliders share a common - * collision layer. + * This function checks if there is any collison layer where each object is located in. + * After checking the layers it checks the names and at last the tags. + * if in all three sets nothing is found collision can not happen. * - * \param layers1 all collision layers for the first collider. - * \param layers2 all collision layers for the second collider. - * \return Returns true if there is at least one common layer, false otherwise. + * \param this_rigidbody Rigidbody of first object + * \param other_rigidbody Rigidbody of second collider + * \param this_metadata Rigidbody of first object + * \param other_metadata Rigidbody of second object + * \return Returns true if there is at least one comparison found. */ - - bool have_common_layer(const std::set<int> & layers1, const std::set<int> & layers2); + bool should_collide(const CollisionInternal & self, + const CollisionInternal & other) const; //done /** * \brief Checks for collision between two colliders. * - * Calls the appropriate collision detection function based on the collider types. + * This function determines whether two colliders are colliding based on their types. + * It calls the appropriate collision detection function based on the collider pair type and stores the collision resolution data. + * If a collision is detected, it returns true, otherwise false. * * \param first_info Collision data for the first collider. * \param second_info Collision data for the second collider. * \param type The type of collider pair. * \return True if a collision is detected, otherwise false. */ - bool get_collision(const CollisionInternal & first_info, - const CollisionInternal & second_info, - CollisionInternalType type) const; + bool detect_collision(CollisionInternal & first_info, CollisionInternal & second_info, + const CollisionInternalType & type); /** * \brief Detects collisions between two BoxColliders. * - * \param box1 The first BoxCollider. - * \param box2 The second BoxCollider. - * \param transform1 Transform of the first object. - * \param transform2 Transform of the second object. - * \param rigidbody1 Rigidbody of the first object. - * \param rigidbody2 Rigidbody of the second object. - * \return True if a collision is detected, otherwise false. + * This function checks whether two `BoxCollider` are colliding based on their positions and scaled dimensions. + * If a collision is detected, it calculates the overlap along the X and Y axes and returns the resolution vector. + * If no collision is detected, it returns a vector with NaN values. + + * \param box1 Information about the first BoxCollider. + * \param box2 Information about the second BoxCollider. + * \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}. */ - bool get_box_box_collision(const BoxCollider & box1, const BoxCollider & box2, - const Transform & transform1, const Transform & transform2, - const Rigidbody & rigidbody1, - const Rigidbody & rigidbody2) const; + vec2 get_box_box_detection(const BoxColliderInternal & box1, + const BoxColliderInternal & box2) const; /** * \brief Check collision for box on circle collider * - * \param box1 The BoxCollider - * \param circle2 The CircleCollider - * \param transform1 Transform of the first object. - * \param transform2 Transform of the second object. - * \param rigidbody1 Rigidbody of the first object. - * \param rigidbody2 Rigidbody of the second object. - * \return True if a collision is detected, otherwise false. + * This function detects if a collision occurs between a rectangular box and a circular collider. + * If a collision is detected, the function calculates the resolution vector to resolve the collision. + * If no collision is detected, it returns a vector with NaN values + * + * \param box1 Information about the BoxCollider. + * \param circle2 Information about the circleCollider. + * \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}. */ - bool get_box_circle_collision(const BoxCollider & box1, const CircleCollider & circle2, - const Transform & transform1, const Transform & transform2, - const Rigidbody & rigidbody1, - const Rigidbody & rigidbody2) const; + vec2 get_box_circle_detection(const BoxColliderInternal & box1, + const CircleColliderInternal & circle2) const; /** * \brief Check collision for circle on circle collider * - * \param circle1 First CircleCollider - * \param circle2 Second CircleCollider - * \param transform1 Transform of the first object. - * \param transform2 Transform of the second object. - * \param rigidbody1 Rigidbody of the first object. - * \param rigidbody2 Rigidbody of the second object. - * \return True if a collision is detected, otherwise false. + * This function detects if a collision occurs between two circular colliders. + * If a collision is detected, it calculates the resolution vector to resolve the collision. + * If no collision is detected, it returns a vector with NaN values. * - * \return status of collision + * \param circle1 Information about the first circleCollider. + * \param circle2 Information about the second circleCollider. + * \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}. */ - bool get_circle_circle_collision(const CircleCollider & circle1, - const CircleCollider & circle2, - const Transform & transform1, - const Transform & transform2, - const Rigidbody & rigidbody1, - const Rigidbody & rigidbody2) const; + vec2 get_circle_circle_detection(const CircleColliderInternal & circle1, + const CircleColliderInternal & circle2) const; }; /** diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp index bbc7366..5e575e4 100644 --- a/src/crepe/system/ParticleSystem.cpp +++ b/src/crepe/system/ParticleSystem.cpp @@ -7,6 +7,7 @@ #include "../api/Transform.h" #include "../manager/ComponentManager.h" #include "../manager/LoopTimerManager.h" +#include "util/AbsolutePosition.h" #include "ParticleSystem.h" @@ -48,9 +49,10 @@ void ParticleSystem::fixed_update() { void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & transform) { constexpr float DEG_TO_RAD = M_PI / 180.0; - vec2 initial_position = emitter.data.offset + transform.position; + vec2 initial_position = AbsolutePosition::get_position(transform, emitter.data.offset); float random_angle - = this->generate_random_angle(emitter.data.min_angle, emitter.data.max_angle); + = this->generate_random_angle(emitter.data.min_angle + transform.rotation, + emitter.data.max_angle + transform.rotation); float random_speed = this->generate_random_speed(emitter.data.min_speed, emitter.data.max_speed); diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp index 33218f6..698301e 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -16,6 +16,9 @@ #include "../facade/Texture.h" #include "../manager/ComponentManager.h" #include "../manager/ResourceManager.h" +#include "api/Text.h" +#include "facade/Font.h" +#include "util/AbsolutePosition.h" #include "RenderSystem.h" #include "types.h" @@ -70,11 +73,34 @@ RefVector<Sprite> RenderSystem::sort(RefVector<Sprite> & objs) const { void RenderSystem::frame_update() { this->clear_screen(); this->render(); + this->render_text(); this->present_screen(); } -bool RenderSystem::render_particle(const Sprite & sprite, const double & scale) { +void RenderSystem::render_text() { + SDLContext & ctx = this->mediator.sdl_context; + ComponentManager & mgr = this->mediator.component_manager; + ResourceManager & resource_manager = this->mediator.resource_manager; + + RefVector<Text> texts = mgr.get_components_by_type<Text>(); + + for (Text & text : texts) { + if (!text.active) continue; + if (!text.font.has_value()) + text.font.emplace(ctx.get_font_from_name(text.font_family)); + + const Font & font = resource_manager.get<Font>(text.font.value()); + const auto & transform + = mgr.get_components_by_id<Transform>(text.game_object_id).front().get(); + ctx.draw_text(SDLContext::RenderText{ + .text = text, + .font = font, + .transform = transform, + }); + } +} +bool RenderSystem::render_particle(const Sprite & sprite, const Transform & transform) { ComponentManager & mgr = this->mediator.component_manager; SDLContext & ctx = this->mediator.sdl_context; ResourceManager & resource_manager = this->mediator.resource_manager; @@ -92,29 +118,30 @@ bool RenderSystem::render_particle(const Sprite & sprite, const double & scale) for (const Particle & p : em.particles) { if (!p.active) continue; + if (p.time_in_life < em.data.begin_lifespan) continue; ctx.draw(SDLContext::RenderContext{ .sprite = sprite, .texture = res, .pos = p.position, - .angle = p.angle, - .scale = scale, + .angle = p.angle + transform.rotation, + .scale = transform.scale, }); } } return rendering_particles; } -void RenderSystem::render_normal(const Sprite & sprite, const Transform & tm) { +void RenderSystem::render_normal(const Sprite & sprite, const Transform & transform) { SDLContext & ctx = this->mediator.sdl_context; ResourceManager & resource_manager = this->mediator.resource_manager; const Texture & res = resource_manager.get<Texture>(sprite.source); - + vec2 pos = AbsolutePosition::get_position(transform, sprite.data.position_offset); ctx.draw(SDLContext::RenderContext{ .sprite = sprite, .texture = res, - .pos = tm.position, - .angle = tm.rotation, - .scale = tm.scale, + .pos = pos, + .angle = transform.rotation, + .scale = transform.scale, }); } @@ -126,35 +153,16 @@ void RenderSystem::render() { ResourceManager & resource_manager = this->mediator.resource_manager; RefVector<Sprite> sorted_sprites = this->sort(sprites); RefVector<Text> text_components = mgr.get_components_by_type<Text>(); - for (Text & text : text_components) { - const Transform & transform - = mgr.get_components_by_id<Transform>(text.game_object_id).front().get(); - this->render_text(text, transform); - } + for (const Sprite & sprite : sorted_sprites) { if (!sprite.active) continue; const Transform & transform = mgr.get_components_by_id<Transform>(sprite.game_object_id).front().get(); - bool rendered_particles = this->render_particle(sprite, transform.scale); + bool rendered_particles = this->render_particle(sprite, transform); if (rendered_particles) continue; this->render_normal(sprite, transform); } } -void RenderSystem::render_text(Text & text, const Transform & tm) { - SDLContext & ctx = this->mediator.sdl_context; - - if (!text.font.has_value()) { - text.font.emplace(ctx.get_font_from_name(text.font_family)); - } - - ResourceManager & resource_manager = this->mediator.resource_manager; - - if (!text.font.has_value()) { - return; - } - const Asset & font_asset = text.font.value(); - const Font & res = resource_manager.get<Font>(font_asset); -} diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h index 656ad5b..627a743 100644 --- a/src/crepe/system/RenderSystem.h +++ b/src/crepe/system/RenderSystem.h @@ -36,34 +36,28 @@ private: //! Updates the active camera used for rendering. void update_camera(); - //! Renders the whole screen + //! Renders all the sprites and particles void render(); + //! Renders all Text components + void render_text(); + +private: /** * \brief Renders all the particles on the screen from a given sprite. * * \param sprite renders the particles with given texture - * \param tm the Transform component for scale. This is not a const reference because each - * particle has a position and rotation that needs to overwrite the transform position and - * rotation without overwriting the current transform. and because the transform - * constructor is now protected i cannot make tmp inside + * \param transform the component that holds the position, rotation, and scale. * \return true if particles have been rendered */ - bool render_particle(const Sprite & sprite, const double & scale); - /** - * \brief Renders all Text components - * - * \param text The text component to be rendered. - * \param tm the Transform component that holds the position,rotation and scale - */ - void render_text(Text & text, const Transform & tm); + bool render_particle(const Sprite & sprite, const Transform & transform); /** * \brief renders a sprite with a Transform component on the screen * * \param sprite the sprite component that holds all the data - * \param tm the Transform component that holds the position,rotation and scale + * \param transform the Transform component that holds the position,rotation and scale */ - void render_normal(const Sprite & sprite, const Transform & tm); + void render_normal(const Sprite & sprite, const Transform & transform); /** * \brief sort a vector sprite objects with @@ -72,12 +66,6 @@ private: * \return returns a sorted reference vector */ RefVector<Sprite> sort(RefVector<Sprite> & objs) const; - - /** - * \todo Add text rendering using SDL_ttf for text components. - * \todo Implement a text component and a button component. - * \todo Consider adding text input functionality. - */ }; } // namespace crepe diff --git a/src/crepe/util/AbsolutePosition.cpp b/src/crepe/util/AbsolutePosition.cpp new file mode 100644 index 0000000..29ade23 --- /dev/null +++ b/src/crepe/util/AbsolutePosition.cpp @@ -0,0 +1,20 @@ +#include "AbsolutePosition.h" + +using namespace crepe; + +vec2 AbsolutePosition::get_position(const Transform & transform, const vec2 & offset) { + // Get the rotation in radians + float radians1 = transform.rotation * (M_PI / 180.0); + + // Calculate total offset with scale + vec2 total_offset = offset * transform.scale; + + // Rotate + float rotated_total_offset_x1 + = total_offset.x * cos(radians1) - total_offset.y * sin(radians1); + float rotated_total_offset_y1 + = total_offset.x * sin(radians1) + total_offset.y * cos(radians1); + + // Final positions considering scaling and rotation + return (transform.position + vec2(rotated_total_offset_x1, rotated_total_offset_y1)); +} diff --git a/src/crepe/util/AbsolutePosition.h b/src/crepe/util/AbsolutePosition.h new file mode 100644 index 0000000..857c1ac --- /dev/null +++ b/src/crepe/util/AbsolutePosition.h @@ -0,0 +1,29 @@ +#pragma once + +#include "api/Transform.h" + +#include "types.h" + +namespace crepe { + +/** + * \brief A class for calculating the absolute position of an object. + * + * This class provides a utility function to get the position of an object in the world space, + * taking into account the transform and any additional offset. + */ +class AbsolutePosition { +public: + /** + * \brief Get the absolute position of an object. + * + * This function calculates the absolute position by combining the transform position with an optional offset. + * + * \param transform The transform of the object, which contains its position, rotation, and scale. + * \param offset The offset to apply to the object's position (in local space). + * \return The absolute position of the object as a 2D vector. + */ + static vec2 get_position(const Transform & transform, const vec2 & offset); +}; + +} // namespace crepe diff --git a/src/crepe/util/CMakeLists.txt b/src/crepe/util/CMakeLists.txt index 94ed906..33160a7 100644 --- a/src/crepe/util/CMakeLists.txt +++ b/src/crepe/util/CMakeLists.txt @@ -1,6 +1,7 @@ target_sources(crepe PUBLIC LogColor.cpp Log.cpp + AbsolutePosition.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -11,5 +12,6 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Proxy.hpp OptionalRef.h OptionalRef.hpp + AbsolutePosition.h ) diff --git a/src/example/game.cpp b/src/example/game.cpp index 3975650..7ee784a 100644 --- a/src/example/game.cpp +++ b/src/example/game.cpp @@ -1,9 +1,12 @@ +#include "api/Asset.h" +#include "api/BehaviorScript.h" #include "api/CircleCollider.h" #include "api/ParticleEmitter.h" #include "api/Scene.h" #include "manager/ComponentManager.h" #include "manager/Mediator.h" #include "types.h" +#include <cmath> #include <crepe/api/BoxCollider.h> #include <crepe/api/Camera.h> #include <crepe/api/Color.h> @@ -17,267 +20,293 @@ #include <crepe/api/Vector2.h> using namespace crepe; - using namespace std; -class MyScript1 : public Script { - bool flip = false; +class ScriptBox : public Script { +public: bool oncollision(const CollisionEvent & test) { - Log::logf("Box {} script on_collision()", test.info.this_collider.game_object_id); + Log::logf("Box {} on_collision() with {}", test.info.self.metadata.game_object_id, test.info.other.metadata.game_object_id); return true; } - bool keypressed(const KeyPressEvent & test) { - Log::logf("Box script keypressed()"); - switch (test.key) { - case Keycode::A: { - Rigidbody & tf = this->get_component<Rigidbody>(); - tf.data.linear_velocity.x -= 1; - break; - } - case Keycode::W: { - Rigidbody & tf = this->get_component<Rigidbody>(); - // tf.data.linear_velocity.y -= 1; - tf.add_force_linear({0, -1}); - break; - } - case Keycode::S: { - Rigidbody & tf = this->get_component<Rigidbody>(); - tf.data.linear_velocity.y += 1; - break; - } - case Keycode::D: { - Rigidbody & tf = this->get_component<Rigidbody>(); - tf.data.linear_velocity.x += 1; - break; - } - case Keycode::E: { - if (flip) { - flip = false; - this->get_component<BoxCollider>().active = true; - this->get_components<Sprite>()[0].get().active = true; - this->get_component<CircleCollider>().active = false; - this->get_components<Sprite>()[1].get().active = false; - } else { - flip = true; - this->get_component<BoxCollider>().active = false; - this->get_components<Sprite>()[0].get().active = false; - this->get_component<CircleCollider>().active = true; - this->get_components<Sprite>()[1].get().active = true; - } - - //add collider switch - break; - } - case Keycode::Q: { - Rigidbody & rg = this->get_component<Rigidbody>(); - rg.data.angular_velocity = 1; - break; - } - default: - break; - } - return false; - } void init() { - Log::logf("init"); subscribe<CollisionEvent>( [this](const CollisionEvent & ev) -> bool { return this->oncollision(ev); }); - subscribe<KeyPressEvent>( - [this](const KeyPressEvent & ev) -> bool { return this->keypressed(ev); }); - } - void update(duration_t) { - Rigidbody & tf = this->get_component<Rigidbody>(); - Log::logf("linear_velocity.x {}", tf.data.linear_velocity.x); - Log::logf("linear_velocity.y {}", tf.data.linear_velocity.y); - // tf.data.linear_velocity = {0,0}; } }; -class MyScript2 : public Script { - bool flip = false; +class ScriptCircle : public Script { +public: bool oncollision(const CollisionEvent & test) { - Log::logf("Box {} script on_collision()", test.info.this_collider.game_object_id); + Log::logf("Circle {} on_collision() with {}", test.info.self.metadata.game_object_id, test.info.other.metadata.game_object_id); return true; } - bool keypressed(const KeyPressEvent & test) { - Log::logf("Box script keypressed()"); - switch (test.key) { - case Keycode::LEFT: { - Transform & tf = this->get_component<Transform>(); - tf.position.x -= 1; - break; - } - case Keycode::UP: { - Transform & tf = this->get_component<Transform>(); - tf.position.y -= 1; - break; - } - case Keycode::DOWN: { - Transform & tf = this->get_component<Transform>(); - tf.position.y += 1; - break; - } - case Keycode::RIGHT: { - Transform & tf = this->get_component<Transform>(); - tf.position.x += 1; - break; - } - case Keycode::PAUSE: { - if (flip) { - flip = false; - this->get_component<BoxCollider>().active = true; - this->get_components<Sprite>()[0].get().active = true; - this->get_component<CircleCollider>().active = false; - this->get_components<Sprite>()[1].get().active = false; - } else { - flip = true; - this->get_component<BoxCollider>().active = false; - this->get_components<Sprite>()[0].get().active = false; - this->get_component<CircleCollider>().active = true; - this->get_components<Sprite>()[1].get().active = true; - } - - //add collider switch - break; - } - default: - break; - } - return false; - } void init() { - Log::logf("init"); subscribe<CollisionEvent>( [this](const CollisionEvent & ev) -> bool { return this->oncollision(ev); }); - subscribe<KeyPressEvent>( - [this](const KeyPressEvent & ev) -> bool { return this->keypressed(ev); }); } +}; + +class ScriptMoveToLeft : public Script { +public: void update() { - // Retrieve component from the same GameObject this script is on + Transform & transform = this->get_component<Transform>(); + transform.position.x -= 0.02; } }; -class ConcreteScene1 : public Scene { +class ScriptMoveToRight : public Script { public: - using Scene::Scene; + void update() { + Transform & transform = this->get_component<Transform>(); + transform.position.x += 0.02; + } +}; +class ConcreteScene1 : public Scene { +public: void load_scene() { + GameObject camera = this->new_object("camera"); + camera.add_component<Camera>(ivec2(1080, 720), vec2(10, 10), Camera::Data{ + .bg_color = Color::WHITE, + .zoom = 1 + }); - Color color(0, 0, 0, 255); + GameObject reference = this->new_object("reference", "tag", vec2(0, 0), 0, 1); + Asset reference_asset = Asset("asset/texture/square.png"); + reference.add_component<Sprite>(reference_asset, Sprite::Data{ + .color = Color::RED, + .sorting_in_layer = 10, + .order_in_layer = 0, + .size = vec2(0.1, 0.1), + .angle_offset = 0, + .scale_offset = 1, + .position_offset = vec2(0, 0), + }); - float screen_size_width = 320; - float screen_size_height = 240; - float world_collider = 1000; - //define playable world - GameObject world = new_object( - "Name", "Tag", vec2{screen_size_width / 2, screen_size_height / 2}, 0, 1); - world.add_component<Rigidbody>(Rigidbody::Data{ - .mass = 0, - .gravity_scale = 0, - .body_type = Rigidbody::BodyType::STATIC, - .offset = {0, 0}, + /*GameObject box_1 = this->new_object("box_1", "tag", vec2(0, 0), 0, 1); + Asset box_1_asset = Asset("asset/texture/square.png"); + box_1.add_component<Sprite>(box_1_asset, Sprite::Data{ + .sorting_in_layer = 0, + .order_in_layer = 0, + .size = vec2(1, 1), + .angle_offset = 0, + .scale_offset = 1, + .position_offset = vec2(0, 0.5), }); - world.add_component<BoxCollider>( - vec2{world_collider, world_collider}, - vec2{0, 0 - (screen_size_height / 2 + world_collider / 2)}); // Top - world.add_component<BoxCollider>( - vec2{world_collider, world_collider}, - vec2{0, screen_size_height / 2 + world_collider / 2}); // Bottom - world.add_component<BoxCollider>( - vec2{world_collider, world_collider}, - vec2{0 - (screen_size_width / 2 + world_collider / 2), 0}); // Left - world.add_component<BoxCollider>( - vec2{world_collider, world_collider}, - vec2{screen_size_width / 2 + world_collider / 2, 0}); // right - world.add_component<Camera>( - ivec2{static_cast<int>(screen_size_width), static_cast<int>(screen_size_height)}, - vec2{screen_size_width, screen_size_height}, - Camera::Data{ - .bg_color = Color::WHITE, - .zoom = 1, - }); + Asset box_1_asset2 = Asset("asset/texture/test_ap43.png"); + box_1.add_component<Sprite>(box_1_asset2, Sprite::Data{ + .sorting_in_layer = 0, + .order_in_layer = 0, + .size = vec2(1, 1), + .angle_offset = 0, + .scale_offset = 1, + .position_offset = vec2(0, -0.5), + });*/ + + /*GameObject particles = this->new_object("particles", "tag", vec2(-1, 0), 180, 1); + Asset particles_asset = Asset("asset/texture/test_ap43.png"); + Sprite & particles_sprite = particles.add_component<Sprite>(particles_asset, Sprite::Data{ + .sorting_in_layer = 0, + .order_in_layer = 0, + .size = vec2(1, 1), + .angle_offset = 0, + .scale_offset = 1, + .position_offset = vec2(0, 0), + }); + ParticleEmitter & particles_emitter = particles.add_component<ParticleEmitter>(particles_sprite, ParticleEmitter::Data{ + .offset = vec2(0, 0), + .emission_rate = 1, + .min_speed = 1, + .max_speed = 1, + .min_angle = 0, + .max_angle = 0, + .end_lifespan = 4, + });*/ + + const bool SCRIPT = false; - GameObject game_object1 = new_object( - "Name", "Tag", vec2{screen_size_width / 2, screen_size_height / 2}, 0, 1); - game_object1.add_component<Rigidbody>(Rigidbody::Data{ - .mass = 1, - .gravity_scale = 1, - .body_type = Rigidbody::BodyType::DYNAMIC, - .linear_velocity = {0, 1}, - .constraints = {0, 0, 0}, - .elastisity_coefficient = 0, - .offset = {0, 0}, + const Rigidbody::BodyType BODYTYPE_LEFT = Rigidbody::BodyType::DYNAMIC; + const Rigidbody::BodyType BODYTYPE_RIGHT = Rigidbody::BodyType::DYNAMIC; + const bool BOUNCE_LEFT = false; + const bool BOUNCE_RIGHT = false; + const bool KINEMATIC_COLLISION = true; + const bool ONTOP = false; + + const float SCALE = 0.5; + const float OFFSET_X_LEFT = 0; + const float OFFSET_X_RIGHT = 0; + const float OFFSET_Y_LEFT = 0; + const float OFFSET_Y_RIGHT = 0; + + GameObject box_1 = this->new_object("box_1", "tag", ONTOP ? vec2(-0.25, -4) : vec2(-2, -4), 0, SCALE); + Asset box_1_asset = Asset("asset/texture/square.png"); + box_1.add_component<Sprite>(box_1_asset, Sprite::Data{ + .sorting_in_layer = 0, + .order_in_layer = 0, + .size = vec2(1, 1), + .angle_offset = 0, + .scale_offset = 1, + .position_offset = vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT), + }); + box_1.add_component<Rigidbody>(Rigidbody::Data{ + .gravity_scale = 0, + .body_type = BODYTYPE_LEFT, + .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{1, 0}, + .elasticity_coefficient = BOUNCE_LEFT ? 0.5 : 0, + .kinematic_collision = KINEMATIC_COLLISION, }); - // add box with boxcollider - game_object1.add_component<BoxCollider>(vec2{20, 20}); - game_object1.add_component<BehaviorScript>().set_script<MyScript1>(); + box_1.add_component<BehaviorScript>().set_script<ScriptMoveToRight>().active = SCRIPT; + box_1.add_component<BoxCollider>(vec2(1, 1), vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT)); + box_1.add_component<BehaviorScript>().set_script<ScriptBox>(); - Asset img1{"asset/texture/square.png"}; - game_object1.add_component<Sprite>(img1, Sprite::Data{ - .size = {20, 20}, - }); + GameObject circle_1 = this->new_object("ricle_1", "tag", ONTOP ? vec2(0.25, -4) : vec2(2, -4), 0, SCALE); + Asset circle_1_asset = Asset("asset/texture/circle.png"); + circle_1.add_component<Sprite>(circle_1_asset, Sprite::Data{ + .sorting_in_layer = 0, + .order_in_layer = 0, + .size = vec2(1, 1), + .angle_offset = 0, + .scale_offset = 1, + .position_offset = vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT), + }); + circle_1.add_component<Rigidbody>(Rigidbody::Data{ + .gravity_scale = 0, + .body_type = BODYTYPE_RIGHT, + .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{-1, 0}, + .elasticity_coefficient = BOUNCE_RIGHT ? 0.5 : 0, + .kinematic_collision = KINEMATIC_COLLISION, + }); + circle_1.add_component<BehaviorScript>().set_script<ScriptMoveToLeft>().active = SCRIPT; + circle_1.add_component<CircleCollider>(0.5, vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT)); + circle_1.add_component<BehaviorScript>().set_script<ScriptCircle>(); - //add circle with cirlcecollider deactiveated - game_object1.add_component<CircleCollider>(10).active = false; - Asset img2{"asset/texture/circle.png"}; - game_object1 - .add_component<Sprite>(img2, - Sprite::Data{ - .size = {20, 20}, - }) - .active - = false; + GameObject circle_2 = this->new_object("ricle_2", "tag", ONTOP ? vec2(-0.25, -1.5) : vec2(-2.5, -1.5), 0, SCALE); + Asset circle_2_asset = Asset("asset/texture/circle.png"); + circle_2.add_component<Sprite>(circle_2_asset, Sprite::Data{ + .sorting_in_layer = 0, + .order_in_layer = 0, + .size = vec2(1, 1), + .angle_offset = 0, + .scale_offset = 1, + .position_offset = vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT), + }); + circle_2.add_component<Rigidbody>(Rigidbody::Data{ + .gravity_scale = 0, + .body_type = BODYTYPE_LEFT, + .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{1, 0}, + .elasticity_coefficient = BOUNCE_LEFT ? 0.5 : 0, + .kinematic_collision = KINEMATIC_COLLISION, + }); + circle_2.add_component<BehaviorScript>().set_script<ScriptMoveToRight>().active = SCRIPT; + circle_2.add_component<CircleCollider>(0.5, vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT)); + circle_2.add_component<BehaviorScript>().set_script<ScriptCircle>(); - GameObject game_object2 = new_object( - "Name", "Tag", vec2{screen_size_width / 2, screen_size_height / 2}, 0, 1); - game_object2.add_component<Rigidbody>(Rigidbody::Data{ - .mass = 1, + GameObject box_2 = this->new_object("box_2", "tag", ONTOP ? vec2(0.25, -1.5) : vec2(2.5, -1.5), 0, SCALE); + Asset box_2_asset = Asset("asset/texture/square.png"); + box_2.add_component<Sprite>(box_2_asset, Sprite::Data{ + .sorting_in_layer = 0, + .order_in_layer = 0, + .size = vec2(1, 1), + .angle_offset = 0, + .scale_offset = 1, + .position_offset = vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT), + }); + box_2.add_component<Rigidbody>(Rigidbody::Data{ .gravity_scale = 0, - .body_type = Rigidbody::BodyType::STATIC, - .linear_velocity = {0, 0}, - .constraints = {0, 0, 0}, - .elastisity_coefficient = 1, - .offset = {0, 0}, + .body_type = BODYTYPE_RIGHT, + .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{-1, 0}, + .elasticity_coefficient = BOUNCE_RIGHT ? 0.5 : 0, + .kinematic_collision = KINEMATIC_COLLISION, }); - // add box with boxcollider - game_object2.add_component<BoxCollider>(vec2{20, 20}); - game_object2.add_component<BehaviorScript>().set_script<MyScript2>(); + box_2.add_component<BehaviorScript>().set_script<ScriptMoveToLeft>().active = SCRIPT; + box_2.add_component<BoxCollider>(vec2(1, 1), vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT)); + box_2.add_component<BehaviorScript>().set_script<ScriptBox>(); - game_object2.add_component<Sprite>(img1, Sprite::Data{ - .size = {20, 20}, - }); + GameObject box_3 = this->new_object("box_3", "tag", ONTOP ? vec2(-0.25, 1.5) : vec2(-3, 1.5), 0, SCALE); + Asset box_3_asset = Asset("asset/texture/square.png"); + box_3.add_component<Sprite>(box_3_asset, Sprite::Data{ + .sorting_in_layer = 0, + .order_in_layer = 0, + .size = vec2(1, 1), + .angle_offset = 0, + .scale_offset = 1, + .position_offset = vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT), + }); + box_3.add_component<Rigidbody>(Rigidbody::Data{ + .gravity_scale = 0, + .body_type = BODYTYPE_LEFT, + .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{1, 0}, + .elasticity_coefficient = BOUNCE_LEFT ? 0.5 : 0, + .kinematic_collision = KINEMATIC_COLLISION, + }); + box_3.add_component<BehaviorScript>().set_script<ScriptMoveToRight>().active = SCRIPT; + box_3.add_component<BoxCollider>(vec2(1, 1), vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT)); + box_3.add_component<BehaviorScript>().set_script<ScriptBox>(); - //add circle with cirlcecollider deactiveated - game_object2.add_component<CircleCollider>(10).active = false; + GameObject box_4 = this->new_object("box_4", "tag", ONTOP ? vec2(0.25, 1.5) : vec2(3, 1.5), 0, SCALE); + Asset box_4_asset = Asset("asset/texture/square.png"); + box_4.add_component<Sprite>(box_4_asset, Sprite::Data{ + .sorting_in_layer = 0, + .order_in_layer = 0, + .size = vec2(1, 1), + .angle_offset = 0, + .scale_offset = 1, + .position_offset = vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT), + }); + box_4.add_component<Rigidbody>(Rigidbody::Data{ + .gravity_scale = 0, + .body_type = BODYTYPE_RIGHT, + .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{-1, 0}, + .elasticity_coefficient = BOUNCE_RIGHT ? 0.5 : 0, + .kinematic_collision = KINEMATIC_COLLISION, + }); + box_4.add_component<BehaviorScript>().set_script<ScriptMoveToLeft>().active = SCRIPT; + box_4.add_component<BoxCollider>(vec2(1, 1), vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT)); + box_4.add_component<BehaviorScript>().set_script<ScriptBox>(); - game_object2 - .add_component<Sprite>(img2, - Sprite::Data{ - .size = {20, 20}, - }) - .active - = false; - Asset img5{"asset/texture/square.png"}; + GameObject circle_3 = this->new_object("ricle_3", "tag", ONTOP ? vec2(-0.25, 4) : vec2(-3.5, 4), 0, SCALE); + Asset circle_3_asset = Asset("asset/texture/circle.png"); + circle_3.add_component<Sprite>(circle_3_asset, Sprite::Data{ + .sorting_in_layer = 0, + .order_in_layer = 0, + .size = vec2(1, 1), + .angle_offset = 0, + .scale_offset = 1, + .position_offset = vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT), + }); + circle_3.add_component<Rigidbody>(Rigidbody::Data{ + .gravity_scale = 0, + .body_type = BODYTYPE_LEFT, + .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{1, 0}, + .elasticity_coefficient = BOUNCE_LEFT ? 0.5 : 0, + .kinematic_collision = KINEMATIC_COLLISION, + }); + circle_3.add_component<BehaviorScript>().set_script<ScriptMoveToRight>().active = SCRIPT; + circle_3.add_component<CircleCollider>(0.5, vec2(OFFSET_X_LEFT, OFFSET_Y_LEFT)); + circle_3.add_component<BehaviorScript>().set_script<ScriptCircle>(); - GameObject particle = new_object( - "Name", "Tag", vec2{screen_size_width / 2, screen_size_height / 2}, 0, 1); - auto & particle_image = particle.add_component<Sprite>(img5, Sprite::Data{ - .size = {5, 5}, - }); - auto & test - = particle.add_component<ParticleEmitter>(particle_image, ParticleEmitter::Data{ - .offset = {0, 0}, - .max_particles = 256, - .emission_rate = 1, - .min_speed = 10, - .max_speed = 20, - .min_angle = -20, - .max_angle = 20, - .begin_lifespan = 0, - .end_lifespan = 5, - }); + GameObject circle_4 = this->new_object("ricle_4", "tag", ONTOP ? vec2(0.25, 4) : vec2(3.5, 4), 0, SCALE); + Asset circle_4_asset = Asset("asset/texture/circle.png"); + circle_4.add_component<Sprite>(circle_4_asset, Sprite::Data{ + .sorting_in_layer = 0, + .order_in_layer = 0, + .size = vec2(1, 1), + .angle_offset = 0, + .scale_offset = 1, + .position_offset = vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT), + }); + circle_4.add_component<Rigidbody>(Rigidbody::Data{ + .gravity_scale = 0, + .body_type = BODYTYPE_RIGHT, + .linear_velocity = SCRIPT ? vec2{0, 0} : vec2{-1, 0}, + .elasticity_coefficient = BOUNCE_RIGHT ? 0.5 : 0, + .kinematic_collision = KINEMATIC_COLLISION, + }); + circle_4.add_component<BehaviorScript>().set_script<ScriptMoveToLeft>().active = SCRIPT; + circle_4.add_component<CircleCollider>(0.5, vec2(OFFSET_X_RIGHT, OFFSET_Y_RIGHT)); + circle_4.add_component<BehaviorScript>().set_script<ScriptCircle>(); } string get_name() const { return "scene1"; } diff --git a/src/example/rendering_particle.cpp b/src/example/rendering_particle.cpp index add43f4..5440fdd 100644 --- a/src/example/rendering_particle.cpp +++ b/src/example/rendering_particle.cpp @@ -1,4 +1,7 @@ + + #include "api/Asset.h" +#include "api/Text.h" #include <crepe/Component.h> #include <crepe/api/Animator.h> #include <crepe/api/Button.h> @@ -13,7 +16,6 @@ #include <crepe/manager/ComponentManager.h> #include <crepe/manager/Mediator.h> #include <crepe/types.h> -#include <iostream> using namespace crepe; using namespace std; @@ -21,15 +23,11 @@ using namespace std; class TestScene : public Scene { public: void load_scene() { - - cout << "TestScene" << endl; - Mediator & mediator = this->mediator; - ComponentManager & mgr = mediator.component_manager; - GameObject game_object = mgr.new_object("", "", vec2{0, 0}, 0, 1); + GameObject game_object = new_object("", "", vec2{0, 0}, 0, 1); Color color(255, 255, 255, 255); - Asset img{"asset/spritesheet/spritesheet_test.png"}; + Asset img{"asset/texture/square.png"}; Sprite & test_sprite = game_object.add_component<Sprite>( img, Sprite::Data{ @@ -37,29 +35,34 @@ public: .flip = Sprite::FlipSettings{false, false}, .sorting_in_layer = 2, .order_in_layer = 2, - .size = {0, 100}, + .size = {1, 1}, .angle_offset = 0, - .position_offset = {0, 0}, + .position_offset = {0, 1}, + .world_space = false, }); + //auto & emitter = game_object.add_component<ParticleEmitter>(test_sprite, ParticleEmitter::Data{}); - //auto & anim = game_object.add_component<Animator>(test_sprite,ivec2{32, 64}, uvec2{4,1}, Animator::Data{}); - //anim.set_anim(0); + Sprite & test_sprite1 + = game_object.add_component<Sprite>(img, Sprite::Data{ + .color = color, + .size = {1, 1}, + .position_offset = {0, -1}, + .world_space = false, + }); - auto & cam = game_object.add_component<Camera>(ivec2{720, 1280}, vec2{400, 400}, + auto & cam = game_object.add_component<Camera>(ivec2{1280, 720}, vec2{5, 5}, Camera::Data{ .bg_color = Color::WHITE, + .postion_offset = {1000, 1000}, }); - function<void()> on_click = [&]() { cout << "button clicked" << std::endl; }; - function<void()> on_enter = [&]() { cout << "enter" << std::endl; }; - function<void()> on_exit = [&]() { cout << "exit" << std::endl; }; + /* + game_object.add_component<Text>(vec2{1, 1}, vec2{0, -0.5}, "ComicSansMS", + Text::Data{.text_color = Color::RED}, "test TEST"); - auto & button - = game_object.add_component<Button>(vec2{200, 200}, vec2{0, 0}, on_click, false); - button.on_mouse_enter = on_enter; - button.on_mouse_exit = on_exit; - button.is_toggle = true; - button.active = true; + game_object.add_component<Text>(vec2{1, 1}, vec2{0, 0.5}, "ComicSansMS", + Text::Data{.text_color = Color::BLACK}, "TEST test"); + */ } string get_name() const { return "TestScene"; }; diff --git a/src/test/CollisionTest.cpp b/src/test/CollisionTest.cpp index 73a964a..c8d804c 100644 --- a/src/test/CollisionTest.cpp +++ b/src/test/CollisionTest.cpp @@ -67,7 +67,6 @@ public: world.add_component<Rigidbody>(Rigidbody::Data{ // TODO: remove unrelated properties: .body_type = Rigidbody::BodyType::STATIC, - .offset = {0, 0}, }); // Create a box with an inner size of 10x10 units world.add_component<BoxCollider>(vec2{100, 100}, vec2{0, -100}); // Top @@ -81,8 +80,7 @@ public: .body_type = Rigidbody::BodyType::DYNAMIC, .linear_velocity = {0, 0}, .constraints = {0, 0, 0}, - .elastisity_coefficient = 1, - .offset = {0, 0}, + .elasticity_coefficient = 1, .collision_layers = {0}, }); game_object1.add_component<BoxCollider>(vec2{10, 10}, vec2{0, 0}); @@ -97,8 +95,7 @@ public: .body_type = Rigidbody::BodyType::DYNAMIC, .linear_velocity = {0, 0}, .constraints = {0, 0, 0}, - .elastisity_coefficient = 1, - .offset = {0, 0}, + .elasticity_coefficient = 1, .collision_layers = {0}, }); game_object2.add_component<BoxCollider>(vec2{10, 10}, vec2{0, 0}); @@ -116,11 +113,11 @@ TEST_F(CollisionTest, collision_example) { bool collision_happend = false; script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 1); + EXPECT_EQ(ev.info.self.transform.game_object_id, 1); }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 2); + EXPECT_EQ(ev.info.self.transform.game_object_id, 2); }; EXPECT_FALSE(collision_happend); collision_sys.fixed_update(); @@ -131,16 +128,16 @@ TEST_F(CollisionTest, collision_box_box_dynamic_both_no_velocity) { bool collision_happend = false; script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 1); + EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, 10); EXPECT_EQ(ev.info.resolution.y, 10); EXPECT_EQ(ev.info.resolution_direction, crepe::CollisionSystem::Direction::BOTH); }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 2); - EXPECT_EQ(ev.info.resolution.x, 10); - EXPECT_EQ(ev.info.resolution.y, 10); + EXPECT_EQ(ev.info.self.transform.game_object_id, 2); + EXPECT_EQ(ev.info.resolution.x, -10); + EXPECT_EQ(ev.info.resolution.y, -10); EXPECT_EQ(ev.info.resolution_direction, crepe::CollisionSystem::Direction::BOTH); }; EXPECT_FALSE(collision_happend); @@ -154,7 +151,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_x_direction_no_velocity) { bool collision_happend = false; script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 1); + EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, -5); EXPECT_EQ(ev.info.resolution.y, 0); EXPECT_EQ(ev.info.resolution_direction, @@ -162,7 +159,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_x_direction_no_velocity) { }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 2); + EXPECT_EQ(ev.info.self.transform.game_object_id, 2); EXPECT_EQ(ev.info.resolution.x, 5); EXPECT_EQ(ev.info.resolution.y, 0); EXPECT_EQ(ev.info.resolution_direction, @@ -179,7 +176,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_y_direction_no_velocity) { bool collision_happend = false; script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 1); + EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, 0); EXPECT_EQ(ev.info.resolution.y, -5); EXPECT_EQ(ev.info.resolution_direction, @@ -187,7 +184,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_y_direction_no_velocity) { }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 2); + EXPECT_EQ(ev.info.self.transform.game_object_id, 2); EXPECT_EQ(ev.info.resolution.x, 0); EXPECT_EQ(ev.info.resolution.y, 5); EXPECT_EQ(ev.info.resolution_direction, @@ -204,16 +201,16 @@ TEST_F(CollisionTest, collision_box_box_dynamic_both) { bool collision_happend = false; script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 1); + EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, 10); EXPECT_EQ(ev.info.resolution.y, 10); EXPECT_EQ(ev.info.resolution_direction, crepe::CollisionSystem::Direction::BOTH); }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 2); - EXPECT_EQ(ev.info.resolution.x, 10); - EXPECT_EQ(ev.info.resolution.y, 10); + EXPECT_EQ(ev.info.self.transform.game_object_id, 2); + EXPECT_EQ(ev.info.resolution.x, -10); + EXPECT_EQ(ev.info.resolution.y, -10); EXPECT_EQ(ev.info.resolution_direction, crepe::CollisionSystem::Direction::BOTH); }; EXPECT_FALSE(collision_happend); @@ -231,7 +228,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_x_direction) { bool collision_happend = false; script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 1); + EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, -5); EXPECT_EQ(ev.info.resolution.y, 5); EXPECT_EQ(ev.info.resolution_direction, @@ -239,7 +236,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_x_direction) { }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 2); + EXPECT_EQ(ev.info.self.transform.game_object_id, 2); EXPECT_EQ(ev.info.resolution.x, 5); EXPECT_EQ(ev.info.resolution.y, -5); EXPECT_EQ(ev.info.resolution_direction, @@ -260,7 +257,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_y_direction) { bool collision_happend = false; script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 1); + EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, 5); EXPECT_EQ(ev.info.resolution.y, -5); EXPECT_EQ(ev.info.resolution_direction, @@ -268,7 +265,7 @@ TEST_F(CollisionTest, collision_box_box_dynamic_y_direction) { }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 2); + EXPECT_EQ(ev.info.self.transform.game_object_id, 2); EXPECT_EQ(ev.info.resolution.x, -5); EXPECT_EQ(ev.info.resolution.y, 5); EXPECT_EQ(ev.info.resolution_direction, @@ -289,15 +286,13 @@ TEST_F(CollisionTest, collision_box_box_static_both) { bool collision_happend = false; script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 1); + EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, 10); EXPECT_EQ(ev.info.resolution.y, 10); EXPECT_EQ(ev.info.resolution_direction, crepe::CollisionSystem::Direction::BOTH); }; - script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { - // is static should not be called - FAIL(); - }; + script_object2_ref->test_fn + = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; }; EXPECT_FALSE(collision_happend); Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); tf.position = {50, 30}; @@ -311,7 +306,7 @@ TEST_F(CollisionTest, collision_box_box_static_x_direction) { bool collision_happend = false; script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 1); + EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, -5); EXPECT_EQ(ev.info.resolution.y, 5); EXPECT_EQ(ev.info.resolution_direction, @@ -319,7 +314,7 @@ TEST_F(CollisionTest, collision_box_box_static_x_direction) { }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { // is static should not be called - FAIL(); + //FAIL(); }; EXPECT_FALSE(collision_happend); Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); @@ -336,7 +331,7 @@ TEST_F(CollisionTest, collision_box_box_static_y_direction) { bool collision_happend = false; script_object1_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 1); + EXPECT_EQ(ev.info.self.transform.game_object_id, 1); EXPECT_EQ(ev.info.resolution.x, 5); EXPECT_EQ(ev.info.resolution.y, -5); EXPECT_EQ(ev.info.resolution_direction, @@ -344,7 +339,7 @@ TEST_F(CollisionTest, collision_box_box_static_y_direction) { }; script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { // is static should not be called - FAIL(); + //FAIL(); }; EXPECT_FALSE(collision_happend); Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); @@ -356,37 +351,3 @@ TEST_F(CollisionTest, collision_box_box_static_y_direction) { collision_sys.fixed_update(); EXPECT_TRUE(collision_happend); } - -TEST_F(CollisionTest, collision_box_box_static_multiple) { //todo check visually - bool collision_happend = false; - float offset_value = 0; - float resolution = 0; - script_object1_ref->test_fn = [&](const CollisionEvent & ev) { - collision_happend = true; - EXPECT_EQ(ev.info.this_collider.game_object_id, 1); - EXPECT_EQ(ev.info.this_collider.offset.x, offset_value); - EXPECT_EQ(ev.info.resolution.x, resolution); - }; - script_object2_ref->test_fn = [&](const CollisionEvent & ev) { - // is static should not be called - FAIL(); - }; - EXPECT_FALSE(collision_happend); - Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); - tf.position = {45, 30}; - Rigidbody & rg1 = this->mgr.get_components_by_id<Rigidbody>(1).front().get(); - 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; - BoxCollider & bxc = this->mgr.get_components_by_id<BoxCollider>(1).front().get(); - bxc.offset = {5, 0}; - this->game_object1.add_component<BoxCollider>(vec2{-5, 0}, vec2{10, 10}); - offset_value = 5; - resolution = 10; - collision_sys.fixed_update(); - offset_value = -5; - resolution = 10; - tf.position = {55, 30}; - collision_sys.fixed_update(); - EXPECT_TRUE(collision_happend); -} diff --git a/src/test/Profiling.cpp b/src/test/Profiling.cpp index f988700..d1da5a0 100644 --- a/src/test/Profiling.cpp +++ b/src/test/Profiling.cpp @@ -32,7 +32,7 @@ using namespace testing; class TestScript : public Script { bool oncollision(const CollisionEvent & test) { - Log::logf("Box {} script on_collision()", test.info.this_collider.game_object_id); + Log::logf("Box {} script on_collision()", test.info.self.transform.game_object_id); return true; } void init() { @@ -50,7 +50,7 @@ public: // Minimum amount to let test pass const int min_gameobject_count = 100; // Maximum amount to stop test - const int max_gameobject_count = 150; + const int max_gameobject_count = 3000; // Amount of times a test runs to calculate average const int average = 5; // Maximum duration to stop test @@ -61,6 +61,8 @@ public: ResourceManager resman{m}; ComponentManager mgr{m}; // Add system used for profling tests + EventManager evmgr{m}; + LoopTimerManager loopmgr{m}; CollisionSystem collision_sys{m}; PhysicsSystem physics_sys{m}; ParticleSystem particle_sys{m}; |