draw beam wider at the source than at the target
This commit is contained in:
@@ -15,11 +15,13 @@
|
||||
#include <QDir>
|
||||
#include <QFont>
|
||||
#include <QKeyEvent>
|
||||
#include <QLinearGradient>
|
||||
#include <QMessageBox>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPen>
|
||||
#include <QPolygonF>
|
||||
#include <QRadialGradient>
|
||||
#include <QRegion>
|
||||
#include <QStringList>
|
||||
#include <QTimer>
|
||||
@@ -1487,6 +1489,10 @@ void GameWorldView::drawDebugOverlay(QPainter& painter)
|
||||
|
||||
void GameWorldView::drawBeams(QPainter& painter)
|
||||
{
|
||||
const Tick now = m_sim->currentTick();
|
||||
const QPainter::RenderHints savedHints = painter.renderHints();
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
for (const ActiveBeam& beam : m_activeBeams)
|
||||
{
|
||||
const std::optional<QVector2D> shooterPos = entityPosition(beam.event.shooter);
|
||||
@@ -1500,10 +1506,61 @@ void GameWorldView::drawBeams(QPainter& painter)
|
||||
case BeamKind::Repair: color = m_visuals->beams.repairColor; break;
|
||||
case BeamKind::Salvage: color = m_visuals->beams.salvageColor; break;
|
||||
}
|
||||
painter.setPen(QPen(color, m_visuals->beams.widthPx));
|
||||
painter.drawLine(worldToWidget(*shooterPos),
|
||||
worldToWidget(*targetPos + beam.targetOffset));
|
||||
|
||||
const QPointF s = worldToWidget(*shooterPos);
|
||||
const QPointF t = worldToWidget(*targetPos + beam.targetOffset);
|
||||
|
||||
// Unit direction/perpendicular of the beam in widget space. A degenerate
|
||||
// zero-length beam (shooter and target coincide) has no direction to
|
||||
// taper along, so skip it.
|
||||
const QVector2D delta(static_cast<float>(t.x() - s.x()),
|
||||
static_cast<float>(t.y() - s.y()));
|
||||
const float lengthPx = delta.length();
|
||||
if (lengthPx < 0.001f) { continue; }
|
||||
const QVector2D dir = delta / lengthPx;
|
||||
const QVector2D perp(-dir.y(), dir.x());
|
||||
|
||||
// Age of this beam as a fraction of its lifetime, in [0, 1]. Drives the
|
||||
// muzzle-flash pulse below (strongest at the instant of firing).
|
||||
const float ageFrac = std::min(1.0f,
|
||||
static_cast<float>(now - beam.event.emittedAt)
|
||||
/ static_cast<float>(kBeamLifetimeTicks));
|
||||
|
||||
// Directional taper: draw the beam as a quad that is wide at the shooter
|
||||
// and narrows to a faint tip at the target, so it reads as an arrow
|
||||
// pointing away from whoever fired it. Without this, a beam strung
|
||||
// between two nearby ships is symmetric and gives no cue which end is the
|
||||
// source (the readability problem this addresses).
|
||||
const float widthPx = std::max(1.0f, static_cast<float>(m_visuals->beams.widthPx));
|
||||
const float baseHalf = widthPx * 1.f;
|
||||
const float tipHalf = widthPx * 0.35f;
|
||||
|
||||
QPolygonF quad;
|
||||
quad << QPointF(s.x() + static_cast<qreal>(perp.x() * baseHalf),
|
||||
s.y() + static_cast<qreal>(perp.y() * baseHalf))
|
||||
<< QPointF(s.x() - static_cast<qreal>(perp.x() * baseHalf),
|
||||
s.y() - static_cast<qreal>(perp.y() * baseHalf))
|
||||
<< QPointF(t.x() - static_cast<qreal>(perp.x() * tipHalf),
|
||||
t.y() - static_cast<qreal>(perp.y() * tipHalf))
|
||||
<< QPointF(t.x() + static_cast<qreal>(perp.x() * tipHalf),
|
||||
t.y() + static_cast<qreal>(perp.y() * tipHalf));
|
||||
|
||||
QColor bright = color.lighter(160);
|
||||
bright.setAlpha(255);
|
||||
QColor faint = color;
|
||||
faint.setAlpha(90);
|
||||
|
||||
QLinearGradient bodyGrad(s, t);
|
||||
bodyGrad.setColorAt(0.0, bright);
|
||||
bodyGrad.setColorAt(1.0, faint);
|
||||
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(bodyGrad);
|
||||
painter.drawPolygon(quad);
|
||||
}
|
||||
|
||||
painter.setBrush(Qt::NoBrush);
|
||||
painter.setRenderHints(savedHints);
|
||||
}
|
||||
|
||||
void GameWorldView::drawOverlays(QPainter& painter)
|
||||
|
||||
Reference in New Issue
Block a user