diff --git a/CMakeLists.txt b/CMakeLists.txt index 66812c1..2dfef16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,4 @@ add_subdirectory(source) add_subdirectory(extlibs) add_subdirectory(tests) -target_include_directories(uml PUBLIC include) - -if (BUILD_TESTS) - add_subdirectory(tests) -endif () \ No newline at end of file +target_include_directories(uml PUBLIC include) \ No newline at end of file diff --git a/include/uml/ProjectilePredictor.h b/include/uml/ProjectilePredictor.h index d7f4b31..373db72 100644 --- a/include/uml/ProjectilePredictor.h +++ b/include/uml/ProjectilePredictor.h @@ -5,34 +5,52 @@ #pragma once #include #include +#include -namespace uml -{ - class Vector3; -} - namespace uml::prediction { + struct Projectile + { + Vector3 m_origin; + float m_velocity{}; + float m_gravityMultiplier = 1.f; + }; + struct Target + { + Vector3 m_origin; + Vector3 m_vecVelocity; + bool m_IsAirborne; + }; class ProjectilePredictor { public: + explicit ProjectilePredictor(float gravityValue, + float maxTimeToTravel = 3.f, + float timeStep = 0.1f); + + [[nodiscard]] - static std::optional CalculateViewAngles(const Vector3& origin, - const Vector3& target, - const Vector3& targetVelocity, - float gravity, - float bulletSpeed, - float bulletGravity, - bool inAir); + std::optional PredictPointToAim(const Target& target, + const Projectile& projectile) const; private: + + const float m_gravity; + const float m_maxTravelTime; + const float m_timeStepSize; + [[nodiscard]] - static std::optional CalculateAimPitch(const Vector3& origin, - const Vector3& target, - float bulletSpeed, - float bulletGravity); - [[nodiscard]] static float ProjectileTravelTime(float distance, float angle, float speed); + Vector3 LinearPrediction(const Target& target, float time) const; + + [[nodiscard]] + std::optional + MaybeCalculateProjectileLaunchPitchAngle(const Projectile& projectile, + const Vector3& targetPosition) const; + + [[nodiscard]] + float ProjectileTravelTime(const Vector3& end, const Projectile& projectile, + const float angle) const; }; }; diff --git a/source/ProjectilePredictor.cpp b/source/ProjectilePredictor.cpp index 3c2f7af..477946f 100644 --- a/source/ProjectilePredictor.cpp +++ b/source/ProjectilePredictor.cpp @@ -10,67 +10,98 @@ namespace uml::prediction { - - std::optional - ProjectilePredictor::CalculateViewAngles(const Vector3 &origin, - const Vector3 &target, - const Vector3 &targetVelocity, - float gravity, - float bulletSpeed, - float bulletGravity, - bool inAir) + ProjectilePredictor::ProjectilePredictor(float gravityValue, + float maxTimeToTravel, + float timeStep) + : m_gravity(gravityValue), m_maxTravelTime(maxTimeToTravel), m_timeStepSize(timeStep) { - constexpr float maxTime = 3.0f; - constexpr float timeStep = 0.001f; - for (float time = 0.0f; time <= maxTime; time += timeStep) + } + + std::optional ProjectilePredictor::PredictPointToAim( + const Target &target, const Projectile &projectile) const + { + for (float time = 0.0f; time <= m_maxTravelTime; time += m_timeStepSize) { - auto predPos = target + targetVelocity * time; + const auto predictedTargetPosition = LinearPrediction(target, time); - if (inAir) - predPos -= Vector3(0, 0, gravity * (time * time) * 0.5); + const auto projectilePitch = + MaybeCalculateProjectileLaunchPitchAngle(projectile, predictedTargetPosition); - const auto angle = CalculateAimPitch(origin, predPos, bulletSpeed, bulletGravity); - - if (!angle.has_value()) + if (!projectilePitch.has_value()) return std::nullopt; - const auto timeToHit = ProjectileTravelTime((predPos - origin).Length2D(), *angle, bulletSpeed); - + const auto timeToHit = ProjectileTravelTime(predictedTargetPosition, + projectile, + projectilePitch.value()); if (timeToHit > time) continue; + return Vector3(); - auto viewAngles = origin.ViewAngleTo(predPos); - viewAngles.x = angle.value(); - - return viewAngles; } + + return std::nullopt; } - std::optional - ProjectilePredictor::CalculateAimPitch(const Vector3 &origin, const Vector3 &target, - float bulletSpeed, float bulletGravity) + Vector3 ProjectilePredictor::LinearPrediction(const Target &target, float time) const { - const auto delta = target - origin; + auto predicted = target.m_origin + target.m_vecVelocity * time; + if (target.m_IsAirborne) + predicted.z -= m_gravity * std::pow(time, 2.f) * 0.5f; + + return predicted; + } + + std::optional + ProjectilePredictor::MaybeCalculateProjectileLaunchPitchAngle(const Projectile &projectile, + const Vector3 &targetPosition) + const + { + const auto delta = targetPosition - projectile.m_origin; const auto distance2d = delta.Length2D(); + const auto projectileGravity = m_gravity * projectile.m_gravityMultiplier; - - float root = powf(bulletSpeed, 4) - bulletGravity * (bulletGravity * distance2d * distance2d + 2.0f * delta.z * powf(bulletSpeed, 2)); + float root = std::pow(projectile.m_velocity, 4.f) - projectileGravity * + (projectileGravity * std::pow(distance2d, 2.f) + + 2.0f * delta.z * std::pow(projectile.m_velocity, 2.f)); if (root < 0.0f) return std::nullopt; root = std::sqrt(root); - float angle = std::atan((powf(bulletSpeed, 2) - root) / (bulletGravity * distance2d)); - return -angles::RadToDeg(angle); + const float angle = std::atan((std::pow(projectile.m_velocity, 2.f) - root) + / (projectile.m_velocity * distance2d)); + + return angles::RadToDeg(angle); } - float ProjectilePredictor::ProjectileTravelTime(float distance, float angle, float speed) + float ProjectilePredictor::ProjectileTravelTime(const Vector3 &end, + const Projectile &projectile, + const float angle) const { - return std::abs(distance / (std::cos(angles::DegToRad(angle)) * speed)); + // return std::abs((end -projectile.m_origin).Length2D() / (std::cos(angles::DegToRad(angle)) * projectile.m_velocity)); + auto launchAngles = projectile.m_origin.ViewAngleTo(end); + launchAngles.x = angle; + + const auto velocity = Vector3::CreateVelocity(launchAngles, projectile.m_velocity); + auto prevProjectilePosition = projectile.m_origin; + + for (float time = 0.0f; time <= m_maxTravelTime; time += 0.0001f) + { + auto currentPos = projectile.m_origin + velocity * time; + currentPos.z -= (m_gravity * projectile.m_gravityMultiplier) + * std::pow(time, 2.f) * 0.5f; + + if (prevProjectilePosition.DistTo(end) < currentPos.DistTo(end)) + return time; + + prevProjectilePosition = currentPos; + } + + return 0; } } \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 86a22af..aaefac7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,6 +4,6 @@ project(unit-tests) file(GLOB TEST_SRC_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) include(GoogleTest) -add_executable(unit-tests ${TEST_SRC_FILES}) -target_link_libraries(unit-tests PRIVATE gtest gtest_main uml) -gtest_discover_tests(unit-tests) \ No newline at end of file +add_executable(unit-tests UnitTestColor.cpp) + +target_link_libraries(unit-tests PRIVATE gtest gtest_main uml) \ No newline at end of file diff --git a/tests/UnitTestColor.cpp b/tests/UnitTestColor.cpp index e69de29..9d26fe5 100644 --- a/tests/UnitTestColor.cpp +++ b/tests/UnitTestColor.cpp @@ -0,0 +1,13 @@ +#include +#include +#include + + +TEST(x,x) +{ + uml::prediction::Target target{.m_origin = {100, 0, 0}, .m_vecVelocity = {0,0, 0}, .m_IsAirborne = false}; + uml::prediction::Projectile proj = {.m_velocity = 600, .m_gravityMultiplier= 0.1}; + auto vel = uml::prediction::ProjectilePredictor(400).PredictPointToAim(target, proj); + + std::print("{}", vel.has_value()); +} \ No newline at end of file