diff options
author | JAROWMR <jarorutjes07@gmail.com> | 2024-12-07 15:24:51 +0100 |
---|---|---|
committer | JAROWMR <jarorutjes07@gmail.com> | 2024-12-07 15:24:51 +0100 |
commit | 478f5f6232c46e1a60c023bf5dcbe2a934f42d84 (patch) | |
tree | 7788dc9ae1f340d30565a92c38aa87d962cb06fe /src | |
parent | fdb4c99e139a264d4e15e6913a3756fc6cccb2f2 (diff) | |
parent | 33a072db28d71ba65e59f9491abd42dbf9695fc4 (diff) |
pulled AI
Diffstat (limited to 'src')
-rw-r--r-- | src/crepe/api/AI.cpp | 7 | ||||
-rw-r--r-- | src/crepe/api/AI.h | 69 | ||||
-rw-r--r-- | src/crepe/api/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/crepe/api/LoopManager.cpp | 6 | ||||
-rw-r--r-- | src/crepe/api/Rigidbody.h | 2 | ||||
-rw-r--r-- | src/crepe/api/Script.h | 1 | ||||
-rw-r--r-- | src/crepe/api/Vector2.h | 24 | ||||
-rw-r--r-- | src/crepe/api/Vector2.hpp | 48 | ||||
-rw-r--r-- | src/crepe/system/AISystem.cpp | 170 | ||||
-rw-r--r-- | src/crepe/system/AISystem.h | 26 | ||||
-rw-r--r-- | src/crepe/system/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/crepe/system/CollisionSystem.cpp | 13 | ||||
-rw-r--r-- | src/example/AITest.cpp | 81 | ||||
-rw-r--r-- | src/example/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/example/game.cpp | 4 | ||||
-rw-r--r-- | src/test/Vector2Test.cpp | 148 |
16 files changed, 602 insertions, 2 deletions
diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp new file mode 100644 index 0000000..d785bb5 --- /dev/null +++ b/src/crepe/api/AI.cpp @@ -0,0 +1,7 @@ +#include "AI.h" + +namespace crepe { + +AI::AI(game_object_id_t id, float max_force) : Component(id), max_force(max_force) {} + +} // namespace crepe diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h new file mode 100644 index 0000000..35b8998 --- /dev/null +++ b/src/crepe/api/AI.h @@ -0,0 +1,69 @@ +#pragma once + +#include "Component.h" +#include "types.h" + +namespace crepe { + +class AI : public Component { +public: + enum BehaviorType { + NONE = 0x00000, + SEEK = 0x00002, + FLEE = 0x00004, + ARRIVE = 0x00008, + PATH_FOLLOW = 0x00010, + }; + +public: + AI(game_object_id_t id, float max_force); + + bool on(BehaviorType behavior) const { return (flags & behavior) == behavior; } + void seek_on() { flags |= SEEK; } + void seek_off() { + if (on(SEEK)) flags ^= SEEK; + } + void flee_on() { flags |= FLEE; } + void flee_off() { + if (on(FLEE)) flags ^= FLEE; + } + void arrive_on() { flags |= ARRIVE; } + void arrive_off() { + if (on(ARRIVE)) flags ^= ARRIVE; + } + void path_follow_on() { flags |= PATH_FOLLOW; } + void path_follow_off() { + if (on(PATH_FOLLOW)) flags ^= PATH_FOLLOW; + } + + void add_path_node(vec2 node) { path.push_back(node); } + +public: + float max_force; + + // The target to seek or arrive at + vec2 seek_target; + // The target to flee from + vec2 flee_target; + // The distance at which the entity will start to flee from the target + float square_flee_panic_distance = 200.0f * 200.0f; + // The deceleration rate for the arrive behavior (higher values will make the entity decelerate faster (less overshoot)) + float arrive_deceleration = 40.0f; + // The path to follow + std::vector<vec2> path; + // The distance from the path node at which the entity will move to the next node + float path_node_distance = 400.0f; + // Looping behavior for the path + bool path_loop = true; + +private: + // The flags for the behaviors + int flags = 0; + // The current path index + size_t path_index = 0; + + // The AISystem is the only class that can access the private members of AI + friend class AISystem; +}; + +} // namespace crepe diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 593c4e6..f869d88 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -24,6 +24,7 @@ target_sources(crepe PUBLIC Script.cpp Button.cpp UIObject.cpp + AI.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -58,4 +59,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Asset.h Button.h UIObject.h + AI.h ) diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index 044f096..fec9f51 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -1,3 +1,5 @@ +#include "../facade/SDLContext.h" +#include "../system/AISystem.h" #include "../system/AnimatorSystem.h" #include "../system/CollisionSystem.h" #include "../system/InputSystem.h" @@ -20,6 +22,7 @@ LoopManager::LoopManager() { this->load_system<RenderSystem>(); this->load_system<ScriptSystem>(); this->load_system<InputSystem>(); + this->load_system<AISystem>(); } void LoopManager::process_input() { this->get_system<InputSystem>().update(); } @@ -35,6 +38,7 @@ void LoopManager::fixed_update() { EventManager & ev = this->mediator.event_manager; ev.dispatch_events(); this->get_system<ScriptSystem>().update(); + this->get_system<AISystem>().update(); this->get_system<PhysicsSystem>().update(); this->get_system<CollisionSystem>().update(); } @@ -75,4 +79,4 @@ void LoopManager::render() { this->get_system<RenderSystem>().update(); } -void LoopManager::update() {} +void LoopManager::update() { this->get_system<AnimatorSystem>().update(); } diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h index 40c6bf1..f641fff 100644 --- a/src/crepe/api/Rigidbody.h +++ b/src/crepe/api/Rigidbody.h @@ -79,7 +79,7 @@ public: //! Linear velocity of the object (speed and direction). vec2 linear_velocity; //! Maximum linear velocity of the object. This limits the object's speed. - vec2 max_linear_velocity = {INFINITY, INFINITY}; + float max_linear_velocity = INFINITY; //! Linear velocity coefficient. This scales the object's velocity for adjustment or damping. vec2 linear_velocity_coefficient = {1, 1}; //! \} diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index d99ab0e..e351e6a 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -7,6 +7,7 @@ #include "../system/CollisionSystem.h" #include "../types.h" #include "../util/OptionalRef.h" +#include "system/CollisionSystem.h" namespace crepe { diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h index c278c87..bf9d124 100644 --- a/src/crepe/api/Vector2.h +++ b/src/crepe/api/Vector2.h @@ -66,6 +66,30 @@ struct Vector2 { //! Checks if this vector is not equal to another vector. bool operator!=(const Vector2<T> & other) const; + + //! Truncates the vector to a maximum length. + void truncate(T max); + + //! Normalizes the vector (resulting in vector with a length of 1). + void normalize(); + + //! Returns the length of the vector. + T length() const; + + //! Returns the squared length of the vector. + T length_squared() const; + + //! Returns the dot product (inwendig product) of this vector and another vector. + T dot(const Vector2<T> & other) const; + + //! Returns the distance between this vector and another vector. + T distance(const Vector2<T> & other) const; + + //! Returns the squared distance between this vector and another vector. + T distance_squared(const Vector2<T> & other) const; + + //! Returns the perpendicular vector to this vector. + Vector2 perpendicular() const; }; } // namespace crepe diff --git a/src/crepe/api/Vector2.hpp b/src/crepe/api/Vector2.hpp index cad15f8..ff53cb0 100644 --- a/src/crepe/api/Vector2.hpp +++ b/src/crepe/api/Vector2.hpp @@ -1,5 +1,7 @@ #pragma once +#include <cmath> + #include "Vector2.h" namespace crepe { @@ -115,4 +117,50 @@ bool Vector2<T>::operator!=(const Vector2<T> & other) const { return !(*this == other); } +template <class T> +void Vector2<T>::truncate(T max) { + if (length() > max) { + normalize(); + *this *= max; + } +} + +template <class T> +void Vector2<T>::normalize() { + T len = length(); + if (len > 0) { + *this /= len; + } +} + +template <class T> +T Vector2<T>::length() const { + return std::sqrt(x * x + y * y); +} + +template <class T> +T Vector2<T>::length_squared() const { + return x * x + y * y; +} + +template <class T> +T Vector2<T>::dot(const Vector2<T> & other) const { + return x * other.x + y * other.y; +} + +template <class T> +T Vector2<T>::distance(const Vector2<T> & other) const { + return (*this - other).length(); +} + +template <class T> +T Vector2<T>::distance_squared(const Vector2<T> & other) const { + return (*this - other).length_squared(); +} + +template <class T> +Vector2<T> Vector2<T>::perpendicular() const { + return {-y, x}; +} + } // namespace crepe diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp new file mode 100644 index 0000000..55dc14c --- /dev/null +++ b/src/crepe/system/AISystem.cpp @@ -0,0 +1,170 @@ +#include <algorithm> +#include <cmath> + +#include "api/LoopTimer.h" +#include "api/Rigidbody.h" +#include "api/Transform.h" +#include "manager/ComponentManager.h" +#include "manager/Mediator.h" + +#include "AISystem.h" +#include "types.h" + +using namespace crepe; + +void AISystem::update() { + const Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + RefVector<AI> ai_components = mgr.get_components_by_type<AI>(); + + double dt = LoopTimer::get_instance().get_delta_time(); + + for (AI & ai : ai_components) { + RefVector<Rigidbody> rigidbodies + = mgr.get_components_by_id<Rigidbody>(ai.game_object_id); + Rigidbody & rigidbody = rigidbodies.front().get(); + + vec2 force = this->calculate(ai); + vec2 acceleration = force / rigidbody.data.mass; + rigidbody.data.linear_velocity += acceleration * dt; + } +} + +vec2 AISystem::calculate(AI & ai) { + vec2 force; + + if (ai.on(AI::BehaviorType::FLEE)) { + vec2 force_to_add = this->flee(ai); + + if (!this->accumulate_force(ai, force, force_to_add)) { + return force; + } + } + if (ai.on(AI::BehaviorType::ARRIVE)) { + vec2 force_to_add = this->arrive(ai); + + if (!this->accumulate_force(ai, force, force_to_add)) { + return force; + } + } + if (ai.on(AI::BehaviorType::SEEK)) { + vec2 force_to_add = this->seek(ai); + + if (!this->accumulate_force(ai, force, force_to_add)) { + return force; + } + } + if (ai.on(AI::BehaviorType::PATH_FOLLOW)) { + vec2 force_to_add = this->path_follow(ai); + + if (!this->accumulate_force(ai, force, force_to_add)) { + return force; + } + } + + return force; +} + +bool AISystem::accumulate_force(AI & ai, vec2 & running_total, vec2 force_to_add) { + float magnitude = running_total.length(); + float magnitude_remaining = ai.max_force - magnitude; + + if (magnitude_remaining <= 0.0f) { + return false; + } + + float magnitude_to_add = force_to_add.length(); + if (magnitude_to_add < magnitude_remaining) { + running_total += force_to_add; + } else { + force_to_add.normalize(); + running_total += force_to_add * magnitude_remaining; + } + + return true; +} + +vec2 AISystem::seek(const AI & ai) { + const Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id); + Transform & transform = transforms.front().get(); + RefVector<Rigidbody> rigidbodies = mgr.get_components_by_id<Rigidbody>(ai.game_object_id); + Rigidbody & rigidbody = rigidbodies.front().get(); + + vec2 desired_velocity = ai.seek_target - transform.position; + desired_velocity.normalize(); + desired_velocity *= rigidbody.data.max_linear_velocity; + + return desired_velocity - rigidbody.data.linear_velocity; +} + +vec2 AISystem::flee(const AI & ai) { + const Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id); + Transform & transform = transforms.front().get(); + RefVector<Rigidbody> rigidbodies = mgr.get_components_by_id<Rigidbody>(ai.game_object_id); + Rigidbody & rigidbody = rigidbodies.front().get(); + + vec2 desired_velocity = transform.position - ai.flee_target; + if (desired_velocity.length_squared() > ai.square_flee_panic_distance) { + return vec2{0, 0}; + } + + desired_velocity.normalize(); + desired_velocity *= rigidbody.data.max_linear_velocity; + + return desired_velocity - rigidbody.data.linear_velocity; +} + +vec2 AISystem::arrive(const AI & ai) { + const Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id); + Transform & transform = transforms.front().get(); + RefVector<Rigidbody> rigidbodies = mgr.get_components_by_id<Rigidbody>(ai.game_object_id); + Rigidbody & rigidbody = rigidbodies.front().get(); + + vec2 to_target = ai.seek_target - transform.position; + float distance = to_target.length(); + if (distance > 0.0f) { + float speed = distance / ai.arrive_deceleration; + speed = std::min(speed, rigidbody.data.max_linear_velocity.length()); + vec2 desired_velocity = to_target * (speed / distance); + + return desired_velocity - rigidbody.data.linear_velocity; + } + + return vec2{0, 0}; +} + +vec2 AISystem::path_follow(AI & ai) { + const Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id); + Transform & transform = transforms.front().get(); + RefVector<Rigidbody> rigidbodies = mgr.get_components_by_id<Rigidbody>(ai.game_object_id); + Rigidbody & rigidbody = rigidbodies.front().get(); + + if (ai.path.empty()) { + return vec2{0, 0}; + } + + vec2 to_target = ai.path.at(ai.path_index) - transform.position; + if (to_target.length_squared() > ai.path_node_distance * ai.path_node_distance) { + ai.seek_target = ai.path.at(ai.path_index); + } else { + ai.path_index++; + if (ai.path_index >= ai.path.size()) { + if (ai.path_loop) { + ai.path_index = 0; + } else { + ai.path_index = ai.path.size() - 1; + return this->arrive(ai); + } + } + } + + return this->seek(ai); +} diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h new file mode 100644 index 0000000..27861d9 --- /dev/null +++ b/src/crepe/system/AISystem.h @@ -0,0 +1,26 @@ +#pragma once + +#include "api/AI.h" + +#include "System.h" +#include "types.h" + +namespace crepe { + +class AISystem : public System { +public: + using System::System; + + void update() override; + +private: + vec2 calculate(AI & ai); + bool accumulate_force(AI & ai, vec2 & running_total, vec2 force_to_add); + + vec2 seek(const AI & ai); + vec2 flee(const AI & ai); + vec2 arrive(const AI & ai); + vec2 path_follow(AI & ai); +}; + +} // namespace crepe diff --git a/src/crepe/system/CMakeLists.txt b/src/crepe/system/CMakeLists.txt index 95f6e33..7de5198 100644 --- a/src/crepe/system/CMakeLists.txt +++ b/src/crepe/system/CMakeLists.txt @@ -7,6 +7,7 @@ target_sources(crepe PUBLIC RenderSystem.cpp AnimatorSystem.cpp InputSystem.cpp + AISystem.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -17,4 +18,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES RenderSystem.h AnimatorSystem.h InputSystem.h + AISystem.h ) diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp index 44a0431..6b1954e 100644 --- a/src/crepe/system/CollisionSystem.cpp +++ b/src/crepe/system/CollisionSystem.cpp @@ -182,7 +182,11 @@ CollisionSystem::collision_handler(CollisionInternal & data1, CollisionInternal vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, data2.rigidbody); resolution = this->get_circle_box_resolution(collider1, collider2, collider_pos1, +<<<<<<< HEAD collider_pos2); +======= + collider_pos2, false); +>>>>>>> 33a072db28d71ba65e59f9491abd42dbf9695fc4 break; } } @@ -268,7 +272,12 @@ vec2 CollisionSystem::get_circle_circle_resolution(const CircleCollider & circle vec2 CollisionSystem::get_circle_box_resolution(const CircleCollider & circle_collider, const BoxCollider & box_collider, const vec2 & circle_position, +<<<<<<< HEAD const vec2 & box_position) const { +======= + const vec2 & box_position, + bool inverse) const { +>>>>>>> 33a072db28d71ba65e59f9491abd42dbf9695fc4 vec2 delta = circle_position - box_position; // Compute half-dimensions of the box @@ -290,7 +299,11 @@ vec2 CollisionSystem::get_circle_box_resolution(const CircleCollider & circle_co // Compute penetration depth float penetration_depth = circle_collider.radius - distance; +<<<<<<< HEAD +======= + if (inverse) collision_normal = -collision_normal; +>>>>>>> 33a072db28d71ba65e59f9491abd42dbf9695fc4 // Compute the resolution vector vec2 resolution = collision_normal * penetration_depth; diff --git a/src/example/AITest.cpp b/src/example/AITest.cpp new file mode 100644 index 0000000..d12a99a --- /dev/null +++ b/src/example/AITest.cpp @@ -0,0 +1,81 @@ +#include <SDL2/SDL_timer.h> +#include <chrono> +#include <crepe/api/AI.h> +#include <crepe/api/BehaviorScript.h> +#include <crepe/api/Camera.h> +#include <crepe/api/Color.h> +#include <crepe/api/GameObject.h> +#include <crepe/api/LoopManager.h> +#include <crepe/api/Rigidbody.h> +#include <crepe/api/Scene.h> +#include <crepe/api/Script.h> +#include <crepe/api/Sprite.h> +#include <crepe/api/Texture.h> +#include <crepe/manager/Mediator.h> +#include <crepe/types.h> + +using namespace crepe; +using namespace std; + +class Script1 : public Script { + bool shutdown(const ShutDownEvent & event) { + // Very dirty way of shutting down the game + throw "ShutDownEvent"; + return true; + } + + bool mousemove(const MouseMoveEvent & event) { + /*RefVector<AI> aivec = this->get_components<AI>(); + AI & ai = aivec.front().get(); + ai.flee_target + = vec2{static_cast<float>(event.mouse_x), static_cast<float>(event.mouse_y)};*/ + return true; + } + + void init() { + subscribe<ShutDownEvent>( + [this](const ShutDownEvent & ev) -> bool { return this->shutdown(ev); }); + subscribe<MouseMoveEvent>( + [this](const MouseMoveEvent & ev) -> bool { return this->mousemove(ev); }); + } +}; + +class Scene1 : public Scene { +public: + void load_scene() override { + Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + + GameObject game_object1 = mgr.new_object("", "", vec2{0, 0}, 0, 1); + GameObject game_object2 = mgr.new_object("", "", vec2{0, 0}, 0, 1); + + Texture img = Texture("asset/texture/test_ap43.png"); + game_object1.add_component<Sprite>(img, Color::MAGENTA, + Sprite::FlipSettings{false, false}, 1, 1, 195); + AI & ai = game_object1.add_component<AI>(300); + // ai.arrive_on(); + // ai.flee_on(); + ai.path_follow_on(); + ai.add_path_node(vec2{1200, 1200}); + ai.add_path_node(vec2{-1200, 1200}); + ai.add_path_node(vec2{1200, -1200}); + ai.add_path_node(vec2{-1200, -1200}); + game_object1.add_component<Rigidbody>(Rigidbody::Data{ + .mass = 0.5f, .max_linear_velocity = {50, 50}, // sqrt(21^2 + 21^2) = 30 + }); + game_object1.add_component<BehaviorScript>().set_script<Script1>(); + + game_object2.add_component<Camera>(Color::WHITE, ivec2{1080, 720}, vec2{5000, 5000}, + 1.0f); + } + + string get_name() const override { return "Scene1"; } +}; + +int main() { + LoopManager engine; + engine.add_scene<Scene1>(); + engine.start(); + + return 0; +} diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt index 8ef71bb..149c412 100644 --- a/src/example/CMakeLists.txt +++ b/src/example/CMakeLists.txt @@ -21,3 +21,4 @@ add_example(savemgr) add_example(rendering_particle) add_example(game) add_example(button) +add_example(AITest) diff --git a/src/example/game.cpp b/src/example/game.cpp index 4239c15..2b4e46f 100644 --- a/src/example/game.cpp +++ b/src/example/game.cpp @@ -66,6 +66,10 @@ class MyScript1 : public Script { //add collider switch break; } + case Keycode::Q: { + throw "Test"; + break; + } default: break; } diff --git a/src/test/Vector2Test.cpp b/src/test/Vector2Test.cpp index 17bca41..1e21af9 100644 --- a/src/test/Vector2Test.cpp +++ b/src/test/Vector2Test.cpp @@ -382,3 +382,151 @@ TEST_F(Vector2Test, NotEquals) { EXPECT_FALSE(long_vec1 != long_vec1); EXPECT_TRUE(long_vec1 != long_vec2); } + +TEST_F(Vector2Test, Truncate) { + Vector2<int> vec = {3, 4}; + vec.truncate(3); + EXPECT_EQ(vec.x, 0); + EXPECT_EQ(vec.y, 0); + + Vector2<double> vec2 = {3.0, 4.0}; + vec2.truncate(3.0); + EXPECT_FLOAT_EQ(vec2.x, 1.8); + EXPECT_FLOAT_EQ(vec2.y, 2.4); + + Vector2<long> vec3 = {3, 4}; + vec3.truncate(3); + EXPECT_EQ(vec3.x, 0); + EXPECT_EQ(vec3.y, 0); + + Vector2<float> vec4 = {3.0f, 4.0f}; + vec4.truncate(3.0f); + EXPECT_FLOAT_EQ(vec4.x, 1.8f); + EXPECT_FLOAT_EQ(vec4.y, 2.4f); +} + +TEST_F(Vector2Test, Normalize) { + Vector2<int> vec = {3, 4}; + vec.normalize(); + EXPECT_EQ(vec.x, 0); + EXPECT_EQ(vec.y, 0); + + Vector2<double> vec2 = {3.0, 4.0}; + vec2.normalize(); + EXPECT_FLOAT_EQ(vec2.x, 0.6); + EXPECT_FLOAT_EQ(vec2.y, 0.8); + + Vector2<long> vec3 = {3, 4}; + vec3.normalize(); + EXPECT_EQ(vec3.x, 0); + EXPECT_EQ(vec3.y, 0); + + Vector2<float> vec4 = {3.0f, 4.0f}; + vec4.normalize(); + EXPECT_FLOAT_EQ(vec4.x, 0.6f); + EXPECT_FLOAT_EQ(vec4.y, 0.8f); +} + +TEST_F(Vector2Test, Length) { + Vector2<int> vec = {3, 4}; + EXPECT_EQ(vec.length(), 5); + + Vector2<double> vec2 = {3.0, 4.0}; + EXPECT_FLOAT_EQ(vec2.length(), 5.0); + + Vector2<long> vec3 = {3, 4}; + EXPECT_EQ(vec3.length(), 5); + + Vector2<float> vec4 = {3.0f, 4.0f}; + EXPECT_FLOAT_EQ(vec4.length(), 5.0f); +} + +TEST_F(Vector2Test, LengthSquared) { + Vector2<int> vec = {3, 4}; + EXPECT_EQ(vec.length_squared(), 25); + + Vector2<double> vec2 = {3.0, 4.0}; + EXPECT_FLOAT_EQ(vec2.length_squared(), 25.0); + + Vector2<long> vec3 = {3, 4}; + EXPECT_EQ(vec3.length_squared(), 25); + + Vector2<float> vec4 = {3.0f, 4.0f}; + EXPECT_FLOAT_EQ(vec4.length_squared(), 25.0f); +} + +TEST_F(Vector2Test, Dot) { + Vector2<int> vec1 = {3, 4}; + Vector2<int> vec2 = {5, 6}; + EXPECT_EQ(vec1.dot(vec2), 39); + + Vector2<double> vec3 = {3.0, 4.0}; + Vector2<double> vec4 = {5.0, 6.0}; + EXPECT_FLOAT_EQ(vec3.dot(vec4), 39.0); + + Vector2<long> vec5 = {3, 4}; + Vector2<long> vec6 = {5, 6}; + EXPECT_EQ(vec5.dot(vec6), 39); + + Vector2<float> vec7 = {3.0f, 4.0f}; + Vector2<float> vec8 = {5.0f, 6.0f}; + EXPECT_FLOAT_EQ(vec7.dot(vec8), 39.0f); +} + +TEST_F(Vector2Test, Distance) { + Vector2<int> vec1 = {1, 1}; + Vector2<int> vec2 = {4, 5}; + EXPECT_EQ(vec1.distance(vec2), 5); + + Vector2<double> vec3 = {1.0, 1.0}; + Vector2<double> vec4 = {4.0, 5.0}; + EXPECT_FLOAT_EQ(vec3.distance(vec4), 5.0); + + Vector2<long> vec5 = {1, 1}; + Vector2<long> vec6 = {4, 5}; + EXPECT_EQ(vec5.distance(vec6), 5); + + Vector2<float> vec7 = {1.0f, 1.0f}; + Vector2<float> vec8 = {4.0f, 5.0f}; + EXPECT_FLOAT_EQ(vec7.distance(vec8), 5.0f); +} + +TEST_F(Vector2Test, DistanceSquared) { + Vector2<int> vec1 = {3, 4}; + Vector2<int> vec2 = {5, 6}; + EXPECT_EQ(vec1.distance_squared(vec2), 8); + + Vector2<double> vec3 = {3.0, 4.0}; + Vector2<double> vec4 = {5.0, 6.0}; + EXPECT_FLOAT_EQ(vec3.distance_squared(vec4), 8.0); + + Vector2<long> vec5 = {3, 4}; + Vector2<long> vec6 = {5, 6}; + EXPECT_EQ(vec5.distance_squared(vec6), 8); + + Vector2<float> vec7 = {3.0f, 4.0f}; + Vector2<float> vec8 = {5.0f, 6.0f}; + EXPECT_FLOAT_EQ(vec7.distance_squared(vec8), 8.0f); +} + +TEST_F(Vector2Test, Perpendicular) { + Vector2<int> vec = {3, 4}; + Vector2<int> result = vec.perpendicular(); + EXPECT_EQ(result.x, -4); + EXPECT_EQ(result.y, 3); + + Vector2<double> vec2 = {3.0, 4.0}; + Vector2<double> result2 = vec2.perpendicular(); + EXPECT_FLOAT_EQ(result2.x, -4.0); + EXPECT_FLOAT_EQ(result2.y, 3.0); + + Vector2<long> vec3 = {3, 4}; + Vector2<long> result3 = vec3.perpendicular(); + EXPECT_EQ(result3.x, -4); + EXPECT_EQ(result3.y, 3); + + Vector2<float> vec4 = {3.0f, 4.0f}; + Vector2<float> result4 = vec4.perpendicular(); + EXPECT_FLOAT_EQ(result4.x, -4.0f); + EXPECT_FLOAT_EQ(result4.y, 3.0f); +} |