refactored projectile prediction

This commit is contained in:
2024-06-09 21:14:33 +03:00
parent c600d53b20
commit 07360f4e91
16 changed files with 222 additions and 197 deletions

View File

@@ -2,6 +2,7 @@ target_sources(uml PRIVATE
Vector3.cpp
matrix.cpp
angles.cpp
ProjectilePredictor.cpp
color.cpp
Vector4.cpp)
Vector4.cpp)
add_subdirectory(prediction)

View File

@@ -1,118 +0,0 @@
//
// Created by vlad on 11/6/23.
//
#include "uml/ProjectilePredictor.h"
#include "uml/Vector3.h"
#include "uml/angles.h"
#include <cmath>
#include <cstdio>
namespace uml::prediction
{
ProjectilePredictor::ProjectilePredictor(float gravityValue,
float maxTimeToTravel,
float timeStep)
: m_gravity(gravityValue), m_maxTravelTime(maxTimeToTravel), m_timeStepSize(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 predictedTargetPosition = LinearPrediction(target, time);
const auto projectilePitch =
MaybeCalculateProjectileLaunchPitchAngle(projectile, predictedTargetPosition);
if (!projectilePitch.has_value()) [[unlikely]]
return std::nullopt;
if (!IsTargetWasHit(predictedTargetPosition, projectile, projectilePitch.value(), time))
continue;
const auto delta2d = (predictedTargetPosition - projectile.m_origin).Length2D();
const auto height = delta2d * std::tan(angles::DegToRad(projectilePitch.value()));
predictedTargetPosition.z = projectile.m_origin.z + height;
return predictedTargetPosition;
}
return std::nullopt;
}
Vector3 ProjectilePredictor::LinearPrediction(const Target &target, float time) const
{
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 bulletGravity = m_gravity * projectile.m_gravityMultiplier;
const auto delta = targetPosition - projectile.m_origin;;
const auto distance2d = delta.Length2D();
float root = std::pow(projectile.m_velocity, 4.f) - bulletGravity * (bulletGravity *
std::pow(distance2d, 2.f) + 2.0f * delta.z * std::pow(projectile.m_velocity, 2.f));
if (root < 0.0f) [[unlikely]]
return std::nullopt;
root = std::sqrt(root);
const float angle = std::atan((std::pow(projectile.m_velocity, 2.f) - root) / (bulletGravity * distance2d));
return angles::RadToDeg(angle);
}
std::optional<float> ProjectilePredictor::ProjectileTravelTime(const Vector3 &end,
const Projectile &projectile,
const float angle) const
{
auto launchAngles = projectile.m_origin.ViewAngleTo(end);
launchAngles.x = angle;
const auto velocity = Vector3::CreateVelocity(launchAngles, projectile.m_velocity);
for (float time = 0.0f; time <= m_maxTravelTime; time += m_timeStepSize)
{
auto currentPos = projectile.m_origin + velocity * time;
currentPos.z -= m_gravity * projectile.m_gravityMultiplier * std::pow(time, 2.f) * 0.5f;
if (currentPos.DistTo(end) <= 10.f)
return time;
}
return std::nullopt;
}
bool ProjectilePredictor::IsTargetWasHit(const Vector3 &end, const Projectile &projectile, const float angle,
const float time) const
{
auto launchAngles = projectile.m_origin.ViewAngleTo(end);
launchAngles.x = angle;
const auto velocity = Vector3::CreateVelocity(launchAngles, projectile.m_velocity);
auto currentPos = projectile.m_origin + velocity * time;
currentPos.z -= m_gravity * projectile.m_gravityMultiplier * std::pow(time, 2.f) * 0.5f;
return currentPos.DistTo(end) <= 10.f;
}
}

View File

@@ -177,13 +177,13 @@ namespace uml
return {x / v.x, y / v.y, z / v.z};
}
Vector3 Vector3::CreateVelocity(const Vector3 &angles, const float length)
Vector3 Vector3::CreateVelocity(const float pitch, const float yaw, const float speed)
{
return
{
std::cos(angles::DegToRad(angles.x)) * std::cos(angles::DegToRad(angles.y)) * length,
std::cos(angles::DegToRad(angles.x)) * std::sin(angles::DegToRad(angles.y)) * length,
std::sin(angles::DegToRad(angles.x)) * length,
std::cos(angles::DegreesToRadians(pitch)) * std::cos(angles::DegreesToRadians(yaw)) * speed,
std::cos(angles::DegreesToRadians(pitch)) * std::sin(angles::DegreesToRadians(yaw)) * speed,
std::sin(angles::DegreesToRadians(pitch)) * speed,
};
}
@@ -205,8 +205,8 @@ namespace uml
// Make x negative since -89 is top and 89 is bottom
return
{
-angles::RadToDeg(asinf(delta.z / distance)),
angles::RadToDeg(atan2f(delta.y, delta.x)),
-angles::RadiansToDegrees(asinf(delta.z / distance)),
angles::RadiansToDegrees(atan2f(delta.y, delta.x)),
0.f
};
}

