don't touch velocity in other systems and don't use snapTarget
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <QVector2D>
|
||||
|
||||
struct DynamicBodyComponent
|
||||
@@ -20,6 +18,4 @@ struct DynamicBodyComponent
|
||||
// --- written each tick by MovementIntentSystem, consumed by DynamicBodySystem ---
|
||||
QVector2D linearAcceleration;
|
||||
float angularAcceleration;
|
||||
std::optional<QVector2D> snapTarget; // set when snap-to-target will fire this tick
|
||||
};
|
||||
|
||||
|
||||
@@ -44,8 +44,7 @@ entt::entity EntityAdmin::spawnShip(QVector2D position, float hp, float maxHp,
|
||||
QVector2D(0.0f, 0.0f), // velocity
|
||||
0.0f, // angularVelocity
|
||||
QVector2D(0.0f, 0.0f), // linearAcceleration
|
||||
0.0f, // angularAcceleration
|
||||
std::nullopt // snapTarget
|
||||
0.0f // angularAcceleration
|
||||
});
|
||||
add<SensorRange>(entity, SensorRange{sensorRange});
|
||||
add<ShipIdentity>(entity, ShipIdentity{level, schematicId});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "DynamicBodySystem.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include <QVector2D>
|
||||
@@ -22,35 +23,27 @@ void DynamicBodySystem::tick(EntityAdmin& admin)
|
||||
admin.forEach<Position, Facing, DynamicBodyComponent>(
|
||||
[](entt::entity /*e*/, Position& pos, Facing& facing, DynamicBodyComponent& body)
|
||||
{
|
||||
// Integrate angular velocity and advance facing.
|
||||
// Integrate angular velocity, clamp to max rotation speed, then advance facing.
|
||||
body.angularVelocity += body.angularAcceleration;
|
||||
body.angularVelocity = std::max(-body.maxRotationSpeedPerTick,
|
||||
std::min(body.angularVelocity,
|
||||
body.maxRotationSpeedPerTick));
|
||||
facing.radians = wrapAngle(facing.radians + body.angularVelocity);
|
||||
|
||||
// Integrate linear velocity.
|
||||
// Integrate linear velocity and cap to max speed.
|
||||
body.velocity += body.linearAcceleration;
|
||||
|
||||
// Speed cap.
|
||||
const float speed = body.velocity.length();
|
||||
if (speed > body.maxSpeedPerTick)
|
||||
{
|
||||
body.velocity = body.velocity.normalized() * body.maxSpeedPerTick;
|
||||
}
|
||||
|
||||
// Snap to target or advance position.
|
||||
if (body.snapTarget.has_value())
|
||||
{
|
||||
pos.value = body.snapTarget.value();
|
||||
body.velocity = QVector2D(0.0f, 0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
pos.value += body.velocity;
|
||||
}
|
||||
// Advance position.
|
||||
pos.value += body.velocity;
|
||||
|
||||
// Reset per-tick fields so stale values don't linger if the intent
|
||||
// system is skipped for this entity in a future tick.
|
||||
body.linearAcceleration = QVector2D(0.0f, 0.0f);
|
||||
body.angularAcceleration = 0.0f;
|
||||
body.snapTarget = std::nullopt;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -27,11 +27,16 @@ void MovementIntentSystem::tick(EntityAdmin& admin)
|
||||
{
|
||||
if (intent.priority == 0)
|
||||
{
|
||||
body.velocity = QVector2D(0.0f, 0.0f);
|
||||
body.angularVelocity = 0.0f;
|
||||
body.linearAcceleration = QVector2D(0.0f, 0.0f);
|
||||
body.angularAcceleration = 0.0f;
|
||||
body.snapTarget = std::nullopt;
|
||||
// No movement intent: brake using available thrust.
|
||||
const float linearBraking = std::min(body.velocity.length(),
|
||||
body.maneuveringAccelerationPerTick);
|
||||
body.linearAcceleration = (body.velocity.length() > 0.0001f)
|
||||
? -body.velocity.normalized() * linearBraking
|
||||
: QVector2D(0.0f, 0.0f);
|
||||
|
||||
const float angBraking = std::min(std::abs(body.angularVelocity),
|
||||
body.angularAccelerationPerTick);
|
||||
body.angularAcceleration = (body.angularVelocity >= 0.0f) ? -angBraking : angBraking;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -40,9 +45,10 @@ void MovementIntentSystem::tick(EntityAdmin& admin)
|
||||
|
||||
if (dist < 0.001f)
|
||||
{
|
||||
body.velocity = QVector2D(0.0f, 0.0f);
|
||||
body.linearAcceleration = QVector2D(0.0f, 0.0f);
|
||||
body.snapTarget = std::nullopt;
|
||||
// Already at target: no new thrust. The ship drifts; it will
|
||||
// re-approach next tick once it has moved away.
|
||||
body.linearAcceleration = QVector2D(0.0f, 0.0f);
|
||||
body.angularAcceleration = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -55,9 +61,10 @@ void MovementIntentSystem::tick(EntityAdmin& admin)
|
||||
std::min(angleDiff, body.angularAccelerationPerTick));
|
||||
|
||||
float newAngVel = body.angularVelocity + rotDelta;
|
||||
newAngVel = std::max(-body.maxRotationSpeedPerTick,
|
||||
std::min(newAngVel, body.maxRotationSpeedPerTick));
|
||||
|
||||
// Overshoot prevention: if the accumulated angular velocity already
|
||||
// exceeds the remaining angle, snap it to exactly that angle so the
|
||||
// ship doesn't rotate past its heading.
|
||||
const bool sameSign = (newAngVel >= 0.0f) == (angleDiff >= 0.0f);
|
||||
if (sameSign && std::abs(newAngVel) > std::abs(angleDiff))
|
||||
{
|
||||
@@ -65,12 +72,13 @@ void MovementIntentSystem::tick(EntityAdmin& admin)
|
||||
}
|
||||
|
||||
body.angularAcceleration = newAngVel - body.angularVelocity;
|
||||
// DynamicBodySystem applies the clamp to maxRotationSpeedPerTick after
|
||||
// integrating, so we do not clamp here.
|
||||
|
||||
// --- Linear acceleration ---
|
||||
// Use the projected facing (after this tick's angular integration) so
|
||||
// that the main thruster aligns with where the ship will actually be
|
||||
// pointing when DynamicBodySystem applies the forces — matching the
|
||||
// original single-pass ordering.
|
||||
// pointing when DynamicBodySystem applies the forces.
|
||||
|
||||
const float projectedRadians = wrapAngle(facing.radians + newAngVel);
|
||||
const QVector2D facingVec(std::cos(projectedRadians), std::sin(projectedRadians));
|
||||
@@ -82,40 +90,20 @@ void MovementIntentSystem::tick(EntityAdmin& admin)
|
||||
? std::sqrt(2.0f * manAccel * dist)
|
||||
: body.maxSpeedPerTick;
|
||||
|
||||
const QVector2D desiredVel = delta.normalized() * desiredSpeed;
|
||||
const QVector2D velError = desiredVel - body.velocity;
|
||||
const QVector2D desiredVel = delta.normalized() * desiredSpeed;
|
||||
const QVector2D velError = desiredVel - body.velocity;
|
||||
|
||||
const float mainAligned = std::max(0.0f,
|
||||
QVector2D::dotProduct(velError, facingVec));
|
||||
const float mainApplied = std::min(mainAligned, body.mainAccelerationPerTick);
|
||||
const QVector2D mainDelta = facingVec * mainApplied;
|
||||
|
||||
const QVector2D remaining = velError - mainDelta;
|
||||
const float remainLen = remaining.length();
|
||||
const QVector2D remaining = velError - mainDelta;
|
||||
const float remainLen = remaining.length();
|
||||
const QVector2D maneuverDelta = (remainLen > manAccel)
|
||||
? remaining.normalized() * manAccel
|
||||
: remaining;
|
||||
|
||||
body.linearAcceleration = mainDelta + maneuverDelta;
|
||||
|
||||
// --- Snap detection ---
|
||||
// Compute the prospective velocity after integration + speed cap to
|
||||
// determine whether the ship will reach (or pass) the target this tick.
|
||||
|
||||
QVector2D prospectiveVel = body.velocity + body.linearAcceleration;
|
||||
const float prospectiveSpeed = prospectiveVel.length();
|
||||
if (prospectiveSpeed > body.maxSpeedPerTick)
|
||||
{
|
||||
prospectiveVel = prospectiveVel.normalized() * body.maxSpeedPerTick;
|
||||
}
|
||||
|
||||
if (dist <= prospectiveVel.length())
|
||||
{
|
||||
body.snapTarget = intent.target;
|
||||
}
|
||||
else
|
||||
{
|
||||
body.snapTarget = std::nullopt;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user