mirror of
https://github.com/orange-cpp/omath.git
synced 2026-02-13 07:03:25 +00:00
improved projectile prediction
This commit is contained in:
@@ -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 ()
|
||||
target_include_directories(uml PUBLIC include)
|
||||
@@ -5,34 +5,52 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <uml/Vector3.h>
|
||||
|
||||
|
||||
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<Vector3> CalculateViewAngles(const Vector3& origin,
|
||||
const Vector3& target,
|
||||
const Vector3& targetVelocity,
|
||||
float gravity,
|
||||
float bulletSpeed,
|
||||
float bulletGravity,
|
||||
bool inAir);
|
||||
std::optional<Vector3> 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<float> 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<float>
|
||||
MaybeCalculateProjectileLaunchPitchAngle(const Projectile& projectile,
|
||||
const Vector3& targetPosition) const;
|
||||
|
||||
[[nodiscard]]
|
||||
float ProjectileTravelTime(const Vector3& end, const Projectile& projectile,
|
||||
const float angle) const;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
@@ -10,67 +10,98 @@
|
||||
|
||||
namespace uml::prediction
|
||||
{
|
||||
|
||||
std::optional<Vector3>
|
||||
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<Vector3> 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<float>
|
||||
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<float>
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
add_executable(unit-tests UnitTestColor.cpp)
|
||||
|
||||
target_link_libraries(unit-tests PRIVATE gtest gtest_main uml)
|
||||
@@ -0,0 +1,13 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <uml/ProjectilePredictor.h>
|
||||
#include <print>
|
||||
|
||||
|
||||
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());
|
||||
}
|
||||
Reference in New Issue
Block a user