diff options
-rwxr-xr-x | asset/texture/green_square.png | bin | 0 -> 135 bytes | |||
-rwxr-xr-x | asset/texture/red_square.png | bin | 0 -> 135 bytes | |||
-rw-r--r-- | src/crepe/Collider.cpp | 2 | ||||
-rw-r--r-- | src/crepe/Collider.h | 8 | ||||
-rw-r--r-- | src/crepe/api/BehaviorScript.hpp | 1 | ||||
-rw-r--r-- | src/crepe/api/BoxCollider.cpp | 7 | ||||
-rw-r--r-- | src/crepe/api/BoxCollider.h | 24 | ||||
-rw-r--r-- | src/crepe/api/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/crepe/api/CircleCollider.cpp | 6 | ||||
-rw-r--r-- | src/crepe/api/CircleCollider.h | 17 | ||||
-rw-r--r-- | src/crepe/api/Event.h | 9 | ||||
-rw-r--r-- | src/crepe/api/LoopManager.cpp | 11 | ||||
-rw-r--r-- | src/crepe/api/Rigidbody.h | 10 | ||||
-rw-r--r-- | src/crepe/system/CollisionSystem.cpp | 356 | ||||
-rw-r--r-- | src/crepe/system/CollisionSystem.h | 180 | ||||
-rw-r--r-- | src/crepe/system/RenderSystem.cpp | 2 | ||||
-rw-r--r-- | src/example/events.cpp | 113 | ||||
-rw-r--r-- | src/example/game.cpp | 88 | ||||
-rw-r--r-- | src/example/rendering.cpp | 59 | ||||
-rw-r--r-- | src/test/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/test/CollisionTest.cpp | 300 | ||||
-rw-r--r-- | src/test/main.cpp | 2 |
22 files changed, 1184 insertions, 16 deletions
diff --git a/asset/texture/green_square.png b/asset/texture/green_square.png Binary files differnew file mode 100755 index 0000000..7772c87 --- /dev/null +++ b/asset/texture/green_square.png diff --git a/asset/texture/red_square.png b/asset/texture/red_square.png Binary files differnew file mode 100755 index 0000000..6ffbbec --- /dev/null +++ b/asset/texture/red_square.png diff --git a/src/crepe/Collider.cpp b/src/crepe/Collider.cpp index bbec488..0706371 100644 --- a/src/crepe/Collider.cpp +++ b/src/crepe/Collider.cpp @@ -2,4 +2,4 @@ using namespace crepe; -Collider::Collider(game_object_id_t id) : Component(id) {} +Collider::Collider(game_object_id_t id, Vector2 offset) : Component(id), offset(offset) {} diff --git a/src/crepe/Collider.h b/src/crepe/Collider.h index 827f83d..0157324 100644 --- a/src/crepe/Collider.h +++ b/src/crepe/Collider.h @@ -1,14 +1,18 @@ #pragma once +#include "api/Vector2.h" + #include "Component.h" namespace crepe { class Collider : public Component { public: - Collider(game_object_id_t id); + Collider(game_object_id_t id, Vector2 offset); - int size; +public: + //! Offset of the collider relative to rigidbody position + Vector2 offset; }; } // namespace crepe diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp index bd59337..484c318 100644 --- a/src/crepe/api/BehaviorScript.hpp +++ b/src/crepe/api/BehaviorScript.hpp @@ -14,7 +14,6 @@ BehaviorScript & BehaviorScript::set_script(Args &&... args) { dbg_trace(); static_assert(std::is_base_of<Script, T>::value); Script * s = new T(std::forward<Args>(args)...); - s->game_object_id = this->game_object_id; s->active = this->active; s->component_manager = this->component_manager; diff --git a/src/crepe/api/BoxCollider.cpp b/src/crepe/api/BoxCollider.cpp new file mode 100644 index 0000000..eafbdb2 --- /dev/null +++ b/src/crepe/api/BoxCollider.cpp @@ -0,0 +1,7 @@ +#include "BoxCollider.h" +#include "../Collider.h" + +using namespace crepe; + + +BoxCollider::BoxCollider(game_object_id_t game_object_id,Vector2 offset, double width, double height) : Collider(game_object_id,offset), width(width), height(height) {} diff --git a/src/crepe/api/BoxCollider.h b/src/crepe/api/BoxCollider.h new file mode 100644 index 0000000..7f51cba --- /dev/null +++ b/src/crepe/api/BoxCollider.h @@ -0,0 +1,24 @@ +#pragma once + +#include "Vector2.h" +#include "../Collider.h" + +namespace crepe { + +/** + * \brief A class representing a box-shaped collider. + * + * This class is used for collision detection with other colliders (e.g., CircleCollider). + */ +class BoxCollider : public Collider { +public: + BoxCollider(game_object_id_t game_object_id,Vector2 offset, double width, double height); + + //! Width of box collider + double width; + + //! Height of box collider + double height; +}; + +} // namespace crepe diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index c3e0b37..057f88c 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -16,6 +16,8 @@ target_sources(crepe PUBLIC SceneManager.cpp Camera.cpp Animator.cpp + BoxCollider.cpp + CircleCollider.cpp EventManager.cpp IKeyListener.cpp IMouseListener.cpp @@ -49,6 +51,8 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES SceneManager.hpp Camera.h Animator.h + BoxCollider.h + CircleCollider.h EventManager.h EventManager.hpp EventHandler.h diff --git a/src/crepe/api/CircleCollider.cpp b/src/crepe/api/CircleCollider.cpp new file mode 100644 index 0000000..04a4995 --- /dev/null +++ b/src/crepe/api/CircleCollider.cpp @@ -0,0 +1,6 @@ +#include "CircleCollider.h" + +using namespace crepe; + + +CircleCollider::CircleCollider(game_object_id_t game_object_id,Vector2 offset, int radius) : Collider(game_object_id,offset), radius(radius) {} diff --git a/src/crepe/api/CircleCollider.h b/src/crepe/api/CircleCollider.h index e77a592..4e04fa4 100644 --- a/src/crepe/api/CircleCollider.h +++ b/src/crepe/api/CircleCollider.h @@ -1,14 +1,23 @@ #pragma once + +#include "Vector2.h" + #include "../Collider.h" namespace crepe { +/** + * \brief A class representing a circle-shaped collider. + * + * This class is used for collision detection with other colliders (e.g., BoxCollider). + */ class CircleCollider : public Collider { public: - CircleCollider(game_object_id_t game_object_id, int radius) - : Collider(game_object_id), - radius(radius) {} - int radius; + + CircleCollider(game_object_id_t game_object_id,Vector2 offset, int radius); + + //! Radius of the circle collider. + double radius; }; } // namespace crepe diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h index b267e3e..259acba 100644 --- a/src/crepe/api/Event.h +++ b/src/crepe/api/Event.h @@ -3,6 +3,8 @@ #include <string> +#include "system/CollisionSystem.h" + #include "KeyCodes.h" namespace crepe { @@ -93,7 +95,12 @@ public: /** * \brief Event triggered during a collision between objects. */ -class CollisionEvent : public Event {}; +class CollisionEvent : public Event { +public: + crepe::CollisionSystem::CollisionInfo info; + CollisionEvent(const crepe::CollisionSystem::CollisionInfo& collisionInfo) + : info(collisionInfo) {} +}; /** * \brief Event triggered when text is submitted, e.g., from a text input. diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index a64366f..586919d 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -6,6 +6,8 @@ #include "../system/PhysicsSystem.h" #include "../system/RenderSystem.h" #include "../system/ScriptSystem.h" +#include "..//system/PhysicsSystem.h" +#include "..//system/CollisionSystem.h" #include "LoopManager.h" #include "LoopTimer.h" @@ -32,7 +34,10 @@ void LoopManager::start() { } void LoopManager::set_running(bool running) { this->game_running = running; } -void LoopManager::fixed_update() {} +void LoopManager::fixed_update() { + this->get_system<PhysicsSystem>().update(); + this->get_system<CollisionSystem>().update(); +} void LoopManager::loop() { LoopTimer & timer = LoopTimer::get_instance(); @@ -41,11 +46,11 @@ void LoopManager::loop() { while (game_running) { timer.update(); - while (timer.get_lag() >= timer.get_fixed_delta_time()) { + //while (timer.get_lag() >= timer.get_fixed_delta_time()) { this->process_input(); this->fixed_update(); timer.advance_fixed_update(); - } + //} this->update(); this->render(); diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h index 3b0588f..b96b463 100644 --- a/src/crepe/api/Rigidbody.h +++ b/src/crepe/api/Rigidbody.h @@ -1,5 +1,7 @@ #pragma once +#include <cmath> + #include "../Component.h" #include "types.h" @@ -58,13 +60,13 @@ public: //! linear velocity of object vec2 linear_velocity; //! maximum linear velocity of object - vec2 max_linear_velocity; + vec2 max_linear_velocity = {INFINITY ,INFINITY}; //! linear damping of object vec2 linear_damping; //! angular velocity of object double angular_velocity = 0.0; //! max angular velocity of object - double max_angular_velocity = 0.0; + double max_angular_velocity = INFINITY; //! angular damping of object double angular_damping = 0.0; //! movements constraints of object @@ -73,6 +75,10 @@ public: bool use_gravity = true; //! if object bounces bool bounce = false; + //! bounce factor of material + double elastisity = 0.0; + //! offset of all colliders relative to transform position + Vector2 offset; }; public: diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp index c74ca1d..9ca8b3a 100644 --- a/src/crepe/system/CollisionSystem.cpp +++ b/src/crepe/system/CollisionSystem.cpp @@ -1,5 +1,359 @@ +#include <cmath> +#include <algorithm> +#include <cstddef> +#include <utility> +#include <variant> + +#include "api/Event.h" +#include "api/EventManager.h" +#include "api/BoxCollider.h" +#include "api/CircleCollider.h" +#include "api/Vector2.h" +#include "api/Rigidbody.h" +#include "api/Transform.h" + +#include "ComponentManager.h" #include "CollisionSystem.h" +#include "Collider.h" using namespace crepe; -void CollisionSystem::update() {} +void CollisionSystem::update() { + // Get collider components and keep them seperate + ComponentManager & mgr = this->component_manager; + std::vector<std::reference_wrapper<BoxCollider>> boxcolliders = mgr.get_components_by_type<BoxCollider>(); + std::vector<std::reference_wrapper<CircleCollider>> circlecolliders = mgr.get_components_by_type<CircleCollider>(); + + // Check between all colliders if there is a collision + std::vector<std::pair<CollidedInfoStor,CollidedInfoStor>> collided = check_collisions(boxcolliders,circlecolliders); + + // For both objects call the collision handler + for (auto& collision_pair : collided) { + collision_handler(collision_pair.first,collision_pair.second); + collision_handler(collision_pair.second,collision_pair.first); + } +} + +void CollisionSystem::collision_handler(CollidedInfoStor& data1,CollidedInfoStor& data2){ + + // Data needed for collision handler info + const Collider* collider1 = nullptr; + const Collider* collider2 = nullptr; + Vector2 move_back; + + // Check collision type and get values for handler + if (std::holds_alternative<std::reference_wrapper<BoxCollider>>(data1.collider)) { + if (std::holds_alternative<std::reference_wrapper<BoxCollider>>(data2.collider)) { + + // Get colliders from variant to be used to determine collision handler info + const BoxCollider& box_collider1 = std::get<std::reference_wrapper<BoxCollider>>(data1.collider).get(); + const BoxCollider& box_collider2 = std::get<std::reference_wrapper<BoxCollider>>(data2.collider).get(); + collider1 = &box_collider1; + collider2 = &box_collider2; + + // TODO: send with the collider info to this function because this is calculated previously + // Get the current position of the collider + Vector2 final_position1 = current_position(box_collider1,data1.transform,data1.rigidbody); + Vector2 final_position2 = current_position(box_collider2,data2.transform,data2.rigidbody); + + // Determine move_back value for smallest overlap (x or y) + move_back = box_box_collision_move_back(box_collider1,box_collider2,final_position1,final_position2); + + } + else { + // TODO: calcualte Box - Circle collision info + const BoxCollider& box_collider = std::get<std::reference_wrapper<BoxCollider>>(data1.collider).get(); + const CircleCollider& circle_collider = std::get<std::reference_wrapper<CircleCollider>>(data2.collider).get(); + collider1 = &box_collider; + collider2 = &circle_collider; + } + } + else { + if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(data2.collider)) { + // TODO: calcualte Circle - Circle collision info + const CircleCollider& circle_collider1 = std::get<std::reference_wrapper<CircleCollider>>(data1.collider).get(); + const CircleCollider& circle_collider2 = std::get<std::reference_wrapper<CircleCollider>>(data2.collider).get(); + collider1 = &circle_collider1; + collider2 = &circle_collider2; + } + else { + // TODO: calcualte Circle - Box collision info + const CircleCollider& circle_collider = std::get<std::reference_wrapper<CircleCollider>>(data1.collider); + const BoxCollider& box_collider = std::get<std::reference_wrapper<BoxCollider>>(data2.collider); + collider1 = &circle_collider; + collider2 = &box_collider; + } + } + + // One vaue is calculated for moving back. Calculate the other value (x or y) relateive to the move_back value. + Direction move_back_direction = Direction::NONE; + if(move_back.x != 0 && move_back.y > 0) { + move_back_direction = Direction::BOTH; + } else if (move_back.x != 0) { + move_back_direction = Direction::X_DIRECTION; + if(data1.rigidbody.data.linear_velocity.y != 0) + move_back.y = data1.rigidbody.data.linear_velocity.y * (move_back.x/data1.rigidbody.data.linear_velocity.x); + } else if (move_back.y != 0) { + move_back_direction = Direction::Y_DIRECTION; + if(data1.rigidbody.data.linear_velocity.x != 0) + move_back.x = data1.rigidbody.data.linear_velocity.x * (move_back.y/data1.rigidbody.data.linear_velocity.y); + } + + // collision info + crepe::CollisionSystem::CollisionInfo collision_info{ + .first={ *collider1, data1.transform, data1.rigidbody }, + .second={ *collider2, data2.transform, data2.rigidbody }, + .move_back_value = move_back, + .move_back_direction = move_back_direction, + }; + + // Determine if static needs to be called + determine_collision_handler(collision_info); +} + + +Vector2 CollisionSystem::box_box_collision_move_back(const BoxCollider& box_collider1,const BoxCollider& box_collider2,Vector2 final_position1,Vector2 final_position2) +{ + Vector2 resolution; // Default resolution vector + Vector2 delta = final_position2 - final_position1; + + // Compute half-dimensions of the boxes + double half_width1 = box_collider1.width / 2.0; + double half_height1 = box_collider1.height / 2.0; + double half_width2 = box_collider2.width / 2.0; + double half_height2 = box_collider2.height / 2.0; + + // Calculate overlaps along X and Y axes + double overlap_x = (half_width1 + half_width2) - std::abs(delta.x); + double overlap_y = (half_height1 + half_height2) - std::abs(delta.y); + + // Check if there is a collision + if (overlap_x > 0 && overlap_y > 0) {//should always be true check if this can be removed + // 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; +} + +void CollisionSystem::determine_collision_handler(CollisionInfo& info){ + // Check rigidbody type for static + if(info.first.rigidbody.data.body_type != Rigidbody::BodyType::STATIC) + { + // If second body is static perform the static collision handler in this system + if(info.second.rigidbody.data.body_type == Rigidbody::BodyType::STATIC){ + static_collision_handler(info); + }; + // Call collision event for user + CollisionEvent data(info); + EventManager::get_instance().trigger_event<CollisionEvent>(data, info.first.collider.game_object_id); + } +} + +void CollisionSystem::static_collision_handler(CollisionInfo& info){ + // Move object back using calculate move back value + info.first.transform.position += info.move_back_value; + + // If bounce is enabled mirror velocity + if(info.first.rigidbody.data.bounce) { + if(info.move_back_direction == Direction::BOTH) + { + info.first.rigidbody.data.linear_velocity.y = -info.first.rigidbody.data.linear_velocity.y * info.first.rigidbody.data.elastisity; + info.first.rigidbody.data.linear_velocity.x = -info.first.rigidbody.data.linear_velocity.x * info.first.rigidbody.data.elastisity; + } + else if(info.move_back_direction == Direction::Y_DIRECTION) { + info.first.rigidbody.data.linear_velocity.y = -info.first.rigidbody.data.linear_velocity.y * info.first.rigidbody.data.elastisity; + } + else if(info.move_back_direction == Direction::X_DIRECTION){ + info.first.rigidbody.data.linear_velocity.x = -info.first.rigidbody.data.linear_velocity.x * info.first.rigidbody.data.elastisity; + } + } + // Stop movement if bounce is disabled + else { + info.first.rigidbody.data.linear_velocity = {0,0}; + } +} + +std::vector<std::pair<CollisionSystem::CollidedInfoStor,CollisionSystem::CollidedInfoStor>> CollisionSystem::check_collisions(const std::vector<std::reference_wrapper<BoxCollider>>& boxcolliders, const std::vector<std::reference_wrapper<CircleCollider>>& circlecolliders) { + ComponentManager & mgr = this->component_manager; + std::vector<std::pair<CollidedInfoStor,CollidedInfoStor>> collisions_ret; + + // 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 + + // Check collisions for each collider + for (size_t i = 0; i < boxcolliders.size(); ++i) { + // Fetch components for the first box collider + if(!boxcolliders[i].get().active) continue; + int game_object_id_1 = boxcolliders[i].get().game_object_id; + Transform& transform1 = mgr.get_components_by_id<Transform>(game_object_id_1).front().get(); + if(!transform1.active) continue; + Rigidbody& rigidbody1 = mgr.get_components_by_id<Rigidbody>(game_object_id_1).front().get(); + if(!rigidbody1.active) continue; + + // Check BoxCollider vs BoxCollider + for (size_t j = i + 1; j < boxcolliders.size(); ++j) { + if(!boxcolliders[j].get().active) continue; + // Skip self collision + int game_object_id_2 = boxcolliders[j].get().game_object_id; + if (game_object_id_1 == game_object_id_2) continue; + + // Fetch components for the second box collider + Transform & transform2 = mgr.get_components_by_id<Transform>(boxcolliders[j].get().game_object_id).front().get(); + if(!transform2.active) continue; + Rigidbody & rigidbody2 = mgr.get_components_by_id<Rigidbody>(boxcolliders[j].get().game_object_id).front().get(); + if(!rigidbody2.active) continue; + // Check collision + if (check_box_box_collision(boxcolliders[i], boxcolliders[j], transform1, transform2, rigidbody1, rigidbody2)) { + collisions_ret.emplace_back(std::make_pair( + CollidedInfoStor{boxcolliders[i].get(), transform1, rigidbody1}, + CollidedInfoStor{boxcolliders[j].get(), transform2, rigidbody2} + )); + } + } + + // Check BoxCollider vs CircleCollider + for (size_t j = 0; j < circlecolliders.size(); ++j) { + if(!circlecolliders[j].get().active) continue; + // Skip self collision + int game_object_id_2 = circlecolliders[j].get().game_object_id; + if (game_object_id_1 == game_object_id_2) continue; + + // Fetch components for the second collider (circle) + Transform & transform2 = mgr.get_components_by_id<Transform>(circlecolliders[j].get().game_object_id).front().get(); + if(!transform2.active) continue; + Rigidbody & rigidbody2 = mgr.get_components_by_id<Rigidbody>(circlecolliders[j].get().game_object_id).front().get(); + if(!rigidbody2.active) continue; + + // Check collision + if (check_box_circle_collision(boxcolliders[i], circlecolliders[j], transform1, transform2, rigidbody1, rigidbody2)) { + + collisions_ret.emplace_back(std::make_pair( + CollidedInfoStor{boxcolliders[i].get(), transform1, rigidbody1}, + CollidedInfoStor{circlecolliders[j].get(), transform2, rigidbody2} + )); + } + } + } + // Check CircleCollider vs CircleCollider + for (size_t i = 0; i < circlecolliders.size(); ++i) { + if(!circlecolliders[i].get().active) continue; + // Fetch components for the first circle collider + int game_object_id_1 = circlecolliders[i].get().game_object_id; + Transform & transform1 = mgr.get_components_by_id<Transform>(circlecolliders[i].get().game_object_id).front().get(); + if(!transform1.active) continue; + Rigidbody & rigidbody1 = mgr.get_components_by_id<Rigidbody>(circlecolliders[i].get().game_object_id).front().get(); + if(!rigidbody1.active) continue; + + for (size_t j = i + 1; j < circlecolliders.size(); ++j) { + if(!circlecolliders[j].get().active) continue; + // Skip self collision + int game_object_id_2 = circlecolliders[j].get().game_object_id; + if (game_object_id_1 == game_object_id_2) continue; + + // Fetch components for the second circle collider + Transform & transform2 = mgr.get_components_by_id<Transform>(circlecolliders[j].get().game_object_id).front().get(); + if(!transform2.active) continue; + Rigidbody & rigidbody2 = mgr.get_components_by_id<Rigidbody>(circlecolliders[j].get().game_object_id).front().get(); + if(!rigidbody2.active) continue; + + // Check collision + if (check_circle_circle_collision(circlecolliders[i], circlecolliders[j], transform1, transform2, rigidbody1, rigidbody2)) { + collisions_ret.emplace_back(std::make_pair( + CollidedInfoStor{circlecolliders[i].get(), transform1, rigidbody1}, + CollidedInfoStor{circlecolliders[j].get(), transform2, rigidbody2} + )); + } + } + } + return collisions_ret; +} + +bool CollisionSystem::check_box_box_collision(const BoxCollider& box1, const BoxCollider& box2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2) +{ + // Get current positions of colliders + Vector2 final_position1 = current_position(box1,transform1,rigidbody1); + Vector2 final_position2 = current_position(box2,transform2,rigidbody2); + + // Calculate half-extents (half width and half height) + double half_width1 = box1.width / 2.0; + double half_height1 = box1.height / 2.0; + double half_width2 = box2.width / 2.0; + double half_height2 = box2.height / 2.0; + + // Check if the boxes overlap along the X and Y axes + return !(final_position1.x + half_width1 <= final_position2.x - half_width2 || // box1 is left of box2 + final_position1.x - half_width1 >= final_position2.x + half_width2 || // box1 is right of box2 + final_position1.y + half_height1 <= final_position2.y - half_height2 || // box1 is above box2 + final_position1.y - half_height1 >= final_position2.y + half_height2); // box1 is below box2 +} + +bool CollisionSystem::check_box_circle_collision(const BoxCollider& box1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2) { + // Get current positions of colliders + Vector2 final_position1 = current_position(box1, transform1, rigidbody1); + Vector2 final_position2 = current_position(circle2, transform2, rigidbody2); + + // Calculate box half-extents + double half_width = box1.width / 2.0; + double half_height = box1.height / 2.0; + + // Find the closest point on the box to the circle's center + double closest_x = std::max(final_position1.x - half_width, std::min(final_position2.x, final_position1.x + half_width)); + double 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 + double distance_x = final_position2.x - closest_x; + double distance_y = final_position2.y - closest_y; + double 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::check_circle_circle_collision(const CircleCollider& circle1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2) { + // Get current positions of colliders + Vector2 final_position1 = current_position(circle1,transform1,rigidbody1); + Vector2 final_position2 = current_position(circle2,transform2,rigidbody2); + + double distance_x = final_position1.x - final_position2.x; + double distance_y = final_position1.y - final_position2.y; + double distance_squared = distance_x * distance_x + distance_y * distance_y; + + // Calculate the sum of the radii + double 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; +} + +Vector2 CollisionSystem::current_position(const Collider& collider, const Transform& transform, const Rigidbody& rigidbody) { + // Get the rotation in radians + double radians1 = transform.rotation * (M_PI / 180.0); + + // Calculate total offset with scale + Vector2 total_offset = (rigidbody.data.offset + collider.offset) * transform.scale; + + // Rotate + double rotated_total_offset_x1 = total_offset.x * cos(radians1) - total_offset.y * sin(radians1); + double rotated_total_offset_y1 = total_offset.x * sin(radians1) + total_offset.y * cos(radians1); + + // Final positions considering scaling and rotation + return(transform.position + Vector2(rotated_total_offset_x1, rotated_total_offset_y1)); + +} diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h index c1a70d8..69d1d23 100644 --- a/src/crepe/system/CollisionSystem.h +++ b/src/crepe/system/CollisionSystem.h @@ -1,13 +1,193 @@ #pragma once +#include <vector> +#include <variant> + +#include "api/Rigidbody.h" +#include "api/Transform.h" +#include "api/BoxCollider.h" +#include "api/CircleCollider.h" +#include "api/Vector2.h" + +#include "Collider.h" #include "System.h" namespace crepe { + +//! A system responsible for detecting and handling collisions between colliders. class CollisionSystem : public System { public: using System::System; +private: + + //! A variant type that can hold either a BoxCollider or a CircleCollider. + // using collider_stor = std::variant<BoxCollider, CircleCollider>; + using collider_stor = std::variant<std::reference_wrapper<BoxCollider>, std::reference_wrapper<CircleCollider>>; + + /** + * \brief A structure to store the collision data of a single collider. + * + * This structure stores the collider type, its associated transform, and its rigidbody. + */ + struct CollidedInfoStor { + //! Store either BoxCollider or CircleCollider + collider_stor collider; + Transform& transform; + Rigidbody& rigidbody; + }; + + //! Enum representing movement directions during collision resolution. + enum class Direction { + NONE, + X_DIRECTION, + Y_DIRECTION, + BOTH + }; + +public: + /** + * \brief A structure representing the collision information between two colliders. + * + * This structure contains both colliders, their associated transforms and rigidbodies, + * as well as the movement vector to resolve the collision. + */ + struct ColliderInfo { + const Collider& collider; + Transform& transform; + Rigidbody& rigidbody; + }; + /** + * \brief A structure representing detailed collision information between two colliders. + * + * This includes the movement data required to resolve the collision. + */ + struct CollisionInfo{ + ColliderInfo first; + ColliderInfo second; + Vector2 move_back_value; + Direction move_back_direction = Direction::NONE; + }; + +public: + + //! Updates the collision system by checking for collisions between colliders and handling them. void update() override; + +private: + /** + * \brief Handles a collision between two colliders. + * + * This function calculates the necessary response to resolve the collision, including + * moving the objects back to prevent overlap and applying any velocity changes. + * + * \param data1 The collision data for the first collider. + * \param data2 The collision data for the second collider. + */ + void collision_handler(CollidedInfoStor& data1,CollidedInfoStor& data2); + + /** + * \brief Resolves the movement of two box colliders that are colliding. + * + * This function calculates the smallest overlap (along the X or Y axis) and determines + * the move-back vector to prevent overlap. + * + * \param box_collider1 The first box collider. + * \param box_collider2 The second box collider. + * \param final_position1 The final position of the first box collider. + * \param final_position2 The final position of the second box collider. + * \return The move-back vector to resolve the collision. + */ + Vector2 box_box_collision_move_back(const BoxCollider& box_collider1,const BoxCollider& box_collider2,Vector2 position1,Vector2 position2); + + /** + * \brief Determines the appropriate collision handler based on the rigidbody types of the colliding objects. + * + * This function checks if the second object is static, and if so, it calls the static collision handler. + * Otherwise, it triggers a collision event. + * + * \param info The collision information containing the colliders, transforms, rigidbodies, and move-back data. + */ + void determine_collision_handler(CollisionInfo& info); + + /** + * \brief Handles the collision with a static object. + * + * This function resolves the collision by moving the object back and applying any bounce or stop behavior. + * + * \param info The collision information containing the colliders, transforms, rigidbodies, and move-back data. + */ + void static_collision_handler(CollisionInfo& info); +private: //detection + + /** + * \brief Checks for collisions between box colliders and circle colliders + * and returns the collision pairs that need to be resolved. + * + * \param boxcolliders A vector of references to all box colliders. + * \param circlecolliders A vector of references to all circle colliders. + * \return A vector of pairs containing collision information for the detected collisions. + */ + std::vector<std::pair<CollidedInfoStor,CollidedInfoStor>> check_collisions(const std::vector<std::reference_wrapper<BoxCollider>>& boxcolliders, const std::vector<std::reference_wrapper<CircleCollider>>& circlecolliders); + + /** + * \brief Checks for a collision between two box colliders. + * + * This function checks if two box colliders overlap based on their positions and dimensions. + * + * \param box1 The first box collider. + * \param box2 The second box collider. + * \param transform1 The transform component of the first box collider. + * \param transform2 The transform component of the second box collider. + * \param rigidbody1 The rigidbody component of the first box collider. + * \param rigidbody2 The rigidbody component of the second box collider. + * \return True if the two box colliders overlap, otherwise false. + */ + bool check_box_box_collision(const BoxCollider& box1, const BoxCollider& box2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2); + + /** + * \brief Checks for a collision between a box collider and a circle collider. + * + * This function checks if a box collider overlaps with a circle collider based on their positions + * and dimensions. + * + * \param box1 The box collider. + * \param circle2 The circle collider. + * \param transform1 The transform component of the box collider. + * \param transform2 The transform component of the circle collider. + * \param rigidbody1 The rigidbody component of the box collider. + * \param rigidbody2 The rigidbody component of the circle collider. + * \return True if the box collider and circle collider overlap, otherwise false. + */ + bool check_box_circle_collision(const BoxCollider& box1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2); + + /** + * \brief Checks for a collision between two circle colliders. + * + * This function checks if two circle colliders overlap based on their positions and radii. + * + * \param circle1 The first circle collider. + * \param circle2 The second circle collider. + * \param transform1 The transform component of the first circle collider. + * \param transform2 The transform component of the second circle collider. + * \param rigidbody1 The rigidbody component of the first circle collider. + * \param rigidbody2 The rigidbody component of the second circle collider. + * \return True if the two circle colliders overlap, otherwise false. + */ + bool check_circle_circle_collision(const CircleCollider& circle1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2); + + /** + * \brief Gets the current position of a collider by combining its transform and rigidbody data. + * + * This function calculates the current position of the collider by considering its transform and + * rigidbody velocity. + * + * \param collider The collider whose position is being determined. + * \param transform The transform component associated with the collider. + * \param rigidbody The rigidbody component associated with the collider. + * \return The current position of the collider as a Vector2. + */ + Vector2 current_position(const Collider& collider, const Transform& transform, const Rigidbody& rigidbody); }; } // namespace crepe diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp index ad510f5..8f9467e 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -28,6 +28,8 @@ void RenderSystem::update_camera() { if (cameras.size() == 0) throw std::runtime_error("No cameras in current scene"); + if (cameras.size() == 0) throw std::runtime_error("No cameras in current scene"); + for (Camera & cam : cameras) { if (!cam.active) continue; this->context.set_camera(cam); diff --git a/src/example/events.cpp b/src/example/events.cpp new file mode 100644 index 0000000..e6d91aa --- /dev/null +++ b/src/example/events.cpp @@ -0,0 +1,113 @@ +#include <iostream> + +#include <crepe/ComponentManager.h> +#include <crepe/system/ScriptSystem.h> +#include <crepe/util/Log.h> + +#include <crepe/api/BehaviorScript.h> +#include <crepe/api/Config.h> +#include <crepe/api/Event.h> +#include <crepe/api/EventManager.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/IKeyListener.h> +#include <crepe/api/IMouseListener.h> +#include <crepe/api/KeyCodes.h> +#include <crepe/api/Script.h> +#include <crepe/api/Transform.h> + +using namespace crepe; +using namespace std; + +class MyScript : public Script, public IKeyListener, public IMouseListener { + void update() { + // Retrieve component from the same GameObject this script is on + Transform & test = get_component<Transform>(); + dbg_logf("Transform(%.2f, %.2f)", test.position.x, test.position.y); + } + + bool on_key_pressed(const KeyPressEvent & event) override { + std::cout << "KeyPressed function" << std::endl; + this->deactivate_keys(); + return false; + } + bool on_key_released(const KeyReleaseEvent & event) override { + std::cout << "KeyRelease function" << std::endl; + return false; + } + bool on_mouse_clicked(const MouseClickEvent & event) override { + std::cout << "MouseClick function" << std::endl; + return false; + } + bool on_mouse_pressed(const MousePressEvent & event) override { + std::cout << "MousePress function" << std::endl; + return false; + } + bool on_mouse_released(const MouseReleaseEvent & event) override { + std::cout << "MouseRelease function" << std::endl; + return false; + } + bool on_mouse_moved(const MouseMoveEvent & event) override { + std::cout << "MouseMove function" << std::endl; + return false; + } +}; +class TestKeyListener : public IKeyListener { +public: + bool on_key_pressed(const KeyPressEvent & event) override { + std::cout << "TestKeyListener: Key Pressed - Code: " + << static_cast<int>(event.key) << std::endl; + return true; // Return true if the listener should remain active + } + bool on_key_released(const KeyReleaseEvent & event) override { + std::cout << "TestKeyListener: Key Released - Code: " + << static_cast<int>(event.key) << std::endl; + return true; + } +}; +int main() { + EventManager & evmgr = EventManager::get_instance(); + ComponentManager mgr{}; + ScriptSystem sys{mgr}; + + // two events to trigger + KeyPressEvent key_press; + key_press.key = Keycode::A; + key_press.repeat = 0; + MouseClickEvent click_event; + click_event.button = MouseButton::LEFT_MOUSE; + click_event.mouse_x = 100; + click_event.mouse_y = 200; + // queue events to test queue + evmgr.queue_event<KeyPressEvent>(std::move(key_press), 0); + evmgr.queue_event<MouseClickEvent>(std::move(click_event), 0); + { + TestKeyListener test_listener; + test_listener.set_channel(1); + auto obj = mgr.new_object("name", "tag", Vector2{1.2, 3.4}, 0, 1); + obj.add_component<BehaviorScript>().set_script<MyScript>(); + + sys.update(); + + // Trigger the events while `testListener` is in scope + evmgr.trigger_event<KeyPressEvent>(key_press, 1); + evmgr.trigger_event(MouseClickEvent{ + .mouse_x = 100, + .mouse_y = 100, + .button = MouseButton::LEFT_MOUSE, + },1); + } + // custom lambda event handler + EventHandler<KeyPressEvent> event_handler = [](const KeyPressEvent & e) { + std::cout << "lambda test" << std::endl; + return false; + }; + evmgr.subscribe<KeyPressEvent>(std::move(event_handler), 0); + // testing trigger with testListener not in scope (unsubscribed) + evmgr.trigger_event<KeyPressEvent>(key_press, 0); + evmgr.trigger_event<MouseClickEvent>(click_event, 0); + // dispatching queued events + evmgr.dispatch_events(); + + evmgr.unsubscribe<KeyPressEvent>(event_handler, 0); + return EXIT_SUCCESS; +} diff --git a/src/example/game.cpp b/src/example/game.cpp new file mode 100644 index 0000000..a557be7 --- /dev/null +++ b/src/example/game.cpp @@ -0,0 +1,88 @@ +#include <crepe/api/GameObject.h> +#include <crepe/api/Transform.h> +#include <crepe/api/Rigidbody.h> +#include <crepe/api/BoxCollider.h> +#include <crepe/api/Camera.h> +#include <crepe/api/Script.h> +#include <crepe/api/Color.h> +#include <crepe/api/Sprite.h> +#include <crepe/api/Texture.h> +#include <crepe/api/Vector2.h> +#include <crepe/api/Event.h> +#include <crepe/api/EventManager.h> +#include <crepe/api/LoopManager.h> + +using namespace crepe; + +using namespace std; + +class MyScript : public Script { + static bool oncollision(const CollisionEvent& test) { + std::cout << "test collision: " << test.info.first.collider.game_object_id << std::endl; + return true; + } + void init() { + EventManager::get_instance().subscribe<CollisionEvent>(oncollision, this->get_game_object_id()); + } + void update() { + // Retrieve component from the same GameObject this script is on + } +}; + +class ConcreteScene1 : public Scene { +public: + using Scene::Scene; + + void load_scene() { + ComponentManager & mgr = this->component_manager; + Color color(0, 0, 0, 0); + + double screen_size_width = 640; + double screen_size_height = 480; + double world_collider = 1000; + //define playable world + GameObject world = mgr.new_object("Name", "Tag", Vector2{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, + .constraints = {0, 0, 0}, + .use_gravity = false, + .bounce = false, + .offset = {0,0} + }); + world.add_component<BoxCollider>(Vector2{0, 0-(screen_size_height/2+world_collider/2)}, world_collider, world_collider);; // Top + world.add_component<BoxCollider>(Vector2{0, screen_size_height/2+world_collider/2}, world_collider, world_collider); // Bottom + world.add_component<BoxCollider>(Vector2{0-(screen_size_width/2+world_collider/2), 0}, world_collider, world_collider); // Left + world.add_component<BoxCollider>(Vector2{screen_size_width/2+world_collider/2, 0}, world_collider, world_collider); // right + + + GameObject game_object1 = mgr.new_object("Name", "Tag", Vector2{screen_size_width/2, screen_size_height/2}, 0, 1); + game_object1.add_component<Rigidbody>(Rigidbody::Data{ + .mass = 1, + .gravity_scale = 0.01, + .body_type = Rigidbody::BodyType::DYNAMIC, + .linear_velocity = {1,0}, + .constraints = {0, 0, 0}, + .use_gravity = true, + .bounce = true, + .elastisity = 1, + .offset = {0,0}, + }); + game_object1.add_component<BoxCollider>(Vector2{0, 0}, 20, 20); + game_object1.add_component<BehaviorScript>().set_script<MyScript>(); + game_object1.add_component<Sprite>( + make_shared<Texture>("/home/jaro/crepe/asset/texture/green_square.png"), color, + FlipSettings{true, true}); + game_object1.add_component<Camera>(Color::WHITE); + } +}; + +int main(int argc, char * argv[]) { + + LoopManager gameloop; + gameloop.scene_manager.add_scene<ConcreteScene1>("scene1"); + gameloop.scene_manager.load_next_scene(); + gameloop.start(); + return 0; +} diff --git a/src/example/rendering.cpp b/src/example/rendering.cpp new file mode 100644 index 0000000..9e3c8cc --- /dev/null +++ b/src/example/rendering.cpp @@ -0,0 +1,59 @@ +#include "api/Camera.h" +#include <crepe/ComponentManager.h> +#include <crepe/api/GameObject.h> +#include <crepe/system/RenderSystem.h> +#include <crepe/util/Log.h> + +#include <crepe/api/AssetManager.h> +#include <crepe/api/Color.h> +#include <crepe/api/Sprite.h> +#include <crepe/api/Texture.h> +#include <crepe/api/Transform.h> +#include <crepe/api/Vector2.h> + +#include <chrono> +#include <memory> + +using namespace std; +using namespace crepe; + +int main() { + dbg_trace(); + + ComponentManager mgr{}; + RenderSystem sys{mgr}; + + GameObject obj = mgr.new_object("name", "tag", Vector2{0, 0}, 1, 1); + GameObject obj1 = mgr.new_object("name", "tag", Vector2{500, 0}, 1, 0.1); + GameObject obj2 = mgr.new_object("name", "tag", Vector2{800, 0}, 1, 0.1); + + // Normal adding components + { + Color color(0, 0, 0, 0); + obj.add_component<Sprite>(make_shared<Texture>("../asset/texture/green_square.png"), color, + FlipSettings{false, false}); + obj.add_component<Camera>(Color::RED); + } + { + Color color(0, 0, 0, 0); + obj1.add_component<Sprite>(make_shared<Texture>("../asset/texture/green_square.png"), color, + FlipSettings{true, true}); + } + + /* + { + Color color(0, 0, 0, 0); + auto img = mgr.cache<Texture>("../asset/texture/second.png"); + obj2.add_component<Sprite>(img, color, FlipSettings{true, true}); + } + */ + + sys.update(); + /* + + auto start = std::chrono::steady_clock::now(); + while (std::chrono::steady_clock::now() - start < std::chrono::seconds(5)) { + sys.update(); + } + */ +} diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index d310f6a..c65940b 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -1,4 +1,5 @@ target_sources(test_main PUBLIC + CollisionTest.cpp main.cpp PhysicsTest.cpp ScriptTest.cpp diff --git a/src/test/CollisionTest.cpp b/src/test/CollisionTest.cpp new file mode 100644 index 0000000..c175eb4 --- /dev/null +++ b/src/test/CollisionTest.cpp @@ -0,0 +1,300 @@ +#include <cmath> +#include <cstddef> +#include <gtest/gtest.h> + +#define private public +#define protected public + +#include <crepe/ComponentManager.h> +#include <crepe/api/Event.h> +#include <crepe/api/EventManager.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/Rigidbody.h> +#include <crepe/api/Script.h> +#include <crepe/api/Transform.h> +#include <crepe/system/CollisionSystem.h> +#include <crepe/system/ScriptSystem.h> +#include <crepe/types.h> +#include <crepe/util/Log.h> + +using namespace std; +using namespace std::chrono_literals; +using namespace crepe; +using namespace testing; + +class CollisionHandler : public Script { +public: + int box_id; + EventManager & evmgr = EventManager::get_instance(); + function<void(const CollisionEvent& ev)> test_fn = [](const CollisionEvent & ev) { }; + + CollisionHandler(int box_id) { + this->box_id = box_id; + } + + bool on_collision(const CollisionEvent& ev) { + Log::logf("Box {} script on_collision()", box_id); + test_fn(ev); + return true; + } + + void init() { + //Log::logf("Box {} script init()", box_id); + + // TODO: this should be built into script + evmgr.subscribe<CollisionEvent>([this](const CollisionEvent & ev) { + return this->on_collision(ev); + }, this->get_game_object_id()); + } +}; + +class CollisionTest : public Test { +public: + ComponentManager mgr; + CollisionSystem collision_sys{mgr}; + ScriptSystem script_sys{mgr}; + + GameObject world = mgr.new_object("world","",{50,50}); + GameObject game_object1 = mgr.new_object("object1", "", { 50, 50}); + GameObject game_object2 = mgr.new_object("object2", "", { 50, 30}); + + CollisionHandler * script_object1_ref = nullptr; + CollisionHandler * script_object2_ref = nullptr; + + void SetUp() override { + world.add_component<Rigidbody>(Rigidbody::Data{ + // TODO: remove unrelated properties: + .body_type = Rigidbody::BodyType::STATIC, + .bounce = false, + .offset = {0,0}, + }); + // Create a box with an inner size of 10x10 units + world.add_component<BoxCollider>(Vector2{0, -100}, 100, 100); // Top + world.add_component<BoxCollider>(Vector2{0, 100}, 100, 100); // Bottom + world.add_component<BoxCollider>(Vector2{-100, 0}, 100, 100); // Left + world.add_component<BoxCollider>(Vector2{100, 0}, 100, 100); // right + + game_object1.add_component<Rigidbody>(Rigidbody::Data{ + .mass = 1, + .gravity_scale = 0.01, + .body_type = Rigidbody::BodyType::DYNAMIC, + .linear_velocity = {0,0}, + .constraints = {0, 0, 0}, + .use_gravity = true, + .bounce = true, + .elastisity = 1, + .offset = {0,0}, + }); + game_object1.add_component<BoxCollider>(Vector2{0, 0}, 10, 10); + BehaviorScript & script_object1 = game_object1.add_component<BehaviorScript>().set_script<CollisionHandler>(1); + script_object1_ref = static_cast<CollisionHandler*>(script_object1.script.get()); + ASSERT_NE(script_object1_ref, nullptr); + + game_object2.add_component<Rigidbody>(Rigidbody::Data{ + .mass = 1, + .gravity_scale = 0.01, + .body_type = Rigidbody::BodyType::DYNAMIC, + .linear_velocity = {0,0}, + .constraints = {0, 0, 0}, + .use_gravity = true, + .bounce = true, + .elastisity = 1, + .offset = {0,0}, + }); + game_object2.add_component<BoxCollider>(Vector2{0, 0}, 10, 10); + BehaviorScript & script_object2 = game_object2.add_component<BehaviorScript>().set_script<CollisionHandler>(2); + script_object2_ref = static_cast<CollisionHandler*>(script_object2.script.get()); + ASSERT_NE(script_object2_ref, nullptr); + + // Ensure Script::init() is called on all BehaviorScript instances + script_sys.update(); + } +}; + +// 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.first.collider.game_object_id, 1); +// }; +// script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +// collision_happend = true; +// EXPECT_EQ(ev.info.first.collider.game_object_id, 2); +// }; +// EXPECT_FALSE(collision_happend); +// collision_sys.update(); +// EXPECT_FALSE(collision_happend); +// } + +// 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.first.collider.game_object_id, 1); +// EXPECT_EQ(ev.info.move_back_value.x, 10); +// EXPECT_EQ(ev.info.move_back_value.y, 10); +// EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::BOTH); +// }; +// script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +// collision_happend = true; +// EXPECT_EQ(ev.info.first.collider.game_object_id, 2); +// EXPECT_EQ(ev.info.move_back_value.x, 10); +// EXPECT_EQ(ev.info.move_back_value.y, 10); +// EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::BOTH); +// }; +// EXPECT_FALSE(collision_happend); +// Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +// tf.position = {50,30}; +// collision_sys.update(); +// EXPECT_TRUE(collision_happend); +// } + +// 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.first.collider.game_object_id, 1); +// EXPECT_EQ(ev.info.move_back_value.x, -5); +// EXPECT_EQ(ev.info.move_back_value.y, 0); +// EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::X_DIRECTION); +// }; +// script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +// collision_happend = true; +// EXPECT_EQ(ev.info.first.collider.game_object_id, 2); +// EXPECT_EQ(ev.info.move_back_value.x, 5); +// EXPECT_EQ(ev.info.move_back_value.y, 0); +// EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::X_DIRECTION); +// }; +// EXPECT_FALSE(collision_happend); +// Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +// tf.position = {45,30}; +// collision_sys.update(); +// EXPECT_TRUE(collision_happend); +// } + +// 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.first.collider.game_object_id, 1); +// EXPECT_EQ(ev.info.move_back_value.x, 0); +// EXPECT_EQ(ev.info.move_back_value.y, -5); +// EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::Y_DIRECTION); +// }; +// script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +// collision_happend = true; +// EXPECT_EQ(ev.info.first.collider.game_object_id, 2); +// EXPECT_EQ(ev.info.move_back_value.x, 0); +// EXPECT_EQ(ev.info.move_back_value.y, 5); +// EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::Y_DIRECTION); +// }; +// EXPECT_FALSE(collision_happend); +// Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +// tf.position = {50,25}; +// collision_sys.update(); +// EXPECT_TRUE(collision_happend); +// } + +// 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.first.collider.game_object_id, 1); +// EXPECT_EQ(ev.info.move_back_value.x, 10); +// EXPECT_EQ(ev.info.move_back_value.y, 10); +// EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::BOTH); +// }; +// script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +// collision_happend = true; +// EXPECT_EQ(ev.info.first.collider.game_object_id, 2); +// EXPECT_EQ(ev.info.move_back_value.x, 10); +// EXPECT_EQ(ev.info.move_back_value.y, 10); +// EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::BOTH); +// }; +// EXPECT_FALSE(collision_happend); +// Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +// tf.position = {50,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.linear_velocity = {10,10}; +// collision_sys.update(); +// EXPECT_TRUE(collision_happend); +// } + +// 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.first.collider.game_object_id, 1); +// EXPECT_EQ(ev.info.move_back_value.x, -5); +// EXPECT_EQ(ev.info.move_back_value.y, -5); +// EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::X_DIRECTION); +// }; +// script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +// collision_happend = true; +// EXPECT_EQ(ev.info.first.collider.game_object_id, 2); +// EXPECT_EQ(ev.info.move_back_value.x, 5); +// EXPECT_EQ(ev.info.move_back_value.y, 5); +// EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::X_DIRECTION); +// }; +// 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.linear_velocity = {10,10}; +// collision_sys.update(); +// EXPECT_TRUE(collision_happend); +// } + +// 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.first.collider.game_object_id, 1); +// EXPECT_EQ(ev.info.move_back_value.x, -5); +// EXPECT_EQ(ev.info.move_back_value.y, -5); +// EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::Y_DIRECTION); +// }; +// script_object2_ref->test_fn = [&collision_happend](const CollisionEvent & ev) { +// collision_happend = true; +// EXPECT_EQ(ev.info.first.collider.game_object_id, 2); +// EXPECT_EQ(ev.info.move_back_value.x, 5); +// EXPECT_EQ(ev.info.move_back_value.y, 5); +// EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::Y_DIRECTION); +// }; +// EXPECT_FALSE(collision_happend); +// Transform & tf = this->mgr.get_components_by_id<Transform>(1).front().get(); +// tf.position = {50,25}; +// 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.linear_velocity = {10,10}; +// collision_sys.update(); +// EXPECT_TRUE(collision_happend); +// } + + +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.first.collider.game_object_id, 1); + EXPECT_EQ(ev.info.move_back_value.x, 10); + EXPECT_EQ(ev.info.move_back_value.y, 10); + EXPECT_EQ(ev.info.move_back_direction, crepe::CollisionSystem::Direction::BOTH); + }; + script_object2_ref->test_fn = [&collision_happend](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 = {50,30}; + Rigidbody & rg2 = this->mgr.get_components_by_id<Rigidbody>(2).front().get(); + rg2.data.body_type = crepe::Rigidbody::BodyType::STATIC; + collision_sys.update(); + EXPECT_TRUE(collision_happend); +} diff --git a/src/test/main.cpp b/src/test/main.cpp index 241015d..f792239 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -9,7 +9,7 @@ int main(int argc, char ** argv) { InitGoogleTest(&argc, argv); auto & cfg = Config::get_instance(); - cfg.log.level = Log::Level::ERROR; + cfg.log.level = Log::Level::INFO; return RUN_ALL_TESTS(); } |