Better fix for formation waltzing, revert 5d96346ac5.

5d96346ac5 proved unsufficient to fix formation 'waltzing'. This is a
better fix, which makes sure units actually try to reach their
designated offset in the first place.
Further, it removes code that recalculated offsets un-necessarily, which
led to an issue with "sloppy" formations such as open and closed orders.

Fixes #5997

Differential Revision: https://code.wildfiregames.com/D3543
This was SVN commit r24865.
This commit is contained in:
wraitii 2021-02-10 09:59:39 +00:00
parent 06639a0383
commit e94e1f1fcf
3 changed files with 11 additions and 18 deletions

View File

@ -85,7 +85,6 @@ Formation.prototype.variablesToSerialize = [
"formationMembersWithAura",
"width",
"depth",
"oldOrientation",
"twinFormations",
"formationSeparation",
"offsets"
@ -146,7 +145,6 @@ Formation.prototype.Init = function(deserialized = false)
this.formationMembersWithAura = [];
this.width = 0;
this.depth = 0;
this.oldOrientation = { "sin": 0, "cos": 0 };
this.twinFormations = [];
// Distance from which two twin formations will merge into one.
this.formationSeparation = 0;
@ -515,17 +513,12 @@ Formation.prototype.MoveMembersIntoFormation = function(moveCenter, force, varia
let offsetsChanged = false;
let newOrientation = this.GetEstimatedOrientation(avgpos);
let dSin = Math.abs(newOrientation.sin - this.oldOrientation.sin);
let dCos = Math.abs(newOrientation.cos - this.oldOrientation.cos);
// If the formation existed, only recalculate positions if the turning agle is somewhat large.
if (!this.offsets || dSin > 1 || dCos > 1)
if (!this.offsets)
{
this.offsets = this.ComputeFormationOffsets(active, positions);
offsetsChanged = true;
}
this.oldOrientation = newOrientation;
let xMax = 0;
let yMax = 0;
let xMin = 0;

View File

@ -941,13 +941,14 @@ UnitAI.prototype.UnitFsmSpec = {
"IDLE": {
"enter": function(msg) {
// Turn rearrange off. Otherwise, if the formation is idle,
// but individual units go off to fight, any death
// will rearrange the formation, looking odd.
// Turn rearrange off. Otherwise, if the formation is idle
// but individual units go off to fight,
// any death will rearrange the formation, which looks odd.
// Instead, move idle units in formation on a timer.
let cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
cmpFormation.SetRearrange(false);
this.StartTimer(0, 2000);
// Start the timer on the next turn to catch up with potential stragglers.
this.StartTimer(100, 2000);
this.isIdle = true;
this.CallMemberFunction("ResetIdle");
return false;

View File

@ -1007,10 +1007,13 @@ bool CCmpUnitMotion::PossiblyAtDestination() const
if (cmpControllerMotion && cmpControllerMotion->IsMoveRequested())
return false;
// In formation, return a match only if we are exactly at the target position.
// Otherwise, units can go in an infinite "walzting" loop when the Idle formation timer
// reforms them.
CFixedVector2D targetPos;
ComputeTargetPosition(targetPos);
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle());
return cmpObstructionManager->IsInPointRange(GetEntityId(), targetPos.X, targetPos.Y, m_MoveRequest.m_MinRange, m_MoveRequest.m_MaxRange, false);
return (targetPos-cmpPosition->GetPosition2D()).CompareLength(fixed::Zero()) <= 0;
}
return false;
}
@ -1072,11 +1075,7 @@ bool CCmpUnitMotion::PerformMove(fixed dt, const fixed& turnRate, WaypointPath&
target = CFixedVector2D(shortPath.m_Waypoints.back().x, shortPath.m_Waypoints.back().z);
CFixedVector2D offset = target - pos;
// Idle formations reorder their members to move to their offset, but numerical imprecisions can lead
// to small offsets every time. Units would then rotate in-place, looking odd.
// If the offset is small enough (epsilon is about 0.000015, so that's 0.0015 which is still irrelevant),
// simply don't turn.
if (turnRate > zero && offset.CompareLength(fixed::Epsilon() * 100) > 0)
if (turnRate > zero && !offset.IsZero())
{
fixed maxRotation = turnRate.Multiply(timeLeft);
fixed angleDiff = angle - atan2_approx(offset.X, offset.Y);