aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/crepe/api/AI.cpp7
-rw-r--r--src/crepe/api/AI.h69
-rw-r--r--src/crepe/api/CMakeLists.txt2
-rw-r--r--src/crepe/api/LoopManager.cpp6
-rw-r--r--src/crepe/api/Rigidbody.h2
-rw-r--r--src/crepe/api/Script.h1
-rw-r--r--src/crepe/api/Vector2.h24
-rw-r--r--src/crepe/api/Vector2.hpp48
-rw-r--r--src/crepe/system/AISystem.cpp170
-rw-r--r--src/crepe/system/AISystem.h26
-rw-r--r--src/crepe/system/CMakeLists.txt2
-rw-r--r--src/crepe/system/CollisionSystem.cpp13
-rw-r--r--src/example/AITest.cpp81
-rw-r--r--src/example/CMakeLists.txt1
-rw-r--r--src/example/game.cpp4
-rw-r--r--src/test/Vector2Test.cpp148
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);
+}