Changeset View
Changeset View
Standalone View
Standalone View
source/simulation2/components/CCmpUnitMotion_System.cpp
Show All 29 Lines | ||||||||||||
void CCmpUnitMotionManager::Register(CCmpUnitMotion* component, entity_id_t ent, bool formationController) | void CCmpUnitMotionManager::Register(CCmpUnitMotion* component, entity_id_t ent, bool formationController) | |||||||||||
{ | { | |||||||||||
MotionState state = { | MotionState state = { | |||||||||||
CmpPtr<ICmpPosition>(GetSimContext(), ent), | CmpPtr<ICmpPosition>(GetSimContext(), ent), | |||||||||||
component, | component, | |||||||||||
CFixedVector2D(), | CFixedVector2D(), | |||||||||||
CFixedVector2D(), | CFixedVector2D(), | |||||||||||
CFixedVector2D(fixed::FromInt(0), fixed::FromInt(0)), | ||||||||||||
fixed::Zero(), | fixed::Zero(), | |||||||||||
fixed::Zero(), | fixed::Zero(), | |||||||||||
false, | false, | |||||||||||
false | false | |||||||||||
}; | }; | |||||||||||
if (!formationController) | if (!formationController) | |||||||||||
ImarokUnsubmitted Not Done Inline Actions
Imarok: | ||||||||||||
m_Units.insert(ent, state); | m_Units.insert(ent, state); | |||||||||||
else | else | |||||||||||
m_FormationControllers.insert(ent, state); | m_FormationControllers.insert(ent, state); | |||||||||||
} | } | |||||||||||
void CCmpUnitMotionManager::Unregister(entity_id_t ent) | void CCmpUnitMotionManager::Unregister(entity_id_t ent) | |||||||||||
{ | { | |||||||||||
EntityMap<MotionState>::iterator it = m_Units.find(ent); | EntityMap<MotionState>::iterator it = m_Units.find(ent); | |||||||||||
Show All 23 Lines | ||||||||||||
void CCmpUnitMotionManager::MoveFormations(fixed dt) | void CCmpUnitMotionManager::MoveFormations(fixed dt) | |||||||||||
{ | { | |||||||||||
Move(m_FormationControllers, dt); | Move(m_FormationControllers, dt); | |||||||||||
} | } | |||||||||||
void CCmpUnitMotionManager::Move(EntityMap<MotionState>& ents, fixed dt) | void CCmpUnitMotionManager::Move(EntityMap<MotionState>& ents, fixed dt) | |||||||||||
{ | { | |||||||||||
m_MovingUnits.clear(); | PROFILE2("MotionMgr_Move"); | |||||||||||
std::set<std::vector<EntityMap<MotionState>::iterator>*> assigned; | ||||||||||||
Not Done Inline ActionsInclude set ? Does it have to be ordered? Stan: Include set ? Does it have to be ordered? | ||||||||||||
for (EntityMap<MotionState>::iterator it = ents.begin(); it != ents.end(); ++it) | for (EntityMap<MotionState>::iterator it = ents.begin(); it != ents.end(); ++it) | |||||||||||
{ | { | |||||||||||
it->second.cmpUnitMotion->PreMove(it->second); | it->second.tempIgnore = false; | |||||||||||
if (!it->second.needUpdate) | if (!it->second.cmpPosition->IsInWorld()) | |||||||||||
{ | ||||||||||||
it->second.needUpdate = false; | ||||||||||||
continue; | continue; | |||||||||||
m_MovingUnits.push_back(it); | } | |||||||||||
else | ||||||||||||
it->second.cmpUnitMotion->PreMove(it->second); | ||||||||||||
it->second.initialPos = it->second.cmpPosition->GetPosition2D(); | it->second.initialPos = it->second.cmpPosition->GetPosition2D(); | |||||||||||
it->second.initialAngle = it->second.cmpPosition->GetRotation().Y; | it->second.initialAngle = it->second.cmpPosition->GetRotation().Y; | |||||||||||
it->second.pos = it->second.initialPos; | it->second.pos = it->second.initialPos; | |||||||||||
it->second.angle = it->second.initialAngle; | it->second.angle = it->second.initialAngle; | |||||||||||
std::vector<EntityMap<MotionState>::iterator>& subdiv = m_MovingUnits.get(it->second.pos.X.ToInt_RoundToZero() / 20, it->second.pos.Y.ToInt_RoundToZero() / 20); | ||||||||||||
subdiv.emplace_back(it); | ||||||||||||
assigned.emplace(&subdiv); | ||||||||||||
} | } | |||||||||||
for (EntityMap<MotionState>::iterator& it : m_MovingUnits) | for (std::vector<EntityMap<MotionState>::iterator>* vec : assigned) | |||||||||||
for (EntityMap<MotionState>::iterator& it : *vec) | ||||||||||||
if (it->second.needUpdate) | ||||||||||||
it->second.cmpUnitMotion->Move(it->second, dt); | it->second.cmpUnitMotion->Move(it->second, dt); | |||||||||||
for (EntityMap<MotionState>::iterator& it : m_MovingUnits) | if (&ents == &m_Units) | |||||||||||
it->second.cmpUnitMotion->PostMove(it->second, dt); | { | |||||||||||
PROFILE2("MotionMgr_Pushing"); | ||||||||||||
for (std::vector<EntityMap<MotionState>::iterator>* vec : assigned) | ||||||||||||
{ | ||||||||||||
ENSURE(!vec->empty()); | ||||||||||||
std::vector<EntityMap<MotionState>::iterator>::iterator cit1 = vec->begin(); | ||||||||||||
do | ||||||||||||
{ | ||||||||||||
std::vector<EntityMap<MotionState>::iterator>::iterator cit2 = cit1; | ||||||||||||
while(++cit2 != vec->end()) | ||||||||||||
Push(**cit1, **cit2); | ||||||||||||
} | ||||||||||||
while(++cit1 != vec->end()); | ||||||||||||
Not Done Inline ActionsCan we resize the vector before pushing in it? Stan: Can we resize the vector before pushing in it? | ||||||||||||
Done Inline Actions? wraitii: ? | ||||||||||||
Not Done Inline ActionsSomething like Stan: Something like
https://www.cplusplus.com/reference/vector/vector/resize/ | ||||||||||||
Done Inline ActionsYou seem to be misunderstanding what "push" does here, it applies the pushing, there's no insertion to speak of. That's done in the "emplace" call above, and I believe I've assumed that it'll settle to a correct value fast enough. wraitii: You seem to be misunderstanding what "push" does here, it applies the pushing, there's no… | ||||||||||||
} | ||||||||||||
} | ||||||||||||
{ | ||||||||||||
PROFILE2("MotionMgr_PushAdjust"); | ||||||||||||
CmpPtr<ICmpPathfinder> cmpPathfinder(GetSystemEntity()); | ||||||||||||
for (std::vector<EntityMap<MotionState>::iterator>* vec : assigned) | ||||||||||||
Not Done Inline ActionsDon't use std::clamp, it's terrible with msvc, use our version. Stan: Don't use std::clamp, it's terrible with msvc, use our version. | ||||||||||||
{ | ||||||||||||
for (EntityMap<MotionState>::iterator& it : *vec) | ||||||||||||
{ | ||||||||||||
if (!it->second.needUpdate) | ||||||||||||
continue; | ||||||||||||
// Prevent pushed units from crossing static boundaries | ||||||||||||
// (we can assume that normal movement didn't push units into impassable terrain). | ||||||||||||
if ((it->second.push.X != entity_pos_t::Zero() || it->second.push.Y != entity_pos_t::Zero()) && | ||||||||||||
!cmpPathfinder->CheckMovement(it->second.cmpUnitMotion->GetObstructionFilter(), | ||||||||||||
it->second.pos.X, it->second.pos.Y, | ||||||||||||
it->second.pos.X + it->second.push.X, it->second.pos.Y + it->second.push.Y, | ||||||||||||
it->second.cmpUnitMotion->m_Clearance, it->second.cmpUnitMotion->m_PassClass)) | ||||||||||||
{ | ||||||||||||
// Mark them as obstructed - this could possibly be optimised | ||||||||||||
// perhaps it'd make more sense to mark the pushers as blocked. | ||||||||||||
it->second.wasObstructed = true; | ||||||||||||
it->second.wentStraight = false; | ||||||||||||
it->second.push = CFixedVector2D(); | ||||||||||||
} | ||||||||||||
// At this point, check if we still have significant path collisions. | ||||||||||||
Not Done Inline ActionsConstant somewhere? Freagarach: Constant somewhere? | ||||||||||||
Done Inline ActionsYeah there's a bunch of magic values to constantify in here. wraitii: Yeah there's a bunch of magic values to constantify in here. | ||||||||||||
for (EntityMap<MotionState>::iterator& otherIt : *vec) | ||||||||||||
{ | ||||||||||||
if (it->first == otherIt->first || otherIt->second.tempIgnore) | ||||||||||||
continue; | ||||||||||||
Not Done Inline Actions
Stan: # Can you do FromFloat(0.1) ?
# Shouldn't there be some kind of epsilon?
# constantify… | ||||||||||||
Done Inline Actions
wraitii: 1. Pretty sure that's risky since it could differ based on the compiler - haven't actually… | ||||||||||||
Done Inline Actions
Stan: 2. Forgot `entity_pos_t` is CFixed, thought it was float. Nvm then. | ||||||||||||
entity_pos_t dist = (it->second.cmpUnitMotion->m_Clearance + otherIt->second.cmpUnitMotion->m_Clearance) / 2; | ||||||||||||
if ((it->second.pos + it->second.push - otherIt->second.pos - otherIt->second.push).CompareLength(dist) > 0) | ||||||||||||
continue; | ||||||||||||
LOGWARNING("blocked %i", it->first); | ||||||||||||
// Block the current entity. This has cascading effects which aren't accounted for, | ||||||||||||
// the assumption being that the original position was valid, so this seems OK. | ||||||||||||
it->second.wasObstructed = true; | ||||||||||||
it->second.wentStraight = false; | ||||||||||||
// To prevent this entity from blocking the other entity (which can deadlock them), ignore it then. | ||||||||||||
it->second.tempIgnore = true; | ||||||||||||
it->second.push = CFixedVector2D(); | ||||||||||||
it->second.pos = (it->second.initialPos + it->second.pos) / 2; | ||||||||||||
break; | ||||||||||||
} | ||||||||||||
// Beyond some level of pushing, consider the unit to be obstructed and don't move it so much | ||||||||||||
if (it->second.push.CompareLength(fixed::FromInt(2)) >= 0) | ||||||||||||
{ | ||||||||||||
LOGWARNING("Too pushed %i", it->first); | ||||||||||||
it->second.wasObstructed = true; | ||||||||||||
it->second.wentStraight = false; | ||||||||||||
it->second.pos = it->second.initialPos + (it->second.pos - it->second.initialPos) / 2 + it->second.push / 2; | ||||||||||||
} | ||||||||||||
else | ||||||||||||
it->second.pos += it->second.push; | ||||||||||||
it->second.push = CFixedVector2D(); | ||||||||||||
} | ||||||||||||
} | ||||||||||||
} | ||||||||||||
{ | ||||||||||||
PROFILE2("MotionMgr_PostMove"); | ||||||||||||
for (EntityMap<MotionState>::value_type& data : ents) | ||||||||||||
{ | ||||||||||||
if (!data.second.needUpdate) | ||||||||||||
continue; | ||||||||||||
data.second.cmpUnitMotion->PostMove(data.second, dt); | ||||||||||||
} | ||||||||||||
} | ||||||||||||
for (std::vector<EntityMap<MotionState>::iterator>* vec : assigned) | ||||||||||||
vec->clear(); | ||||||||||||
} | ||||||||||||
void CCmpUnitMotionManager::Push(EntityMap<MotionState>::value_type& a, EntityMap<MotionState>::value_type& b) | ||||||||||||
{ | ||||||||||||
CFixedVector2D offset = a.second.pos - b.second.pos; | ||||||||||||
int movingPush = a.second.cmpUnitMotion->IsMoveRequested() + b.second.cmpUnitMotion->IsMoveRequested(); | ||||||||||||
entity_pos_t combinedClearance = (a.second.cmpUnitMotion->m_Clearance + b.second.cmpUnitMotion->m_Clearance) / 2; | ||||||||||||
// Distance reduces the pushing forces. Movement increases it. | ||||||||||||
entity_pos_t maxDist = combinedClearance * (3 + movingPush) / 2; | ||||||||||||
if (offset.CompareLength(maxDist) > 0) | ||||||||||||
return; | ||||||||||||
entity_pos_t offsetLength = offset.Length(); | ||||||||||||
// If the offset is small enough that precision would be problematic, pick an arbitrary vector instead. | ||||||||||||
if (offsetLength <= entity_pos_t::Epsilon() * 10) | ||||||||||||
{ | ||||||||||||
offset.X = entity_pos_t::FromInt(1); | ||||||||||||
offset.Y = entity_pos_t::FromInt(0); | ||||||||||||
offsetLength = entity_pos_t::FromInt(1); | ||||||||||||
} | ||||||||||||
else | ||||||||||||
{ | ||||||||||||
offset.X = offset.X / offsetLength; | ||||||||||||
offset.Y = offset.Y / offsetLength; | ||||||||||||
} | ||||||||||||
// TODO: ought to simulate in-flight pushing, e.g. perpendicular effect for units that cross each other's paths. | ||||||||||||
entity_pos_t distanceFactor = maxDist - offsetLength; | ||||||||||||
if (distanceFactor < fixed::FromInt(1)/10) | ||||||||||||
return; | ||||||||||||
a.second.needUpdate = true; | ||||||||||||
b.second.needUpdate = true; | ||||||||||||
offset = offset.Multiply(distanceFactor); | ||||||||||||
int factA = 5; | ||||||||||||
int factB = 5; | ||||||||||||
if (a.second.cmpUnitMotion->IsMoveRequested() && !b.second.cmpUnitMotion->IsMoveRequested()) | ||||||||||||
{ | ||||||||||||
factA = 8; | ||||||||||||
factB = 2; | ||||||||||||
} | ||||||||||||
else if (!a.second.cmpUnitMotion->IsMoveRequested() && b.second.cmpUnitMotion->IsMoveRequested()) | ||||||||||||
{ | ||||||||||||
factA = 2; | ||||||||||||
factB = 8; | ||||||||||||
} | ||||||||||||
a.second.push += offset * factA / 10; | ||||||||||||
b.second.push -= offset * factB / 10; | ||||||||||||
} | } |
Wildfire Games · Phabricator