Skip to content

Commit 695a803

Browse files
committed
Adds engine traits for projectile prediction
Implements engine-specific traits for projectile and target position prediction. Each trait encapsulates logic tailored to a specific game engine (IW, OpenGL, Unity), accounting for engine-specific coordinate systems and calculations. This allows for accurate projectile prediction across different game environments.
1 parent d12b236 commit 695a803

File tree

3 files changed

+238
-0
lines changed

3 files changed

+238
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//
2+
// Created by Vlad on 8/6/2025.
3+
//
4+
#pragma once
5+
6+
#include "omath/engines/iw_engine/formulas.hpp"
7+
#include "omath/projectile_prediction/projectile.hpp"
8+
#include "omath/projectile_prediction/target.hpp"
9+
#include <optional>
10+
11+
namespace omath::projectile_prediction::traits
12+
{
13+
class IwEngineTrait final
14+
{
15+
public:
16+
constexpr static Vector3<float> predict_projectile_position(const Projectile& projectile, const float pitch,
17+
const float yaw, const float time,
18+
const float gravity) noexcept
19+
{
20+
auto current_pos = projectile.m_origin
21+
+ iw_engine::forward_vector({iw_engine::PitchAngle::from_degrees(-pitch),
22+
iw_engine::YawAngle::from_degrees(yaw),
23+
iw_engine::RollAngle::from_degrees(0)})
24+
* projectile.m_launch_speed * time;
25+
current_pos.z -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f;
26+
27+
return current_pos;
28+
}
29+
[[nodiscard]]
30+
static constexpr Vector3<float> predict_target_position(const Target& target, const float time,
31+
const float gravity) noexcept
32+
{
33+
auto predicted = target.m_origin + target.m_velocity * time;
34+
35+
if (target.m_is_airborne)
36+
predicted.z -= gravity * (time * time) * 0.5f;
37+
38+
return predicted;
39+
}
40+
[[nodiscard]]
41+
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
42+
{
43+
return std::sqrt(delta.x * delta.x + delta.y * delta.y);
44+
}
45+
46+
[[nodiscard]]
47+
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
48+
{
49+
return vec.z;
50+
}
51+
52+
[[nodiscard]]
53+
static Vector3<float> calc_viewpoint_from_angles(const Projectile& projectile,
54+
Vector3<float> predicted_target_position,
55+
const std::optional<float> projectile_pitch) noexcept
56+
{
57+
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
58+
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
59+
60+
return {predicted_target_position.x, predicted_target_position.y, projectile.m_origin.z + height};
61+
}
62+
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
63+
// 89 look up, -89 look down
64+
[[nodiscard]]
65+
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
66+
{
67+
const auto distance = origin.distance_to(view_to);
68+
const auto delta = view_to - origin;
69+
70+
return angles::radians_to_degrees(std::asin(delta.z / distance));
71+
}
72+
[[nodiscard]]
73+
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
74+
{
75+
const auto delta = view_to - origin;
76+
77+
return angles::radians_to_degrees(std::atan2(delta.y, delta.x));
78+
};
79+
};
80+
} // namespace omath::projectile_prediction::traits
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//
2+
// Created by Vlad on 8/6/2025.
3+
//
4+
#pragma once
5+
#include "omath/engines/opengl_engine/formulas.hpp"
6+
#include "omath/projectile_prediction/projectile.hpp"
7+
#include "omath/projectile_prediction/target.hpp"
8+
#include <optional>
9+
10+
namespace omath::projectile_prediction::traits
11+
{
12+
class OpenGlEngineTrait final
13+
{
14+
public:
15+
constexpr static Vector3<float> predict_projectile_position(const Projectile& projectile, const float pitch,
16+
const float yaw, const float time,
17+
const float gravity) noexcept
18+
{
19+
auto current_pos = projectile.m_origin
20+
+ opengl_engine::forward_vector({opengl_engine::PitchAngle::from_degrees(-pitch),
21+
opengl_engine::YawAngle::from_degrees(yaw),
22+
opengl_engine::RollAngle::from_degrees(0)})
23+
* projectile.m_launch_speed * time;
24+
current_pos.y -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f;
25+
26+
return current_pos;
27+
}
28+
[[nodiscard]]
29+
static constexpr Vector3<float> predict_target_position(const Target& target, const float time,
30+
const float gravity) noexcept
31+
{
32+
auto predicted = target.m_origin + target.m_velocity * time;
33+
34+
if (target.m_is_airborne)
35+
predicted.y -= gravity * (time * time) * 0.5f;
36+
37+
return predicted;
38+
}
39+
[[nodiscard]]
40+
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
41+
{
42+
return std::sqrt(delta.x * delta.x + delta.z * delta.z);
43+
}
44+
45+
[[nodiscard]]
46+
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
47+
{
48+
return vec.z;
49+
}
50+
51+
[[nodiscard]]
52+
static Vector3<float> calc_viewpoint_from_angles(const Projectile& projectile,
53+
Vector3<float> predicted_target_position,
54+
const std::optional<float> projectile_pitch) noexcept
55+
{
56+
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
57+
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
58+
59+
return {predicted_target_position.x, predicted_target_position.y + height, projectile.m_origin.z};
60+
}
61+
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
62+
// 89 look up, -89 look down
63+
[[nodiscard]]
64+
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
65+
{
66+
const auto distance = origin.distance_to(view_to);
67+
const auto delta = view_to - origin;
68+
69+
return angles::radians_to_degrees(std::asin(delta.y / distance));
70+
}
71+
[[nodiscard]]
72+
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
73+
{
74+
const auto delta = view_to - origin;
75+
76+
return angles::radians_to_degrees(std::atan2(delta.z, delta.x));
77+
};
78+
};
79+
} // namespace omath::projectile_prediction::traits
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//
2+
// Created by Vlad on 8/6/2025.
3+
//
4+
#pragma once
5+
#include "omath/engines/unity_engine/formulas.hpp"
6+
#include "omath/projectile_prediction/projectile.hpp"
7+
#include "omath/projectile_prediction/target.hpp"
8+
#include <optional>
9+
10+
namespace omath::projectile_prediction::traits
11+
{
12+
class UnityEngineTrait final
13+
{
14+
public:
15+
constexpr static Vector3<float> predict_projectile_position(const Projectile& projectile, const float pitch,
16+
const float yaw, const float time,
17+
const float gravity) noexcept
18+
{
19+
auto current_pos = projectile.m_origin
20+
+ unity_engine::forward_vector({unity_engine::PitchAngle::from_degrees(-pitch),
21+
unity_engine::YawAngle::from_degrees(yaw),
22+
unity_engine::RollAngle::from_degrees(0)})
23+
* projectile.m_launch_speed * time;
24+
current_pos.y -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f;
25+
26+
return current_pos;
27+
}
28+
[[nodiscard]]
29+
static constexpr Vector3<float> predict_target_position(const Target& target, const float time,
30+
const float gravity) noexcept
31+
{
32+
auto predicted = target.m_origin + target.m_velocity * time;
33+
34+
if (target.m_is_airborne)
35+
predicted.y -= gravity * (time * time) * 0.5f;
36+
37+
return predicted;
38+
}
39+
[[nodiscard]]
40+
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
41+
{
42+
return std::sqrt(delta.x * delta.x + delta.z * delta.z);
43+
}
44+
45+
[[nodiscard]]
46+
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
47+
{
48+
return vec.z;
49+
}
50+
51+
[[nodiscard]]
52+
static Vector3<float> calc_viewpoint_from_angles(const Projectile& projectile,
53+
Vector3<float> predicted_target_position,
54+
const std::optional<float> projectile_pitch) noexcept
55+
{
56+
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
57+
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
58+
59+
return {predicted_target_position.x, predicted_target_position.y + height, projectile.m_origin.z};
60+
}
61+
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
62+
// 89 look up, -89 look down
63+
[[nodiscard]]
64+
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
65+
{
66+
const auto distance = origin.distance_to(view_to);
67+
const auto delta = view_to - origin;
68+
69+
return angles::radians_to_degrees(std::asin(delta.y / distance));
70+
}
71+
[[nodiscard]]
72+
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
73+
{
74+
const auto delta = view_to - origin;
75+
76+
return angles::radians_to_degrees(std::atan2(delta.z, delta.x));
77+
};
78+
};
79+
} // namespace omath::projectile_prediction::traits

0 commit comments

Comments
 (0)