fix Lead Indicator rendering when target off-screen
This commit is contained in:
parent
9eafcd27fb
commit
2f50dc1522
187
src/Space.cpp
187
src/Space.cpp
@ -25,13 +25,6 @@
|
||||
#include "network/LocalClient.h"
|
||||
#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
|
||||
{
|
||||
|
||||
@ -1153,16 +1146,16 @@ namespace ZL
|
||||
// Vector3f shooterVel = ForwardFromRotation(Environment::shipState.rotation) * Environment::shipState.velocity;
|
||||
|
||||
float shooterSpeed = std::abs(Environment::shipState.velocity);
|
||||
// В нашей физике линейная скорость корабля всегда направлена по его forward (-Z).
|
||||
// Когда игрок "наводится" на lead, forward (и скорость) становятся сонаправлены с выстрелом,
|
||||
// В нашей физике линейная скорость корабля всегда направлена по его forward (-Z)
|
||||
// Когда игрок наводится на lead indicator, forward (и скорость) становятся сонаправлены с выстрелом
|
||||
// поэтому эффективная скорость снаряда в мире ≈ muzzle + shipSpeed.
|
||||
const float effectiveProjectileSpeed = projectileSpeed + shooterSpeed;
|
||||
Vector3f shooterVel = Vector3f::Zero(); // скорость уже учтена в effectiveProjectileSpeed
|
||||
Vector3f targetVel = ForwardFromRotation(st.rotation) * st.velocity;
|
||||
|
||||
// --- ВАЖНО: у нас remote state берется на now_ms - CLIENT_DELAY
|
||||
// ВАЖНО: remote state берется на now_ms - CLIENT_DELAY
|
||||
// Значит shipWorld - это позиция ~0.5 сек назад.
|
||||
// Для корректного lead нужно предсказать положение цели на "сейчас".
|
||||
// Для корректного lead нужно предсказать положение цели на сейчас
|
||||
const float clientDelaySec = (float)CLIENT_DELAY / 1000.0f;
|
||||
Vector3f targetPosNow = shipWorld + targetVel * clientDelaySec;
|
||||
|
||||
@ -1172,7 +1165,6 @@ namespace ZL
|
||||
// альфа круга
|
||||
float leadAlpha = targetMoving ? 1.0f : 0.5f;
|
||||
|
||||
// Vector3f leadWorld = shipWorld;
|
||||
Vector3f leadWorld = targetPosNow;
|
||||
bool haveLead = false;
|
||||
|
||||
@ -1203,30 +1195,60 @@ namespace ZL
|
||||
haveLead = true;
|
||||
}
|
||||
|
||||
// 2) проекция
|
||||
// Проекция цели (для рамок/стрелки)
|
||||
float ndcX, ndcY, ndcZ, clipW;
|
||||
if (!projectToNDC(shipWorld, ndcX, ndcY, ndcZ, clipW)) return;
|
||||
|
||||
// behind camera?
|
||||
bool behind = (clipW <= 0.0f);
|
||||
|
||||
// on-screen check (NDC)
|
||||
bool onScreen = (!behind &&
|
||||
ndcX >= -1.0f && ndcX <= 1.0f &&
|
||||
ndcY >= -1.0f && ndcY <= 1.0f);
|
||||
|
||||
// 3) расстояние
|
||||
float dist = (Environment::shipState.position - shipWorld).norm();
|
||||
|
||||
// time for arrow bob
|
||||
float t = static_cast<float>(SDL_GetTicks64()) * 0.001f;
|
||||
|
||||
// 4) Настройки стиля
|
||||
// Проекция Lead
|
||||
float leadNdcX = 0.f, leadNdcY = 0.f, leadNdcZ = 0.f, leadClipW = 0.f;
|
||||
bool leadOnScreen = false;
|
||||
|
||||
if (haveLead) {
|
||||
if (projectToNDC(leadWorld, leadNdcX, leadNdcY, leadNdcZ, leadClipW) && leadClipW > 0.0f) {
|
||||
leadOnScreen =
|
||||
(leadNdcX >= -1.0f && leadNdcX <= 1.0f &&
|
||||
leadNdcY >= -1.0f && leadNdcY <= 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// Настройки HUD стилизация
|
||||
Eigen::Vector4f enemyColor(1.f, 0.f, 0.f, 1.f); // красный
|
||||
float thickness = 2.0f; // толщина линий (px)
|
||||
float z = 0.0f; // 2D слой
|
||||
|
||||
// 5) Если цель в кадре: рисуем скобки
|
||||
auto drawLeadRing2D = [&](float lx, float ly)
|
||||
{
|
||||
float distLead = (Environment::shipState.position - leadWorld).norm();
|
||||
float r = 30.0f / (distLead * 0.01f + 1.0f);
|
||||
r = std::clamp(r, 6.0f, 18.0f);
|
||||
|
||||
float thicknessPx = 2.5f;
|
||||
float innerR = max(1.0f, r - thicknessPx);
|
||||
float outerR = r + thicknessPx;
|
||||
|
||||
Eigen::Vector4f leadColor = enemyColor;
|
||||
leadColor.w() = leadAlpha;
|
||||
|
||||
renderer.RenderUniform4fv("uColor", leadColor.data());
|
||||
VertexDataStruct ring = MakeRing2D(lx, ly, innerR, outerR, 0.0f, 32, enemyColor);
|
||||
hudTempMesh.AssignFrom(ring);
|
||||
renderer.DrawVertexRenderStruct(hudTempMesh);
|
||||
|
||||
// вернуть цвет HUD обратно
|
||||
renderer.RenderUniform4fv("uColor", enemyColor.data());
|
||||
};
|
||||
|
||||
|
||||
// Цель в кадре: рамки
|
||||
if (onScreen)
|
||||
{
|
||||
// перевод NDC -> экран (в пикселях)
|
||||
@ -1275,40 +1297,9 @@ namespace ZL
|
||||
renderer.LoadIdentity();
|
||||
|
||||
renderer.EnableVertexAttribArray("vPosition");
|
||||
renderer.RenderUniform4fv("uColor", enemyColor.data());
|
||||
|
||||
Eigen::Vector4f hudColor = enemyColor;
|
||||
renderer.RenderUniform4fv("uColor", hudColor.data());
|
||||
|
||||
|
||||
if (haveLead) {
|
||||
float leadNdcX, leadNdcY, leadNdcZ, leadClipW;
|
||||
if (projectToNDC(leadWorld, leadNdcX, leadNdcY, leadNdcZ, leadClipW) && leadClipW > 0.0f) {
|
||||
if (leadNdcX >= -1 && leadNdcX <= 1 && leadNdcY >= -1 && leadNdcY <= 1) {
|
||||
float lx = (leadNdcX * 0.5f + 0.5f) * Environment::projectionWidth;
|
||||
float ly = (leadNdcY * 0.5f + 0.5f) * Environment::projectionHeight;
|
||||
|
||||
float distLead = (Environment::shipState.position - leadWorld).norm();
|
||||
float r = 30.0f / (distLead * 0.01f + 1.0f);
|
||||
r = std::clamp(r, 6.0f, 18.0f);
|
||||
|
||||
float thicknessPx = 2.5f;
|
||||
float innerR = max(1.0f, r - thicknessPx);
|
||||
float outerR = r + thicknessPx;
|
||||
Eigen::Vector4f leadColor = enemyColor;
|
||||
leadColor.w() = leadAlpha;
|
||||
renderer.RenderUniform4fv("uColor", leadColor.data());
|
||||
|
||||
VertexDataStruct ring = MakeRing2D(lx, ly, innerR, outerR, 0.0f, 32, enemyColor);
|
||||
hudTempMesh.AssignFrom(ring);
|
||||
renderer.DrawVertexRenderStruct(hudTempMesh);
|
||||
|
||||
renderer.RenderUniform4fv("uColor", hudColor.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderer.EnableVertexAttribArray("vPosition");
|
||||
|
||||
// рамки
|
||||
drawBar(left + cornerLen * 0.5f, top, cornerLen, thickness);
|
||||
drawBar(left, top - cornerLen * 0.5f, thickness, cornerLen);
|
||||
|
||||
@ -1321,9 +1312,14 @@ namespace ZL
|
||||
drawBar(right - cornerLen * 0.5f, bottom, cornerLen, thickness);
|
||||
drawBar(right, bottom + cornerLen * 0.5f, thickness, cornerLen);
|
||||
|
||||
// LEAD — независимо от рамок: если его точка на экране, рисуем
|
||||
if (haveLead && leadOnScreen) {
|
||||
float lx = (leadNdcX * 0.5f + 0.5f) * Environment::projectionWidth;
|
||||
float ly = (leadNdcY * 0.5f + 0.5f) * Environment::projectionHeight;
|
||||
drawLeadRing2D(lx, ly);
|
||||
}
|
||||
|
||||
renderer.DisableVertexAttribArray("vPosition");
|
||||
|
||||
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
@ -1335,6 +1331,8 @@ namespace ZL
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Цель вне экрана: стрелка
|
||||
float dirX = ndcX;
|
||||
float dirY = ndcY;
|
||||
|
||||
@ -1407,18 +1405,30 @@ namespace ZL
|
||||
renderer.PushMatrix();
|
||||
renderer.LoadIdentity();
|
||||
|
||||
renderer.EnableVertexAttribArray("vPosition");
|
||||
renderer.RenderUniform4fv("uColor", enemyColor.data());
|
||||
|
||||
// стрелка
|
||||
drawTri(tip, left, right);
|
||||
|
||||
float tailLen = 14.0f;
|
||||
float tailX = edgeX - dirX * 6.0f;
|
||||
float tailY = edgeY - dirY * 6.0f;
|
||||
|
||||
drawBar(tailX, tailY, max(thickness, tailLen), thickness);
|
||||
|
||||
// LEAD — рисуем даже когда цель вне экрана (если lead точка на экране)
|
||||
if (haveLead && leadOnScreen) {
|
||||
float lx = (leadNdcX * 0.5f + 0.5f) * Environment::projectionWidth;
|
||||
float ly = (leadNdcY * 0.5f + 0.5f) * Environment::projectionHeight;
|
||||
drawLeadRing2D(lx, ly);
|
||||
}
|
||||
|
||||
renderer.DisableVertexAttribArray("vPosition");
|
||||
renderer.PopMatrix();
|
||||
renderer.PopProjectionMatrix();
|
||||
renderer.shaderManager.PopShader();
|
||||
|
||||
// дистанция около стрелки
|
||||
{
|
||||
std::string d = std::to_string((int)dist) + "m";
|
||||
float tx = edgeX + px * 18.0f;
|
||||
@ -1444,7 +1454,6 @@ namespace ZL
|
||||
firePressed = false;
|
||||
if (now_ms - lastProjectileFireTime >= static_cast<uint64_t>(projectileCooldownMs)) {
|
||||
lastProjectileFireTime = now_ms;
|
||||
// const float projectileSpeed = 250.0f;
|
||||
const float projectileSpeed = PROJECTILE_VELOCITY;
|
||||
|
||||
this->fireProjectiles();
|
||||
@ -1455,70 +1464,6 @@ namespace ZL
|
||||
Eigen::Vector3f centerPos = Environment::shipState.position +
|
||||
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);
|
||||
float speedToSend = projectileSpeed + Environment::shipState.velocity;
|
||||
int shotCount = 2;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user