Fix lead indicator prediction & align projectile lifetime (client/server)
This commit is contained in:
parent
5fcb1d1234
commit
9eafcd27fb
@ -423,7 +423,7 @@ private:
|
|||||||
float len = worldForward.norm();
|
float len = worldForward.norm();
|
||||||
if (len > 1e-6f) worldForward /= len;
|
if (len > 1e-6f) worldForward /= len;
|
||||||
pr.vel = worldForward * velocity;
|
pr.vel = worldForward * velocity;
|
||||||
pr.lifeMs = 5000.0f;
|
pr.lifeMs = 15000.0f;
|
||||||
g_projectiles.push_back(pr);
|
g_projectiles.push_back(pr);
|
||||||
|
|
||||||
std::cout << "Server: Created projectile from player " << id_
|
std::cout << "Server: Created projectile from player " << id_
|
||||||
|
|||||||
114
src/Space.cpp
114
src/Space.cpp
@ -25,6 +25,13 @@
|
|||||||
#include "network/LocalClient.h"
|
#include "network/LocalClient.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// --- TEMP DEBUG SHOT LOG ---
|
||||||
|
#define DEBUG_SHOTLOG 1
|
||||||
|
#if DEBUG_SHOTLOG
|
||||||
|
static uint64_t g_lastShotLogMs = 0;
|
||||||
|
static uint64_t g_lastAimLogMs = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ZL
|
namespace ZL
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -1143,40 +1150,56 @@ namespace ZL
|
|||||||
Vector3f shooterPos = Environment::shipState.position + Environment::shipState.rotation * Vector3f{ 0.0f, 0.9f - 6.0f, 5.0f };
|
Vector3f shooterPos = Environment::shipState.position + Environment::shipState.rotation * Vector3f{ 0.0f, 0.9f - 6.0f, 5.0f };
|
||||||
|
|
||||||
// скорость цели в мире (вектор)
|
// скорость цели в мире (вектор)
|
||||||
Vector3f shooterVel = ForwardFromRotation(Environment::shipState.rotation) * Environment::shipState.velocity;
|
// Vector3f shooterVel = ForwardFromRotation(Environment::shipState.rotation) * Environment::shipState.velocity;
|
||||||
|
|
||||||
|
float shooterSpeed = std::abs(Environment::shipState.velocity);
|
||||||
|
// В нашей физике линейная скорость корабля всегда направлена по его forward (-Z).
|
||||||
|
// Когда игрок "наводится" на lead, forward (и скорость) становятся сонаправлены с выстрелом,
|
||||||
|
// поэтому эффективная скорость снаряда в мире ≈ muzzle + shipSpeed.
|
||||||
|
const float effectiveProjectileSpeed = projectileSpeed + shooterSpeed;
|
||||||
|
Vector3f shooterVel = Vector3f::Zero(); // скорость уже учтена в effectiveProjectileSpeed
|
||||||
Vector3f targetVel = ForwardFromRotation(st.rotation) * st.velocity;
|
Vector3f targetVel = ForwardFromRotation(st.rotation) * st.velocity;
|
||||||
|
|
||||||
|
// --- ВАЖНО: у нас remote state берется на now_ms - CLIENT_DELAY
|
||||||
|
// Значит shipWorld - это позиция ~0.5 сек назад.
|
||||||
|
// Для корректного lead нужно предсказать положение цели на "сейчас".
|
||||||
|
const float clientDelaySec = (float)CLIENT_DELAY / 1000.0f;
|
||||||
|
Vector3f targetPosNow = shipWorld + targetVel * clientDelaySec;
|
||||||
|
|
||||||
const float minTargetSpeed = 0.5f; // подобрать (в твоих единицах)
|
const float minTargetSpeed = 0.5f; // подобрать (в твоих единицах)
|
||||||
bool targetMoving = (targetVel.norm() > minTargetSpeed);
|
bool targetMoving = (targetVel.norm() > minTargetSpeed);
|
||||||
|
|
||||||
// альфа круга
|
// альфа круга
|
||||||
float leadAlpha = targetMoving ? 1.0f : 0.5f;
|
float leadAlpha = targetMoving ? 1.0f : 0.5f;
|
||||||
|
|
||||||
Vector3f leadWorld = shipWorld;
|
// Vector3f leadWorld = shipWorld;
|
||||||
|
Vector3f leadWorld = targetPosNow;
|
||||||
bool haveLead = false;
|
bool haveLead = false;
|
||||||
|
|
||||||
// чтобы круг не улетал далеко: максимум 4 секунды (подстроить под игру)
|
// Дистанцию лучше считать от реальной точки вылета
|
||||||
float distToTarget = (Environment::shipState.position - shipWorld).norm();
|
float distToTarget = (shooterPos - targetPosNow).norm();
|
||||||
float maxLeadTime = std::clamp((distToTarget / projectileSpeed) * 1.2f, 0.05f, 4.0f);
|
// Максимальное время перехвата ограничиваем жизнью пули
|
||||||
|
const float projectileLifeSec = (float)PROJECTILE_LIFE / 1000.0f;
|
||||||
|
float maxLeadTime = std::clamp((distToTarget / effectiveProjectileSpeed) * 1.25f, 0.01f, projectileLifeSec * 0.98f);
|
||||||
|
|
||||||
if (!targetMoving) {
|
if (!targetMoving) {
|
||||||
// Цель стоит: рисуем lead прямо на ней, но полупрозрачный
|
// Цель стоит: рисуем lead прямо на ней, но полупрозрачный
|
||||||
leadWorld = shipWorld;
|
leadWorld = targetPosNow;
|
||||||
haveLead = true;
|
haveLead = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
float tLead = 0.0f;
|
float tLead = 0.0f;
|
||||||
|
|
||||||
// 1) Пытаемся “правильное” решение перехвата
|
// 1) Пытаемся “правильное” решение перехвата
|
||||||
bool ok = SolveLeadInterceptTime(shooterPos, shooterVel, shipWorld, targetVel, projectileSpeed, tLead);
|
bool ok = SolveLeadInterceptTime(shooterPos, shooterVel, targetPosNow, targetVel, effectiveProjectileSpeed, tLead);
|
||||||
|
|
||||||
// 2) Если решения нет / оно плохое — fallback (чтобы круг не пропадал при пролёте "вбок")
|
// 2) Если решения нет / оно плохое — fallback (чтобы круг не пропадал при пролёте "вбок")
|
||||||
// Это ключевое изменение: lead всегда будет.
|
// Это ключевое изменение: lead всегда будет.
|
||||||
if (!ok || !(tLead > 0.0f) || tLead > maxLeadTime) {
|
if (!ok || !(tLead > 0.0f) || tLead > maxLeadTime) {
|
||||||
tLead = std::clamp(distToTarget / projectileSpeed, 0.05f, maxLeadTime);
|
tLead = std::clamp(distToTarget / effectiveProjectileSpeed, 0.05f, maxLeadTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
leadWorld = shipWorld + targetVel * tLead;
|
leadWorld = targetPosNow + targetVel * tLead;
|
||||||
haveLead = true;
|
haveLead = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1421,7 +1444,8 @@ namespace ZL
|
|||||||
firePressed = false;
|
firePressed = false;
|
||||||
if (now_ms - lastProjectileFireTime >= static_cast<uint64_t>(projectileCooldownMs)) {
|
if (now_ms - lastProjectileFireTime >= static_cast<uint64_t>(projectileCooldownMs)) {
|
||||||
lastProjectileFireTime = now_ms;
|
lastProjectileFireTime = now_ms;
|
||||||
const float projectileSpeed = 250.0f;
|
// const float projectileSpeed = 250.0f;
|
||||||
|
const float projectileSpeed = PROJECTILE_VELOCITY;
|
||||||
|
|
||||||
this->fireProjectiles();
|
this->fireProjectiles();
|
||||||
|
|
||||||
@ -1429,7 +1453,71 @@ namespace ZL
|
|||||||
Eigen::Vector3f worldForward = (Environment::shipState.rotation * localForward).normalized();
|
Eigen::Vector3f worldForward = (Environment::shipState.rotation * localForward).normalized();
|
||||||
|
|
||||||
Eigen::Vector3f centerPos = Environment::shipState.position +
|
Eigen::Vector3f centerPos = Environment::shipState.position +
|
||||||
Environment::shipState.rotation * Vector3f{ 0, 0.9f, 5.0f };
|
Environment::shipState.rotation * Vector3f{ 0, 0.9f - 6.0f, 5.0f };
|
||||||
|
|
||||||
|
#if DEBUG_SHOTLOG
|
||||||
|
// лог не чаще чем раз в 250мс, чтобы не заспамить консоль
|
||||||
|
if (now_ms - g_lastShotLogMs > 250) {
|
||||||
|
g_lastShotLogMs = now_ms;
|
||||||
|
|
||||||
|
// 1) кого мы сейчас таргетим (как в HUD)
|
||||||
|
int tid = pickTargetId();
|
||||||
|
if (tid >= 0 && remotePlayerStates.count(tid)) {
|
||||||
|
const ClientState& st = remotePlayerStates.at(tid);
|
||||||
|
|
||||||
|
// shooterPos ДОЛЖЕН совпадать с тем, что используется в lead (HUD)
|
||||||
|
Vector3f shooterPos = Environment::shipState.position
|
||||||
|
+ Environment::shipState.rotation * Vector3f{ 0.0f, 0.9f - 6.0f, 5.0f };
|
||||||
|
|
||||||
|
Vector3f targetPos = st.position;
|
||||||
|
Vector3f targetVel = ForwardFromRotation(st.rotation) * st.velocity;
|
||||||
|
|
||||||
|
const float clientDelaySec = (float)CLIENT_DELAY / 1000.0f;
|
||||||
|
Vector3f targetPosNow = targetPos + targetVel * clientDelaySec;
|
||||||
|
|
||||||
|
float shooterSpeed = std::abs(Environment::shipState.velocity);
|
||||||
|
float effSpeed = PROJECTILE_VELOCITY + shooterSpeed;
|
||||||
|
|
||||||
|
float dist = (shooterPos - targetPosNow).norm();
|
||||||
|
float projectileLifeSec = (float)PROJECTILE_LIFE / 1000.0f;
|
||||||
|
float tStraight = dist / max(1e-3f, effSpeed);
|
||||||
|
|
||||||
|
// попытка solve lead
|
||||||
|
float tLead = 0.0f;
|
||||||
|
bool ok = SolveLeadInterceptTime(shooterPos, Vector3f::Zero(), targetPosNow, targetVel, effSpeed, tLead);
|
||||||
|
|
||||||
|
// clamp как у HUD
|
||||||
|
float maxLeadTime = std::clamp(tStraight * 1.25f, 0.01f, projectileLifeSec * 0.98f);
|
||||||
|
if (!ok || !(tLead > 0.0f) || tLead > maxLeadTime) {
|
||||||
|
tLead = std::clamp(tStraight, 0.05f, maxLeadTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector3f leadWorld = targetPosNow + targetVel * tLead;
|
||||||
|
|
||||||
|
std::cout
|
||||||
|
<< "\n[SHOTLOG] now_ms=" << now_ms
|
||||||
|
<< " tid=" << tid
|
||||||
|
<< " dist=" << dist
|
||||||
|
<< " tStraight=" << tStraight
|
||||||
|
<< " tLead=" << tLead
|
||||||
|
<< " lifeSec=" << projectileLifeSec
|
||||||
|
<< " effSpeed=" << effSpeed
|
||||||
|
<< " shipSpeed=" << shooterSpeed
|
||||||
|
<< "\n shooterPos=(" << shooterPos.x() << "," << shooterPos.y() << "," << shooterPos.z() << ")"
|
||||||
|
<< "\n centerPos (sent)=(" << centerPos.x() << "," << centerPos.y() << "," << centerPos.z() << ")"
|
||||||
|
<< "\n targetPos(raw)=(" << targetPos.x() << "," << targetPos.y() << "," << targetPos.z() << ")"
|
||||||
|
<< "\n targetPosNow =(" << targetPosNow.x() << "," << targetPosNow.y() << "," << targetPosNow.z() << ")"
|
||||||
|
<< "\n targetVel=(" << targetVel.x() << "," << targetVel.y() << "," << targetVel.z() << ")"
|
||||||
|
<< "\n leadWorld=(" << leadWorld.x() << "," << leadWorld.y() << "," << leadWorld.z() << ")"
|
||||||
|
<< "\n okSolve=" << (ok ? "true" : "false")
|
||||||
|
<< "\n WILL_REACH=" << ((tStraight <= projectileLifeSec) ? "YES" : "NO (life too short)")
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::cout << "\n[SHOTLOG] now_ms=" << now_ms << " no target\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Eigen::Quaternionf q(Environment::shipState.rotation);
|
Eigen::Quaternionf q(Environment::shipState.rotation);
|
||||||
float speedToSend = projectileSpeed + Environment::shipState.velocity;
|
float speedToSend = projectileSpeed + Environment::shipState.velocity;
|
||||||
@ -1743,8 +1831,8 @@ namespace ZL
|
|||||||
const float size = 0.5f;
|
const float size = 0.5f;
|
||||||
for (const auto& pi : pending) {
|
for (const auto& pi : pending) {
|
||||||
const std::vector<Vector3f> localOffsets = {
|
const std::vector<Vector3f> localOffsets = {
|
||||||
Vector3f{ -1.5f, 0.9f, 5.0f },
|
Vector3f{ -1.5f, 0.9f - 6.0f, 5.0f },
|
||||||
Vector3f{ 1.5f, 0.9f, 5.0f }
|
Vector3f{ 1.5f, 0.9f - 6.0f, 5.0f }
|
||||||
};
|
};
|
||||||
|
|
||||||
Vector3f localForward = { 0, 0, -1 };
|
Vector3f localForward = { 0, 0, -1 };
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user