View File

@@ -8,13 +8,13 @@
namespace uml::angles
{
float RadToDeg(float rads)
float RadiansToDegrees(const float radiands)
{
return rads * 180.f / std::numbers::pi_v<float>;
return radiands * (180.f / std::numbers::pi_v<float>);
}
float DegToRad(float degrees)
float DegreesToRadians(const float degrees)
{
return degrees * std::numbers::pi_v<float> / 180.f;
return degrees * (std::numbers::pi_v<float> / 180.f);
}
}

View File

@@ -0,0 +1 @@
target_sources(uml PRIVATE Engine.cpp Projectile.cpp Target.cpp)

View File

@@ -0,0 +1,71 @@
//
// Created by Vlad on 6/9/2024.
//
#include "uml/prediction/Engine.h"
#include <cmath>
#include <uml/angles.h>
namespace uml::prediction
{
Engine::Engine(const float gravityConstant, const float simulationTimeStep, const float maximumSimulationTime)
: m_gravityConstant(gravityConstant),
m_simulationTimeStep(simulationTimeStep),
m_maximumSimulationTime(maximumSimulationTime)
{
}
std::optional<Vector3> Engine::MaybeCalculateAimPoint(const Projectile &projectile, const Target &target) const
{
for (float time = 0.f; time < m_maximumSimulationTime; time += m_simulationTimeStep)
{
const auto predictedTargetPosition = target.PredictPosition(time, m_gravityConstant);
const auto projectilePitch = MaybeCalculateProjectileLaunchPitchAngle(projectile, predictedTargetPosition);
if (!projectilePitch.has_value()) [[unlikely]]
continue;
if (!IsProjectileReachedTarget(predictedTargetPosition, projectile, projectilePitch.value(), time))
continue;
const auto delta2d = (predictedTargetPosition - projectile.m_origin).Length2D();
const auto height = delta2d * std::tan(angles::DegreesToRadians(projectilePitch.value()));
return Vector3(predictedTargetPosition.x, predictedTargetPosition.y, projectile.m_origin.z + height);
}
return std::nullopt;
}
std::optional<float> Engine::MaybeCalculateProjectileLaunchPitchAngle(const Projectile &projectile,
const Vector3 &targetPosition) const
{
const auto bulletGravity = m_gravityConstant * projectile.m_gravityScale;
const auto delta = targetPosition - projectile.m_origin;;
const auto distance2d = delta.Length2D();
float root = std::pow(projectile.m_launchSpeed, 4.f) - bulletGravity * (bulletGravity *
std::pow(distance2d, 2.f) + 2.0f * delta.z * std::pow(projectile.m_launchSpeed, 2.f));
if (root < 0.0f) [[unlikely]]
return std::nullopt;
root = std::sqrt(root);
const float angle = std::atan((std::pow(projectile.m_launchSpeed, 2.f) - root) / (bulletGravity * distance2d));
return angles::RadiansToDegrees(angle);
}
bool Engine::IsProjectileReachedTarget(const Vector3 &targetPosition, const Projectile &projectile,
const float pitch, const float time) const
{
const auto yaw = projectile.m_origin.ViewAngleTo(targetPosition).y;
const auto projectilePosition = projectile.PredictPosition(pitch, yaw, time, m_gravityConstant);
return projectilePosition.DistTo(targetPosition) <= 10.f;
}
}

View File

@@ -0,0 +1,23 @@
//
// Created by Vlad on 6/9/2024.
//
#include "uml/prediction/Projectile.h"
#include <cmath>
namespace uml::prediction
{
Vector3 Projectile::CalculateVelocity(const float pitch, const float yaw) const
{
return Vector3::CreateVelocity(pitch, yaw, m_launchSpeed);
}
Vector3 Projectile::PredictPosition(const float pitch, const float yaw, const float time, const float gravity) const
{
auto currentPos = m_origin + Vector3::CreateVelocity(pitch, yaw, m_launchSpeed) * time;
currentPos.z -= (gravity * m_gravityScale) * std::pow(time, 2.f) * 0.5f;
return currentPos;
}
}

View File

@@ -0,0 +1,20 @@
//
// Created by Vlad on 6/9/2024.
//
#include "uml/prediction/Target.h"
#include <cmath>
namespace uml::prediction
{
Vector3 Target::PredictPosition(const float time, const float gravity) const
{
auto predicted = m_origin + m_velocity * time;
if (m_isAirborne)
predicted.z -= gravity * std::pow(time, 2.f) * 0.5f;
return predicted;
}
}